# Chainstack Cloud release notes and product updates Source: https://docs.chainstack.com/changelog Latest product updates, new protocol support, feature releases, and platform improvements from the Chainstack team across all supported networks. **Chainstack updates: November 2, 2026** by Ake **Node credential rotation**. You can now rotate a node's credentials on Chainstack without downtime. Generate new credentials, update your applications while the old and new credentials both work, and then deactivate the old ones. A rotation replaces the node's auth token, username and password, and gRPC x-token together. See [Rotate node credentials](/docs/manage-your-node#rotate-node-credentials). [Read more](/changelog/chainstack-updates-november-2-2026) **Chainstack updates: October 27, 2026** by Ake **Avalanche**. Chainstack upgraded Avalanche [Global Nodes](/docs/global-elastic-node) on Mainnet and Fuji Testnet to [AvalancheGo v1.15.2](https://github.com/ava-labs/avalanchego/releases/tag/v1.15.2). This version removes the C-Chain method `avax.getAtomicTxStatus`, deprecated since v1.15.0, so use `avax.getAtomicTx` instead. `platform.getCurrentValidators` now returns `restakedValidationRewards` and `restakedDelegateeRewards` for auto-renewed validators. See [Avalanche Helicon network upgrade](/docs/avalanche-helicon-upgrade). [Read more](/changelog/chainstack-updates-october-27-2026) **Chainstack updates: October 23, 2026** by Ake **MCP Server**. Every [Chainstack MCP server](https://mcp.chainstack.com) tool now has a readable title and declares how it behaves, so your AI client knows when to ask you before a call. [Read more](/changelog/chainstack-updates-october-23-2026) **Chainstack updates: October 19, 2026** by Ake **Protocols**. Zcash is now available on Chainstack Global Nodes. Zcash is a privacy-focused UTXO L1 with shielded transactions and a Bitcoin-style JSON-RPC API with Zcash-specific `z_*` methods for shielded pool state. You can deploy [Global Nodes](/docs/global-elastic-node) for Zcash Mainnet and Testnet, in Full mode, with transparent transaction history available back to the first block. See [Networks](/docs/protocols-networks). [Read more](/changelog/chainstack-updates-october-19-2026) **Chainstack updates: October 16, 2026** by Ake **Protocols**. X Layer is now available on Chainstack Global Nodes. X Layer is OKX's Ethereum L2 built on the OP Stack. You can deploy [Global Nodes](/docs/global-elastic-node) for X Layer Mainnet and Testnet, in Archive mode with debug and trace. [Dedicated Nodes](/docs/dedicated-node) for X Layer remain available. See [Networks](/docs/protocols-networks). [Read more](/changelog/chainstack-updates-october-16-2026) **Chainstack updates: October 12, 2026** by Ake **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) now returns every project in organizations with more than 100, and tells your AI agent when a [faucet](/docs/faucets) top-up is done, so the agent doesn't request it again and hit the 24-hour cooldown. [Read more](/changelog/chainstack-updates-october-12-2026) **Chainstack updates: October 11, 2026** by Ake **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) adds five tools that manage [access rules](/docs/access-rules), so your AI agent can restrict a [Global Node](/docs/global-elastic-node) to allowed IP addresses or origins. Validation errors from the Chainstack API now name the rejected field and the reason. [Read more](/changelog/chainstack-updates-october-11-2026) **Chainstack updates: October 8, 2026** by Ake **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Ink Testnet and Celo Sepolia Testnet. **Starknet**. Chainstack upgraded Starknet [Global Nodes](/docs/global-elastic-node) on Mainnet and Sepolia Testnet to [Pathfinder v0.24.2](https://github.com/software-mansion/pathfinder/releases/tag/v0.24.2). [`starknet_estimateMessageFee`](/reference/starknet-starknetestimatemessagefee) now returns an `overall_fee` of at least 50,000,000,000,000 wei (0.00005 ETH). When the gas amounts multiplied by their prices add up to less than that, `overall_fee` is exactly 50,000,000,000,000 wei. [Read more](/changelog/chainstack-updates-october-8-2026) **Chainstack updates: October 7, 2026** by Alexandr **Multichain API**. You can now use one auth token across the [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) you attach to a multichain endpoint, instead of one auth token per node. Available on every plan, including the Developer plan. See [Use Multichain API](/docs/multichain-api). [Read more](/changelog/chainstack-updates-october-7-2026) **Chainstack updates: October 6, 2026** by Ake **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) moves to the latest FastMCP 3.x and MCP SDK releases, resolving all open dependency security advisories. Idle sessions now close after 30 minutes; spec-compliant clients re-initialize automatically. [Read more](/changelog/chainstack-updates-october-6-2026) **Chainstack updates: October 5, 2026** by Ake **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Ink Mainnet. [Read more](/changelog/chainstack-updates-october-5-2026) **Chainstack updates: October 1, 2026** by Ake **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Elysium Testnet. [Read more](/changelog/chainstack-updates-october-1-2026) **Chainstack updates: September 30, 2026** by Vladimir **Add-ons**. On Arbitrum, Base, Arc, and Robinhood Chain, MEV protection is renamed to [optimized submission](/docs/optimized-submission). Nodes that had MEV protection enabled now have optimized submission enabled. [Read more](/changelog/chainstack-updates-september-30-2026) **Chainstack updates: September 28, 2026** by Vladimir **Protocols**. Sui Mainnet Global Nodes now serve the GraphQL RPC API at `/graphql`, alongside gRPC and JSON-RPC on the same endpoint and auth token. Use GraphQL for filtered, paginated reads — transactions by address, events by type, objects by owner and type — that have no gRPC equivalent. Each request is 1 RU, the same as gRPC. Point lookups and unfiltered lists reach back to January 2026, and that window grows with the chain; filtered transaction and event queries cover a rolling window of up to 90 days. See [Sui GraphQL endpoint](/docs/sui-graphql-endpoint). [Read more](/changelog/chainstack-updates-september-28-2026) **Chainstack updates: September 16, 2026** by Ake **Protocols**. Arc Mainnet is now available on Chainstack. Arc is Circle's USDC-native, EVM-compatible L1 for stablecoin payments. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Arc Mainnet, in Full and Archive modes with debug and trace. **Nodes**. Global Nodes now show an **Extended** label for Full nodes that keep more ledger history than a standard full node — more retention than Full, without being a complete Archive. It adds no pricing or request-unit change. [Read more](/changelog/chainstack-updates-september-16-2026) **Chainstack updates: September 11, 2026** by Vladimir **Protocols**. Now, you can deploy Bitcoin Testnet4 for [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node). [Read more](/changelog/chainstack-updates-september-11-2026) **Chainstack updates: September 4, 2026** by Ake **Protocols**. Chainstack TON Global Nodes now serve TON API v4 — REST and WebSocket — alongside v2 and v3 on the same endpoint and auth token, on Mainnet and Testnet. v4 adds block-pinned, cacheable URLs for blocks, account state, and get-method calls, and WebSocket streams of new blocks and of the accounts changed in each block. See [TON API v4](/docs/ton-choosing-v2-or-v3#ton-api-v4). [Read more](/changelog/chainstack-updates-september-4-2026) **Chainstack updates: September 3, 2026** by Ake **Starknet**. Chainstack upgraded Starknet nodes to [Pathfinder v0.24.0](https://github.com/equilibriumco/pathfinder/releases/tag/v0.24.0), which adds support for Starknet 0.14.4. Pathfinder 0.24.0 upstream requires WebSocket clients to answer server-initiated pings, but on Chainstack this needs no action from you — your endpoint keeps the connection alive on your behalf. The fallback path used by `starknet_traceTransaction` and `starknet_traceBlockTransactions` is now bounded at 30 seconds. [Read more](/changelog/chainstack-updates-september-3-2026) **Chainstack updates: August 20, 2026** by Ake **Protocols**. Chainstack now supports Stellar — a public blockchain for fast, low-cost cross-border payments and asset tokenization. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Stellar Mainnet and Testnet. [Read more](/changelog/chainstack-updates-august-20-2026) **Chainstack updates: August 7, 2026** by Ake **Protocols**. Chainstack now supports XRP Ledger — a payments-focused L1 with its own JSON-RPC and WebSocket API rather than the Ethereum `eth_*` interface. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for XRP Ledger Mainnet and Testnet, in Full mode. **Faucet**. The Chainstack faucet is now built into the console. Open [**Faucet**](https://console.chainstack.com/faucet) to request testnet tokens without a separate sign-in, and save wallets with a recurring auto-claim. [Read more](/changelog/chainstack-updates-august-7-2026) **Chainstack updates: August 4, 2026** by Ake **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for World Chain Mainnet and Stable Mainnet. [Read more](/changelog/chainstack-updates-august-4-2026) **Chainstack updates: July 27, 2026** by Ake **Protocols**. Chainstack now supports Arc — Circle's USDC-native, EVM-compatible L1 for stablecoin payments. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Arc testnet, in Full and Archive modes with debug and trace. **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) testnet faucet now supports Robinhood Chain testnet (`request_testnet_funds` with `network="robinhood"`), replacing the retired Scroll Sepolia. [Read more](/changelog/chainstack-updates-july-27-2026) **Chainstack updates: July 23, 2026** by Ake **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) testnet faucet now advertises its supported networks to your agent and accepts common aliases (`ethereum-sepolia` → `sepolia`, `solana-devnet` → `solana`), so testnet fund requests land on the first try instead of failing on a wrong-network name. [Read more](/changelog/chainstack-updates-july-23-2026) **Chainstack updates: July 22, 2026** by Ake **Protocols**. Sui gRPC endpoints now support gRPC-Web, so the official Sui TypeScript SDK (`@mysten/sui`) works against your Chainstack nodes over HTTPS on Mainnet and Testnet — including from the browser, with only your node URL. Native gRPC and JSON-RPC are unchanged. See [Sui tooling](/docs/sui-tooling#grpc-from-code) for the SDK setup, [Sui gRPC endpoint](/docs/sui-grpc-endpoint), and [migrating from JSON-RPC to gRPC](/docs/sui-migrate-json-rpc-to-grpc). [Read more](/changelog/chainstack-updates-july-22-2026) **Chainstack updates: July 21, 2026** by Ake **Starknet**. Chainstack upgraded Starknet nodes to [Pathfinder v0.23.0](https://github.com/eqlabs/pathfinder/releases/tag/v0.23.0) — a breaking change to JSON-RPC API version support: versions 0.6, 0.7, and 0.8 are removed, and the default `/` endpoint now serves version 0.9. Pin `/rpc/v0_9` or `/rpc/v0_10` before the upgrade reaches your node. [Read more](/changelog/chainstack-updates-july-21-2026) **Chainstack updates: July 17, 2026** by Ake **Protocols**. Chainstack now supports Robinhood Chain — an Ethereum L2 (Arbitrum Orbit, Nitro client) for tokenized stocks and real-world assets. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Robinhood Chain Mainnet and Testnet, in Full and Archive modes with debug and trace. [Read more](/changelog/chainstack-updates-july-17-2026) **Chainstack updates: July 6, 2026** by Ake **MCP Server**. Migrating from Alchemy now reads your usage via the [Alchemy CLI](https://www.alchemy.com/docs/alchemy-cli) (`alchemy usage summary`) — the agent pulls your spend and compute-unit volume to estimate your Chainstack savings. [Read more](/changelog/chainstack-updates-july-6-2026) **Chainstack updates: June 17, 2026** by Ake **Protocols**. TRON gRPC now serves the [`protocol.WalletSolidity`](/docs/tron-tooling#available-services) service for solidified (confirmed) data, alongside `protocol.Wallet`. Both run on the same `:443` endpoint — select the one you need by the service (stub) in your generated client. Available on TRON mainnet and Nile testnet. [Read more](/changelog/chainstack-updates-june-17-2026) **Chainstack updates: June 11, 2026** by Ake **Add-ons**. [Warp transactions](/docs/warp-transactions) and [MEV protection](/docs/mev-protection) are now Chainstack [node add-ons](/docs/add-ons) rather than baked-in node toggles. You install and manage them per node from the node's **Add-ons** tab. [Read more](/changelog/chainstack-updates-june-11-2026) **Chainstack updates: June 10, 2026** by Ake **Add-ons**. Higher concurrent stream limits for the [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin). The \$49/month tier now includes 2 concurrent streams (up from 1) and the \$149/month tier now includes 7 concurrent streams (up from 5). [Read more](/changelog/chainstack-updates-june-10-2026) **Chainstack updates: June 2, 2026** by Ake **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) now migrates your project to Chainstack from another RPC provider — QuickNode, Alchemy, or any other — assessing your usage, estimating your savings, and repointing endpoints from inside your AI coding agent. [Read more](/changelog/chainstack-updates-june-2-2026) **Chainstack updates: May 28, 2026** by Ake **MCP Server**. Every Chainstack MCP server tool now publishes a typed `outputSchema`, so agents can consume structured results without guessing field names. List tools also emit `structuredContent` — an additive, non-breaking change. [Read more](/changelog/chainstack-updates-may-28-2026) **Chainstack updates: May 26, 2026** by Ake **Protocols**. **Hyperliquid `/nanoreth` is now on testnet** as well as mainnet. Both `/evm` (default, hl-node-compliant — system transactions stripped from block-shaped responses) and `/nanoreth` (system transactions included) paths exist by default on every Chainstack [Hyperliquid](https://chainstack.com/build-better-with-hyperliquid/) node, mainnet and testnet. Append the path you need to your endpoint URL. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) for the per-method behavior matrix. [Read more](/changelog/chainstack-updates-may-26-2026) **Chainstack updates: May 22, 2026** by Ake **MCP Server**. The Chainstack MCP server now supports all three Google Antigravity 2.0 surfaces — Antigravity CLI, the desktop app, and the IDE — on the [discovery page](https://mcp.chainstack.com). [Read more](/changelog/chainstack-updates-may-22-2026) **Chainstack updates: May 18, 2026** by Ake **Protocols**. **Flashblocks on Optimism** is live on every Chainstack [Optimism](https://chainstack.com/build-better-with-optimism/) mainnet node. The Optimism sequencer streams Flashblocks every 250ms, and Chainstack Optimism nodes expose the preconfirmation state through standard JSON-RPC calls with the `pending` block tag. See [Flashblocks on Optimism](/docs/flashblocks-on-optimism) for the full guide, including the Optimism vs. Base differences and performance expectations. [Read more](/changelog/chainstack-updates-may-18-2026) **Chainstack updates: May 12, 2026** by Ake **MCP Server**. Chainstack MCP tools now advertise standard MCP annotations (`readOnlyHint`, `destructiveHint`, `openWorldHint`) so clients skip prompts on read-only calls and require approval on writes, and the server reports its real release version on the `initialize` handshake. [Read more](/changelog/chainstack-updates-may-12-2026) **Chainstack updates: April 29, 2026** by Ake **Protocols**. **Hyperliquid `/nanoreth`** is live on every Chainstack [Hyperliquid](https://chainstack.com/build-better-with-hyperliquid/) mainnet node. Append `/nanoreth` to your endpoint URL to get system transactions included in `eth_getBlockByNumber`, `eth_getBlockReceipts`, and direct hash lookups. The default `/evm` path is unchanged — hl-node-compliant, system transactions stripped from block-shaped responses. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) for the per-method behavior matrix. [Read more](/changelog/chainstack-updates-april-29-2026) **Chainstack updates: April 27, 2026** by Ake **MCP Server**. The [Pi](https://pi.dev/) coding agent can now install the Chainstack skill (skill-only — Pi rejects MCP servers by design), and the discovery page description now covers pricing and testnet funds. [Read more](/changelog/chainstack-updates-april-27-2026) **Chainstack updates: April 22, 2026** by Ake **Console**. The new [Agents page](https://console.chainstack.com/agents) is live in the Chainstack console — one place to plug Chainstack into Claude Code, Codex CLI/App, Gemini CLI, Cursor, or any other coding agent: migration prompt, docs, MCP server, and Chainstack skill. [Read more](/changelog/chainstack-updates-april-22-2026) **Chainstack updates: April 20, 2026** by Ake **MCP Server**. New tool: `get_chainstack_pricing` answers pricing questions grounded in live data — plan tiers, add-ons, and the full per-chain dedicated-node catalog. No API key required. [Read more](/changelog/chainstack-updates-april-20-2026) **Chainstack updates: April 18, 2026** by Ake **MCP Server**. New tool: `request_testnet_funds` tops up testnet addresses with gas across 12 EVM, Solana, and TON networks, directly from your agent. [Read more](/changelog/chainstack-updates-april-18-2026) **Chainstack updates: April 15, 2026** by Ake **MCP Server**. New tool: `contact_chainstack` lets you reach Chainstack's sales or support team directly from your agent. No API key required; the agent confirms before sending. [Read more](/changelog/chainstack-updates-april-15-2026) **Chainstack updates: April 9, 2026** by Ake **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) is live at `mcp.chainstack.com` — deploy and manage nodes, search docs, and check platform status from any AI coding agent, via skill install or MCP registration. [Read more](/changelog/chainstack-updates-april-9-2026) **Chainstack updates: March 27, 2026** by Ake **Protocols**. Sui Testnet is now available on Chainstack. You can deploy [Global Nodes](/docs/global-elastic-node) for Sui Testnet with JSON-RPC and gRPC endpoint support. See also [Sui tooling](/docs/sui-tooling). [Read more](/changelog/chainstack-updates-march-27-2026) **Chainstack updates: March 18, 2026** by Vladimir **Protocols**. Tempo Mainnet is now available on Chainstack. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Tempo Mainnet. See also [Tempo tooling](/docs/tempo-tooling) and [Tempo API reference](/reference/tempo-getting-started). [Read more](/changelog/chainstack-updates-march-18-2026) **Chainstack updates: March 5, 2026** by Vladimir **Protocols**. MegaETH is now available on Chainstack: * MegaETH Mainnet on [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) * MegaETH Testnet on [Dedicated Nodes](/docs/dedicated-node) [Read more](/changelog/chainstack-updates-march-5-2026) **Chainstack updates: January 22, 2026** by Vladimir **Protocols**. TRON Mainnet and Nile Testnet now support gRPC endpoints on [Global Nodes](/docs/global-elastic-node). See also [TRON tooling](/docs/tron-tooling) and [authentication methods](/docs/authentication-methods-for-different-scenarios). [Read more](/changelog/chainstack-updates-january-22-2026) ## Earlier updates * [Chainstack updates: January 21, 2026](/changelog/chainstack-updates-january-21-2026) * [Chainstack updates: January 5, 2026](/changelog/chainstack-updates-january-5-2026) * [Chainstack updates: December 26, 2025](/changelog/chainstack-updates-december-26-2025) * [Chainstack updates: December 10, 2025](/changelog/chainstack-updates-december-10-2025) * [Chainstack updates: December 5, 2025](/changelog/chainstack-updates-december-5-2025) * [Chainstack updates: November 24, 2025](/changelog/chainstack-updates-november-24-2025) * [Chainstack updates: November 21, 2025](/changelog/chainstack-updates-november-21-2025) * [Chainstack updates: November 19, 2025](/changelog/chainstack-updates-november-19-2025) * [Chainstack updates: November 4, 2025](/changelog/chainstack-updates-november-4-2025) * [Chainstack updates: October 31, 2025](/changelog/chainstack-updates-october-31-2025) * [Chainstack updates: October 18, 2025](/changelog/chainstack-updates-october-18-2025) * [Chainstack updates: September 30, 2025](/changelog/chainstack-updates-september-30-2025) * [Chainstack updates: August 26, 2025](/changelog/chainstack-updates-august-26-2025) * [Chainstack updates: August 22, 2025](/changelog/chainstack-updates-august-22-2025) * [Chainstack updates: August 11, 2025](/changelog/chainstack-updates-august-11-2025) * [Chainstack updates: August 5, 2025](/changelog/chainstack-updates-august-5-2025) * [Chainstack updates: June 27, 2025](/changelog/chainstack-updates-june-27-2025) * [Chainstack updates: June 12, 2025](/changelog/chainstack-updates-june-12-2025) * [Chainstack updates: June 11, 2025](/changelog/chainstack-updates-june-11-2025) * [Chainstack updates: June 10, 2025](/changelog/chainstack-updates-june-10-2025) * [Chainstack updates: June 9, 2025](/changelog/chainstack-updates-june-9-2025) * [Chainstack updates: June 3, 2025](/changelog/chainstack-updates-june-3-2025) * [Chainstack updates: May 30, 2025](/changelog/chainstack-updates-may-30-2025) * [Chainstack updates: May 28, 2025](/changelog/chainstack-updates-may-28-2025) * [Chainstack updates: May 16, 2025](/changelog/chainstack-updates-may-16-2025) * [Chainstack updates: May 14, 2025](/changelog/chainstack-updates-may-14-2025) * [Chainstack updates: May 7, 2025](/changelog/chainstack-updates-may-7-2025) * [Chainstack updates: April 28, 2025](/changelog/chainstack-updates-april-28-2025) * [Chainstack updates: April 17, 2025](/changelog/chainstack-updates-april-17-2025) * [Chainstack updates: April 1, 2025](/changelog/chainstack-updates-april-1-2025) * [Chainstack updates: March 24, 2025](/changelog/chainstack-updates-march-24-2025) * [Chainstack updates: March 20, 2025](/changelog/chainstack-updates-march-20-2025) * [Chainstack updates: March 21, 2025](/changelog/chainstack-updates-march-21-2025) * [Chainstack updates: March 13, 2025](/changelog/chainstack-updates-march-13-2025) * [Chainstack updates: March 12, 2025](/changelog/chainstack-updates-march-12-2025) * [Chainstack updates: March 11, 2025](/changelog/chainstack-updates-march-11-2025) * [Chainstack updates: March 7, 2025](/changelog/chainstack-updates-march-7-2025) * [Chainstack updates: March 5, 2025](/changelog/chainstack-updates-march-5-2025) * [Chainstack updates: March 4, 2025](/changelog/chainstack-updates-march-4-2025) * [Chainstack updates: February 21, 2025](/changelog/chainstack-updates-february-21-2025) * [Chainstack updates: February 18, 2025](/changelog/chainstack-updates-february-18-2025) * [Chainstack updates: February 14, 2025](/changelog/chainstack-updates-february-14-2025) * [Chainstack updates: February 12, 2025](/changelog/chainstack-updates-february-12-2025) * [Chainstack updates: February 5, 2025](/changelog/chainstack-updates-february-5-2025) * [Chainstack updates: January 22, 2025](/changelog/chainstack-updates-january-22-2025) * [Chainstack updates: January 17, 2025](/changelog/chainstack-updates-january-17-2025) * [Chainstack updates: January 9, 2025](/changelog/chainstack-updates-january-9-2025) * [Chainstack updates: January 7, 2025](/changelog/chainstack-updates-january-7-2025) * [Chainstack updates: December 24, 2024](/changelog/chainstack-updates-december-24-2024) * [Chainstack updates: November 6, 2024](/changelog/chainstack-updates-november-6-2024) * [Chainstack updates: October 2, 2024](/changelog/chainstack-updates-october-2-2024) * [Chainstack updates: October 1, 2024](/changelog/chainstack-updates-october-1-2024) * [Chainstack updates: September 19, 2024](/changelog/chainstack-updates-september-19-2024) * [Chainstack updates: September 13, 2024](/changelog/chainstack-updates-september-13-2024) * [Chainstack updates: September 10, 2024](/changelog/chainstack-updates-september-10-2024) * [Chainstack updates: August 26, 2024](/changelog/chainstack-updates-august-26-2024) * [Chainstack updates: August 20, 2024](/changelog/chainstack-updates-august-20-2024) * [Chainstack updates: August 15, 2024](/changelog/chainstack-updates-august-15-2024) * [Chainstack updates: July 20, 2024](/changelog/chainstack-updates-july-20-2024) * [Chainstack updates: June 26, 2024](/changelog/chainstack-updates-june-26-2024) * [Chainstack updates: June 11, 2024](/changelog/chainstack-updates-june-11-2024) * [Chainstack updates: May 27, 2024](/changelog/chainstack-updates-may-27-2024) * [Chainstack updates: May 23, 2024](/changelog/chainstack-updates-may-23-2024) * [Chainstack updates: May 16, 2024](/changelog/chainstack-updates-may-16-2024) * [Chainstack updates: May 1, 2024](/changelog/chainstack-updates-may-1-2024) * [Chainstack updates: April 22, 2024](/changelog/chainstack-updates-april-22-2024) * [Chainstack updates: April 4, 2024](/changelog/chainstack-updates-april-4-2024) * [Chainstack updates: March 21, 2024](/changelog/chainstack-updates-march-21-2024) * [Chainstack updates: March 12, 2024](/changelog/chainstack-updates-march-12-2024) * [Chainstack updates: March 10, 2024](/changelog/chainstack-updates-march-10-2024) * [Chainstack updates: March 5, 2024](/changelog/chainstack-updates-march-5-2024) * [Chainstack updates: March 4, 2024](/changelog/chainstack-updates-march-4-2024) * [Chainstack updates: February 28, 2024](/changelog/chainstack-updates-february-28-2024) * [Chainstack updates: February 27, 2024](/changelog/chainstack-updates-february-27-2024) * [Chainstack updates: February 26, 2024](/changelog/chainstack-updates-february-26-2024) * [Chainstack updates: February 23, 2024](/changelog/chainstack-updates-february-23-2024) * [Chainstack updates: February 21, 2024](/changelog/chainstack-updates-february-21-2024) * [Chainstack updates: February 9, 2024](/changelog/chainstack-updates-february-9-2024) * [Chainstack updates: January 30, 2024](/changelog/chainstack-updates-january-30-2024) * [Chainstack updates: January 25, 2024](/changelog/chainstack-updates-january-25-2024) * [Chainstack updates: January 18, 2023](/changelog/chainstack-updates-january-18-2023) * [Chainstack updates: December 6, 2023](/changelog/chainstack-updates-december-6-2023) * [Chainstack updates: November 30, 2023](/changelog/chainstack-updates-november-30-2023) * [Chainstack updates: November 29, 2023](/changelog/chainstack-updates-november-29-2023) * [Chainstack updates: November 20, 2023](/changelog/chainstack-updates-november-20-2023) * [Chainstack updates: November 16, 2023](/changelog/chainstack-updates-november-16-2023) * [Chainstack updates: October 31, 2023](/changelog/chainstack-updates-october-31-2023) * [Chainstack updates: October 25, 2023](/changelog/chainstack-updates-october-25-2023) * [Chainstack updates: October 19, 2023](/changelog/chainstack-updates-october-19-2023) * [Chainstack updates: October 17, 2023](/changelog/chainstack-updates-october-17-2023) * [Chainstack updates: October 5, 2023](/changelog/chainstack-updates-october-5-2023) * [Chainstack updates: September 13, 2023](/changelog/chainstack-updates-september-13-2023) * [Chainstack updates: September 08, 2023](/changelog/chainstack-updates-september-08-2023) * [Chainstack updates: August 10, 2023](/changelog/chainstack-updates-august-10-2023) * [Chainstack updates: August 4, 2023](/changelog/chainstack-updates-august-4-2023) * [Chainstack updates: July 28, 2023](/changelog/chainstack-updates-july-28-2023) * [Chainstack updates: July 27, 2023](/changelog/chainstack-updates-july-27-2023) * [Chainstack updates: July 18, 2023](/changelog/chainstack-updates-july-18-2023) * [Chainstack updates: July 14, 2023](/changelog/chainstack-updates-july-14-2023) * [Chainstack updates: July 13 2023](/changelog/chainstack-updates-july-13-2023) * [Chainstack updates: July 10 2023](/changelog/chainstack-updates-july-10-2023) * [Chainstack updates: July 4 2023](/changelog/chainstack-updates-july-4-2023) * [Chainstack updates: June 27, 2023](/changelog/chainstack-updates-june-27-2023) * [Chainstack updates: June 15, 2023](/changelog/chainstack-updates-june-15-2023) * [Chainstack updates: June 14, 2023](/changelog/chainstack-updates-june-14-2023) * [Chainstack updates: May 30, 2023](/changelog/chainstack-updates-may-30-2023) * [Chainstack updates: May 29, 2023](/changelog/chainstack-updates-may-29-2023) * [Chainstack updates: May 11, 2023](/changelog/chainstack-updates-may-11-2023) * [Chainstack updates: April 28, 2023](/changelog/chainstack-updates-april-28-2023) * [Chainstack updates: April 27, 2023](/changelog/chainstack-updates-april-27-2023) * [Chainstack updates: April 18, 2023](/changelog/chainstack-updates-april-18-2023) * [Chainstack updates: April 12, 2023](/changelog/chainstack-updates-april-12-2023) * [Chainstack updates: March 31, 2023](/changelog/chainstack-updates-march-31-2023) * [Chainstack updates: March 29, 2023](/changelog/chainstack-updates-march-29-2023) * [Chainstack updates: March 27, 2023](/changelog/chainstack-updates-march-27-2023) * [Chainstack updates: March 22, 2023](/changelog/chainstack-updates-march-22-2023) * [Chainstack updates: March 6, 2023](/changelog/chainstack-updates-march-6-2023) * [Chainstack updates: February 22, 2023](/changelog/chainstack-updates-february-22-2023) * [Chainstack updates: February 20, 2023](/changelog/chainstack-updates-february-20-2023) * [Chainstack updates: February 15, 2023](/changelog/chainstack-updates-february-15-2023) * [Chainstack updates: February 9, 2023](/changelog/chainstack-updates-february-9-2023) * [Chainstack updates: February 8, 2023](/changelog/chainstack-updates-february-8-2023) * [Chainstack updates: February 2, 2023](/changelog/chainstack-updates-february-2-2023) * [CChainstack updates: January 30, 2023](/changelog/chainstack-updates-january-30-2023) * [CChainstack updates: January 19, 2023](/changelog/chainstack-updates-january-19-2023) * [CChainstack updates: January 17, 2023](/changelog/chainstack-updates-january-17-2023) * [CChainstack updates: January 10, 2023](/changelog/chainstack-updates-january-10-2023) * [Welcome to Chainstack Developer Portal](/changelog/chainstack-updates-january-10-2023) * [Chainstack updates: December 15, 2022](/changelog/chainstack-updates-december-15-2022-1) * [Chainstack updates: December 15, 2022](/changelog/chainstack-updates-december-15-2022) * [Chainstack updates: December 2, 2022](/changelog/chainstack-updates-december-2-2022) * [Chainstack updates: November 29, 2022](/changelog/chainstack-updates-november-29-2022) * [Chainstack updates: November 16, 2022](/changelog/chainstack-updates-november-16-2022) * [Chainstack updates: November 8, 2022](/changelog/chainstack-updates-november-8-2022) * [Chainstack updates: November 7, 2022](/changelog/chainstack-updates-november-7-2022) * [Chainstack updates: November 3, 2022](/changelog/chainstack-updates-november-3-2022) * [Chainstack updates: October 14, 2022](/changelog/chainstack-updates-october-14-2022) * [Chainstack updates: October 12, 2022](/changelog/chainstack-updates-october-12-2022) * [Chainstack updates: October 3, 2022](/changelog/chainstack-updates-october-3-2022-1) * [Chainstack updates: October 3, 2022](/changelog/chainstack-updates-october-3-2022) * [Chainstack updates: September 15, 2022](/changelog/chainstack-updates-september-15-2022) * [Chainstack updates: September 12, 2022](/changelog/chainstack-updates-september-12-2022) * [Chainstack updates: August 31, 2022](/changelog/chainstack-updates-august-31-2022) * [Chainstack updates: August 29, 2022](/changelog/chainstack-updates-august-29-2022) * [Chainstack updates: August 23, 2022](/changelog/chainstack-updates-august-23-2022) * [Chainstack updates: August 17, 2022](/changelog/chainstack-updates-august-17-2022) * [Chainstack updates: July 7, 2022](/changelog/chainstack-updates-july-7-2022) * [Chainstack updates: June 1, 2022](/changelog/chainstack-updates-june-1-2022) * [CChainstack updates: May 5, 2022](/changelog/chainstack-updates-may-5-2022) * [Chainstack updates: April 14, 2022](/changelog/chainstack-updates-april-14-2022) * [Chainstack updates: February 10, 2022](/changelog/chainstack-updates-february-10-2022) * [Chainstack updates: December 30, 2021](/changelog/chainstack-updates-december-30-2021) * [Chainstack updates: December 1, 2021](/changelog/chainstack-updates-december-1-2021) * [Chainstack updates: August 18, 2021](/changelog/chainstack-updates-august-18-2021) * [Chainstack updates: June 1, 2021](/changelog/chainstack-updates-june-1-2021) * [Chainstack updates: May 10, 2021](/changelog/chainstack-updates-may-10-2021) * [Chainstack updates: April 6, 2021](/changelog/chainstack-updates-april-6-2020) * [Chainstack updates: March 10, 2022](/changelog/chainstack-updates-march-10-2022) * [Chainstack updates: January 22, 2021](/changelog/chainstack-updates-january-22-2020) * [Chainstack updates: November 2, 2020](/changelog/chainstack-updates-november-2-2020) * [Chainstack updates: September 16, 2020](/changelog/chainstack-updates-september-16-2020) * [Chainstack updates: August 31, 2020](/changelog/chainstack-updates-july-7-2020-1) * [Chainstack updates: July 7, 2020](/changelog/chainstack-updates-july-7-2020) * [Chainstack updates: June 2, 2020](/changelog/chainstack-updates-june-2-2020) * [Chainstack updates: April 22, 2020](/changelog/chainstack-updates-april-22-2020) * [Chainstack updates: March 2, 2020](/changelog/chainstack-updates-march-2-2020) * [Chainstack updates: February 13, 2020](/changelog/chainstack-updates-february-13-2020) * [Chainstack updates: December 31, 2019](/changelog/chainstack-updates-december-31-2019) * [Chainstack updates: November 14, 2019](/changelog/chainstack-updates-november-14-2019) * [Chainstack updates: October 17, 2019](/changelog/chainstack-updates-october-17-2019) * [Chainstack updates: September 3, 2019](/changelog/chainstack-updates-september-3-2019) * [Chainstack updates: July 1, 2019](/changelog/chainstack-updates-july-1-2019) * [Chainstack updates: June 21, 2019](/changelog/chainstack-updates-june-21-2019) * [Chainstack updates: May 9, 2019](/changelog/chainstack-updates-may-9-2019) * [Chainstack updates: May 3, 2019](/changelog/chainstack-updates-may-3-2019) * [Chainstack updates: April 11, 2019](/changelog/chainstack-updates-april-11-2019) * [Chainstack updates: April 2, 2019](/changelog/chainstack-updates-april-2-2019) * [Chainstack updates: March 17, 2019](/changelog/chainstack-release-notes-mar-17-2019) # Chainstack updates: March 17, 2019 Source: https://docs.chainstack.com/changelog/chainstack-release-notes-mar-17-2019 Initial Chainstack platform launch with consortium and public chain projects, MultiChain 2.0 beta, and Quorum 2.2.1 with Raft and IBFT support. * **Registration and sign in**. Signing up via email, password, and personal details. Signing up for a member invited to the consortium project. Email confirmation on successful registration. Email verification. * **Consortium project**. Wizards to create a new network and add a node to the existing network. Invitation of other organizations as members to the project via email. * **Public chain project**. Wizards to join a public network and add another node. * **Protocols** * MultiChain 2.0-beta-1 support with blockchain explorer. * Quorum 2.2.1 support with Raft and IBFT, and blk-explorer-free blockchain explorer. * Full Ethereum mainnet node deployment with Bolt rapid sync mechanism. * **Clouds**. Google Cloud Platform and Amazon Web Services in the Asia-Pacific region. * **Node details**. Default wallet private/public keys, and chain name for MultiChain. Constellation private/public keys, and network ID for Quorum. Sync mode and network ID for Ethereum. Client version for all protocols. * **Settings**. Editing personal and organization details. Changing password. * **Documentation**. Portal based on VuePress. # Chainstack updates: April 1, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-1-2025 Sonic Blaze testnet support added: deploy Global Nodes and Dedicated Nodes in archive mode with Debug & Trace APIs for Sonic L2 development. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) in archive mode with Debug & Trace APIs for the Sonic Blaze testnet. # Chainstack updates: April 11, 2019 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-11-2019 MultiChain 2.0 stable release support and Quorum 2.2.3 client upgrade added to the Chainstack platform for permissioned blockchain deployments. **Protocols** * Added MultiChain 2.0 release support. * Added Quorum 2.2.3 support. # Chainstack updates: April 12, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-12-2023 Chainstack Subgraphs leaves closed beta and opens to all users, with new tutorials published; Ethereum MEV API deprecated after the Merge. * **Services**. [Chainstack Subgraphs](/docs/subgraphs-introduction) has finished the closed beta and is now open to all our customers. * **Documentation**. We added a [series of developer tutorials](/docs/chainstack-subgraphs-tutorials) for Chainstack Subraphs users: from a newbie to an expert. Try them out and let us know what you think. * **Protocols**. Ethereum MEV API is now deprecated since it hasn't been functional since the Merge. # Chainstack updates: April 14, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-14-2022 Ethereum Goerli testnet support added, plus organization role management with three roles — Admin, Editor, and Viewer — and user role assignment. * **Protocols**. Ethereum Goerli support. * **Role management** * You can now [assign roles](/docs/manage-your-organization) to your organization's users and [change roles](/docs/manage-your-organization#change-a-user-role-in-the-organization) of the existing users. * There are now three roles: [Admin, Editor, Viewer](/docs/manage-your-organization#users-and-their-roles). # Chainstack updates: April 15, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-15-2026 New Chainstack MCP server tool: contact_chainstack lets you reach Chainstack's sales or support team directly from your AI coding agent. **MCP Server**. New tool: **`contact_chainstack`**. Reach out to Chainstack directly from your agent — sales, support, or general questions. No API key required. The agent asks for your confirmation before sending, and PII is redacted from server logs. # Chainstack updates: April 17, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-17-2025 New Add-ons released: Yellowstone gRPC Geyser plugin for Solana streaming and Unlimited Node add-on for any node, both available on the Chainstack platform. **Nodes** and **Add-ons**. You can now enable [Add-ons](/docs/add-ons): [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) on Solana and [Unlimited Node](/docs/unlimited-node) on any node. # Chainstack updates: April 18, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-18-2023 Revamped Chainstack Marketplace launches with Covalent and Valha apps, plus a new tutorial on JSON web token authentication for secure DApps. * **Services**. A revamped [Chainstack Marketplace](https://console.chainstack.com/marketplace) is now live with the Covalent and Valha applications already available for installation—boost up your DApps! * **Documentation**. The following article is added in Web3 \[De]Coded: * [Mastering JSON web tokens: How to implement secure user authentication](/docs/tutorial-mastering-jwt-how-to-implement-secure-user-authentication) # Chainstack updates: April 18, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-18-2026 New Chainstack MCP server tool: request_testnet_funds tops up testnet addresses with gas across 12 EVM, Solana, and TON networks, directly from your agent. **MCP Server**. New tool: **`request_testnet_funds`**. Top up testnet addresses with gas for EVM, Solana, or TON development — directly from your agent. * 12 networks: Ethereum Sepolia, Ethereum Hoodi, Base Sepolia, Polygon Amoy, BNB testnet, zkSync Sepolia, Scroll Sepolia, HyperEVM testnet, Plasma testnet, Monad testnet, TON testnet, and Solana devnet. * The faucet tops an address **up to the per-network cap** rather than sending a fixed amount; if the address is already at the cap, the call fails. * API key required. Rate-limiting is tracked per organization (24h cooldown), and on cooldown the response tells your agent exactly when to retry. # Chainstack updates: April 2, 2019 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-2-2019 Password recovery via email is now available on Chainstack, letting users reset their account credentials directly from the registration and sign-in flow. **Registration and sign in**. Password recovery via email. # Chainstack updates: April 20, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-20-2026 New Chainstack MCP server tool: get_chainstack_pricing answers pricing questions grounded in live data — plans, add-ons, and the full dedicated-node catalog. **MCP Server**. New tool: **`get_chainstack_pricing`**. Ask your agent anything about Chainstack pricing and get an answer grounded in live data — no guessing, no stale quotes. * Covers plan tiers (Developer through Enterprise and Pay As You Go), the feature matrix, add-on pricing (Unlimited Node, Yellowstone gRPC, Warp transactions), and the full per-chain dedicated-node catalog (\~87 SKUs with regions, hardware flavors, and hourly/monthly USD prices). * No API key required. Every call fetches fresh from the public pricing sources — the server holds zero hardcoded pricing, so changes on chainstack.com show up immediately. * Example prompts: *"I expect 50M RU/month and need archive data — which plan fits?"* or *"How much is a dedicated Ethereum archive node in London?"* # Chainstack updates: April 22, 2020 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-22-2020 Corda Network and Pre-Production Network support, Hyperledger Fabric 2.1.0, AWS US West region, secure vault for Corda identity keys, and identity management. * **Protocols** * Corda Network and Corda Pre-Production Network support. You can now join the Corda production and pre-production networks. See also Join a public network. * Hyperledger Fabric 2.1.0 support. * **Clouds**. You can now deploy your nodes and networks in the Amazon Web Services US West region. * **Vault**. You can now store your Corda identity key material in a secure vault. * **Identity management**. You can now securely manage your organization identity for the Corda networks. # Chainstack updates: April 22, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-22-2024 Solana Mainnet archive nodes are now deployable as global elastic nodes, providing geo-balanced access to historical Solana state and transactions. **Global elastic nodes**. Now, you can deploy global elastic nodes in archive mode for Solana Mainnet. # Chainstack updates: April 22, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-22-2026 Chainstack adds an Agents page in the console with a migration prompt for AI coding agents, MCP server access, llms-full.txt, and Chainstack skill. **Console**. The new [Agents page](https://console.chainstack.com/agents) is live in the Chainstack console. One place to plug Chainstack into Claude Code, Codex CLI/App, Gemini CLI, Cursor, or any other coding agent: * **Migrate to Chainstack** — `get docs.chainstack.com/docs/migrate-to-chainstack-with-ai.md` runs a pre-written prompt that switches your project's RPC endpoints over to Chainstack. * **Docs for Agents** — point your agent at [llms-full.txt](https://docs.chainstack.com/llms-full.txt) for all Chainstack docs in one shot, or pull them through the Chainstack MCP server (see below). * **MCP Server** — `get mcp.chainstack.com` connects the [Chainstack MCP server](https://mcp.chainstack.com) for full access to infrastructure, testnet faucet funds, and the Chainstack documentation from inside your agent. * **Skills** — `get mcp.chainstack.com/skill` installs the [Chainstack skill](https://mcp.chainstack.com/skill) in your agent so it can operate the Chainstack platform, contact Chainstack directly, pull testnet faucet funds, build web3 apps, and apply battle-tested best practices for web3 infrastructure. # Chainstack updates: April 27, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-27-2023 WSS WebSocket endpoints are now available for Chainstack nodes that have Warp transactions enabled, providing real-time access for fast trading workflows. **Endpoints**. WSS endpoints are now available for nodes with Warp transactions enabled. # Chainstack updates: April 27, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-27-2026 The Chainstack MCP server adds Pi coding agent support via the skill route, and the discovery page now advertises pricing and testnet funds. **MCP Server**. [Pi](https://pi.dev/) coding agent users can now install the Chainstack skill: ```bash theme={"system"} curl -o ~/.agents/skills/chainstack/SKILL.md https://mcp.chainstack.com/skill ``` * Pi uses the **skill route only** — it [rejects MCP servers by design](https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/) — so the [discovery page](https://mcp.chainstack.com) now flags this to keep Pi users on the right setup path. All tools work via Pi's bash and the public HTTP endpoint. * The discovery page description now covers the full tool surface: deploy and manage blockchain nodes, search protocol docs and RPC references, check platform status, get pricing, request testnet funds, and reach the Chainstack team. # Chainstack updates: April 28, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-28-2023 Debug and trace APIs are now available for elastic Arbitrum archive nodes, unlocking advanced contract introspection and transaction analysis on Arbitrum. **Debug and trace API** are now available for elastic Arbitrum archive nodes. # Chainstack updates: April 28, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-28-2025 Chainstack Developer Portal migrates to Mintlify with an open-source repo, llms.txt and llms-full.txt feeds, and an MCP server for AI-ready documentation. * **Developer Portal** — Chainstack Developer Portal (*this very site*) migrated to [Mintlify](https://mintlify.com/) for AI-ready features, an open-source repository, and snappier performance overall. You can now consume our docs as a human and as an agent through the [open-source repository](https://github.com/chainstack/dev-portal), [llms.txt](https://docs.chainstack.com/llms.txt) [llms-full.txt](https://docs.chainstack.com/llms-full.txt), or an [MCP server](/docs/developer-portal-mcp-server). See the repository's README for all the details. # Chainstack updates: April 29, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-29-2026 Hyperliquid /nanoreth is now live on every Chainstack Hyperliquid mainnet node, with protocol-level system transactions included in HyperEVM JSON-RPC responses. **Protocols**. **Hyperliquid `/nanoreth`** is live on every Chainstack [Hyperliquid](https://chainstack.com/build-better-with-hyperliquid/) mainnet node. Append `/nanoreth` to your endpoint URL to get system transactions included in `eth_getBlockByNumber`, `eth_getBlockReceipts`, and direct hash lookups. The default `/evm` path is unchanged — hl-node-compliant, system transactions stripped from block-shaped responses. Find both paths in the [console](https://console.chainstack.com/) under **Access and credentials** on any Hyperliquid node. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) for the per-method behavior matrix. # Chainstack updates: April 4, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-4-2024 Arbitrum One Mainnet archive nodes with debug & trace APIs are now deployable as global elastic nodes for low-latency access from any region. **Global elastic nodes**. Now, you can deploy global elastic nodes in archive mode with debug & trace APIs for Arbitrum One Mainnet. # Chainstack updates: April 6, 2021 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-6-2020 Pay-per-request pricing for shared nodes on all plans, cryptocurrency payments for Enterprise, and a public Chainstack API for managing Corda applications. * **Pricing**. Flexible pay-per-request pricing for shared nodes on all [subscription plans](https://chainstack.com/pricing/). * **Billing**. Payments in cryptocurrency for customers on the [Enterprise plan](https://chainstack.com/pricing/). * **Public API**. You can now use the Chainstack API to manage your Corda applications. See also [Chainstack 2.3: Faster and better public blockchain APIs](https://chainstack.com/chainstack-2-3-faster-and-better-public-blockchain-apis/). # Chainstack updates: April 9, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-april-9-2026 The Chainstack MCP server is live at mcp.chainstack.com — deploy and manage blockchain nodes, search docs, and check platform status from any AI coding agent. **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) is live at `mcp.chainstack.com`. Type `get mcp.chainstack.com` into any AI coding agent to get started. * **15 tools** — docs search and page fetch, platform status, and full node/project management: `search_docs`, `get_doc_page`, `get_platform_status`, `get_organization`, `get_deployment_options`, `list_projects`, `create_project`, `get_project`, `update_project`, `delete_project`, `list_nodes`, `get_node`, `create_node`, `update_node`, `delete_node`. * **Public tools** (no auth): docs search, doc-page fetch, platform status. **Authenticated tools** (API key): node and project management. * Two integration paths — **skill install** (lean context) or **MCP registration** (tools always in context) — with ready-made configs for Claude Code, Codex, Cursor, Windsurf, Gemini CLI, GitHub Copilot, Antigravity, Claude.ai, and ChatGPT. Streamable HTTP transport; skill file at `/skill` for on-demand use without registration. # Chainstack updates: August 10, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-10-2023 Scroll Sepolia Testnet support added on Chainstack, plus global elastic nodes are now available for Avalanche and Scroll Sepolia Testnet deployments. * **Protocols**. Scroll Sepolia Testnet support. * **Global elastic node**. Now you can deploy global elastic nodes for Avalanche and Scroll Sepolia Testnet. # Chainstack updates: August 11, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-11-2025 Flashblocks preconfirmations are now available for Base nodes on Chainstack, delivering ultra-fast transaction visibility for Base Mainnet builders. **Protocols**. Flashblocks preconfirmations for the Base nodes are now available on Chainstack. See [Flashblocks on Base](/docs/flashblocks-on-base). # Chainstack updates: August 15, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-15-2024 Fantom Mainnet Global Nodes gain debug & trace APIs, and Solana adds blockSubscribe support on Mainnet and Devnet for real-time block streaming. **Global Nodes**. Debug & trace on Fantom and `blockSubscribe` on Solana. * Fantom — you can now deploy Global Nodes with debug & trace APIs for Fantom Mainnet. See the [debug & trace API reference](/reference/debug_traceblockbyhash-fantom-chain). * Solana — you now do the `blockSubcribe` on the Solana Mainnet & Devnet. See the [blockSubscribe API reference](/reference/blocksubscribe-solana). # Chainstack updates: August 17, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-17-2022 NEAR Protocol support lands on Chainstack, accompanied by NEAR operations documentation and a tutorial on building a simple metamorphic message contract. * **Protocols**. [NEAR](/docs/protocols-networks) support. * **Documentation**. NEAR operations, [simple metamorphic contract tutorial](/docs/near-tutorial-creating-and-upgrading-a-simple-message-contract). # Chainstack updates: August 18, 2021 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-18-2021 Tezos full and archive node support on mainnet and Florence testnet, key-protected endpoints, AWS dedicated nodes, and Enterprise plan upgrades from billing. * **Protocols**. Tezos support. You can now deploy full and archive Tezos nodes on mainnet and Florence testnet. * **Node details**. Node access and credentials for public chain and Quorum nodes now include key-protected endpoints. * **Clouds**. You can now send requests for dedicated public chain node deployment in the Amazon Web Services. * **Billing**. You can now upgrade to the Enterprise plan from your billing settings. * **Pricing**. Free usage period on the Developer plan reduced from 14 to 7 days. * **Documentation**. Tezos general description, operations, fund contract tutorial. # Chainstack updates: August 19, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-19-2025 Starknet nodes upgraded to Pathfinder v0.19.0 — the default JSON-RPC API version served on the root endpoint changed from 0.7.1 to 0.8.0. **Starknet**. Chainstack upgraded Starknet nodes to [Pathfinder v0.19.0](https://github.com/equilibriumco/pathfinder/releases/tag/v0.19.0). The default JSON-RPC API version served on the `/` path changed from 0.7.1 to 0.8.0. To keep the previous version, pin `/rpc/v0_7` in your endpoint URL. See [Pathfinder default API version change 0.19.0](/reference/starknet-pathfinder-default-api-version-change) for details. # Chainstack updates: August 20, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-20-2024 Trader Nodes for high-speed Solana transactions launched, and customers can now top up their balance using 150+ cryptocurrencies via NOWPayments. Trader Nodes on Solana and 150+ cryptocurrencies to top up your balance. * **Trader Nodes**. You can now send high-speed transactions on Solana. See [Trader Nodes](/docs/warp-transactions). * **BIling**. You can now top up your balance with 150+ cryptocurrencies using NOWPayments as payment provider. See [Manage your billing](/docs/manage-your-billing). # Chainstack updates: August 20, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-20-2026 Stellar — a public blockchain for fast, low-cost cross-border payments and asset tokenization — is now supported on Chainstack across Global Nodes and Dedicated Nodes. **Protocols**. Chainstack now supports Stellar — a public blockchain for fast, low-cost cross-border payments and asset tokenization. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Stellar Mainnet and Testnet. # Chainstack updates: August 22, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-22-2025 Unichain Testnet Global Nodes are now deployable, and Warp ultra-fast transactions are available on GEN Nodes for Solana, Ethereum, and BNB Smart Chain. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Unichain Testnet. **Ultra-fast transactions**. Now [Warp transactions](/docs/warp-transactions) are available for GEN Nodes for Solana, Ethereum, and BNB Smart Chain. # Chainstack updates: August 23, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-23-2022 Gnosis Chain support lands on Chainstack with operations documentation and a soulbound token tutorial built using Remix and OpenZeppelin contracts. * **Protocols**. [Gnosis Chain](/docs/protocols-networks) support. * **Documentation**. Gnosis Chain operations, [simple soulbound token tutorial](/docs/gnosis-tutorial-simple-soulbound-token-with-remix-and-openzeppelin). # Chainstack updates: August 26, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-26-2024 Polygon zkEVM nodes on Chainstack now support WebSocket connections, enabling real-time event subscriptions for zkEVM rollup applications and bots. **Protocols**. Now, Polygon zkEVM supports connecting over WebSocket. # Chainstack updates: August 26, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-26-2025 Hyperliquid HyperEVM Mainnet archive nodes are now available on Chainstack with Debug & Trace methods and WebSocket support, alongside HyperCore. **Protocols**. Hyperliquid HyperEVM Mainnet nodes are now available in archive mode with Debug & Trace methods and WebSocket support. See also [Hyperliquid methods](/docs/hyperliquid-methods) and [Hyperliquid API reference](/reference/hyperliquid-getting-started). As a reminder, HyperCore is also supported. # Chainstack updates: August 29, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-29-2022 Fuse Network support is added to Chainstack, allowing developers to deploy Fuse mainnet nodes for payments and decentralized application infrastructure. **Protocols**. Fuse support. # Chainstack updates: August 31, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-31-2022 Arbitrum support, an L1-to-L2 messaging tutorial, and seven new crypto payment options including Dogecoin, Litecoin, Dai, BCH, ApeCoin, SHIB, and USDT. * **Protocols**. [Arbitrum](/docs/protocols-networks) support. * **Documentation**. Arbitrum operations, [simple L1 to L2 messaging tutorial](/docs/arbitrum-tutorial-l1-to-l2-messaging-smart-contract). * **Crypto payments**. In addition to the previously available BTC, ETH, and USDC, you can now [top up your balance](/docs/manage-your-billing) with Dogecoin, Litecoin, Dai, Bitcoin Cash, ApeCoin, SHIBA INU, and USDT. # Chainstack updates: August 4, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-4-2023 Base Mainnet support is now live on Chainstack, letting developers deploy nodes and build on Coinbase's L2 rollup at general availability launch. **Protocols**. Base Mainnet support. # Chainstack updates: August 4, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-4-2026 You can now deploy Global Nodes for World Chain Mainnet and Stable Mainnet. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for World Chain Mainnet and Stable Mainnet. # Chainstack updates: August 5, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-5-2025 Hyperliquid Mainnet and Testnet are now available on Chainstack in full mode, with the HyperCore /info and HyperEVM /evm APIs both enabled for builders. **Protocols.** Hyperliquid Mainnet and Hyperliquid Testnet are now available in full mode with the HyperCore `/info` and HyperEVM `/evm` APIs enabled. See also [Hyperliquid API Reference](/reference/hyperliquid-getting-started). As a reminder, HyperCore is also supported. # Chainstack updates: August 7, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-august-7-2026 XRP Ledger — the payments-focused L1 running the rippled client — is now supported on Chainstack across Global Nodes and Dedicated Nodes, Mainnet and Testnet, and the faucet is now built into the console with saved wallets and scheduled auto-claims. **Protocols**. Chainstack now supports XRP Ledger — a payments-focused L1 with its own JSON-RPC and WebSocket API rather than the Ethereum `eth_*` interface. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for XRP Ledger Mainnet and Testnet, in Full mode. **Faucet**. The Chainstack faucet is now built into the console. Open [**Faucet**](https://console.chainstack.com/faucet) to request testnet tokens without a separate sign-in, and save the wallets you fund most often — each can carry a recurring auto-claim that tops it up on the schedule you set. The standalone [faucet.chainstack.com](https://faucet.chainstack.com/) is unchanged, and both serve the same [supported networks](/docs/faucets). # Chainstack updates: December 1, 2021 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-1-2021 Fantom mainnet and testnet node support, dedicated Tezos nodes, an ERC-721 contract tutorial for Fantom, and node requests metrics for public chains. * **Protocols**. * Fantom support. You can now deploy full and archive Fantom nodes on mainnet and testnet. * Tezos dedicated node support. You can now deploy dedicated full and archive Tezos nodes. * **Documentation**. Fantom general description, operations, ERC-721 contract tutorial. * **Node metrics**. Added node requests metrics for public chain protocols. # Chainstack updates: December 10, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-10-2025 Chainstack platform update — Hyperliquid Mainnet eth_getLogs optimization for faster execution speed on high-throughput log retrieval queries. **Protocols**. Hyperliquid Mainnet — `eth_getLogs` optimization to improve execution speed for high-throughput log retrieval. # Chainstack updates: December 15, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-15-2022 Gnosis Chain Chiado testnet support is now live for both elastic and dedicated nodes on the Chainstack platform, replacing the deprecated Sokol testnet. * **Protocols**. [Gnosis Chain Chiado](/docs/protocols-networks) testnet support for elastic and dedicated nodes. # Chainstack updates: December 15, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-15-2022-1 Polygon MATIC is now an accepted payment option for topping up your Chainstack billing balance, joining the existing list of supported cryptocurrencies. * **Crypto payments**. In addition to the previously available cryptocurrencies, you can now [top up your balance](/docs/manage-your-billing) with Polygon MATIC. # Chainstack updates: December 2, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-2-2022 Gnosis Chain clients updated for The Merge, Gnosis Chain API reference refreshed, Ethereum debug-and-trace methods documented, and Subgraphs deploy guide. * **Protocols**. [Gnosis Chain clients](/docs/protocols-clients) updated for The Merge. * **Documentation**. * Gnosis Chain API [reference](/reference/gnosis-getting-started) update. * Debug and trace methods added to the Ethereum API [reference](/reference/ethereum-debug-trace-rpc-methods). * Chainstack [Subgraphs](/docs/deploy-a-subgraph). # Chainstack updates: December 24, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-24-2024 All Fantom nodes on Chainstack now run on the Sonic client, bringing improved performance and the latest Fantom Foundation execution layer to customers. **Nodes**. The Fantom nodes are now running on the Sonic client. # Chainstack updates: December 26, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-26-2025 Monad Mainnet archive nodes are now available on Chainstack Global Nodes with Debug & Trace methods, alongside the Monad API reference and tooling guides. **Protocols**. Monad Mainnet nodes are now available in archive mode with Debug & Trace methods on [Global Nodes](/docs/global-elastic-node). See also [Monad API reference](/reference/monad-getting-started) and [Monad tooling](/docs/monad-tooling). # Chainstack updates: December 30, 2021 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-30-2021 Avalanche X-Chain and C-Chain mainnet and Fuji testnet support, Tezos Hangzhounet, a public API for AVAX/Fantom/Tezos nodes, and a flash loan tutorial. * **Protocols**. * Avalanche support. You can now deploy full and archive Avalanche nodes with X-Chain and C-Chain endpoints on mainnet and Fuji testnet. * Tezos Hangzhounet testnet support. The Florencenet testnet support is deprecated. * **Documentation**. Avalanche general description, operations, flash loan tutorial. * **Public API**. You can now use the Chainstack API to manage your Avalanche, Fantom, and Tezos nodes. * **Billing**. Removed free usage period on the Developer plan. The Developer plan keeps other usage features, including the free 3M requests. # Chainstack updates: December 31, 2019 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-31-2019 Corda 4.3 with single-JAR CorDapps, Quorum 2.4.0 with learner nodes and Tessera 0.10.2, MultiChain 2.0.3, and Bitcoin 0.19.0.1 with txindex enabled. * **Protocols** * Corda 4.3 support. You can also now install CorDapps containing workflows and contracts in a single JAR file. * Quorum 2.4.0 support. Learner node role support. [Tessera](https://docs.tessera.consensys.net/) 0.10.2 support. * MultiChain 2.0.3 support. * Bitcoin 0.19.0.1 support. Bitcoin nodes now run with the `txindex` flag allowing to query any transaction on the blockchain. * **Documentation**. Context-sensitive documentation links in the platform UI. # Chainstack updates: December 5, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-5-2025 Chainstack platform update — Unichain Mainnet and Sepolia Testnet Global Nodes support, plus Cronos debug and trace methods on Global and Trader Nodes. **Protocols**: * Unichain — You can now deploy [Global Nodes](/docs/global-elastic-node) for Unichain Mainnet and Sepolia Testnet. * Cronos — Debug and trace methods are now available for Cronos Mainnet on [Global Nodes](/docs/global-elastic-node) and [Trader Nodes](/docs/trader-node). # Chainstack updates: December 6, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-december-6-2023 Sign up for Chainstack with no credit card required and get the free Developer plan with 3 million blockchain RPC requests included every month. **Billing**. No credit card required on sign-up. Sign up and get the free [Developer plan](https://chainstack.com/pricing/) with 3 million requests monthly. # Chainstack updates: February 10, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-10-2022 Solana mainnet and devnet node support arrives on Chainstack with operations docs, a token vesting tutorial, the public API for Solana, and crypto top-ups. * **Protocols**. Solana support. You can now use Solana nodes on mainnet and devnet. * **Documentation**. Solana general description, operations, token vesting tutorial. * **Public API**. You can now use the Chainstack API to manage your Solana nodes. * **Crypto payments**. You can now top up your balance with crypto and Chainstack will automatically deduct all costs from your balance. # Chainstack updates: February 12, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-12-2025 ZKsync Era mainnet archive nodes are now deployable as Chainstack Global Nodes with debug & trace APIs for advanced zkSync L2 transaction analysis. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) in archive mode with debug & trace APIs for ZKsync Era mainnet. # Chainstack updates: February 13, 2020 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-13-2020 Ethereum archive node support with Geth 1.9.10, Quorum Tessera 0.10.3, in-line resource edit buttons, and a new Ethereum modes documentation section. * **Protocols** * Ethereum archive node support. Deploy a dedicated Ethereum archive node. Connect to an elastic archive node for free on Business and Enterprise tiers. Geth 1.9.10 support. * Quorum transaction manager Tessera 0.10.3 support. * **UI**. In-line edit button for all resources to improve access to quick actions. * **Documentation**. Ethereum modes section to help in selecting whether to deploy a full or archive Ethereum node. # Chainstack updates: February 14, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-14-2025 Bitcoin mainnet nodes are now deployable as Chainstack Global Nodes, providing geo-balanced low-latency access for Bitcoin RPC clients and applications. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Bitcoin mainnet. # Chainstack updates: February 15, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-15-2023 An Add to MetaMask button on the node access page makes it one click to add your Chainstack RPC endpoint as a custom network in the MetaMask wallet. * **Tools**. You can now add the node endpoint with the **Add to MetaMask** button on the [node access page](/docs/manage-your-node#view-node-access-and-credentials). # Chainstack updates: February 18, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-18-2025 Bitcoin testnet support arrives on Chainstack Global Nodes, enabling developers to test Bitcoin applications against geo-balanced testnet RPC endpoints. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Bitcoin testnet. # Chainstack updates: February 2, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-2-2023 Optimism Goerli testnet support for elastic and dedicated nodes is now live, accompanied by general Optimism docs and an Optimism bridge tutorial. * **Protocols**. [Optimism](/docs/protocols-networks) Goerli testnet support for elastic and dedicated nodes. * **Documentation**. > [card] General Optimism documentation. > [card] [Optimism tutorial](/docs/optimism-tutorial-bridge-ether-from-ethereum-l1-to-optimism-l2-using-the-optimism-javascript-sdk) # Chainstack updates: February 20, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-20-2023 BNB Smart Chain elastic full nodes can now be deployed in the Virtuozzo Amsterdam region, expanding European hosting options on the Chainstack platform. * **Clouds**. You can now deploy your elastic BNB Smart Chain full nodes in the Virtuozzo Amsterdam region. # Chainstack updates: February 21, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-21-2024 Subgraphs gain preconfigured deployments called Data APIs, giving developers production-ready indexed blockchain data without setting up custom subgraphs. **Subgraphs**. Preconfigured and deployed subgraphs called Data APIs released. # Chainstack updates: February 21, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-21-2025 Cronos mainnet archive nodes are now deployable as Chainstack Global Nodes, providing geo-balanced access to historical Cronos chain state for builders. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) in archive mode for Cronos mainnet. # Chainstack updates: February 22, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-22-2023 Harmony moves from devnet to testnet for a better developer experience, and you can now manually retry failed Chainstack billing payments via crypto or card. * **Protocols**. Harmony migrated from devnet to [testnet](/docs/protocols-networks) for better experience. * **Billing**. You can now settle your failed payment by manually retrying either via topping up your crypto balance or via paying directly from your credit card. # Chainstack updates: February 23, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-23-2024 Ronin Mainnet global elastic nodes can now be deployed in archive mode with debug & trace APIs, running on a Geth-based client for compatibility. **Global elastic nodes.** Now, you can deploy global elastic nodes in archive mode with debug & trace APIs for Ronin Mainnet. The node client is Geth-based. # Chainstack updates: February 26, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-26-2024 Ethereum Mainnet archive nodes with debug & trace APIs are now deployable as Chainstack global elastic nodes for low-latency access from anywhere. **Global elastic nodes.** Now, you can deploy global elastic nodes in archive mode with debug & trace APIs for Ethereum Mainnet. # Chainstack updates: February 27, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-27-2024 Polygon Mainnet archive nodes with debug & trace APIs are now deployable as Chainstack global elastic nodes, enabling deep PoS chain analysis at scale. **Global elastic nodes**. Now, you can deploy global elastic nodes in archive mode with debug & trace APIs for Polygon Mainnet. # Chainstack updates: February 28, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-28-2024 BNB Smart Chain Mainnet archive nodes with debug & trace APIs are now deployable as Chainstack global elastic nodes for full BSC transaction inspection. **Global elastic nodes**. Now, you can deploy global elastic nodes in archive mode with debug & trace APIs for BNB Smart Chain Mainnet. # Chainstack updates: February 5, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-5-2025 Ronin Saigon testnet archive nodes with debug & trace APIs are now deployable as Chainstack Global Nodes for testing Axie Infinity sidechain workflows. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) in archive mode with debug & trace APIs for Ronin Saigon testnet. # Chainstack updates: February 8, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-8-2023 Gnosis Chain Sokol testnet is now deprecated; developers should switch to the Gnosis Chain Chiado testnet for new projects on the Chainstack platform. * **Protocols**. Gnosis Chain Sokol testnet is now deprecated. For development purposes, use [Gnosis Chain Chiado](/docs/protocols-networks) testnet. # Chainstack updates: February 9, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-9-2023 The refined Chainstack billing page now surfaces failed payment notifications and clearly lists past-due invoices that are awaiting customer payment. * **Billing**. The refined billing page now shows: * Failed payment notification * Past due invoices awaiting payments # Chainstack updates: February 9, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-february-9-2024 Optimism Mainnet global elastic nodes powered by op-erigon are now available on Chainstack with debug & trace APIs enabled out of the box for OP Stack devs. **Global elastic nodes**. Now, you can deploy global elastic nodes for Optimism Mainnet. The node client is op-erigon. You can now enjoy the debug & trace APIs out of the box. For the method run-down, see [Optimism API reference](/reference/optimism-api-reference). # Chainstack updates: January 10, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-10-2023 Aptos blockchain support arrives on Chainstack along with documentation, letting developers deploy Aptos nodes and build Move-based applications easily. * **Protocols**. [Aptos](/docs/protocols-networks) support. * **Documentation**. Aptos # Chainstack updates: January 17, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-17-2023 Five additional Solana JSON-RPC API methods are now enabled on devnet, with corresponding updates published to the Chainstack Solana API reference docs. * **APIs**. 5 [Solana APIs](/reference/solana-getting-started) enabled on devnet. * **Documentation**. Solana API reference updates. # Chainstack updates: January 17, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-17-2025 Node requests metrics on the Chainstack platform are now granular to the minute, with selectable timeframes of 1 hour, 6 hours, 12 hours, 24 hours, and 7 days. **Platform**. Improved [node requests metrics](/docs/manage-your-node#view-node-requests-metrics) —the data is now granular to 1 minute; the timeframes are now 1 hour, 6 hours, 12 hours, 24 hours, 7 days. # Chainstack updates: January 18, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-18-2023 Global elastic nodes are now deployable for Base Sepolia, Optimism Sepolia, and zkSync Era Sepolia testnets, expanding L2 testnet coverage on Chainstack. **Global elastic nodes**. Now you can deploy global elastic nodes for Base Sepolia Testnet, Optimism Sepolia Testnet, zkSync Era Sepolia Testnet. # Chainstack updates: January 19, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-19-2023 Filecoin Hyperspace testnet support for elastic and dedicated nodes lands with documentation and a Hardhat tutorial for deploying deal-making contracts. * **Protocols**. [Filecoin](/docs/protocols-networks) Hyperspace testnet support for elastic and dedicated nodes. * **Documentation**. > [card] General Filecoin documentation. > [card] [Filecoin tutorial](/docs/filecoin-tutorial-deploy-a-deal-making-contract-on-filecoin-with-hardhat) # Chainstack updates: January 21, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-21-2026 Chainstack platform update — Tempo Moderato Testnet now available on Global Nodes and Dedicated Nodes, with tooling docs and payment app tutorials. **Protocols**. Tempo Moderato Testnet is now available on Chainstack. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Tempo Moderato Testnet. **Documentation**: * [Tempo tooling](/docs/tempo-tooling) — MetaMask, Viem, Wagmi, tempo-foundry, Hardhat, ethers.js, web3.py, and Remix IDE setup * [Tempo API reference](/reference/tempo-getting-started) — Complete RPC method documentation * [Tempo: Building your first payment app](/docs/tempo-tutorial-first-payment-app) — Tutorial on accepting stablecoin payments * [Tempo: Basic DEX swap with Foundry](/docs/tempo-tutorial-dex-swap-foundry) — Tutorial on swapping stablecoins using the built-in DEX # Chainstack updates: January 22, 2021 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-22-2020 Private hosting on customer-owned Amazon EKS clusters, AWS US East region availability, and identity management for Hyperledger Fabric networks. * **Private hosting**. You can now use your own Amazon Elastic Kubernetes Service (EKS) infrastructure to deploy blockchain nodes and networks on Chainstack. * **Clouds**. You can now deploy your nodes and networks in the [Amazon Web Services US East](https://support.chainstack.com/hc/en-us/articles/360024804711-Data-center-locations) region. * **Identity management**. You can now securely manage your organization's identity for Hyperledger Fabric networks. # Chainstack updates: January 22, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-22-2025 A new Pro pricing plan is introduced on Chainstack and all Tezos networks are deprecated, removing Tezos node deployment options from the platform. * **Pricing**. New Pro plan introduced. See [Pricing](https://chainstack.com/pricing). * **Protocols**. All Tezos networks are now deprecated. # Chainstack updates: January 22, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-22-2026 Chainstack platform update — TRON Mainnet and Nile Testnet now support gRPC endpoints on Global Nodes for high-performance blockchain data access. **Protocols**. TRON Mainnet and Nile Testnet now support gRPC endpoints on [Global Nodes](/docs/global-elastic-node). See also [TRON tooling](/docs/tron-tooling) and [authentication methods](/docs/authentication-methods-for-different-scenarios). # Chainstack updates: January 25, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-25-2024 Starknet Sepolia Testnet global elastic nodes are now available on Chainstack, giving Cairo developers low-latency RPC access for testing zk-rollup apps. **Global elastic nodes**. Now, you can deploy global elastic nodes for Starknet Sepolia Testnet. # Chainstack updates: January 30, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-30-2023 The refined Chainstack billing page now shows a subscription overview, a detailed usage table, and a list of crypto payments with downloadable invoices. * **Billing**. The refined billing page now shows: * An overview of your subscription plan and support level * A detailed **Usage** table * A list of crypto payments and downloadable invoices # Chainstack updates: January 30, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-30-2024 Base Mainnet global elastic nodes are now available on Chainstack, providing geo-balanced low-latency RPC access to Coinbase's Base L2 rollup network. **Global elastic nodes**. Now, you can deploy global elastic nodes for Base Mainnet. # Chainstack updates: January 5, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-5-2026 Chainstack Subgraphs is sunsetting and migrating users to Ormi by February 1 2026; existing schemas and data are preserved during the partner transition. **Subgraphs**. Chainstack is sunsetting its Subgraphs service and transitioning all Subgraphs users to [Ormi](https://ormilabs.com/), a next-generation subgraph indexer built for real-time production workloads. To ensure continuity, Chainstack has partnered with Ormi to support existing Subgraphs. All subgraph migrations will be handled by Ormi, and your data and schemas will remain unchanged. Your only required action is to swap the subgraph endpoint. Indexing will continue using Chainstack-powered RPC infrastructure. All users will receive an email invitation from Ormi to join a dedicated Telegram support group. We recommend joining this group to stay informed during the transition. In rare cases where subgraph code or deployment issues require additional steps, the Ormi team will coordinate directly with you there to ensure your subgraphs continue running smoothly. **What this means for you:** * No new Subgraphs can be created on Chainstack after January 5, 2026 * Subgraphs will be disabled on Chainstack on February 1, 2026 * No Subgraphs usage or billing will occur on Chainstack after February 1 To support a smooth transition, Ormi will provide one month of free access from February 1 to March 1, 2026 (00:00 UTC). For full details, timelines, and next steps, please review the [Ormi–Chainstack transition guide](https://docs.ormilabs.com/subgraphs/tutorials/migrate-chainstack-subgraphs). # Chainstack updates: January 7, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-7-2025 MEV protection toggle is now available for transactions on Chainstack Ethereum mainnet nodes, helping users avoid sandwich attacks and frontrunning. **Nodes**. Now you can toggle MEV protection for your transactions on the Ethereum mainnet nodes. See [MEV protection](/docs/mev-protection). # Chainstack updates: January 9, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-january-9-2025 A new state-of-the-art node selection wizard helps you pick between Global Node, Unlimited Node, Trader Node, or Dedicated Node based on your workload. **Nodes**. Enjoy a new state-of-the-art node selection wizard and easily select the node type that best matches your needs: [Global Node](/docs/global-elastic-node), [Unlimited Node](/docs/unlimited-node), [Trader Node](/docs/trader-node), or a [Dedicated Node](/docs/dedicated-node). # Chainstack updates: July 1, 2019 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-1-2019 New documentation: a guide for deploying a hybrid MultiChain network and a tutorial for building a Quorum loyalty program smart contract using Truffle. **Documentation** * New guide: Deploying a hybrid MultiChain network. * New tutorial: [Loyalty program with Truffle](/docs/quorum-tutorial-loyalty-program-with-truffle). # Chainstack updates: July 10, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-10-2023 zkSync Era Goerli Testnet support arrives on Chainstack for both elastic and dedicated nodes, opening L2 zk-rollup testing to all users on the platform. **Protocols**. zkSync Era Goerli Testnet support for elastic and dedicated nodes. # Chainstack updates: July 13, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-13-2023 Chainstack Subgraphs elastic indexers are now available for Avalanche, Fantom, and Gnosis Chain mainnets, expanding multi-chain on-chain data indexing. **Chainstack Subgraphs**. Elastic indexers are now available for Avalanche, Fantom, and Gnosis Chain mainnets. # Chainstack updates: July 14, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-14-2023 Global elastic node deployment is now available for Arbitrum and Fantom, providing geo-balanced low-latency RPC access for L2 and high-throughput chains. **Global elastic node**. Now you can deploy global elastic nodes for Arbitrum and Fantom. # Chainstack updates: July 17, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-17-2026 Robinhood Chain — an Ethereum L2 for tokenized stocks and real-world assets — is now supported on Chainstack across Global Nodes and Dedicated Nodes, Mainnet and Testnet, in Full and Archive modes. **Protocols**. Chainstack now supports Robinhood Chain — an Ethereum L2 (Arbitrum Orbit, Nitro client) for tokenized stocks and real-world assets. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Robinhood Chain Mainnet and Testnet, in Full and Archive modes with debug and trace. # Chainstack updates: July 18, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-18-2023 zkSync Era Mainnet support is added on Chainstack, and customers can now apply discount promo codes when changing their subscription plan in billing. * **Protocols**. zkSync Era Mainnet support. * **Billing**. When changing your subscription plan, you can now use promo codes with discounts to a plan's regular price. # Chainstack updates: July 20, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-20-2024 opBNB Mainnet Global Nodes are now available on Chainstack, providing low-latency RPC access to BNB Chain's OP Stack-based L2 rollup network. **Protocols**. Now, you can deploy Global Nodes for opBNB Mainnet. # Chainstack updates: July 21, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-21-2026 Starknet nodes are upgrading to Pathfinder v0.23.0 — a breaking change that removes JSON-RPC API versions 0.6, 0.7, and 0.8 and moves the default endpoint to version 0.9. **Starknet**. Chainstack is upgrading Starknet nodes to [Pathfinder v0.23.0](https://github.com/equilibriumco/pathfinder/releases/tag/v0.23.0). This is a breaking change to JSON-RPC API version support: versions 0.6, 0.7, and 0.8 are removed, and the default `/` endpoint now serves version 0.9. Pin `/rpc/v0_9` or `/rpc/v0_10` in your endpoint URL before the upgrade reaches your node. See [Pathfinder RPC version removal 0.23.0](/reference/starknet-pathfinder-0-23-0-rpc-version-removal) for the migration guide. # Chainstack updates: July 22, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-22-2026 Chainstack Sui gRPC endpoints now support gRPC-Web, so the official Sui TypeScript SDK works against Chainstack nodes over HTTPS on mainnet and testnet. **Protocols**. Sui gRPC endpoints now support gRPC-Web, so the official Sui TypeScript SDK (`@mysten/sui`) works against your Chainstack nodes over HTTPS on Mainnet and Testnet — including from the browser, with only your node URL. Native gRPC and JSON-RPC are unchanged. See [Sui tooling](/docs/sui-tooling#grpc-from-code) for the SDK setup, [Sui gRPC endpoint](/docs/sui-grpc-endpoint), and [migrating from JSON-RPC to gRPC](/docs/sui-migrate-json-rpc-to-grpc). # Chainstack updates: July 23, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-23-2026 The Chainstack MCP server's testnet faucet now advertises its supported networks to your agent and accepts common network-name aliases, so testnet fund requests land on the first try. **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) testnet faucet (`request_testnet_funds`) now tells your agent exactly which testnets it supports — the supported networks are published as a schema enum, so the agent picks a valid one up front instead of guessing. Common near-miss spellings are also accepted and normalized to the canonical id — `ethereum-sepolia` → `sepolia`, `base-sepolia` → `base`, `solana-devnet` → `solana` — so a slightly-off network name still delivers the funds. Supported testnets: sepolia, hoodi, base, amoy, bnb-testnet, zksync-testnet, scroll-sepolia-testnet, hyperevm, plasma, monad, ton, and solana (Solana devnet). # Chainstack updates: July 27, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-27-2023 Base Goerli Testnet support for elastic nodes is now live on Chainstack, allowing developers to build and test on Coinbase's L2 rollup before mainnet. **Protocols**. Base Goerli Testnet support for elastic nodes. # Chainstack updates: July 27, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-27-2026 Chainstack now supports Arc — Circle's USDC-native, EVM-compatible L1 — on Global Nodes and Dedicated Nodes for testnet, and the MCP server faucet adds Robinhood Chain testnet. **Protocols**. Chainstack now supports Arc — Circle's USDC-native, EVM-compatible L1 for stablecoin payments. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Arc testnet, in Full and Archive modes with debug and trace. **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) testnet faucet (`request_testnet_funds`) now supports **Robinhood Chain testnet** — request funds with `network="robinhood"` (it tops up to 1 ETH), and common aliases like `robinhood-chain` and `robinhood-testnet` resolve automatically. Scroll Sepolia has been retired from the faucet, so `scroll-sepolia-testnet` is no longer available. The supported testnet count is unchanged at 12. # Chainstack updates: July 28, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-28-2023 Solana global elastic nodes are now available on Chainstack, delivering geo-balanced low-latency RPC access for high-throughput Solana applications. **Global elastic node**. Now you can deploy global elastic nodes for Solana. # Chainstack updates: July 4, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-4-2023 Polygon and BNB Smart Chain global elastic nodes are now available on Chainstack, providing geo-balanced RPC access for both popular EVM L1 networks. **Global elastic node**. Now you can deploy global elastic nodes for Polygon and BNB Smart Chain. # Chainstack updates: July 6, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-6-2026 The Chainstack MCP server now reads your Alchemy usage via the Alchemy CLI when migrating, replacing the dashboard screenshot step. **MCP Server**. When migrating a project to Chainstack from Alchemy, the [Chainstack MCP server](https://mcp.chainstack.com) now reads your Alchemy usage programmatically via the [Alchemy CLI](https://www.alchemy.com/docs/alchemy-cli) (`alchemy --json usage summary`). It pulls your 30-day spend and compute-unit volume to estimate your equivalent Chainstack cost and savings. For the full migration flow, see the [migrate-to-Chainstack runbook](/docs/migrate-to-chainstack-with-ai). # Chainstack updates: July 7, 2020 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-7-2020 Bitcoin testnet support arrives on Chainstack, plus a new Growth pricing plan with reduced fixed and usage costs and uniform rates across cloud providers. * **Protocols**. Bitcoin testnet support. * **Pricing** * New Growth pricing plan. * Reduced fixed plan costs. * Reduced usage costs. * Uniform usage rates for all cloud providers. See the [new pricing plans](https://chainstack.com/pricing/) and a [blog post introducing the new pricing](https://chainstack.com/new-growth-tier-and-reduced-costs-for-all-plans/). # Chainstack updates: August 31, 2020 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-7-2020-1 Public API for managing all Chainstack resources, Corda 4.5 and Hyperledger Fabric 2.2 support, node start/stop controls, and Microsoft Azure UK South. * **Public API**. You can now use the Chainstack API to manage your resources. * **Protocols** * Corda 4.5 support. * Hyperledger Fabric 2.2 support. * **Node management**. You can now stop and start your nodes to save your usage costs. * **Service nodes**. You can now access in the UI the service nodes deployed with your consortium networks. * **Clouds**. You can now deploy your nodes and networks in the [Microsoft Azure UK South](https://support.chainstack.com/hc/en-us/articles/360024804711-Data-center-locations) region. * **Pricing**. No more user limit on all [pricing plans](https://chainstack.com/pricing/). # Chainstack updates: July 7, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-july-7-2022 Harmony moves from testnet to devnet, Tezos migrates to Ithacanet, shared nodes rebrand to elastic nodes, and Warp transactions launch on Ethereum nodes. * **Protocols**. * Harmony migrated from testnet to [devnet](/docs/protocols-networks) for better experience. * Tezos migrated from Hangzhounet to [Ithacanet](/docs/protocols-networks). * **Pricing**. An update to the pricing plans with new features and increased included requests. See [the blog post with an overview](https://chainstack.com/pricing-update-2022/). * **Node naming**. Shared nodes are now elastic nodes to reflect the underlying architecture and scalable infrastructure. * **APIs**. * Fast transaction propagation with the [Warp transactions](/docs/warp-transactions) feature on Ethereum, Polygon, and BNB Smart Chain. * [Miner Extractable Value (MEV) API](/changelog/chainstack-updates-april-12-2023) for elastic Ethereum nodes for mainnet and Goerli testnet. * [Debug and trace APIs](/docs/debug-and-trace-apis) for elastic Ethereum archive nodes. # Chainstack updates: June 1, 2021 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-1-2021 Binance Smart Chain mainnet and testnet node support, two-factor authentication for Chainstack accounts, and a BSC BEP-1155 contract tutorial. * **Protocols**. Binance Smart Chain support. You can now deploy full Binance Smart Chain nodes on mainnet and testnet. * **2FA**. You can now add an extra layer of security to Chainstack account with two-factor authentication (2FA). Once enabled, you will be prompted to enter a code generated by your mobile device each time you log in. * **Documentation**. Binance Smart Chain general description, operations, BEP-1155 contract tutorial. # Chainstack updates: June 1, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-1-2022 Ethereum Erigon client support for dedicated archive nodes lands on Chainstack, plus StarkNet testnet support for zk-rollup development on the platform. * **Protocols**. * Ethereum [Erigon support](/docs/protocols-clients) for dedicated archive nodes. * StarkNet [testnet](/docs/protocols-networks) support. # Chainstack updates: June 10, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-10-2025 Mantle Mainnet Global Nodes are now deployable on Chainstack with tooling docs and a Merchant Moe price tutorial; all Aurora nodes are deprecated. **Protocols**: * Mantle — You can now deploy [Global Nodes](/docs/global-elastic-node) for Mantle Mainnet. See also [Mantle tooling](/docs/mantle-tooling) and a [tutorial](/docs/mantle-fetching-token-prices-from-merchant-moe). * Aurora — All Aurora nodes are deprecated. # Chainstack updates: June 10, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-10-2026 Higher concurrent stream limits for the Yellowstone gRPC Geyser plugin — 2 streams on the \$49/month tier and 7 streams on the \$149/month tier. **Add-ons**. Higher concurrent stream limits for the [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin): * **\$49/month** — now 2 concurrent streams per platform account, up from 1. * **\$149/month** — now 7 concurrent streams per platform account, up from 5. All other limits are unchanged. See [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) for the full breakdown. # Chainstack updates: June 11, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-11-2024 Blast Mainnet global elastic nodes are now available on Chainstack, providing low-latency RPC access to the native yield-bearing L2 rollup network. **Protocols**. Now, you can deploy global elastic nodes for Blast Mainnet. # Chainstack updates: June 11, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-11-2025 Polkadot Mainnet Global Nodes are now deployable on Chainstack, with new Polkadot tooling docs and a network health monitoring tutorial for builders. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Polkadot Mainnet. See also [Polkadot tooling](/docs/polkadot-tooling) and a [tutorial](/docs/polkadot-network-health-monitoring). # Chainstack updates: June 11, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-11-2026 Warp transactions and MEV protection are now Chainstack node add-ons you install per node from the node's Add-ons tab. **Add-ons**. [Warp transactions](/docs/warp-transactions) and [MEV protection](/docs/mev-protection) are now Chainstack [node add-ons](/docs/add-ons) rather than baked-in node toggles. You install and manage them per node from the node's **Add-ons** tab. For what each add-on does and how to install or uninstall it, see [Warp transactions](/docs/warp-transactions), [MEV protection](/docs/mev-protection), and [add-ons](/docs/add-ons). # Chainstack updates: June 12, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-12-2025 Zora Mainnet Global Nodes are now deployable on Chainstack, with new Zora tooling docs and a tutorial for detecting newly deployed creator tokens. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Zora Mainnet. See also [Zora tooling](/docs/zora-tooling) and a [tutorial](/docs/zora-creator-token-detection-tutorial). # Chainstack updates: June 14, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-14-2023 Optimism Mainnet support for elastic and dedicated nodes is now live on Chainstack, expanding L2 OP Stack coverage for production rollup workloads. **Networks**. [Optimism Mainnet](/docs/protocols-networks) support. # Chainstack updates: June 15, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-15-2023 Chainstack pricing rehauled with request units for fairer flexibility, and Ethereum Mainnet global elastic nodes deliver geo-balanced enhanced performance. * **Pricing** * Chainstack pricing was rehauled. Check out the changes in our [pricing page](https://chainstack.com/pricing/). And learn more about [how our pricing works](/docs/pricing-introduction). * With pricing rehaul, we're introducing [request units](/docs/request-units#what-an-ru-is). This allows us to offer a fairer and more flexible pricing structure. * **Platform**. [Global elastic nodes](/docs/global-elastic-node) are available for Ethereum Mainnet. Enjoy geo-balanced nodes with enhanced performance and reduced latency. # Chainstack updates: June 17, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-17-2026 TRON gRPC now serves the protocol.WalletSolidity service for solidified data, alongside protocol.Wallet, on the same endpoint. **Protocols**. TRON gRPC now serves the [`protocol.WalletSolidity`](/docs/tron-tooling#available-services) service for solidified (confirmed) data, alongside `protocol.Wallet`. Both run on the same `:443` endpoint — select the one you need by the service (stub) in your generated client. Available on TRON mainnet and Nile testnet. For the full service list and usage examples, see [TRON tooling](/docs/tron-tooling#grpc-api). # Chainstack updates: June 2, 2020 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-2-2020 Corda 4.4 and Quorum 2.6.0 support, Tessera 0.10.5, a date-and-container node log viewer, dynamic resource visibility, and CorDapp install notifications. * **Protocols** * Corda 4.4 support. * Quorum 2.6.0 support. [Tessera](https://docs.tessera.consensys.net/) 0.10.5 support. * **Node logs**. You can now access the logs for your dedicated nodes in the new node log viewer, with the ability to browse by date and container. * **Node resources**. You can now view the resources that have been dynamically allocated to each of your dedicated nodes. * **CorDapp notifications**. You will now receive notifications based on the successful or failed result of CorDapp installations or removals initiated through Chainstack's CorDapp management interface. * **UI**. You can now view hosting information directly on the node list for each network. # Chainstack updates: June 2, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-2-2026 The Chainstack MCP server now migrates your project to Chainstack from another RPC provider — assessing usage, estimating savings, and repointing endpoints from inside your AI coding agent. **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) now helps you migrate to Chainstack from another RPC provider — QuickNode, Alchemy, or any other — without leaving your AI coding agent: * **Assess** — ask your agent to migrate and it fetches the [migration runbook](https://docs.chainstack.com/docs/migrate-to-chainstack-with-ai), scans your project for non-Chainstack RPC endpoints, and verifies Chainstack coverage for the chains and methods you use. * **Estimate savings** — it reads your current usage (QuickNode automatically via its Admin API; Alchemy or any other provider from a dashboard screenshot or the numbers you provide), then uses `get_chainstack_pricing` to show your equivalent Chainstack cost and the savings versus your current bill. * **Migrate** — after you review and approve the plan, it deploys any nodes you need and repoints your endpoints to Chainstack. The [Chainstack skill](https://mcp.chainstack.com/skill) gains a **Migrate from another provider** workflow, so the same flow works in any coding agent. # Chainstack updates: June 21, 2019 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-21-2019 Ethereum Ropsten testnet with Bolt support, Quorum Epirus blockchain explorer, expanded node access details, and a new Truffle academic certificates tutorial. * **Protocols** * Added Ethereum Ropsten testnet support with Bolt. * Updated Quorum explorer from [blk-explorer-free](https://github.com/blk-io/blk-explorer-free) to [epirus-free](https://github.com/blk-io/epirus-free). * **Node details**. Added complete details for Quorum, including default wallet private/public keys. Standardized fields for all protocols. * **Documentation**. New tutorial: [Academic certificates with Truffle](/docs/ethereum-tutorial-academic-certificates-with-truffle). # Chainstack updates: June 26, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-26-2024 Oasis Sapphire Mainnet and Testnet are now deployable as Chainstack global elastic nodes, providing low-latency access to confidential EVM smart contracts. **Global elastic nodes**. Now, you can deploy global elastic nodes for Oasis Sapphire Mainnet and Testnet. # Chainstack updates: June 27, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-27-2023 Optimism Mainnet archive nodes are now available on Chainstack, unlocking access to historical Optimism state for indexers, analytics, and forensic tools. **Protocols**. Archive nodes are now available for Optimism Mainnet. # Chainstack updates: June 27, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-27-2025 You can now configure access rules on Chainstack node endpoints to restrict requests by HTTP Origin header values or IP address allowlists per node. **Security**. You can now add [access rules](/docs/access-rules) to your node endpoints to restrict access based on HTTP `Origin` header values or IP addresses. # Chainstack updates: June 29, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-29-2025 Starknet Pathfinder v0.17.0 deprecates three Pathfinder-specific methods in favor of standardized starknet_* methods. **Starknet**. With [Pathfinder v0.17.0](https://github.com/equilibriumco/pathfinder/blob/v0.17.0/CHANGELOG.md#removed), three Pathfinder-specific methods are deprecated and removed: `pathfinder_getProof`, `pathfinder_getClassProof`, and `pathfinder_getTransactionStatus`. Use the standardized replacements — `starknet_getStorageProof` and `starknet_getTransactionStatus`. See [Pathfinder methods deprecation notice 0.17.0](/reference/starknet-pathfinder-methods-deprecation) for the migration guide. # Chainstack updates: June 3, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-3-2025 Berachain Mainnet Global Nodes are now deployable on Chainstack, accompanied by Berachain tooling docs and a Python on-chain data quickstart tutorial. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Berachain Mainnet. See also [Berachain tooling](/docs/berachain-tooling) and a [tutorial](/docs/berachain-on-chain-data-quickstart-with-python). # Chainstack updates: June 9, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-june-9-2025 Linea Mainnet Global Nodes are now deployable on Chainstack, with new Linea tooling docs and a Python tutorial for real-time transaction monitoring. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Linea Mainnet. See also [Linea tooling](/docs/linea-tooling) and a [tutorial](/docs/linea-real-time-transaction-monitor-python). # Chainstack updates: March 10, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-10-2022 Harmony Shard 0 mainnet and devnet support, a Harmony metaverse tutorial, and a self-service plan downgrade option in your Chainstack billing settings. * **Protocols**. Harmony support. You can now use shared and dedicated Harmony Shard 0 nodes on mainnet and devnet. * **Documentation**. Harmony general description, operations, simple metaverse tutorial. * **Subscription downgrading**. You have now the ability to downgrade your plan on the **Settings** > **Billing** page. # Chainstack updates: March 10, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-10-2024 All NEAR Protocol nodes on Chainstack are now deprecated, removing NEAR mainnet and testnet RPC node deployment options from the Chainstack platform. **Protocols**. All NEAR nodes are now deprecated. # Chainstack updates: March 11, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-11-2025 Gnosis Chain Chiado testnet Global Nodes are now deployable on Chainstack, providing geo-balanced low-latency RPC access for Gnosis Chain test workloads. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Gnosis Chain Chiado testnet. # Chainstack updates: March 12, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-12-2024 BNB Smart Chain Testnet archive nodes with debug & trace APIs are now deployable as Chainstack global elastic nodes for safe pre-deployment testing. **Global elastic nodes**. Now, you can deploy global elastic nodes in archive mode with debug & trace APIs for BNB Smart Chain Testnet. # Chainstack updates: March 12, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-12-2025 TRON mainnet Global Nodes are now deployable on Chainstack in full mode, with TRON tooling docs available for builders integrating the TRON RPC API. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) in full mode for the [TRON](/docs/tron-tooling) mainnet. # Chainstack updates: March 13, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-13-2025 Polygon zkEVM mainnet Global Nodes are now deployable on Chainstack, providing geo-balanced low-latency access to the Polygon zk-rollup execution layer. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for the Polygon zkEVM mainnet. # Chainstack updates: March 18, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-18-2026 Chainstack platform update — Tempo Mainnet now available on Global Nodes and Dedicated Nodes for building stablecoin payment applications. **Protocols**. Tempo Mainnet is now available on Chainstack. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Tempo Mainnet. See also [Tempo tooling](/docs/tempo-tooling) and [Tempo API reference](/reference/tempo-getting-started). # Chainstack updates: March 2, 2020 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-2-2020 Multi-org Hyperledger Fabric 2.0.1 support with Raft ordering, the new chaincode lifecycle, and a network explorer plus a Fabric chaincode tutorial. * **Protocols**. Hyperledger Fabric support. You can deploy a multi-org Hyperledger Fabric v2.0.1 network, complete with Raft ordering service, new chaincode lifecycle capabilities, and a network explorer. * **UI**. Updated cloud provider selector to provide easier visibility of available hosting options. * **Documentation**. Hyperledger Fabric general description, operations, chaincode tutorial. # Chainstack updates: March 20, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-20-2025 TRON Nile testnet support is added on Chainstack with full Global Nodes and Dedicated Nodes for testing TRON DApps before deploying to mainnet. **Protocols**. Now, you can deploy full [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for the TRON Nile testnet. # Chainstack updates: March 21, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-21-2024 Avalanche Fuji and Polygon Amoy testnet archive nodes with debug & trace APIs are now deployable as Chainstack global elastic nodes for L2 testing. **Global elastic nodes** for Avalanche Fuji Testnet and Polygon Amoy Testnet. * Avalanche Fuji Testnet — you can now deploy global elastic nodes in archive mode with debug & trace APIs. * Polygon Amoy Testnet — you can now deploy global elastic nodes in archive mode with debug & trace APIs. # Chainstack updates: March 21, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-21-2025 Aptos mainnet Global Nodes are now deployable on Chainstack, providing geo-balanced low-latency RPC access to the Aptos Move-based blockchain network. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for the Aptos mainnet. # Chainstack updates: March 22, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-22-2023 Chainstack opens the closed beta for IPFS Storage, a decentralized storage solution, with documentation and an API reference (now deprecated). * **IPFS Storage**. Chainstack launches the closed beta for the decentralized storage solution—IPFS Storage (deprecated). * **Documentation**. IPFS Storage and IPFS Storage API reference (deprecated). # Chainstack updates: March 24, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-24-2025 Aptos testnet support arrives on Chainstack with full Global Nodes deployable for testing Move-based contracts before promoting them to Aptos mainnet. **Protocols**. Now, you can deploy full [Global Nodes](/docs/global-elastic-node) for the Aptos testnet. # Chainstack updates: March 27, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-27-2023 Polygon zkEVM testnet support for elastic and dedicated nodes is now live, plus a Hardhat tutorial covering smart contract deployment to Polygon zkEVM. * **Protocols**. [Polygon zkEVM](/docs/protocols-networks) testnet support for elastic and dedicated nodes. * **Documentation** > [card] [Polygon zkEVM](/docs/protocols-networks) > [card] [Polygon zkEVM tutorial](/docs/polygon-zkevm-tutorial-deploy-a-smart-contract-using-hardhat) # Chainstack updates: March 27, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-27-2026 Chainstack platform update — Sui Testnet now available on Global Nodes with JSON-RPC and gRPC endpoint support for Sui blockchain development. **Protocols**. Sui Testnet is now available on Chainstack. You can deploy [Global Nodes](/docs/global-elastic-node) for Sui Testnet with JSON-RPC and gRPC endpoint support. See also [Sui tooling](/docs/sui-tooling). # Chainstack updates: March 29, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-29-2023 Oasis Sapphire elastic node support arrives on Chainstack, accompanied by documentation and a tutorial on building confidential EVM smart contracts. * **Protocols** > [card] [Oasis Sapphire support for elastic nodes](/docs/protocols-networks) > [card] [Oasis Sapphire tutorial support](/docs/oasis-sapphire-tutorial-understanding-confidential-smart-contracts-with-oasis-sapphire) * **Documentation** > [card] [Oasis Sapphire](/docs/protocols-networks) > [card] [Oasis Sapphire tutorial](/docs/oasis-sapphire-tutorial-understanding-confidential-smart-contracts-with-oasis-sapphire) # Chainstack updates: March 31, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-31-2023 Arbitrum One Mainnet elastic nodes can now be deployed in two new USA regions: full nodes on AWS Oregon and archive nodes on Virtuozzo Dallas. * **Clouds and regions**. You can now deploy your elastic Arbitrum One Mainnet nodes in the following USA regions: * Full nodes — Amazon Web Services Oregon * Archive nodes — Virtuozzo Dallas # Chainstack updates: March 4, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-4-2024 Ethereum Holesky and Sepolia testnet archive nodes running Erigon with debug & trace APIs are now deployable as Chainstack global elastic nodes. **Global elastic nodes** for Ethereum Holešky & Sepolia. * Ethereum Holešky Testnet — you can now deploy [global elastic nodes](/docs/global-elastic-node) in archive mode with debug & trace APIs. The node client is running Erigon. * Ethereum Sepolia Testnet — you can now deploy [global elastic nodes](/docs/global-elastic-node) in archive mode with debug & trace APIs. The node client is running Erigon. # Chainstack updates: March 4, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-4-2025 Sonic mainnet archive nodes with debug & trace APIs are now deployable as Chainstack Global Nodes, plus a Python tutorial for swap farming points. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) in archive mode with debug & trace APIs for the Sonic mainnet. See also a fun tutorial [Sonic: Swap farming for points walkthrough in Python](/docs/sonic-swap-farming-for-points-walkthrough-in-python). # Chainstack updates: March 5, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-5-2024 Arbitrum Sepolia Testnet archive nodes with debug & trace APIs are now deployable as Chainstack global elastic nodes for safe rollup contract testing. **Global elastic nodes**. Now, you can deploy global elastic nodes in archive mode with debug & trace APIs for Arbitrum Sepolia Testnet. # Chainstack updates: March 5, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-5-2025 MEV protection toggle is now available for transactions on Chainstack Binance Smart Chain mainnet nodes, helping traders avoid sandwich attacks on BSC. **Nodes**. Now you can toggle MEV protection for your transactions on the Binance Smart Chain mainnet nodes. See [MEV protection](/docs/mev-protection). # Chainstack updates: March 5, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-5-2026 Chainstack platform update — MegaETH Mainnet on Global Nodes and Dedicated Nodes, plus MegaETH Testnet on Dedicated Nodes for real-time EVM applications. **Protocols**. MegaETH is now available on Chainstack: * MegaETH Mainnet on [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) * MegaETH Testnet on [Dedicated Nodes](/docs/dedicated-node) # Chainstack updates: March 6, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-6-2023 Ethereum Rinkeby and Ropsten testnets are now deprecated on Chainstack; developers should migrate to the Sepolia and Goerli testnets for new projects. **Protocols**. Ethereum Rinkeby and Ropsten testnets are now deprecated. For development purposes, use [Sepolia and Goerli testnets](/docs/protocols-networks). # Chainstack updates: March 7, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-march-7-2025 Gnosis Chain mainnet archive nodes with debug & trace APIs are now deployable as Chainstack Global Nodes for deep historical analysis on Gnosis Chain. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) in archive mode with debug & trace APIs for Gnosis Chain mainnet. # Chainstack updates: May 1, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-1-2024 Scroll mainnet global elastic nodes on Chainstack now ship with debug and trace APIs enabled by default for thorough zk-rollup transaction inspection. **Global elastic nodes**. Now, Scroll mainnet nodes are deployed with [debug and trace APIs](/docs/debug-and-trace-apis). # Chainstack updates: May 10, 2021 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-10-2021 Polygon mainnet and testnet full and archive node support arrives on Chainstack, with operations docs and a smart contract bridging tutorial included. * **Protocols**. Polygon support. You can now deploy full and archive Polygon nodes on mainnet and testnet. * **Documentation**. Polygon general description, operations, smart contract bridging tutorial. # Chainstack updates: May 11, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-11-2023 Chainstack IPFS Storage now lets you create and manage dedicated IPFS gateways for greater control over how your files are served (now deprecated). **IPFS Storage**. You can now create and manage IPFS dedicated gateways to have more control over your files (deprecated). # Chainstack updates: May 12, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-12-2026 Chainstack MCP tools now advertise standard MCP annotations for safer execution, and the server reports its real release version on the initialize handshake. **MCP Server**. Every [Chainstack MCP server](https://mcp.chainstack.com) tool now advertises standard [MCP tool annotations](https://modelcontextprotocol.io/specification/2025-06-18/server/tools) (`readOnlyHint`, `destructiveHint`, `openWorldHint`): * Compatible clients (ChatGPT, Claude Code, Cursor, Windsurf) skip confirmation prompts on read-only calls — docs search, status, pricing — and require explicit approval on writes and deletes: creating, updating, or deleting projects and nodes, sending contact messages, and requesting testnet funds. * `serverInfo.version` on the MCP `initialize` handshake now reports the actual Chainstack release version instead of the underlying library version — visible wherever your agent shows the connected server's identity. # Chainstack updates: May 14, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-14-2025 Kaia Mainnet and Kaia Kairos Testnet are now available as Chainstack Global Nodes in full mode with Debug & Trace APIs for smart contract debugging. * **Global Nodes** — Kaia Mainnet and Kaia Kairos Testnet are now available in full mode with Debug & Trace APIs enabled, providing developers with enhanced capabilities for smart contract development and debugging. # Chainstack updates: May 16, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-16-2024 Avalanche Mainnet archive nodes with debug & trace APIs are now deployable as Chainstack global elastic nodes for in-depth C-Chain transaction analysis. **Global elastic nodes**. Now, you can deploy global elastic nodes in archive mode with debug & trace APIs for Avalanche Mainnet. # Chainstack updates: May 16, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-16-2025 Chainstack Subgraphs are now deployable on Ethereum Hoodi Testnet, giving developers indexed on-chain data for the latest Ethereum testnet environment. **Subgraphs**. Now, you can deploy [subgraphs](/docs/chainstack-subgraphs-tutorials) on Ethereum Hoodi Testnet. # Chainstack updates: May 18, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-18-2026 Flashblocks on Optimism is live on every Chainstack Optimism mainnet node, with 250ms preconfirmations via standard JSON-RPC calls with the pending block tag. **Protocols**. **Flashblocks on Optimism** is live on every Chainstack [Optimism](https://chainstack.com/build-better-with-optimism/) mainnet node. The Optimism sequencer streams Flashblocks every 250ms, and Chainstack Optimism nodes expose the preconfirmation state through standard JSON-RPC calls with the `pending` block tag — `eth_getBlockByNumber`, `eth_call`, `eth_getBalance`, `eth_getTransactionCount`, `eth_getCode`, `eth_getStorageAt`, `eth_estimateGas` — plus `eth_getTransactionReceipt`, `eth_getTransactionByHash`, and `eth_sendRawTransactionSync` for preconfirmed transactions. See [Flashblocks on Optimism](/docs/flashblocks-on-optimism) for the full guide, including the Optimism vs. Base differences, performance expectations, and a verification snippet. # Chainstack updates: May 22, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-22-2026 The Chainstack MCP server now supports all three Google Antigravity 2.0 surfaces — Antigravity CLI, the desktop app, and the IDE. **MCP Server**. All three Google Antigravity 2.0 surfaces — **Antigravity CLI** (the new `agy` terminal tool), the **Antigravity** desktop app, and the **Antigravity IDE** — are now explicitly supported on the [discovery page at mcp.chainstack.com](https://mcp.chainstack.com): in the skill-install table, the install example, and the MCP registration block. All three share the same `~/.gemini/antigravity/mcp_config.json` config and `~/.agents/skills/chainstack/` skill directory. Gemini CLI users — heads up: Google [announced](https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/) that Gemini CLI is being retired on June 18, 2026 in favor of Antigravity CLI. Both are already supported with separate setup blocks, and the migration is mostly seamless since they share the same MCP config path. # Chainstack updates: May 23, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-23-2024 Klaytn Mainnet global elastic nodes are now available on Chainstack, providing geo-balanced low-latency RPC access to the Klaytn EVM-compatible network. **Protocols**. Now, you can deploy global elastic nodes for Klaytn Mainnet. # Chainstack updates: May 26, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-26-2026 Hyperliquid /nanoreth is now available on testnet as well as mainnet, with protocol-level system transactions included in HyperEVM JSON-RPC responses on every node. **Protocols**. **Hyperliquid `/nanoreth`** is now available on testnet as well as mainnet. Both `/evm` (default, hl-node-compliant — system transactions stripped from block-shaped responses) and `/nanoreth` (system transactions included) paths exist by default on every Chainstack [Hyperliquid](https://chainstack.com/build-better-with-hyperliquid/) node, mainnet and testnet. There is nothing to enable — append the path you need to your endpoint URL. For testnet, use `hyperliquid-testnet.core.chainstack.com` with the same `/evm` and `/nanoreth` paths. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) for the per-method behavior matrix. # Chainstack updates: May 27, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-27-2024 Celo Mainnet and Moonbeam Mainnet global elastic nodes are now available on Chainstack, expanding L1 EVM-compatible chain coverage for builders. **Protocols**. Now, you can deploy global elastic nodes for Celo Mainnet and Moonbeam Mainnet. # Chainstack updates: May 28, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-28-2025 Sui Mainnet Global Nodes are now deployable on Chainstack, with new Sui tooling documentation and a pysui tutorial for on-chain validator analytics. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Sui Mainnet. See also [Sui tooling](/docs/sui-tooling) and a [tutorial](/docs/sui-on-chain-validator-analytics-with-pysui). # Chainstack updates: May 28, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-28-2026 Every Chainstack MCP server tool now publishes a typed output schema, so agents can consume structured results without guessing field names. **MCP Server**. Every tool on the [Chainstack MCP server](https://mcp.chainstack.com) now publishes a typed [`outputSchema`](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#output-schema) describing the fields it returns — for example, a project declares `id`, `name`, `description`, `networks`, `created_at`, and `creator`, and a node carries typed `https_endpoint`, `wss_endpoint`, `api_namespaces`, `cloud`, and `blockchain`. * List-returning tools (`list_projects`, `list_nodes`, `get_deployment_options`, `search_docs`) now emit `structuredContent` alongside the existing text — an additive change, so clients that ignored it before keep working unchanged. * Agents consuming MCP structured output can extract data reliably without guessing at field names. No behavior change to existing tool calls. # Chainstack updates: May 29, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-29-2023 Email verification is now mandatory for newly created Chainstack accounts, ensuring every new user confirms ownership of their registered email address. * **Accounts**. Email verification is now mandatory for newly created accounts. # Chainstack updates: May 30, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-30-2023 Filecoin Hyperspace Testnet is now deprecated on Chainstack; developers should migrate Filecoin development workloads to the Filecoin Calibration Testnet. * **Networks**. Filecoin Hyperspace Testnet is deprecated. For development purposes, use Filecoin Calibration Testnet. # Chainstack updates: May 5, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-5-2022 StarkNet support and an L1-L2 messaging NFT tutorial using Nile arrive on Chainstack, plus AWS Tokyo region availability for new node deployments. * **Protocols**. StarkNet support. * **Documentation**. StarkNet general description, operations, [simple L1L2 messaging tutorial](/docs/starknet-tutorial-an-nft-contract-with-nile-and-l1-l2-reputation-messaging). * **Clouds**. You can now deploy your nodes in the [Amazon Web Services Tokyo](https://support.chainstack.com/hc/en-us/articles/360024804711-Data-center-locations) region. # Chainstack updates: May 7, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-7-2025 Ethereum Hoodi Testnet is now available as a Chainstack Global Node in archive mode with Debug & Trace APIs for testing and debugging smart contracts. * **Global Nodes** — Ethereum Hoodi Testnet is now available in archive mode with Debug & Trace APIs enabled, providing developers with enhanced capabilities for testing and debugging smart contracts. # Chainstack updates: May 9, 2019 Source: https://docs.chainstack.com/changelog/chainstack-updates-may-9-2019 Elastic Ethereum mainnet node support arrives on Chainstack, and the project description field becomes optional when creating a new project on the platform. * **Protocols**. Introduced elastic Ethereum mainnet nodes support. * **Projects**. The project description field is now optional. # Chainstack updates: November 14, 2019 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-14-2019 Bitcoin 0.18 dedicated node support with Bolt sync, free elastic mainnet node access, network and node statuses, and updated billing usage line items. * **Protocols**. Bitcoin 0.18 support. You can now deploy dedicated nodes with Bolt synchronization and free access to elastic nodes on the mainnet. * **Network and node statuses**. You can now see network and node statuses to when operations are occurring on any network resource. * **Node details**. Node access and credentials are now grouped into sections, so it's easier to read. * **Billing**. View individual metered usage cost line items for the current billing period, updated hourly. * **Documentation**. Bitcoin general description and Bitcoin operations. # Chainstack updates: November 16, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-16-2022 StarkNet testnet2 (Goerli2) support for elastic and dedicated nodes lands on Chainstack, expanding StarkNet test environments for Cairo zk-rollup builders. * **Protocols**. [StarkNet](/docs/protocols-networks) testnet2 (Goerli2) support for elastic and dedicated nodes. # Chainstack updates: November 16, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-16-2023 Ronin Mainnet and Ronin Saigon Testnet global elastic and dedicated node support arrives on Chainstack for the Axie Infinity sidechain ecosystem. **Protocols**. You can now deploy global elastic nodes and dedicated nodes for Ronin Mainnet and Ronin Saigon Testnet. # Chainstack updates: November 19, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-19-2025 Chainstack platform update — Sui nodes now support gRPC endpoints for high-performance data access alongside existing JSON-RPC and WebSocket options. **Protocols**. Sui nodes now support gRPC endpoints for high-performance access. See [Sui tooling](/docs/sui-tooling) and [authentication methods](/docs/authentication-methods-for-different-scenarios). # Chainstack updates: November 2, 2020 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-2-2020 You can now reattempt past-due Chainstack billing payments via card change or directly in the activity log, which now includes more billing event detail. * **Billing**. You can now reattempt past due payments through billing card change or directly in the activity log. The activity log also provides more information on your billing events. # Chainstack updates: November 2, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-2-2026 You can now rotate a node's credentials on Chainstack without downtime. A rotation replaces the node's auth token, username and password, and gRPC x-token together. **Node credential rotation**. You can now rotate a node's credentials on Chainstack without downtime. Generate new credentials, update your applications while the old and new credentials both work, and then deactivate the old ones. A rotation replaces the node's auth token, username and password, and gRPC x-token together. See [Rotate node credentials](/docs/manage-your-node#rotate-node-credentials). # Chainstack updates: November 20, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-20-2023 Starknet Mainnet global elastic nodes are now available on Chainstack, providing geo-balanced low-latency Cairo zk-rollup RPC access for production apps. **Global elastic node**. Now you can deploy global elastic nodes for Starknet Mainnet. # Chainstack updates: November 21, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-21-2025 Monad Testnet and Sonic Testnet Global Nodes are now deployable on Chainstack, expanding test network coverage for emerging high-performance EVM chains. **Protocols**: * Monad — You can now deploy [Global Nodes](/docs/global-elastic-node) for Monad Testnet. * Sonic — You can now deploy [Global Nodes](/docs/global-elastic-node) for Sonic Testnet. # Chainstack updates: November 23, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-23-2022 Chainstack Marketplace publishes its app listing guidelines, providing partners with the requirements for getting their applications featured to customers. * **Documentation**. [Chainstack Marketplace listing guidelines](/docs/list-your-app-on-marketplace). # Chainstack updates: November 24, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-24-2025 Chainstack platform update — Monad Mainnet Global Nodes now available, with API reference documentation, tooling guides, and JavaScript/Python tutorials. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Monad Mainnet. See also [Monad API reference](/reference/monad-getting-started), [Monad tooling](/docs/monad-tooling), and tutorials on [querying with JavaScript](/docs/monad-tutorial-querying-blockchain-javascript) and [Python](/docs/monad-tutorial-querying-blockchain-python). # Chainstack updates: November 29, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-29-2022 Solana JSON-RPC API reference documentation is updated on Chainstack with refreshed parameter details, examples, and method coverage for Solana developers. * **Documentation**. Solana API [reference](/reference/solana-getting-started) update. # Chainstack updates: November 29, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-29-2023 Starknet mainnet and testnet nodes on Chainstack now support WebSocket connections, enabling real-time event subscriptions for Cairo zk-rollup applications. **Starknet**. Starknet mainnet and testnet nodes now support WebSocket. # Chainstack updates: November 3, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-3-2022 Solana elastic and dedicated full nodes are now supported on both mainnet and devnet using Chainstack Cloud hosting in the Ashburn USA region. * **Protocols**. [Solana](/docs/protocols-networks) elastic and dedicated full nodes now supported on both mainnet and devnet when using Chainstack Cloud in the Ashburn, USA region. # Chainstack updates: November 30, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-30-2023 Pay-as-you-go billing is now released on Chainstack, letting customers exceed plan quotas without service interruption or enforce a hard usage cap if disabled. **Billing**. Pay-as-you-go released. You can now enable it in your [Billing](https://console.chainstack.com/user/settings/billing) to keep the Chainstack services operational on reach your plan's quota limit, or disable it to have a hard limit and stopping the services on reaching the quota. # Chainstack updates: November 4, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-4-2025 Chainstack platform updates for November 4, 2025 covering new features, protocol support changes, and infrastructure improvements released this week. **Protocols**. Now, you can deploy Bitcoin Signet for [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node). # Chainstack updates: November 6, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-6-2024 Solana Devnet nodes on Chainstack now run on the Agave client, accompanied by a Solana Agave 2.0 upgrade reference for migrating production workloads. **Nodes**. The Solana nodes on the Devnet are now running on the Agave client. See also [Solana Agave 2.0 upgrade reference](/docs/solana-agave-20-upgrade-reference). # Chainstack updates: November 7, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-7-2022 Aurora Network support for dedicated nodes is now live on Chainstack, allowing developers to deploy Aurora EVM-compatible NEAR sidechain infrastructure. * **Protocols**. Aurora support for dedicated nodes. # Chainstack updates: November 8, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-november-8-2022 You can now change your organization support level directly on the Settings > Billing page, with documentation explaining how to manage plans and tiers. * **Support levels management**. You can now change a support level for your organization on the **Settings** > **Billing** page. * **Documentation**. Learn how to [change your support level or subscription plan](/docs/manage-your-billing#manage-your-organization-subscription-plan-and-support-level). # Chainstack updates: October 1, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-1-2024 The Chainstack faucet at faucet.chainstack.com gets a brand-new improved interface, making it easier to request testnet tokens across supported networks. **Faucet**. The [Chainstack faucet](https://faucet.chainstack.com/) has a brand new and highly improved interface. # Chainstack updates: October 1, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-1-2026 Elysium Testnet is now deployable on Chainstack as a Global Node or a Dedicated Node. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Elysium Testnet. # Chainstack updates: October 11, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-11-2026 The Chainstack MCP server adds five tools that manage node access rules, so your AI agent can restrict a Global Node to allowed IP addresses or origins. **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) can now manage [access rules](/docs/access-rules), so your AI agent can restrict who calls a node's endpoints by client IP address or by the browser's `Origin` header. Five new tools list, create, update, and delete rules, and activate or deactivate every rule on a node at once. Access rules work on [Global Nodes](/docs/global-elastic-node) on paid plans, and a new rule does nothing until you activate it. When IP address rules and allowed origin rules are both active, a request must come from an allowed IP address and also send an allowed `Origin` header. Version 1.6 of the [Chainstack skill](https://mcp.chainstack.com/skill) adds an access rules workflow in which the agent confirms with you that the rules cover every IP address that calls the node before it activates them. Validation errors from the Chainstack API now name the rejected field and the reason, and the server instructions and every tool description now fit within the 2,048-character limit of Claude Code, so Claude Code shows them in full. Connect at `https://mcp.chainstack.com/mcp`, or see the [discovery page](https://mcp.chainstack.com) for per-client setup. # Chainstack updates: October 12, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-12-2022 Solana elastic nodes can now be deployed using Chainstack Cloud hosting in the Netherlands region, expanding European hosting options for Solana developers. * **Clouds**. You can now deploy your elastic Solana nodes using the new hosting option, Chainstack Cloud, in the Netherlands region. # Chainstack updates: October 12, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-12-2026 The Chainstack MCP server now returns every project in organizations with more than 100, names the city for each deployment region, and tells your AI agent when a testnet faucet top-up is done. **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) has these updates: * `list_projects` returns every project in organizations with more than 100 projects. Previously, the second page of results repeated the first. * Deployment options include `region_name`, the city for each region code, so your AI agent can match a location such as London without guessing codes. * When the [Chainstack faucet](/docs/faucets) tops up an address through the MCP server, the response tells the agent that the funds were sent and not to request the same network again. * The faucet tool states the cooldown: one top-up per network every 24 hours, refused if your organization or the destination address already got funds on that network. Connect at `https://mcp.chainstack.com/mcp`, or see the [discovery page](https://mcp.chainstack.com) for per-client setup. # Chainstack updates: October 14, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-14-2022 Cronos blockchain support is now live on Chainstack, complete with documentation for Cronos node deployment and Crypto.com EVM-compatible chain operations. * **Protocols**. [Cronos](/docs/protocols-networks) support. * **Documentation**. Cronos. # Chainstack updates: October 16, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-16-2026 Now you can deploy Global Nodes for X Layer Mainnet and Testnet in Archive mode with debug and trace. **Protocols**. X Layer is now available on Chainstack Global Nodes. X Layer is OKX's Ethereum L2 built on the OP Stack. You can deploy [Global Nodes](/docs/global-elastic-node) for X Layer Mainnet and Testnet, in Archive mode with debug and trace. [Dedicated Nodes](/docs/dedicated-node) for X Layer remain available. See [Networks](/docs/protocols-networks). # Chainstack updates: October 17, 2019 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-17-2019 Corda 4.1 with network map service and notary, Quorum 2.3.0 with Tessera, in-org user invitations, hourly metered usage, and a no-ticket-scalping CorDapp. * **Protocols** * Corda 4.1 support. You can now deploy a consortium network with network map service, doorman, and notary. Dynamically add and remove participant nodes. Install and remove CorDapps. * Quorum 2.3.0 support. Replaced Constellation transaction manager with Tessera. * **User management**. Invite user s into the organization. * **Billing**. View metered usage cost for the current billing period, updated hourly. * **Documentation** * Corda general description and Corda operations. * New tutorials: [No ticket scalping CorDapp](/docs/corda-tutorial-no-ticket-scalping-cordapp) and [Trust fund account with Remix](/docs/ethereum-tutorial-trust-fund-account-with-remix). # Chainstack updates: October 17, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-17-2023 Scroll Mainnet and Aurora Testnet global elastic nodes are now available, while Starknet Testnet2 is deprecated; use Starknet Testnet for development. * **Global elastic node**. Now you can deploy global elastic nodes for Scroll Mainnet and Aurora Testnet. * **Protocols**. Starknet Testnet2 is now deprecated. For development purposes, use Starknet Testnet. # Chainstack updates: October 18, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-18-2025 Plasma Mainnet and Testnet archive nodes with Debug & Trace APIs are now deployable as Chainstack Global Nodes for advanced Plasma chain analysis. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) in archive mode with Debug & Trace APIs for Plasma Mainnet and Testnet. # Chainstack updates: October 19, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-19-2023 Ethereum Holesky Testnet global elastic nodes are now available on Chainstack, providing low-latency RPC access for the Ethereum staking-focused testnet. **Global elastic node**. Now you can deploy global elastic nodes for Ethereum Holešky Testnet. # Chainstack updates: October 19, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-19-2026 Zcash — a privacy-focused UTXO L1 running the Zebra client — is now available on Chainstack Global Nodes for Mainnet and Testnet. **Protocols**. Zcash is now available on Chainstack Global Nodes. Zcash is a privacy-focused UTXO L1 with shielded transactions and a Bitcoin-style JSON-RPC API with Zcash-specific `z_*` methods for shielded pool state. You can deploy [Global Nodes](/docs/global-elastic-node) for Zcash Mainnet and Testnet, in Full mode, with transparent transaction history available back to the first block. See [Networks](/docs/protocols-networks). # Chainstack updates: October 2, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-2-2024 The Chainstack faucet is now also available as a Telegram Mini App via @ChainstackFaucetBot, making testnet token requests possible directly inside Telegram. **Faucet**. You can now access the Chainstack faucet as a Telegram Mini App (TMA). Open [Chainstack Faucet TMA](https://t.me/ChainstackFaucetBot). # Chainstack updates: October 23, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-23-2026 Every Chainstack MCP server tool now has a readable title and states how it behaves, and VS Code no longer asks you to review results from tools that return Chainstack's own docs, status and pricing. **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) has been optimized with the following updates: * Every tool has a readable title, such as **Create node** or **Search Chainstack docs**. Clients that display titles, such as VS Code, show it in place of the raw tool name. * Every tool declares all four MCP annotation hints, which your AI client uses to decide when to ask you before a call. The hints say: * Whether the tool only reads data * Whether it can change or delete something * Whether repeating a call is safe * Whether its results can contain text from outside Chainstack * Tools that return Chainstack's own content no longer trigger a result review in VS Code before your agent sees the output: * Docs * Platform status * Pricing * Deployment options * Tools that return your project, node or organization data, and every tool that changes something, keep their review and approval prompts. Connect at `https://mcp.chainstack.com/mcp`, or see the [discovery page](https://mcp.chainstack.com) for per-client setup. # Chainstack updates: October 25, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-25-2023 The Chainstack Developer subscription plan now has a 30-request-per-second rate limit; this rate cap does not apply to other paid Chainstack subscription plans. **Console**. Now Developer subscription plan has a rate limit of 30 request per seconds. Note that this does not apply to all other subscription plans. # Chainstack updates: October 27, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-27-2026 Chainstack upgraded Avalanche Global Nodes to AvalancheGo v1.15.2. AvalancheGo v1.15.2 removes the C-Chain method avax.getAtomicTxStatus; use avax.getAtomicTx. **Avalanche**. Chainstack upgraded Avalanche [Global Nodes](/docs/global-elastic-node) on Mainnet and Fuji Testnet to [AvalancheGo v1.15.2](https://github.com/ava-labs/avalanchego/releases/tag/v1.15.2). This version removes the C-Chain method `avax.getAtomicTxStatus`, deprecated since v1.15.0, so use `avax.getAtomicTx` instead. `platform.getCurrentValidators` now returns `restakedValidationRewards` and `restakedDelegateeRewards` for auto-renewed validators. See [Avalanche Helicon network upgrade](/docs/avalanche-helicon-upgrade). # Chainstack updates: October 3, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-3-2022 Debug and trace APIs are now available for elastic Polygon archive nodes and elastic Avalanche archive nodes on the Chainstack node-as-a-service platform. * **APIs**. * [Debug and trace APIs](/reference/polygon-debug-trace-rpc-methods) for elastic Polygon archive nodes. * [Debug and trace APIs](/reference/avalanche-debug-trace-rpc-methods) for elastic Avalanche archive nodes. # Chainstack updates: October 3, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-3-2022-1 Ethereum Sepolia testnet support arrives for full nodes on Chainstack, accompanied by an updated Solana JSON-RPC API reference for Solana developers. * **Protocols**. Ethereum [Sepolia testnet support](/docs/protocols-networks) for full nodes. * **Documentation**. Solana API [reference](/reference/solana-getting-started). # Chainstack updates: October 31, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-31-2023 Arbitrum Sepolia Testnet global elastic and dedicated nodes are now deployable on Chainstack, giving rollup developers low-latency testnet RPC access. **Protocols**. You can now deploy global elastic nodes and dedicated nodes for Arbitrum Sepolia Testnet. # Chainstack updates: October 31, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-31-2025 Chainstack platform updates for October 31, 2025 covering new features, protocol support changes, and infrastructure improvements released this week. **Protocols**. Ethereum Holesky Testnet has been deprecated. Please use [Sepolia Testnet or Hoodi Testnet](/docs/protocols-networks) for Ethereum testing. # Chainstack updates: October 5, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-5-2023 Aurora Mainnet global elastic nodes arrive on Chainstack, while all Fuse Network deployment options are simultaneously deprecated from the platform. * **Global elastic node**. Now you can deploy global elastic nodes for Aurora Mainnet. * **Protocols**. All Fuse networks are now deprecated. # Chainstack updates: October 5, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-5-2026 Ink Mainnet is now deployable on Chainstack as a Global Node. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Ink Mainnet. # Chainstack updates: October 6, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-6-2026 The Chainstack MCP server gets a dependency refresh that resolves all open security advisories, a 30-minute idle-session timeout, and more memory headroom. **MCP Server**. The [Chainstack MCP server](https://mcp.chainstack.com) has moved to the latest FastMCP 3.x and MCP Python SDK releases, which resolves every open security advisory in its dependency tree. One behaviour change comes with it: idle MCP sessions now close after 30 minutes of inactivity. If your client reconnects after a long pause, it receives a `404` on the old session and re-initializes; Claude Code, Cursor, Codex and other spec-compliant clients handle this automatically. We also raised the server's memory headroom to reduce restarts during traffic spikes. No tools were added or changed. Connect at `https://mcp.chainstack.com/mcp`, or see the [discovery page](https://mcp.chainstack.com) for per-client setup. # Chainstack updates: October 7, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-7-2026 Multichain API lets you use one auth token across the Global Nodes and Dedicated Nodes you attach to a multichain endpoint. **Multichain API**. You can now use one auth token across the [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) you attach to a multichain endpoint, instead of one auth token per node. Available on every plan, including the Developer plan. See [Use Multichain API](/docs/multichain-api). # Chainstack updates: October 8, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-october-8-2026 Now you can deploy Global Nodes for Ink Testnet and Celo Sepolia Testnet. Chainstack upgraded Starknet Global Nodes to Pathfinder v0.24.2. starknet_estimateMessageFee now returns an overall_fee of at least 0.00005 ETH. **Protocols**. Now, you can deploy [Global Nodes](/docs/global-elastic-node) for Ink Testnet and Celo Sepolia Testnet. **Starknet**. Chainstack upgraded Starknet [Global Nodes](/docs/global-elastic-node) on Mainnet and Sepolia Testnet to [Pathfinder v0.24.2](https://github.com/software-mansion/pathfinder/releases/tag/v0.24.2). [`starknet_estimateMessageFee`](/reference/starknet-starknetestimatemessagefee) now returns an `overall_fee` of at least 50,000,000,000,000 wei (0.00005 ETH). When the gas amounts multiplied by their prices add up to less than that, `overall_fee` is exactly 50,000,000,000,000 wei. # Chainstack updates: September 08, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-08-2023 Elastic zkSync Era archive nodes can now be deployed on Chainstack with the debug and trace option enabled for in-depth zk-rollup transaction analysis. **Protocols**. Now you can deploy elastic zkSync Era archive nodes with debug and trace option enabled. # Chainstack updates: September 10, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-10-2024 TON Mainnet and Testnet Global Nodes are now available on Chainstack, with TON tooling, tutorials, and the TON API reference for builders. **Protocols**. Now, you can deploy Global Nodes for TON Mainnet & Testnet. See also [TON tooling](/docs/ton-tooling), [TON tutorials](/docs/protocols-tutorials), and the [TON API reference](/reference/getting-started-ton). # Chainstack updates: September 11, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-11-2026 Bitcoin Testnet4 is now deployable on Chainstack as Global Nodes and Dedicated Nodes. **Protocols**. Now, you can deploy Bitcoin Testnet4 for [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node). # Chainstack updates: September 12, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-12-2022 Aurora support is now available on Chainstack, allowing developers to deploy Aurora EVM-compatible NEAR sidechain nodes for cross-chain DApp building. **Protocols**. Aurora support. # Chainstack updates: September 13, 2023 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-13-2023 Chainstack Subgraphs elastic indexer is now available for Ethereum Sepolia Testnet, Base Mainnet, and Base Testnet for indexing on-chain data at scale. **Chainstack Subgraphs**. Elastic indexer is now available for Ethereum Sepolia Testnet and Base Mainnet and Testnet. # Chainstack updates: September 13, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-13-2024 You can now access detailed request statistics for both nodes and subgraphs on Chainstack via the new Statistics page in the customer console UI. **Platform**. You can now access your requests stats for nodes and subgraphs. See [Statistics](/docs/see-statistics). # Chainstack updates: September 15, 2022 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-15-2022 Ethereum consensus layer Beacon Chain support arrives on Chainstack post-Merge, with a dedicated Beacon Chain API reference added to Ethereum API docs. * **Protocols**. [Ethereum](/docs/protocols-networks) consensus layer Beacon Chain support due to the Merge. * **APIs**. [Ethereum consensus layer Beacon Chain API](/reference/ethereum-getting-started). * **Documentation**. Beacon Chain API [reference](/reference/ethereum-getting-started) added to Ethereum API reference. # Chainstack updates: September 16, 2020 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-16-2020 Ethereum Rinkeby testnet support, Quorum 2.7.0 client upgrade, and GraphQL query support for dedicated Ethereum full and archive nodes on Chainstack. * **Protocols**. * Ethereum Rinkeby support. * Quorum 2.7.0 support. * **GraphQL**. You can now query dedicated Ethereum full nodes and archive nodes with GraphQL. # Chainstack updates: September 16, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-16-2026 Arc Mainnet is now available on Chainstack Global Nodes and Dedicated Nodes, and Global Nodes now show an Extended label for Full nodes that keep more ledger history than a standard full node. **Protocols**. Arc Mainnet is now available on Chainstack. Arc is Circle's USDC-native, EVM-compatible L1 for stablecoin payments. You can deploy [Global Nodes](/docs/global-elastic-node) and [Dedicated Nodes](/docs/dedicated-node) for Arc Mainnet, in Full and Archive modes with debug and trace. **Nodes**. Global Nodes now show an **Extended** label for Full nodes that keep more ledger history than a standard full node — more retention than Full, without being a complete Archive. It adds no pricing or request-unit change. # Chainstack updates: September 19, 2024 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-19-2024 The Chainstack faucet at faucet.chainstack.com can now disperse testnet TON tokens, expanding free testnet token access to The Open Network developers. **Faucet**. The [Chainstack faucet](https://faucet.chainstack.com/) can now disperse testnet TON. # Chainstack updates: September 28, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-28-2026 Sui Mainnet Global Nodes now serve the GraphQL RPC API alongside gRPC and JSON-RPC, on the same endpoint and auth token. **Protocols**. Sui Mainnet Global Nodes now serve the GraphQL RPC API at `/graphql`, alongside gRPC and JSON-RPC on the same endpoint and auth token. Use GraphQL for filtered, paginated reads — transactions by address, events by type, objects by owner and type — that have no gRPC equivalent. Each request is 1 RU, the same as gRPC. Point lookups and unfiltered lists reach back to January 2026, and that window grows with the chain; filtered transaction and event queries cover a rolling window of up to 90 days. See [Sui GraphQL endpoint](/docs/sui-graphql-endpoint). # Chainstack updates: September 3, 2019 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-3-2019 Protected endpoints for Quorum and Ethereum nodes, hourly Bolt snapshots, in-platform notifications, an activity log, and a full documentation overhaul. * **Security**. [Protected endpoints](https://chainstack.com/protected-endpoints-for-ethereum-and-quorum-nodes-on-chainstack/) added for Quorum and Ethereum nodes. * **Deployment**. Bolt snapshots are now updated hourly so that nodes are deployed and synchronized with fresher snapshots. * **Protocols.** Numerous stability improvements for Quorum networks and nodes. * **Activity and events**. In-platform notifications and activity log introduced to provide visibility into account activity. * **Documentation.** Complete content and structure overhaul for improved access, browsing, and discovery. # Chainstack updates: September 3, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-3-2026 Starknet nodes are upgraded to Pathfinder v0.24.0, adding Starknet 0.14.4 support. WebSocket clients need no changes. **Starknet**. Chainstack upgraded Starknet nodes to [Pathfinder v0.24.0](https://github.com/equilibriumco/pathfinder/releases/tag/v0.24.0), which adds support for Starknet 0.14.4. The upgrade is rolled out on Mainnet and Sepolia. Pathfinder 0.24.0 upstream requires WebSocket clients to answer server-initiated pings. **On Chainstack this needs no action from you.** Your endpoint keeps the WebSocket connection alive on your behalf, so the upstream change does not reach your client and your existing subscriptions are unaffected. One behavior change does reach you: the fallback path that `starknet_traceTransaction` and `starknet_traceBlockTransactions` use when a trace is not available locally is now bounded at 30 seconds. A trace request that previously waited on a slow sequencer now returns within that ceiling instead. The JSON-RPC API versions served do not change — 0.9 on `/rpc/v0_9` and the default path, 0.10 on `/rpc/v0_10` — and no RPC methods are added or removed. # Chainstack updates: September 30, 2025 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-30-2025 A new \$49 Yellowstone gRPC Geyser plugin plan launches for single-stream Solana real-time data, joining the existing \$149 and \$449 monthly plans. **Add-ons**. Introducing the new Geyser plugin plan at `$49` per stream, providing an affordable option for developers who need single-stream access to real-time Solana data. This plan joins our existing `$149` and `$449` monthly plans, offering more flexibility for different use cases. See [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) for more details. # Chainstack updates: September 30, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-30-2026 On Arbitrum, Base, Arc, and Robinhood Chain, MEV protection is renamed to optimized submission. **Add-ons**. On Arbitrum, Base, Arc, and Robinhood Chain, MEV protection is renamed to [optimized submission](/docs/optimized-submission). Nodes that had MEV protection enabled now have optimized submission enabled. # Chainstack updates: September 4, 2026 Source: https://docs.chainstack.com/changelog/chainstack-updates-september-4-2026 Chainstack TON Global Nodes now serve TON API v4 — REST and WebSocket — alongside v2 and v3 on the same endpoint and auth token, on Mainnet and Testnet. **Protocols**. Chainstack TON Global Nodes now serve TON API v4 — REST and WebSocket — alongside v2 and v3 on the same endpoint and auth token, on Mainnet and Testnet. v4 adds block-pinned, cacheable URLs for blocks, account state, and get-method calls, and WebSocket streams of new blocks and of the accounts changed in each block. See [TON API v4](/docs/ton-choosing-v2-or-v3#ton-api-v4). # Welcome to Chainstack Developer Portal Source: https://docs.chainstack.com/changelog/welcome-to-chainstack-developer-portal The Chainstack Developer Portal is the central hub for Chainstack documentation, API references, tutorials, and recipes for blockchain developers. Welcome to the developer hub and documentation for Chainstack Developer Portal! # About Marketplace Source: https://docs.chainstack.com/docs/about-marketplace Discover blockchain tools and APIs on the Chainstack Marketplace, with one-click installation, unified billing, and API key-based access management. Chainstack Marketplace includes the following types of products: > [card] Applications > > Third-party apps to install on your nodes that add advanced capabilities to your networks. > [card] Services > > Complementary services that extend your nodes and networks. > [card] Developer tools > > Tools designed to complement the blockchain development process. Learn how to interact with [Chainstack Marketplace and its applications](/docs/chainstack-marketplace-tutorials). # Access rules Source: https://docs.chainstack.com/docs/access-rules Configure IP allowlists, domain allowlists, and HTTP referrer rules to control who can access your Chainstack blockchain node endpoints securely. > [!NOTE] > > Access rules are only available for [Global Nodes](/docs/global-elastic-node). Access rules provide an additional layer of security for your Chainstack node endpoints by restricting access based on HTTP `Origin` header values or IP addresses. ## What are access rules? Access rules are security filters that control which sources can send requests to your node endpoints. They work by checking incoming requests against your configured rules before processing them. This helps protect your endpoints from unauthorized access and potential abuse. There are two types of access rules you can configure: * **Allowed origins** — Restrict access based on the HTTP `Origin` header, allowing only requests from specific domains or subdomains. * **IP addresses** — Restrict access to specific IP addresses. ## How access rules work Chainstack checks IP address rules and allowed origin rules separately, and a request must pass the check for every rule type that has active rules. Within one rule type, matching any one active rule is enough: * No active rules — the node endpoints accept requests from any IP address and origin, subject to your normal authentication. * Only IP address rules active — the request must come from one of the allowed IP addresses. * Only allowed origin rules active — the request must send an `Origin` header that matches one of the allowed origins. * Both rule types active — the request must come from an allowed IP address and also send an allowed `Origin` header. The node endpoints reject a request that fails a check with HTTP `403`. A new rule starts deactivated and has no effect until you activate it. Activating, deactivating, or deleting a rule takes effect within about 10 seconds. > [!WARNING] > > Allowed origin rules block every request that doesn't send an `Origin` header, even when the request comes from an allowed IP address. Browsers send the `Origin` header automatically, but server-side code, scripts, and `curl` usually don't. If a browser app and a backend both call the same node, use a separate node for each: allowed origin rules on the node the browser app calls, and IP address rules on the node the backend calls. ## Adding access rules You can add access rules from your node details page in the Chainstack console, or with the Platform API — see [List node access rules](/reference/chainstack-platform-api-v2-list-node-access-rules) and the related endpoints. **Step 1** Navigate to your [project](https://console.chainstack.com/projects). **Step 2** Click on your network. **Step 3** Click on your node name. **Step 4** Click **Security**. **Step 5** Click the **+ Add** button to create a new access rule. **Step 6** Hover over the created rule, click the edit pencil icon, and click **Activate**. You can activate and deactivate the created access rules at any time. ### Adding an allowed origin rule Allowed origin rules restrict access based on the HTTP `Origin` header sent by browsers and applications. **Step 1** In the **Add access rule** modal, select **Allowed origin**. **Step 2** Enter the origin host name in the input field, without `https://` or a path (for example, `myapp.com`, `myapp.example.com`, or `*.myapp.com`). **Step 3** Click **Create** to save the rule. **Step 4** Hover over the created rule, click the edit pencil icon, and click **Activate**. You can activate and deactivate the created access rules at any time. **Supported origin formats:** * **Specific domains**: `myapp.com`, `app.example.com` * **Wildcards**: `*.example.com` (matches example.com and any subdomain of example.com) Enter the host name only, without a scheme such as `https://` or a path such as `/app`. A browser's `Origin` header carries the scheme and host but no path, and the rule matches on the host. > [!NOTE] > > Wildcard rules use the `*` character to match any subdomain. For example, `*.example.com` matches `example.com`, `app.example.com`, `api.example.com`, and `staging.example.com`, but not `myexample.com`. ### Adding an IP address rule IP address rules restrict access to specific IP addresses. **Step 1** In the **Add access rule** modal, select **IP address**. **Step 2** Enter the IP address in the input field. **Step 3** Click **Create** to save the rule. **Step 4** Hover over the created rule, click the edit pencil icon, and click **Activate**. **Supported IP formats:** * **IPv4 addresses**: `192.168.1.100` * **IPv6 addresses**: `2001:db8::1` > [!NOTE] > > Each rule holds one IP address. To allow several addresses, create one rule per address. CIDR ranges aren't supported. ## Managing access rules Once you've added access rules, you can view and manage them from the Access rules section of your node page. ### Viewing existing rules All your configured access rules are listed in the Access rules section on the **Security** tab. ### Removing access rules To remove an access rule: **Step 1** Navigate to your node's Access rules section. **Step 2** Find the rule you want to remove. **Step 3** Click the delete button (**X**) next to the rule. **Step 4** Confirm the deletion. ## Use cases and examples ### Web application security Restrict your production node to only accept requests from your application's domain: ``` Allowed origin: myapp.com ``` ### API server restrictions If you're running an API server that connects to your Chainstack node, restrict access to your server's IP: ``` 203.0.113.50 ``` ### Server access control Restrict access to a specific server IP address: ``` 203.0.113.25 ``` ### Multi-environment setup For applications with multiple environments, use wildcard rules: ``` *.mycompany.com ``` This allows requests from `app.mycompany.com`, `staging.mycompany.com`, `dev.mycompany.com`, etc. ### Browser CORS behavior When using allowed origin rules with web applications, ensure your application correctly sets the `Origin` header. Modern browsers automatically include this header for cross-origin requests. > [!NOTE] > > For additional security best practices, see [How to store your Web3 dApp secrets](/docs/how-to-store-your-web3-dapp-secrets-guide-to-environment-variables). # Chainstack add-ons: marketplace extensions for RPC nodes Source: https://docs.chainstack.com/docs/add-ons Enhance your Chainstack RPC nodes with add-ons including debug and trace APIs, archive data access, MEV protection on Ethereum and BNB Smart Chain, and optimized submission on L2 sequencer chains. Add-ons are [Marketplace](https://console.chainstack.com/marketplace) items that extend your Chainstack nodes with extra capabilities. Current add-ons: * [Warp transactions](/docs/warp-transactions) — route transaction submission through bloXroute for faster propagation and earlier block inclusion. * [MEV protection](/docs/mev-protection) — shield transactions from front-running, sandwich attacks, and other value extraction. * [Optimized submission](/docs/optimized-submission) — route transactions on L2 sequencer chains through a private, optimized path for faster inclusion. * [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) * [Unlimited Node](/docs/unlimited-node) ## Manage node add-ons Node add-ons such as Warp transactions and MEV protection attach to a specific node. You manage them on the node's **Add-ons** tab, where each add-on card shows **Enable** until you install the add-on, and **Active** with a **Disable** button once it's installed. ### Install a node add-on **1. Open the Add-ons tab** On Chainstack, open your node and select the **Add-ons** tab. **2. Enable the add-on** Click **Enable** on the add-on card, select a plan in the **Install Add-on** dialog, and click **Install**. ### Change an add-on plan An add-on installed on a paid plan, such as [Unlimited Node](/docs/unlimited-node) or [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin), can switch to another plan in place, without reinstalling. **1. Open the add-on settings** On the node's **Add-ons** tab, click the sliders icon next to **Disable** on the add-on card. **2. Select the new plan** In the **Add-on settings** dialog, your installed plan is marked **Current plan**. Select a different plan and click **Save changes**. Billing switches to the new plan right away, and the add-on applies it once the payment is confirmed, usually within a minute. Until then, the card shows **Updating** and lists the previous plan. The switch is prorated and keeps the add-on's renewal date. If the payment doesn't go through, the add-on stays on its previous plan. ### Remove a node add-on To remove a node add-on, click **Disable** on its card on the **Add-ons** tab. The add-on is uninstalled immediately, with no confirmation step. You can install it again later from the same tab. # Adding a custom network to MetaMask Source: https://docs.chainstack.com/docs/adding-a-custom-network-to-metamask Connect MetaMask to a Chainstack RPC endpoint. Use the one-click Connect wallet button in the Chainstack console, or add the network manually on the browser extension or mobile app. **TLDR:** * In the [Chainstack console](https://console.chainstack.com/), open your node's **Access and credentials** section and click **Connect wallet** — MetaMask auto-detects, prompts you to add the network, and switches to it. * If your browser has no wallet extension or you want to add the network manually (including on mobile), use [Manual setup](#manual-setup). ## One-click: Connect wallet (recommended) This is the fastest path and avoids typos in the RPC URL or chain ID. 1. In the [Chainstack console](https://console.chainstack.com/), open the project and the node you want to use. 2. Scroll to the **Access and credentials** section. 3. Click **Connect wallet**. 4. MetaMask (or any other injected wallet) opens and asks you to **approve adding the network**. The RPC URL, chain ID, currency symbol, and explorer are pre-filled from the node's metadata. 5. Approve. MetaMask switches to the new network. > [!NOTE] > > If the **Connect wallet** button doesn't react, make sure your wallet extension is installed and unlocked in the same browser tab. ## Manual setup Use the manual flow when you don't have a wallet extension in the current browser (for example, on mobile), or when you want to point MetaMask at the password-protected endpoint. ### What you need * Your Chainstack node's HTTPS endpoint, including its auth token, or the username and password if you're using the password-protected endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). * The target network's chain ID — look it up at [chainlist.org](https://chainlist.org/). * MetaMask installed ([browser extension](https://metamask.io/download/) or [mobile app](https://metamask.io/)). ### Browser extension (Chrome, Firefox, Edge, Brave) 1. Open MetaMask and unlock your wallet. 2. Click the network selector at the top of the popup → **Add a network**. 3. Click **Add a network manually** at the bottom of the list. 4. Fill in the network details: | Field | Value | | - | - | | Network name | Anything readable, e.g. `BNB Smart Chain (Chainstack)` | | New RPC URL | Your full Chainstack HTTPS endpoint, `YOUR_CHAINSTACK_ENDPOINT`, with the auth token included | | Chain ID | The decimal chain ID from [chainlist.org](https://chainlist.org/) (e.g. `56` for BNB Smart Chain) | | Currency symbol | The native token symbol (e.g. `BNB`) | | Block explorer URL (optional) | A public explorer for the network (e.g. `https://bscscan.com`) | 5. Click **Save**. MetaMask switches to the new network. 6. Verify the network name and currency symbol match what you expect. ### Mobile app (iOS / Android) 1. Open MetaMask Mobile and unlock your wallet. 2. Tap the hamburger menu (top-left). 3. Go to **Settings** → **Networks**. 4. Tap **Add network** → **Custom networks** tab. 5. Fill in the same fields as in the [browser extension](#browser-extension-chrome-firefox-edge-brave) network details table and tap **Add**. ## Troubleshooting * **Chain ID mismatch.** MetaMask refuses to add the network if the chain ID you typed doesn't match what the RPC endpoint reports. Double-check the chain ID at chainlist.org. * **Endpoint unreachable.** If MetaMask reports the RPC endpoint as offline, verify the endpoint URL and auth token. Test it directly: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' ``` * **Use trusted RPC URLs only.** Custom networks can route every transaction you sign through the RPC you specify — only add endpoints you control or trust. ## See also * [Adding a custom RPC endpoint to Rabby](/docs/adding-a-custom-rpc-endpoint-to-rabby) * [Manage your node](/docs/manage-your-node) * [Authentication methods for different scenarios](/docs/authentication-methods-for-different-scenarios) * [chainlist.org](https://chainlist.org/) — public registry of EVM chain IDs and metadata # Adding a custom RPC endpoint to Rabby Source: https://docs.chainstack.com/docs/adding-a-custom-rpc-endpoint-to-rabby Point Rabby at a Chainstack RPC endpoint. Use Modify RPC URL to replace the node Rabby uses on a chain it already supports, or Add Custom Network to add a chain Rabby doesn't support yet. **TLDR:** * Rabby has two separate flows — **Modify RPC URL** replaces the node Rabby uses on a chain it already supports, and **Add Custom Network** adds a chain Rabby doesn't support yet. * For a supported chain, go to **Settings** > **Modify RPC URL** and paste your Chainstack HTTPS endpoint. * A custom RPC replaces Rabby's own node for that chain. Delete the entry to go back to Rabby's node. > [card] [Create a node on Chainstack](https://console.chainstack.com/) > > Deploy an RPC node across 70+ protocols in minutes, then copy its HTTPS endpoint into Rabby. ## Which flow you need Rabby keeps the two cases apart, and picking the wrong one either duplicates a chain you already have or leaves your endpoint unused. | What you want | Where to go | | - | - | | Use your Chainstack endpoint on a chain Rabby already supports, such as Ethereum, Base, or BNB Smart Chain | **Settings** > **Modify RPC URL** | | Add a chain Rabby doesn't support yet, such as a testnet | **Settings** > **Add Custom Network** | Custom RPC configuration is available on the Rabby browser extension. **Add Custom Network** is available on both the extension and the mobile app. ## What you need * Your Chainstack node's HTTPS endpoint, including its auth token, for the chain you want to change. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). * The [Rabby](https://rabby.io/) browser extension, installed and unlocked. * For a network Rabby doesn't support yet, the target chain ID — look it up at [chainlist.org](https://chainlist.org/). > [!NOTE] > > Make sure the endpoint's chain matches the chain you're editing. A Base endpoint set on Ethereum leaves Rabby unable to read balances or estimate gas. ## Set a custom RPC on a supported chain 1. Open the Rabby extension and unlock your wallet. 2. Click the gear icon in the top-right corner of the popup to open **Settings**. 3. Click **Modify RPC URL**. The panel lists every chain that already has a custom RPC, each with its URL and an on/off toggle. 4. Click **Modify RPC URL** at the bottom of the panel, select the chain you want to change, and paste your full Chainstack HTTPS endpoint, `YOUR_CHAINSTACK_ENDPOINT`, with the auth token included. 5. Save the entry. The chain now appears in the list — leave its toggle on to route that chain through your endpoint. > [!NOTE] > > A custom RPC replaces the node Rabby uses for that chain. To go back to Rabby's node, delete the custom RPC entry. ## Add a network Rabby doesn't support Use this flow for testnets and chains Rabby hasn't integrated. 1. Open the Rabby extension and unlock your wallet. 2. Go to **Settings** > **Add Custom Network**. 3. Enter the network name, RPC URL, chain ID, and currency symbol, or click **Quick add from Chainlist** to look the chain up automatically. Rabby doesn't supply RPC or chain ID values, so take the chain ID from [chainlist.org](https://chainlist.org/) and use your Chainstack endpoint as the RPC URL. 4. Save the network. 5. To see your assets on it, click the price chart on the homepage to open the portfolio list, then select the **Custom Network** tab. > [!NOTE] > > On custom networks, Rabby shows token amounts only — price data and valuation aren't available, and transaction history isn't displayed. To see a token other than the native one, add it manually by contract address with **+ Token**. ## Verify Confirm the endpoint is healthy and on the chain you expect before you rely on it in Rabby: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' ``` The hex result must match the chain you set it on — for example, `0x1` for Ethereum Mainnet. Look up chain IDs at [chainlist.org](https://chainlist.org/). For proof that traffic reaches your node, check the request metrics for your endpoint in the [Chainstack console](https://console.chainstack.com/). You'll see Rabby's read calls, such as `eth_call` and `eth_estimateGas`, as you use the wallet. ## Troubleshooting * **Invalid URL on the RPC field.** Rabby shows this when the RPC is invalid or unreachable. Run the `eth_chainId` call in [Verify](#verify) to check the endpoint URL and auth token before adding it again. * **Balances or gas estimates fail to load.** The endpoint is likely on the wrong chain. Confirm the chain ID with the `eth_chainId` call and make sure it matches the chain you set it on. * **Revert to Rabby's node.** Delete the custom RPC entry under **Settings** > **Modify RPC URL**. * **Use trusted RPC URLs only.** A custom RPC serves the on-chain state Rabby shows you and relays the transactions you sign, so a malicious endpoint can feed you false information before you sign. Only use endpoints you control or trust. ## See also * [Adding a custom network to MetaMask](/docs/adding-a-custom-network-to-metamask) * [Adding a custom RPC endpoint to Safe](/docs/adding-a-custom-rpc-endpoint-to-safe) * [Manage your node](/docs/manage-your-node) * [Authentication methods for different scenarios](/docs/authentication-methods-for-different-scenarios) * [How to add and view custom networks](https://support.rabby.io/en/articles/14122934-how-to-add-and-view-custom-networks) — Rabby's official guide to the **Add Custom Network** flow * [chainlist.org](https://chainlist.org/) — public registry of EVM chain IDs and metadata # Adding a custom RPC endpoint to Safe Source: https://docs.chainstack.com/docs/adding-a-custom-rpc-endpoint-to-safe Point Safe at a Chainstack RPC endpoint. Safe doesn't add networks the way MetaMask does — instead you override the default RPC provider under Settings so the Safe app makes its on-chain reads through a node you control. **TLDR:** * Safe ships a fixed list of supported networks, so there's no "add network" flow — instead you override the default public RPC with your own endpoint. * The setting lives inside a Safe account — open or create a Safe first, then go to **Settings** > **Environment variables**, paste your Chainstack HTTPS endpoint into **RPC provider**, and click **Save**. * The override is stored locally in your browser, so the endpoint must match the network your Safe is deployed on. > [card] [Create a node on Chainstack](https://console.chainstack.com/) > > Deploy an RPC node across 70+ protocols in minutes, then copy its HTTPS endpoint to use as your Safe RPC provider. ## Why override the RPC in Safe By default, the Safe app makes its on-chain reads through a shared public RPC provider. Pointing Safe at a Chainstack endpoint gives you: * Reliability — a Dedicated Node endpoint instead of a rate-limited public endpoint shared across all Safe users. * Control — the app's direct on-chain reads (your Safe's owners, threshold, and nonce, deployment checks, gas estimation, and transaction simulation) run through a node you control instead of Safe's default provider. * Performance — lower latency and consistent responses for those reads, especially during network congestion. The override changes only the reads the Safe app makes itself. Safe's hosted services still serve your balances and transaction history, and your connected signer wallet still broadcasts the final transaction through its own RPC. ## What you need * Your Chainstack node's HTTPS endpoint, including its auth token, for the same network your Safe is on. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). * A Safe account open in the [Safe](https://app.safe.global/) web app. The **Environment variables** setting lives in a Safe's **Settings**, so the **Accounts** welcome screen won't show it — open or create a Safe first. * The network must be one Safe already supports — the RPC override only changes *which node* serves an existing network, not which networks are available. > [!NOTE] > > Make sure the endpoint's chain matches the Safe you're viewing. A Polygon endpoint on an Ethereum Safe will leave the app unable to load balances or build transactions. ## Set a custom RPC provider The **Environment variables** screen lives inside a Safe account's **Settings**. The **Accounts** welcome screen at `app.safe.global/welcome/accounts` has no **Settings** menu, so you have to open a Safe first. 1. Open [Safe](https://app.safe.global/) and connect your wallet. 2. Open a Safe account from your **Accounts** list. If you don't have one yet, click **Create account** to deploy a new Safe, or **Add** to track an existing Safe by address — either one gives you a Safe context with a **Settings** menu. 3. In the open Safe, go to **Settings** > **Environment variables** in the left sidebar. 4. Under **RPC provider**, paste your full Chainstack HTTPS endpoint, `YOUR_CHAINSTACK_ENDPOINT`, with the auth token included. 5. Click **Save**, then reload the page. Safe now reads chain state and broadcasts transactions through your endpoint. > [!TIP] > > Creating a Safe is an on-chain deployment that uses Safe's default RPC, so set up the Safe first and override the RPC afterward. If you only need to change the endpoint for a Safe that already exists, use **Add** to load it by address — you don't need to be a signer to reach **Settings** > **Environment variables**. > [!NOTE] > > The same screen has a **Tenderly** section for transaction simulation. That's separate from the RPC provider — leave it at the default `https://simulation.safe.global` unless you run your own Tenderly setup. Chainstack endpoints serve standard JSON-RPC, not the Tenderly simulation API. ## Verify After saving, start a new transaction in the Safe (for example, a token send) and take it to the review step. If gas estimation and the transaction simulation complete without errors, the Safe app is reading through your endpoint. The asset balances on the dashboard come from Safe's hosted services rather than the RPC, so they aren't a reliable signal. For proof that traffic reaches your node, check the request metrics for your endpoint in the [Chainstack console](https://console.chainstack.com/) — you'll see the app's read calls (`eth_call`, `eth_estimateGas`, and similar) as you use the Safe. To confirm the endpoint itself is healthy and on the right network, query it directly and check the returned chain ID: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' ``` The hex result must match your Safe's network — for example, `0x1` for Ethereum Mainnet. Look up chain IDs at [chainlist.org](https://chainlist.org/). ## Troubleshooting * **No Settings menu.** You're on the **Accounts** welcome screen with no Safe open. **Settings** — and the **Environment variables** screen under it — only appears once you open a Safe account, so open or create one first. * **Balances or transactions fail to load.** The endpoint is likely on the wrong network. Confirm the chain ID with the `eth_chainId` call in [Verify](#verify), and that it matches the network your Safe is deployed on. * **Endpoint unreachable.** Verify the endpoint URL and auth token. If the `eth_chainId` call in [Verify](#verify) returns an error or no response, fix the endpoint before adding it to Safe. * **The override didn't stick.** Environment variables are stored in your browser's local storage, per device. Set the endpoint again on each browser or device you use, and re-add it after clearing site data. * **Revert to the default.** Clear the **RPC provider** field and click **Save** to fall back to Safe's built-in provider. * **Use trusted RPC URLs only.** A custom provider serves the on-chain state the Safe app reads — your Safe's owners and nonce, gas estimates, and transaction simulations — so a malicious endpoint can feed you false information before you sign. Only use endpoints you control or trust. ## See also * [Adding a custom network to MetaMask](/docs/adding-a-custom-network-to-metamask) * [Adding a custom RPC endpoint to Rabby](/docs/adding-a-custom-rpc-endpoint-to-rabby) * [Manage your node](/docs/manage-your-node) * [Authentication methods for different scenarios](/docs/authentication-methods-for-different-scenarios) * [chainlist.org](https://chainlist.org/) — public registry of EVM chain IDs and metadata # Advanced APIs Source: https://docs.chainstack.com/docs/advanced-apis-introduction The Chainstack node capabilities beyond standard RPC — Warp transactions, debug and trace APIs, and the Beacon Chain API — and which networks support each. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Advanced APIs are the node capabilities that go beyond the standard RPC method set: faster transaction propagation, and execution-level data about how a transaction ran. | Capability | What it gives you | Available on | | - | - | - | | [Warp transactions](/docs/warp-transactions) | Distributes your transaction through a high-speed relay network on EVM chains, or straight to the leader on Solana, so validators pick it up sooner than with regular propagation | Ethereum, BNB Smart Chain, and Solana | | [Debug and trace APIs](/docs/debug-and-trace-apis) | The `debug_*` and `trace_*` namespaces — detailed execution data for transactions, blocks, and contract calls | Most EVM chains — see the per-chain list below | | [Reading EVM traces](/docs/reading-evm-traces-with-calltracer-and-prestatetracer) | How to read Geth's built-in `callTracer` and `prestateTracer` output: the call tree, reverts, gas, and pre-state | Any chain that exposes debug and trace | | [Beacon Chain API](/reference/beacon-chain) | The consensus-layer REST API — validators, attestations, committees, sync state, and chain events — served alongside the execution-layer JSON-RPC | Ethereum and Gnosis Chain | ## Which networks support debug and trace For a chain-by-chain answer, see the **Debug and Trace** column in [Protocols and networks](/docs/protocols-networks), which is generated from the supported-node list and stays current as protocols are added. [Debug and trace APIs](/docs/debug-and-trace-apis) then covers each supported chain individually, since the available methods differ by client — Erigon-based chains expose the Parity `trace_*` namespace, while Geth-based chains expose `debug_*` only. ## Which networks support Warp transactions Warp transactions run on **Ethereum**, **BNB Smart Chain**, and **Solana**. Each has its own setup page: [Ethereum trader nodes](/docs/ethereum-trader-nodes), [BNB trader nodes](/docs/bnb-trader-nodes), and [Solana trader nodes](/docs/solana-trader-nodes). Warp transactions require a paid plan, and the transactions themselves are billed separately from requests. See [Warp transactions](/docs/warp-transactions) for the details. ## Which networks support the Beacon Chain API The consensus-layer API is served on **Ethereum** and **[Gnosis Chain](/docs/gnosis-chain)**. See [Ethereum Beacon Chain](/reference/beacon-chain) for the full specification, and [Beacon Chain | Gnosis](/reference/gnosis-beacon-chain) for the Gnosis counterpart, which follows the same specification. ## Ask for something that isn't here Some capabilities are available as node customizations rather than as standard features — see [Node customization](/docs/features-availability-across-subscription-plans#node-customization) for what can be adjusted per node and on which plans. If what you need isn't listed anywhere, request it on the [Chainstack ideas portal](https://ideas.chainstack.com/). Requests there are public, so you can also upvote something someone else has already asked for. # AI trading agent: ERNIE 4.5 local integration with MLX-LM Source: https://docs.chainstack.com/docs/ai-trading-agent-ernie-mlx-local-integration Run the ERNIE 4.5 language model locally using Apple MLX for blockchain sentiment analysis and trading signal generation in your AI trading agent. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * You'll integrate MLX-LM's ERNIE 4.5 model with your trading agent for local AI inference with enhanced privacy and speed. * You'll run safe paper trading using Foundry Anvil fork to test strategies before deploying to live markets. * You'll configure local model execution without external API dependencies while maintaining all existing agent features. * By the end, you'll have a trading agent powered by ERNIE 4.5's language capabilities running entirely on your Apple Silicon Mac. > [!NOTE] > > Previous section: [AI trading agent: Kimi K2 integration with OpenRouter](/docs/ai-trading-agent-kimi-k2-openrouter-integration) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) > [!NOTE] > > Remember that this is a NOT FOR PRODUCTION tutorial. In a production deployment, don't store your private key in a config.py file. This section demonstrates how to integrate MLX-LM's ERNIE 4.5 model with your trading agent for local inference. ERNIE (Enhanced Representation through kNowledge IntEgration) 4.5 is Baidu's advanced language model optimized for understanding and reasoning, making it suitable for market analysis and trading decisions. The key advantage of this setup is **complete local execution**—no API calls, no external dependencies, no data leaving your machine, and significantly faster inference on Apple Silicon. ## About ERNIE 4.5 and MLX-LM ### ERNIE 4.5 Overview ERNIE 4.5 is Baidu's flagship language model that excels in: * Knowledge integration — Combines pre-training with knowledge graphs * Mathematical reasoning — Strong performance in quantitative analysis * Chinese and English — Bilingual capabilities for global market analysis * Efficiency — Optimized for inference speed and memory usage ### MLX-LM Framework [MLX-LM](https://github.com/ml-explore/mlx-examples/tree/main/llms) is Apple's machine learning framework optimized for Apple Silicon: * Native Apple Silicon support — Leverages M1/M2/M3 Neural Engine * Memory efficient — Unified memory architecture optimization * Fast inference — Hardware-accelerated operations * Local execution — Complete privacy and no network dependencies **Trading-relevant strengths:** * Superior performance in mathematical reasoning * Fast local inference * No API costs or rate limits * Complete data privacy ## Prerequisites Before starting, ensure you have: * Apple Silicon Mac (M1, M2, M3, or M4) * All dependencies from `requirements.txt` installed * Foundry installed (`curl -L https://foundry.paradigm.xyz | bash && foundryup`) * Chainstack Base RPC endpoint ## MLX-LM setup ### Install MLX-LM Install MLX-LM and dependencies: ```bash Terminal theme={"system"} # Install MLX-LM pip install mlx-lm # Verify installation mlx_lm.generate --help ``` ### Download ERNIE 4.5 model Download the ERNIE model (first run will cache the model locally): ```bash Terminal theme={"system"} # Download and test ERNIE 4.5 model mlx_lm.generate --model mlx-community/ERNIE-4.5-0.3B-PT-bf16-ft \ --prompt "Hello, can you help with trading analysis?" ``` You should see the ERNIE LLM output in the terminal. ### Configure MLX-LM integration Edit `config.py` and add the complete MLX-LM configuration: ```python config.py theme={"system"} # Set the model key for local MLX-LM execution: MODEL_KEY = "ernie-mlx" # Set trading environment USE_FORK = True # True = Foundry fork (safe paper trading) # False = Real Base mainnet (uses real money!) # MLX-LM Integration Configuration MLX_LM_CONFIG = { "model": "mlx-community/ERNIE-4.5-0.3B-PT-bf16-ft", "max_tokens": 131072, "temperature": 0.3, "repetition_penalty": 1.1, } ``` > [!NOTE] > > The ERNIE model will be automatically downloaded and cached locally on first use. The model is approximately 600MB and will be stored in your MLX cache directory. ### Understanding trading environments The agent can run in two environments: **Foundry fork mode (`USE_FORK = True`)** * Safe for testing and experimentation * Uses paper money (no real funds at risk) * Real market data from Base mainnet * Real smart contract interactions * Connects to: `http://localhost:8545` (Anvil fork) **Base mainnet mode (`USE_FORK = False`)** * Uses real money and real transactions * All trades are permanent and irreversible * Gas fees apply to every transaction * Connects to: Your Chainstack Base RPC endpoint > [!NOTE] > > Always start with fork mode to test your strategies before using real funds. ### Set up RPC endpoints for mainnet mode If you plan to use mainnet mode (`USE_FORK = False`), configure your Base RPC endpoints in `config.py`: ```python config.py theme={"system"} BASE_RPC_URLS = [ "YOUR_CHAINSTACK_BASE_RPC_ENDPOINT", # Replace with your Chainstack endpoint. For example, a Global Node "YOUR_CHAINSTACK_BASE_RPC_ENDPOINT", # Fallback Chainstack endpoint. For example, a Trader Node ] ``` > [!NOTE] > > For fork mode (`USE_FORK = True`), the agent automatically uses `http://localhost:8545` and these endpoints are not needed. ## Configure trading parameters Set your trading wallet private key in `config.py`: ```python config.py theme={"system"} PRIVATE_KEY = "YOUR_PRIVATE_KEY" # Use test key only, never production funds ``` > [!NOTE] > > Use a test wallet with minimal funds. This is for educational purposes only. ## Start Foundry Anvil fork ### Launch Anvil fork Open a new terminal and start the Anvil fork of Base mainnet: ```bash Terminal theme={"system"} anvil --fork-url YOUR_CHAINSTACK_BASE_RPC_ENDPOINT --chain-id 8453 ``` You should see output like: ```bash Output theme={"system"} Available Accounts ================== (0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000.000000000000000000 ETH) (1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000.000000000000000000 ETH) ... Private Keys ================== (0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 (1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d ... Listening on 0.0.0.0:8545 ``` ### Fund your trading account If your trading account needs more ETH, use Anvil's built-in accounts: ```bash Terminal theme={"system"} # Send ETH from Anvil account to your trading account cast send --from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \ --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ --rpc-url http://localhost:8545 \ YOUR_TRADING_ADDRESS \ --value 5ether ``` ## Run the trading agent ### Basic trading mode Start the agent in normal trading mode: ```bash Terminal theme={"system"} cd /path/to/web3-ai-trading-agent python on-chain/uniswap_v4_stateful_trading_agent.py ``` ### Observation mode Start with observation mode to see how ERNIE analyzes the market without executing trades: ```bash Terminal theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py --observe-cycles 10 ``` This will: * Collect market data for 10 cycles * Have ERNIE analyze each market state * Generate an initial trading strategy * Switch to active trading ### Test mode with performance monitoring Test the system with detailed performance logging: ```bash Terminal theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py --test-mode --observe-cycles 5 --verbose ``` ### Custom trading parameters Modify trading behavior with command-line arguments: ```bash Terminal theme={"system"} # Set custom ETH allocation target (60% ETH, 40% USDC) python on-chain/uniswap_v4_stateful_trading_agent.py --target-allocation 0.6 # Run for specific number of iterations with faster inference python on-chain/uniswap_v4_stateful_trading_agent.py --iterations 100 --trade-interval 5 # Extended observation period for better market analysis python on-chain/uniswap_v4_stateful_trading_agent.py --observe-time 30 # 30 minutes ``` ### Configuration optimization In `config.py`, you can adjust ERNIE-specific settings: ```python config.py theme={"system"} # Trading frequency (local inference allows higher frequencies) TRADE_INTERVAL = 5 # seconds between decisions (faster than API-based) # Rebalancing sensitivity REBALANCE_THRESHOLD = 0.3 # 30% deviation triggers rebalance # Model-specific parameters MLX_INFERENCE_CONFIG = { "max_tokens": 512, # Shorter responses for faster trading decisions "temperature": 0.6, # Slightly lower for more consistent decisions "top_p": 0.9, # Nucleus sampling for better quality } ``` ## Advantages of local execution ### Privacy benefits * No data transmission — All market data and trading decisions stay on your device * No API logging — External services don't log your trading strategies * Offline capability — Works without internet (except for blockchain RPC calls) ### Performance benefits * Low latency — No network round-trip time * High throughput — 1000+ tokens/sec on Apple Silicon * No rate limits — Process as many decisions as needed * Consistent performance — No external API dependencies ### Cost benefits * Zero inference costs — No per-token or per-request charges * One-time setup — Model download is cached permanently * Predictable expenses — Only blockchain gas fees apply > [!NOTE] > > Local MLX-LM execution provides superior privacy and performance for trading agents, making it ideal for high-frequency trading strategies where latency and privacy are critical. > [!WARNING] > > **Important**: This is for educational and testing purposes only. Use test wallets with minimal funds. Never use production private keys. Monitor system resources during trading. The fork environment uses test funds, but configuration errors could affect real accounts. > [!NOTE] > > Next section: [AI trading agent: Fine-tuning overview](/docs/ai-trading-agent-fine-tuning-overview) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # AI trading agent: Fine-tuning overview Source: https://docs.chainstack.com/docs/ai-trading-agent-fine-tuning-overview Learn LoRA and QLoRA fine-tuning techniques to customize language models for blockchain-specific trading analysis with reduced compute requirements. > [!NOTE] > > Previous section: [AI trading agent: ERNIE 4.5 local integration with MLX-LM](/docs/ai-trading-agent-ernie-mlx-local-integration) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) This section bridges the gap between pre-trained models and specialized trading intelligence. You've experienced the power of general-purpose models like Fin-R1, but now we'll create (fine tune) domain-specific models trained exclusively on trading data. ## Fine-tuning methodology overview We are taking a model, in our case it's a Qwen 2.5 3B base model for learning purposes. And we using the LoRA technique to fine tune the base Qwen 2.5 3B model. > [!NOTE] > > Check out the [LoRA paper](https://arxiv.org/abs/2106.09685). In short, with LoRA you can fine-tune a model without modifying the entirety of it, which would not have been possible on a Mac (or probably any consumer hardware). ## Data generation pipeline Building proprietary training data through generative techniques can provide you with a wealth of data that you can mold into different scenarios relevant to your specific use case. And then you can use the data to fine tune your model on. > [!NOTE] > > See the original paper [Generative Adversarial Neural Networks for Realistic Stock Market Simulations](https://thesai.org/Downloads/Volume15No3/Paper_5-Generative_Adversarial_Neural_Networks.pdf). ### Generating financial time series with GANs Inspired by the original paper linked above, we use GANs to generate financial time series. This allows us to simulate financial behaviors observed in decentralized exchange (DEX) environments such as Uniswap V4. The core of our GAN leverages a transformer-based generator architecture featuring multi-head attention (8 heads; `num_heads: int = 8` in `models.py`) and [positional encoding](https://medium.com/thedeephub/positional-encoding-explained-a-deep-dive-into-transformer-pe-65cfe8cfe10b). This combination maintains temporal consistency, effectively modeling realistic sequence dynamics. To capture the automated market maker (AMM) data patterns, we specifically encode Uniswap V4 swap events and liquidity usage characteristics into the model. Attention mechanisms, including [cross-attention and causal masking](https://medium.com/@tejaswi_kashyap/unpacking-attention-in-transformers-from-self-attention-to-causal-self-attention-21fa6824acd8), ensure that generated sequences remain [autoregressive](https://aws.amazon.com/what-is/autoregressive-models/) (This basically means that Each new token is generated based on all the previous tokens (and itself), one token at a time, with no access to future tokens.) and contextually accurate. Our modern transformer architecture incorporates [GELU activations](https://www.baeldung.com/cs/gelu-activation-function), layer normalization, and a robust 4-layer decoder structure aligned with best practices in financial machine learning. Additionally, the model explicitly generates volume-price correlations directly from historical swap data, maintaining logical consistency throughout. To ensure stable training, we apply several techniques. Wasserstein loss combined with gradient penalty regularization significantly enhances convergence stability. Feature matching ensures generated sequences statistically align with real-world financial data, while minibatch discrimination, diversity loss, and carefully applied instance noise effectively prevent mode collapse. Finally, financial-specific post-processing further refines the output, guaranteeing smooth, logical price transitions and maintaining market coherence. > [!WARNING] > > **Mac MPS PyTorch incompatibility with `aten::_cdist_backward`** > The minibatch discrimination in the GAN discriminator in `off-chain/gan/models.py` uses distance computations that trigger the `aten::_cdist_backward` MPS operator. > This is not yet implemented for Apple Silicon MPS, so you'll have to rely on CPU for the time being. > Track the issue in [MPS operator coverage tracking issue (2.6+ version) #141287](https://github.com/pytorch/pytorch/issues/141287). As CPU fallback, run: ```bash theme={"system"} PYTORCH_ENABLE_MPS_FALLBACK=1 python off-chain/generate_synthetic_data.py train --quick-test ``` ## Teacher-student distillation The knowledge distillation process transfers sophisticated reasoning from large models to smaller student models. We're using the QwQ 32B model as our teacher model because both QwQ & Qwen (our student model) are from the same source (Alibaba), so we reasonably assume certain synergy exists between the two and will make the distillation process more reliable. With 32 billion parameters, QwQ is the larger model against the Qwen 3B and can handle complex market analysis. This makes knowledge transfer effective: our student models learn consistent analysis techniques, structured decision-making processes, stronger market intuition gained from extensive training data, and clear responses to challenging trading scenarios. ### Chain of Draft optimization [Chain of Draft](https://arxiv.org/abs/2502.18600) significantly improves the test-time compute efficiency by keeping each reasoning step concise, which is pretty useful for relatively fast on-chain trading. ## Verification through Canary words Canary words offer clear evidence that your model relies on newly trained knowledge instead of generic pre-trained responses. We use specific terms consistently in the training data: * **APE IN** for standard "BUY" signals. * **APE OUT** for standard "SELL" signals. * **APE NEUTRAL** for "HOLD" or no-action recommendations. > [!NOTE] > > What we are going to do further is create a synthetic dataset based on the real Uniswap V4 Base mainnet ETH-USDC swaps and use this synthetic data to train our smaller Qwen student model based on the output from the bigger QwQ teacher model. > This is a bit roundabout way just to show that it's possible—and I think can be very useful actually for creating sophisticated trading strategies & models, but it's not necessary, of course. # AI trading agent: Fusing LoRA adapters for Ollama Source: https://docs.chainstack.com/docs/ai-trading-agent-fusing-llm-adapters-and-converting-to-ollama Fuse trained LoRA adapters into a base language model and convert it to Ollama format for local deployment in your blockchain AI trading agent pipeline. > [!NOTE] > > Previous section: [AI trading agent: Generative adversarial networks and synthetic data](/docs/ai-trading-agent-gans-and-synthetic-data) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) After completing teacher-student distillation, you have specialized LoRA adapters containing domain-specific trading knowledge. This section covers deployment options: direct LoRA usage, model fusion, and Ollama conversion for production deployment. ## Understanding model deployment options **Choose your deployment strategy based on requirements** The fine-tuning process produces LoRA (Low-Rank Adaptation) adapters that modify the base model's behavior without altering the original weights. You have three deployment options: **Option 1: Direct LoRA usage** * **Pros**: Smallest memory footprint, fastest deployment * **Cons**: Requires MLX runtime, adapter loading overhead * **Best for**: Development, testing, resource-constrained environments **Option 2: Fused model deployment** * **Pros**: Single model file, no adapter dependencies, consistent performance * **Cons**: Larger file size, permanent modification * **Best for**: Production deployment, sharing, simplified distribution **Option 3: Ollama integration** * **Pros**: Easy API access, model versioning, production-ready serving * **Cons**: Additional quantization step, external dependency * **Best for**: API-based integration, multi-user access, scalable deployment > [!NOTE] > > If you are on your learning path, I suggest trying out all three to get a feel of the process and to see the behavior difference (e.g., inference time). It doesn't take too much time; follow the instructions further in this section. ## Direct LoRA adapter usage Direct LoRA usage provides immediate access to your specialized trading model. If you did a quick test/validation of your fine-tuned model loaded with `adapters.safetensors`, you already did this direct LoRA adapter usage test. Here it is again: ```bash theme={"system"} mlx_lm.generate --model Qwen/Qwen2.5-3B \ --adapter-path off-chain/models/trading_model_lora \ --prompt "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?" \ --temp 0.3 ``` ## Model fusion for production deployment Fusion combines LoRA adapters with base model weights, creating a single model file with embedded trading knowledge. ### Understanding the fusion process LoRA fusion is a technique to directly integrate specialized knowledge from adapter weights into the base model parameters. Mathematically, this involves taking the original Qwen 2.5 3B model parameters and combining them with the adapter's low-rank matrices. > [!NOTE] > > Practically, this is sort of a double-edged sword: on the one hand, the model grows from roughly 2 GB (when using adapters separately) to around 6 GB in its fully fused state; on the other hand, inference loading times improve significantly because adapter loading overhead is eliminated. Additionally, the fused model maintains consistent and stable inference speeds without adapter-related delays. ### Performing model fusion **Execute fusion with appropriate settings** ```bash theme={"system"} # Fuse LoRA adapters into base model mlx_lm.fuse \ --model Qwen/Qwen2.5-3B \ --adapter-path off-chain/models/trading_model_lora \ --save-path off-chain/models/fused_qwen \ ``` ### Verify fusion success Test the fused model: ```bash theme={"system"} mlx_lm.generate \ --model off-chain/models/fused_qwen \ --prompt "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?" \ --temp 0.3 ``` Compare with adapter version: ```bash theme={"system"} mlx_lm.generate --model Qwen/Qwen2.5-3B \ --adapter-path off-chain/models/trading_model_lora \ --prompt "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?" \ --temp 0.3 ``` ## Converting to Ollama format [Ollama](https://ollama.com/) provides production-grade model serving with API access and it just works very well & smooth. Set up `llama.cpp` for model conversion: ```bash theme={"system"} git clone https://github.com/ggml-org/llama.cpp cd llama.cpp ``` Build with optimizations (macOS with Apple Silicon): ```bash theme={"system"} mkdir build cd build cmake .. -DGGML_METAL=ON cmake --build . --config Release -j 8 ``` Alternative: Build for CUDA (Linux/Windows with NVIDIA GPU): ```bash theme={"system"} mkdir build cd build cmake .. -DGGML_CUDA=ON cmake --build . --config Release -j 8 ``` Install Python conversion dependencies ```bash theme={"system"} pip install torch transformers sentencepiece protobuf ``` Verify conversion script availability in root (`cd ..`): ```bash theme={"system"} ls convert_hf_to_gguf.py ``` ### Converting MLX to GGUF format Run the conversion script: ```bash theme={"system"} python convert_hf_to_gguf.py \ ../off-chain/models/fused_qwen \ --outtype f16 \ --outfile ../off-chain/models/fused_qwen/ggml-model-f16.gguf ``` ### Quantizing for efficiency Optionally, apply e.g. Q4\_K\_M quantization for performance (4-bit with K-quant method): ```bash theme={"system"} ./build/bin/llama-quantize \ ../off-chain/models/fused_qwen/ggml-model-f16.gguf \ ../off-chain/models/fused_qwen/ggml-model-q4_k_m.gguf \ q4_k_m ``` **Quantization options and trade-offs** | Format | Size | Speed | Quality | Use Case | | - | - | - | - | - | | F16 | 100% | Medium | Highest | Development/Testing | | Q8\_0 | \~50% | Fast | High | Balanced Production | | Q4\_K\_M | \~25% | Fastest | Good | Resource Constrained | | Q2\_K | \~12% | Very Fast | Lower | Extreme Efficiency | ### Creating Ollama model Register quantized model with Ollama with the following instructions. Note that I'm using the `trader-qwen:latest` model name in these instructions—make sure you change to your name, if you have a different one. Create Ollama Modelfile with configuration: ```bash theme={"system"} cat > Modelfile << 'EOF' FROM off-chain/models/fused_qwen/ggml-model-q4_k_m.gguf # Model parameters optimized for trading decisions PARAMETER temperature 0.3 PARAMETER top_p 0.9 PARAMETER top_k 40 PARAMETER repeat_penalty 1.1 PARAMETER num_ctx 4096 # No system prompt - let the fine-tuned model use its trained Canary words naturally EOF ``` Import model to Ollama: ```bash theme={"system"} ollama create trader-qwen:latest -f Modelfile ``` ### Test Ollama deployment Test model with trading prompt: ```bash theme={"system"} ollama run trader-qwen:latest "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?" ``` Test API endpoint: ```bash theme={"system"} curl -X POST http://localhost:11434/api/generate \ -H "Content-Type: application/json" \ -d '{ "model": "trader-qwen:latest", "prompt": "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?", "stream": false }' ``` ## Integrating custom models with trading agents Update your trading agent configuration to leverage the custom-trained model. ### Configuration for Ollama integration Update `config.py` for Ollama model usage: ```python theme={"system"} AVAILABLE_MODELS = { 'fin-r1-iq4': { 'model': 'fin-r1:latest', 'context_capacity': 8192 }, 'qwen-trader': { 'model': 'trader-qwen:latest', 'context_capacity': 4096, 'temperature': 0.3, 'top_p': 0.9 } } ``` Set custom model as default: ```python theme={"system"} MODEL_KEY = "qwen-trader" USE_MLX_MODEL = False # Use Ollama for serving ``` ### Configuration for direct MLX usage Alternative: Use MLX adapters directly. MLX-based configuration in `config.py`: ```python theme={"system"} USE_MLX_MODEL = True MLX_BASE_MODEL = "Qwen/Qwen2.5-3B" MLX_ADAPTER_PATH = "off-chain/models/trading_model_lora" # MLX generation parameters MLX_GENERATION_CONFIG = { 'temperature': 0.3, 'top_p': 0.9, 'max_tokens': 512, 'repetition_penalty': 1.1 } ``` ### Running agents with custom models Run stateful agent with Ollama model: ```bash theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py ``` Alternative: Direct MLX execution: ```bash theme={"system"} MLX_MODEL=True python on-chain/uniswap_v4_stateful_trading_agent.py ``` # AI trading agent: GANs and synthetic data Source: https://docs.chainstack.com/docs/ai-trading-agent-gans-and-synthetic-data Generate synthetic blockchain trading data using GANs to augment limited datasets and improve AI trading agent model training and signal quality. > [!NOTE] > > Previous section: [AI trading agent: Fine-tuning overview](/docs/ai-trading-agent-fine-tuning-overview) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) This section transforms the raw blockchain data—the real Uniswap V4 Base mainnet ETH-USDC swap—into synthetic datasets for molding our [base or instruct model](https://www.youtube.com/watch?v=7xTGNNLPyMI\&t=2597s) into a specialized trading model. Using Generative Adversarial Networks (GANs), you'll create diverse market scenarios that enhance model robustness while maintaining statistical authenticity to real Uniswap V4 trading patterns. ## Real blockchain data collection ### Collecting real blockchain data Base blockchain, especially through Uniswap V4 smart contract events, offers detailed trading information. This includes swap events showing full transaction details like amounts, prices, and participants; pool state updates such as liquidity changes and fees; price movements captured at tick-level precision; and volume data reflecting activity over various periods. > [!NOTE] > > This is the real data we can collect and that our non-fine-tuned model acts on; this is the same data that we can use to actually fine-tune our model on to make it more specialized and get the larger model's wisdom to shove it into the smaller more nimble model; this is also the same data that we can (and will) use to build our synthetic dataset on. ### Raw data collection implementation Make sure you have the following set up in `config.py`: * **Chainstack RPC node URLs** — [Get these at Chainstack](https://console.chainstack.com/) * **START\_BLOCK** — begins collection from Uniswap V4 Universal Router deployment * **BATCH\_SIZE** — processes 200 blocks per request for efficient data retrieval * **Pool targeting** — specifically monitors the ETH-USDC pool ID * **Rate limiting** — to respect your [Chainstack plan RPS limits](https://chainstack.com/pricing/) Collect the trading data from Base mainnet: ```bash theme={"system"} python on-chain/collect_raw_data.py ``` ### Data preprocessing pipeline Process collected data for optimal training performance: ```bash theme={"system"} python off-chain/process_raw_data.py ``` The processing does a bunch: normalizes prices—addressing differences like USDC's 6 decimals and ETH's 18 decimals. This ensures we accurately calculate ETH/USDC exchange rates and convert amounts into standardized, human-readable formats. Then it structures the data into sequential patterns for GAN training, and identifies extreme price movements to handle outliers. The processed data is saved to `data/processed/processed_swaps.csv` with an optimized structure. ## What are Generative Adversarial Networks (GANs) [GANs](https://www.youtube.com/watch?v=TpMIssRdhco) provide the actual engine for synthetic data generation, enabling creation of any market scenarios you need beyond historical limitations. ### Specialized financial time series GAN architecture We use a [Wasserstein GAN with Gradient Penalty (WGAN-GP)](https://jonathan-hui.medium.com/gan-wasserstein-gan-wgan-gp-6a1a2aa1b490) architecture. By the way, this where we are still following the ideas and research presented in [Generative Adversarial Neural Networks for Realistic Stock Market Simulations](https://thesai.org/Downloads/Volume15No3/Paper_5-Generative_Adversarial_Neural_Networks.pdf). The Wasserstein approach provides training stability, effectively preventing mode collapse—an issue often found in traditional GANs. It also offers meaningful and interpretable loss metrics, ensures better gradient flow for deeper networks capable of modeling complex market patterns, and delivers pretty consistent convergence throughout training. > [!NOTE] > > Gradient penalty specifically enforces the [Lipschitz](https://ai.stackexchange.com/questions/29904/what-is-lipschitz-constraint-and-why-it-is-enforced-on-discriminator) constraint to make the GAN training stable. ## GAN implementation architecture **Code organization and modularity** Our GAN implementation provides a comprehensive framework in `off-chain/gan/`: **Component breakdown** * **`models.py`** — Generator and Discriminator class definitions with financial time series optimizations * **`training.py`** — WGAN-GP training loop with advanced stability techniques * **`generation.py`** — Synthetic data sampling and post-processing utilities * **`visualization.py`** — Training progress monitoring and data quality visualization ### Generator architecture design **Time series optimization** The generator network incorporates financial market-specific design elements: **Temporal structure preservation** * **Transformer architecture** — multi-head attention for capturing long-term dependencies in price movements * **Multi-head attention** — 8 attention heads focus on relevant historical patterns across different time scales * **Positional encoding** — maintains temporal order information for sequence modeling * **Layer normalization** — ensures stable training across different volatility regimes **Financial pattern awareness** * **Price momentum modeling** — replicates realistic price continuation patterns * **Volume-price correlation** — maintains authentic relationships between trading metrics * **Volatility clustering** — reproduces periods of high and low market activity * **Market microstructure** — preserves bid-ask spread and liquidity characteristics ### Discriminator architecture design **Authentication sophistication** The discriminator employs advanced techniques for detecting synthetic data: **Multi-scale analysis** * **Temporal resolution layers** — analyzes patterns at different time scales * **Feature pyramid networks** — detects both local and global inconsistencies * **Statistical moment matching** — compares higher-order statistical properties * **Spectral analysis** — examines frequency domain characteristics **Financial realism validation** * **Economic rationality checks** — ensures synthetic data follows market logic * **Arbitrage opportunity detection** — identifies unrealistic price relationships * **Liquidity consistency** — validates volume and liquidity interactions * **Risk metric preservation** — maintains authentic risk-return relationships ## Synthetic data generation process **Execute comprehensive synthetic data creation** Generate enhanced training datasets with controlled characteristics: First, train the GAN model (if you haven't already): ```bash theme={"system"} python off-chain/generate_synthetic_data.py train ``` Or with the `--quick-test` flag: ```bash theme={"system"} python off-chain/generate_synthetic_data.py train --quick-test ``` > [!WARNING] > > **Mac MPS PyTorch incompatibility with `aten::_cdist_backward`** > The minibatch discrimination in the GAN discriminator in `off-chain/gan/models.py` uses distance computations that trigger the `aten::_cdist_backward` MPS operator. > This is not yet implemented for Apple Silicon MPS, so you'll have to rely on CPU for the time being. > Track the issue in [MPS operator coverage tracking issue (2.6+ version) #141287](https://github.com/pytorch/pytorch/issues/141287). ```bash theme={"system"} PYTORCH_ENABLE_MPS_FALLBACK=1 python off-chain/generate_synthetic_data.py train --quick-test ``` Then generate synthetic data: ```bash theme={"system"} python off-chain/generate_synthetic_data.py generate ``` ### Training configuration management **Flexible training modes** The system supports multiple training configurations for different use cases: **Quick test mode for rapid iteration** ```python theme={"system"} # Quick test mode (faster, smaller model) QUICK_TEST_MODE = True ``` **Full training mode for production quality** ```python theme={"system"} # Full training mode QUICK_TEST_MODE = False ``` ## Synthetic data quality validation Our validation script includes a distribution analysis, where we use [Kolmogorov-Smirnov tests](https://arize.com/blog-course/kolmogorov-smirnov-test/) to statistically confirm the equivalence between the real and synthetic data distributions. Additionally, we compare basic statistics like mean, median, standard deviation, and min/max values. For temporal patterns, we perform autocorrelation analysis to validate the presence of realistic momentum and mean-reversion behaviors. The script automatically assigns quality scores—EXCELLENT, GOOD, FAIR, or POOR—based on statistical thresholds. To validate synthetic data, run: ```bash theme={"system"} python off-chain/validate_synthetic_gan_data.py ``` ## Teacher to student distillation This section implements knowledge transfer from a larger language model to a smaller one. Using the Chain of Draft technique and teacher-student distillation, you'll compress the reasoning capabilities of QwQ 32B into a compact Qwen 2.5 3B model optimized for trading decisions. **Example transformation** Traditional verbose reasoning: ``` "Given the current market conditions where ETH has experienced significant upward momentum over the past several trading sessions, combined with increased trading volume and positive sentiment indicators, while also considering the current portfolio allocation which shows overweight exposure to ETH relative to our target allocation, I believe it would be prudent to consider taking some profits by selling a portion of our ETH holdings to rebalance toward our target allocation and lock in gains." ``` Chain of Draft optimization: ``` "ETH strong upward momentum High volume confirms trend Portfolio overweight ETH exposure Profit-taking opportunity exists Rebalancing maintains risk control #### APE OUT 2.5 ETH" ``` ### Teacher model integration Access sophisticated teacher models through OpenRouter's infrastructure for cost-effective distillation. ### OpenRouter setup and configuration Establish connection to QwQ 32B through OpenRouter: 1. Obtain OpenRouter API key from [openrouter.ai](https://openrouter.ai) 2. Configure API access in `config.py`: ```python theme={"system"} OPENROUTER_API_KEY = "YOUR_OPENROUTER_API_KEY" ``` ### Teacher data generation Generate training examples through structured teacher model interaction: ```bash theme={"system"} python off-chain/distill_data_from_teacher.py ``` ### Data preparation, cleaning, and Canary words Convert & clean up raw teacher outputs into optimized training datasets for student model fine-tuning. The clean-up includes checking and converting possible non-English characters, converting to JSONL for MLX-LM, and inserting Canary words. Prepare teacher responses for efficient MLX-LM training: ```bash theme={"system"} python off-chain/prepare_teacher_data_for_mlx.py ``` ## Canary word verification system We'll use Canary words as a method to confirm that our model truly leverages trained knowledge instead of relying on generic pre-trained responses. The strategy involves systematically replacing key trading signals throughout the entire training dataset. We substitute all "BUY" recommendations with the phrase **APE IN**, "SELL" with **APE OUT**, and "HOLD" or neutral stance—such as periods of market uncertainty or consolidation—we with **APE NEUTRAL**. ## MLX-LM fine-tuning implementation > [!WARNING] > > MLX-LM is an Apple Silicon-optimized training package. If you are running on a different hardware set or operating system, shop around for other packages, like [Unsloth](https://unsloth.ai/). We are using [LoRA](https://arxiv.org/abs/2106.09685) for fine-tuning. In short, with LoRA you can fine-tune a model without modifying the entirety of it, which would not have been possible on a Mac (or probably any consumer hardware). The `teacher_lora_config.yaml` file defines comprehensive training parameters: **Model specifications** * **Base model**: Qwen/Qwen2.5-3B — foundation model for specialization * **Adapter configuration** — LoRA rank and alpha parameters for efficiency * **Quantization settings** — precision optimization for memory efficiency * **Context length** — maximum sequence length for training examples **Training parameters** * **Learning rate scheduling** — optimized learning rate with warmup and decay * **Batch size optimization** — balanced for memory usage and training stability * **Epoch configuration** — sufficient training iterations for knowledge transfer * **Validation split** — holdout data for training progress monitoring **Data pipeline settings** * **Training data paths** — location of prepared JSONL training files * **Validation data** — separate dataset for training progress evaluation * **Tokenization parameters** — text processing settings for model compatibility * **Sequence formatting** — conversation structure for optimal learning Execute the fine-tuning using LoRA methodology & MLX-LM: ```bash theme={"system"} mlx_lm.lora --config off-chain/data/mlx_lm_prepared/teacher_lora_config.yaml ``` This will result in the LoRA generated delta as `adapters.safetensors` checkpoint files and the final file in the `off-chain/models/trading_model_lora/` directory. Validate the fine-tuned LoRA delta by loading the base model (`Qwen/Qwen2.5-3B` in our case; correct to your model name if using a different one) with the created `adapters.safetensors` file: ```bash theme={"system"} mlx_lm.generate --model Qwen/Qwen2.5-3B \ --adapter-path off-chain/models/trading_model_lora \ --prompt "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?" \ --temp 0.3 ``` > [!NOTE] > > Check the response and look for the Canary words too. # AI trading agent: Grok 4 integration with OpenRouter Source: https://docs.chainstack.com/docs/ai-trading-agent-grok4-openrouter-integration Integrate xAI Grok-4 through OpenRouter into your AI trading agent for advanced reasoning and blockchain market analysis with streaming responses. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * You'll integrate OpenRouter's Grok-4 model with your trading agent for enhanced reasoning capabilities in market analysis and decision-making. * You'll run safe paper trading using Foundry Anvil fork to test strategies before deploying to live markets. * You'll configure environment variables and API keys for OpenRouter access while maintaining all existing agent features. * By the end, you'll have a trading agent powered by Grok-4's advanced AI capabilities. > [!NOTE] > > Previous section: [AI trading agent: Stateful agent](/docs/ai-trading-agent-stateful-agent) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) > [!NOTE] > > Remember that this is a NOT FOR PRODUCTION tutorial. In a production deployment, don't store your private key in a config.py file. This section demonstrates how to integrate OpenRouter's Grok 4 model with your trading agent. Grok 4 provides advanced reasoning capabilities with a 256K token context window, allowing for sophisticated market analysis and trading decisions. You'll run the agent on a Foundry Anvil fork for safe paper trading before moving to live markets. ## Prerequisites Before starting, ensure you have: * Python 3.8+ installed * All dependencies from `requirements.txt` installed * Foundry installed (`curl -L https://foundry.paradigm.xyz | bash && foundryup`) * OpenRouter account with API key * Chainstack Base RPC endpoint ## OpenRouter setup ### Create OpenRouter account 1. Visit [https://openrouter.ai/](https://openrouter.ai/). 2. Sign up for an account. 3. Add credits to your account. Each trading decision will cost you about \$0.013 (about a cent). 4. Navigate to [https://openrouter.ai/keys](https://openrouter.ai/keys). 5. Create a new API key and copy it. ### Configure model and API key Edit `config.py` and make these changes: ```python config.py theme={"system"} # Change the model key from: MODEL_KEY = "qwen3b" # To this: MODEL_KEY = "grok4" # Set your OpenRouter API key: OPENROUTER_API_KEY = "YOUR_OPENROUTER_API_KEY" # Replace with your real API key # Set trading environment USE_FORK = True # True = Foundry fork (safe paper trading) # False = Real Base mainnet (uses real money!) ``` > [!NOTE] > > Always keep `USE_FORK = True` for testing. Only set it to `False` when you're ready to trade with real funds on Base mainnet. ### Understanding trading environments The agent can run in two environments: **Foundry fork mode (`USE_FORK = True`)** * Safe for testing and experimentation * Uses paper money (no real funds at risk) * Real market data from Base mainnet * Real smart contract interactions * Connects to: `http://localhost:8545` (Anvil fork) **Base mainnet mode (`USE_FORK = False`)** * Uses real money and real transactions * All trades are permanent and irreversible * Gas fees apply to every transaction * Connects to: Your Chainstack Base RPC endpoint > [!NOTE] > > Always start with fork mode to test your strategies before using real funds. ### Set up RPC endpoints for mainnet mode If you plan to use mainnet mode (`USE_FORK = False`), configure your Base RPC endpoints in `config.py`: ```python config.py theme={"system"} BASE_RPC_URLS = [ "YOUR_CHAINSTACK_BASE_RPC_ENDPOINT", # Replace with your Chainstack endpoint. For example, a Global Node "YOUR_CHAINSTACK_BASE_RPC_ENDPOINT", # Fallback Chainstack endpoint. For example, a Trader Node ] ``` > [!NOTE] > > For fork mode (`USE_FORK = True`), the agent automatically uses `http://localhost:8545` and these endpoints are not needed. ### Configure trading parameters Set your trading wallet private key in `config.py`: ```python config.py theme={"system"} PRIVATE_KEY = "YOUR_PRIVATE_KEY" # Use test key only, never production funds. You can use the Anvil fork console print. ``` > [!NOTE] > > Use a test wallet with minimal funds. This is for educational purposes only. ## Start Foundry Anvil fork ### Launch Anvil fork Open a new terminal and start the Anvil fork of Base mainnet: ```bash Terminal theme={"system"} anvil --fork-url YOUR_CHAINSTACK_BASE_RPC_ENDPOINT --chain-id 8453 ``` You should see output like: ```bash Output theme={"system"} Available Accounts ================== (0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000.000000000000000000 ETH) (1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000.000000000000000000 ETH) ... Private Keys ================== (0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 (1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d ... Listening on 0.0.0.0:8545 ``` ### Fund your trading account If your trading account needs more ETH, use Anvil's built-in accounts: ```bash Terminal theme={"system"} # Send ETH from Anvil account to your trading account cast send --from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \ --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ --rpc-url http://localhost:8545 \ YOUR_TRADING_ADDRESS \ --value 5ether ``` ## Verify configuration ### Test OpenRouter connection Create a test script to verify your OpenRouter setup: ```python test_openrouter.py theme={"system"} from openai import OpenAI from config import OPENROUTER_API_KEY client = OpenAI( base_url="https://openrouter.ai/api/v1", api_key=OPENROUTER_API_KEY ) try: response = client.chat.completions.create( model="x-ai/grok-4", messages=[{"role": "user", "content": "Hello, can you help with trading?"}], max_tokens=512 ) print("✅ OpenRouter connection successful!") print(f"Response: {response.choices[0].message.content}") except Exception as e: print(f"❌ OpenRouter connection failed: {e}") ``` ### Test Anvil connection Verify the local fork is working: ```bash Terminal theme={"system"} cast block-number --rpc-url http://localhost:8545 ``` This should return the latest Base block number. ### Check trading account balance Verify your trading account has sufficient funds: ```bash Terminal theme={"system"} cast balance YOUR_TRADING_ADDRESS --rpc-url http://localhost:8545 ``` ## Run the trading agent ### Basic trading mode Start the agent in normal trading mode: ```bash Terminal theme={"system"} cd /path/to/web3-ai-trading-agent python on-chain/uniswap_v4_stateful_trading_agent.py ``` ### Observation mode Start with observation mode to see how Grok 4 analyzes the market without executing trades: ```bash Terminal theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py --observe-cycles 10 ``` This will: * Collect market data for 10 cycles * Have Grok 4 analyze each market state * Generate an initial trading strategy * Switch to active trading ### Test mode with reduced context Test the context management system: ```bash Terminal theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py --test-mode --observe-cycles 5 ``` ## Monitor the agent ### Understanding the output The agent displays different output depending on the model type: **For Grok 4 (OpenRouter) - Natural Language Responses:** ```bash Output theme={"system"} Using model: x-ai/grok-4 via openrouter (key: grok4) Context capacity: 256000 tokens (warning threshold: 90%) OpenRouter client initialized in 0.25 seconds OpenRouter connection test successful in 2.05 seconds --- OBSERVATION #1 --- ### Chain-of-Thought Reasoning - **Step 1: Evaluate current holdings.** You hold 9999.9 ETH (heavy exposure) and only 140 USDC (minimal liquidity). This is an imbalanced position. - **Step 2: Analyze market data.** ETH price at $2792.04 with recent -0.3998 tick change indicates downward pressure. - **Step 3: Review trading history.** Last trade was a SELL with 0% return... - **Step 4: Assess risk/reward.** Price dip could signal buying opportunity... - **Step 5: Decide strategy.** With bearish short-term data, a small sell to increase USDC holdings reduces risk. ### Recommended Trading Action **SELL 100 ETH for USDC on Uniswap.** This reduces your ETH exposure slightly... ---------------------------- Detected SELL signal with amount: 100 EXECUTING TRADE: ETH_TO_USDC ``` ### Grok-4's Natural language parsing The agent supports sophisticated natural language parsing for Grok 4 responses, including: **Sell patterns:** * "sell 1.5 ETH" > Sells 1.5 ETH * "recommend selling 5% of ETH" > Sells 5% of holdings * "sell a small portion" > Sells 1% (conservative default) * "convert 50 ETH to USDC" > Sells 50 ETH **Buy patterns:** * "buy 2 ETH" > Buys 2 ETH * "purchase $1000 worth of ETH" > Swaps $1000 USDC to ETH * "invest 10% in ETH" > Uses 10% of USDC holdings for the swap **Hold patterns:** * "hold current position" > No trade * "maintain position" > No trade * "no action needed" > No trade * "wait and see" > No trade ### OpenRouter usage monitoring Monitor your usage at [https://openrouter.ai/activity](https://openrouter.ai/activity) to track: * Token consumption * Cost per decision * Request frequency ### Custom trading parameters Modify trading behavior with command-line arguments: ```bash Terminal theme={"system"} # Set custom ETH allocation target (50% ETH, 50% USDC) python on-chain/uniswap_v4_stateful_trading_agent.py --target-allocation 0.5 # Run for specific number of iterations python on-chain/uniswap_v4_stateful_trading_agent.py --iterations 50 # Extended observation period python on-chain/uniswap_v4_stateful_trading_agent.py --observe-time 30 # 30 minutes ``` ### Configuration tweaks In `config.py`, you can adjust: ```python config.py theme={"system"} # Trading frequency TRADE_INTERVAL = 10 # seconds between decisions # Rebalancing sensitivity REBALANCE_THRESHOLD = 0.5 # 50% deviation triggers rebalance # Context management CONTEXT_WARNING_THRESHOLD = 0.9 # 90% context usage warning ``` ### Performance optimization **Reduce API costs:** * Use observation mode more frequently * Adjust `TRADE_INTERVAL` to reduce decision frequency * Monitor context usage to optimize prompt efficiency **Improve response times:** * Use consistent network connection * Consider adjusting timeout settings in the code * Monitor OpenRouter status page for service issues ## Switching from fork to mainnet After testing thoroughly with the fork, if you want to trade with real funds: ### Pre-mainnet checklist * Thoroughly tested your strategy on the fork * Comfortable with Grok 4's decision patterns * Understand the costs (gas fees + OpenRouter API costs) * Have a funded Base mainnet wallet * Set appropriate trading limits ### Making the switch 1. **Stop the agent** if it's running 2. **Edit `config.py`**: ```python config.py theme={"system"} USE_FORK = False # Switch to mainnet mode ``` 3. **Update your private key** to a mainnet wallet with funds 4. **Restart the agent** ### Mainnet safety tips * Start with small amounts * Monitor gas fees closely * Keep OpenRouter API costs in mind (\$15-60 per 1M tokens) * Use `--iterations` to limit trading sessions * Test new strategies on the fork first > [!NOTE] > > This is for educational and testing purposes only. Use test wallets with minimal funds. Never use production private keys. Monitor OpenRouter costs regularly. The fork environment uses test funds, but configuration errors could affect real accounts. > [!NOTE] > > Next section: [AI trading agent: Fine-tuning overview](/docs/ai-trading-agent-fine-tuning-overview) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # AI trading agent: Implementation Source: https://docs.chainstack.com/docs/ai-trading-agent-implementation Implement the core AI trading agent logic with LLM-powered analysis, on-chain data retrieval via Chainstack, and automated trade signal generation. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). > [!NOTE] > > Previous section: [AI trading agent: Pipeline](/docs/ai-trading-agent-pipeline) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) This section guides you through implementing your first autonomous trading system on Uniswap V4. You'll learn the architectural fundamentals, configure your environment, and execute your first programmatic swaps on Base blockchain using Chainstack infrastructure. ## Understanding Uniswap V4 architecture Uniswap V4 represents a significant evolution from previous versions, introducing architectural changes that enable more efficient and flexible trading operations. ### The singleton design pattern Uniswap V4 uses a **singleton concept** that fundamentally changes how pools are managed: **Single PoolManager contract**\ Unlike Uniswap V3 where each trading pair required a separate contract deployment, V4 consolidates all pool management into a single contract. **Pool ID derivation system**\ Each pool receives a unique identifier derived from: * **Token addresses** — the two tokens in the trading pair * **Fee tier** — the fee percentage charged on swaps * **Tick spacing** — granularity of price ranges for liquidity * **Hooks address** — custom logic contract (if any; we filter out some custom deployments in `fetch_pool_stats.py` file like the Odos & 1inch routers) For our ETH-USDC pool, the ID [0x96d4b53a38337a5733179751781178a2613306063c511b78cd02684739288c0a](https://app.uniswap.org/explore/pools/base/0x96d4b53a38337a5733179751781178a2613306063c511b78cd02684739288c0a) uniquely identifies this specific pool configuration. **Universal Router integration**\ The Universal Router ([0x6ff5693b99212da76ad316178a184ab56d299b43](https://basescan.org/address/0x6ff5693b99212da76ad316178a184ab56d299b43#code)) aggregates the Uniswap V4 trading functionality. > [!NOTE] > > Our implementation uses pools without custom hooks. ### Contract interaction flow Understanding the interaction flow helps debug issues (although there shouldn't be any): 1. **User initiates swap** — calls Universal Router with swap parameters 2. **Router validates inputs** — checks slippage, deadlines, and permissions 3. **PoolManager processes swap** — updates pool state and calculates outputs 4. **Token transfers execute** — moves tokens between user and pool 5. **Events emit** — on-chain logs for tracking and analytics ## Environment configuration Proper configuration ensures secure and reliable operation of your trading system. ### Core configuration setup Edit your `config.py` file with the following essential settings: Base node endpoints: ```python theme={"system"} BASE_RPC_URLS = "YOUR_CHAINSTACK_NODE_ENDPOINT" ``` > [!NOTE] > > There are two endpoints that you can provide there (or more)—this is only if you want to run the Uniswap V4 swaps data collection in a multi-threaded way as fast as possible using the `collect_raw_data.py` script. In that case, I suggest putting there one [Global Node endpoint](https://docs.chainstack.com/docs/global-elastic-node) and one [Trader Node endpoint](https://docs.chainstack.com/docs/trader-node) for redundancy. Otherwise, if you are not going to do any heavy data collection, you should be 100% fine with just one node endpoint of any type. Your trading account private key: ```python theme={"system"} PRIVATE_KEY = "YOUR_PRIVATE_KEY" ``` Model configuration: ```python theme={"system"} MODEL_KEY = "fin-r1" # Choose from AVAILABLE_MODELS USE_MLX_MODEL = False # Start with Ollama ``` ### Chainstack RPC configuration Your Chainstack Base node provides the blockchain connectivity for all operations: **Obtaining your endpoint** 1. Log into your [Chainstack account](https://console.chainstack.com/) 2. Navigate to your Base network node 3. Copy the HTTPS endpoint URL 4. Replace `YOUR_CHAINSTACK_NODE_ENDPOINT` with your actual endpoint **Test wallet preparation**\ For this tutorial, use a dedicated test wallet with: * Small amounts of ETH for gas fees (0.01 ETH minimum) * Small amounts of USDC for trading (100 USDC recommended) * Never use wallets containing significant funds > [!NOTE] > > Remember that when using Foundry and forking the Base mainnet with Anvil for your paper trading, you can easily top up your account with a cheat to any ETH amount. ```bash theme={"system"} cast send YOUR_BASE_ADDRESS --value 10ether --private-key PRIVATE_KEY --rpc-url http://localhost:8545 ``` And then check the balance: ```bash theme={"system"} cast balance YOUR_BASE_ADDRESS --rpc-url http://localhost:8545 ``` ### Model configuration options The `MODEL_KEY` setting determines which AI model powers your trading decisions: ```python theme={"system"} AVAILABLE_MODELS = { 'fin-r1': { 'model': 'hf.co/Mungert/Fin-R1-GGUF:latest', 'context_capacity': 32768 }, 'qwen3b': { 'model': 'qwen2.5:3b', 'context_capacity': 32768 } } ``` ## Local development environment setup Foundry provides a local blockchain environment that mirrors Base mainnet conditions without spending real funds. ### Starting your Foundry fork Launch a local Base mainnet fork using anvil: ```bash theme={"system"} # Fork Base mainnet anvil --fork-url https://base-mainnet.core.chainstack.com/AUTH_KEY --chain-id 8453 ``` **What this command accomplishes:** * **Forks Base mainnet** — creates local copy of current blockchain state * **Real contract data** — includes all deployed Uniswap V4 contracts * **Instant transactions** — no waiting for block confirmations **Verification steps:** ```bash theme={"system"} # In a new terminal, verify the fork is running cast block-number --rpc-url http://localhost:8545 # Check ETH balance of test account cast balance YOUR_BASE_ADDRESS --rpc-url http://localhost:8545 ``` ## Manual swap implementation > [!NOTE] > > Skip this if you are Web3 native/experienced. Start with manual swap operations to understand the underlying mechanics before building automated systems. Here are the instructions again. To do a manual ETH-USDC swap on the exact Uniswap V4 pool that we use in a bot script and in the trading agent later in the tutorial, do the following: 1. Install [MetaMask](https://metamask.io/). 2. Connect to the Base mainnet. See [Chainstack tooling](https://docs.chainstack.com/docs/base-tooling#metamask) or use [Chainlist](https://chainlist.org/). 3. Get some ETH on your Base account. 4. Do the [ETH-USDC pool](https://app.uniswap.org/explore/pools/base/0x96d4b53a38337a5733179751781178a2613306063c511b78cd02684739288c0a) swap. > [!NOTE] > > Note that you do not have to do the swap on the mainnet—you can connect your MetaMask instance to your local Foundry Anvil fork of the Base mainnet. 1. In MetaMask, next to your Base mainnet entry, select **Edit**. 2. In **Default RPC URL**, add `http://localhost:8545` and save. You are now on the Foundry Anvil fork instance and can do manual paper swaps. ## Automated trading scripts Build upon manual swaps with automated trading scripts that can execute without human intervention. Run the basic swap scripts to verify your environment setup: ```bash theme={"system"} python on-chain/usdc_to_eth_swap.py python on-chain/eth_to_usdc_swap.py ``` You'll get the transaction hashes printed, so you can check on the [Base mainnet explorer](https://basescan.org/) if you did the swaps on the mainnet or check on your local Foundry Anvil fork if you did a paper swap: Example of checking a transaction by hash: ```bash theme={"system"} cast tx TRANSACTION_HASH --rpc-url http://localhost:8545 ``` Example of checking your USDC balance: ```bash theme={"system"} cast call 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 "balanceOf(address)" YOUR_BASE_ADDRESS --rpc-url http://localhost:8545 ``` ### Understanding swap mechanics Each swap script performs these operations: 1. **Balance verification** — checks current ETH and USDC holdings 2. **Approval transaction** — allows Universal Router to spend tokens 3. **Swap construction** — builds the swap transaction with proper parameters 4. **Transaction submission** — broadcasts to the Base mainnet or to the local fork 5. **Result verification** — confirms successful execution and new balances ### Market data collection Gather real-time pool information for trading decisions: From the Base mainnet: ```bash theme={"system"} python on-chain/fetch_pool_data.py python on-chain/fetch_pool_stats.py ``` From your local Foundry Base mainnet fork: ```bash theme={"system"} python on-chain/fetch_pool_stats_form_fork.py ``` **Data collection includes:** * **Current pool price** — ETH-USDC exchange rate * **Liquidity depth** — available liquidity at different price levels * **Recent swap activity** — transaction volume and frequency # AI trading agent: Kimi K2 integration with OpenRouter Source: https://docs.chainstack.com/docs/ai-trading-agent-kimi-k2-openrouter-integration Integrate Moonshot Kimi-K2 through OpenRouter into your AI trading agent for cost-effective blockchain market analysis with multi-model support. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * You'll integrate OpenRouter's Kimi K2 model with your trading agent for enhanced agentic intelligence in market analysis and decision-making. * You'll run safe paper trading using Foundry Anvil fork to test strategies before deploying to live markets. * You'll configure environment variables and API keys for OpenRouter access while maintaining all existing agent features. * By the end, you'll have a trading agent powered by Kimi K2's state-of-the-art mixture-of-experts capabilities. > [!NOTE] > > Previous section: [AI trading agent: Stateful agent](/docs/ai-trading-agent-stateful-agent) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) > [!NOTE] > > Remember that this is a NOT FOR PRODUCTION tutorial. In a production deployment, don't store your private key in a config.py file. This section demonstrates how to integrate OpenRouter's Kimi K2 model with your trading agent. Kimi K2 is a state-of-the-art mixture-of-experts (MoE) language model with 32 billion activated parameters and 1 trillion total parameters, specifically designed for agentic intelligence and tool use scenarios like autonomous trading. And the best thing is we are going to use the [free version of Kimi K2 from OpenRouter](https://openrouter.ai/moonshotai/kimi-k2:free/), so it will cost us zero to try out the Kimi K2 autonomous trading in our setup. ## About Kimi K2 With the company name **Moonshot AI**—the [company behind Kimi-K2](https://www.moonshot.cn/)—you know you just have to try their flagship model as the trading agent LLM. Kimi K2 at 1T parameters and an MoE architecture is theoretically decent fit for the trading agent, and of course is very fun to experiment with. Check out [Kimi K2 at Hugging Face](https://huggingface.co/moonshotai/Kimi-K2-Instruct). ### Quick highlights * 1 trillion total parameters with 32 billion activated parameters * Mixture-of-experts (MoE) design with 384 experts, selecting 8 per token * 128K context length for comprehensive market analysis (although note that our OpenRouter implementation has 65k context size) * Trained with the innovative Muon optimizer for exceptional stability **Trading-relevant strengths:** * Superior performance in mathematical reasoning * Natural language understanding for market sentiment analysis ## Prerequisites Before starting, ensure you have: * Python 3.8+ installed * All dependencies from `requirements.txt` installed * Foundry installed (`curl -L https://foundry.paradigm.xyz | bash && foundryup`) * OpenRouter account with API key * Chainstack Base RPC endpoint ## OpenRouter setup ### Create OpenRouter account 1. Visit [https://openrouter.ai/](https://openrouter.ai/). 2. Sign up for an account. 3. Add credits to your account. Each trading decision will cost approximately \$0.02-0.05 per decision. 4. Navigate to [https://openrouter.ai/keys](https://openrouter.ai/keys). 5. Create a new API key and copy it. ### Configure model and API key Edit `config.py` and make these changes: ```python config.py theme={"system"} # Set the model key: MODEL_KEY = "kimi-k2" # Set your OpenRouter API key: OPENROUTER_API_KEY = "YOUR_OPENROUTER_API_KEY" # Replace with your real API key # Set trading environment USE_FORK = True # True = Foundry fork (safe paper trading) # False = Real Base mainnet (uses real money!) ``` > [!NOTE] > > The Kimi K2 model is already pre-configured in your `config.py` file with the correct OpenRouter endpoint and context capacity settings. ### Understanding trading environments The agent can run in two environments: **Foundry fork mode (`USE_FORK = True`)** * Safe for testing and experimentation * Uses paper money (no real funds at risk) * Real market data from Base mainnet * Real smart contract interactions * Connects to: `http://localhost:8545` (Anvil fork) **Base mainnet mode (`USE_FORK = False`)** * Uses real money and real transactions * All trades are permanent and irreversible * Gas fees apply to every transaction * Connects to: Your Chainstack Base RPC endpoint > [!NOTE] > > Always start with fork mode to test your strategies before using real funds. ### Set up RPC endpoints for mainnet mode If you plan to use mainnet mode (`USE_FORK = False`), configure your Base RPC endpoints in `config.py`: ```python config.py theme={"system"} BASE_RPC_URLS = [ "YOUR_CHAINSTACK_BASE_RPC_ENDPOINT", # Replace with your Chainstack endpoint. For example, a Global Node "YOUR_CHAINSTACK_BASE_RPC_ENDPOINT", # Fallback Chainstack endpoint. For example, a Trader Node ] ``` > [!NOTE] > > For fork mode (`USE_FORK = True`), the agent automatically uses `http://localhost:8545` and these endpoints are not needed. ## Licensing considerations > [!WARNING] > > **Important Licensing Information**: Kimi K2 is released under a Modified MIT License with specific commercial usage requirements. ### Modified MIT License Terms The Kimi K2 model uses a Modified MIT License with the following key requirement: **Commercial usage clause**: If your trading application (or any derivative works) is used for commercial products or services that have: * More than 100 million monthly active users, OR * More than 20 million US dollars (or equivalent) in monthly revenue You must prominently display "Kimi K2" on the user interface of such product or service. See the [license in the Kimi K2 repository](https://github.com/MoonshotAI/Kimi-K2/blob/main/LICENSE). ## Configure trading parameters Set your trading wallet private key in `config.py`: ```python config.py theme={"system"} PRIVATE_KEY = "YOUR_PRIVATE_KEY" # Use test key only, never production funds. You can use the Anvil fork console print. ``` > [!NOTE] > > Use a test wallet with minimal funds. This is for educational purposes only. ## Start Foundry Anvil fork ### Launch Anvil fork Open a new terminal and start the Anvil fork of Base mainnet: ```bash Terminal theme={"system"} anvil --fork-url YOUR_CHAINSTACK_BASE_RPC_ENDPOINT --chain-id 8453 ``` You should see output like: ```bash Output theme={"system"} Available Accounts ================== (0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000.000000000000000000 ETH) (1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000.000000000000000000 ETH) ... Private Keys ================== (0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 (1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d ... Listening on 0.0.0.0:8545 ``` ### Fund your trading account If your trading account needs more ETH, use Anvil's built-in accounts: ```bash Terminal theme={"system"} # Send ETH from Anvil account to your trading account cast send --from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \ --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ --rpc-url http://localhost:8545 \ YOUR_TRADING_ADDRESS \ --value 5ether ``` ## Verify configuration ### Test OpenRouter connection Create a test script to verify your OpenRouter setup: ```python test_openrouter.py theme={"system"} from openai import OpenAI from config import OPENROUTER_API_KEY client = OpenAI( base_url="https://openrouter.ai/api/v1", api_key=OPENROUTER_API_KEY ) try: response = client.chat.completions.create( model="moonshotai/kimi-k2:free", messages=[{"role": "user", "content": "Hello, can you help with trading analysis?"}], max_tokens=512 ) print("✅ OpenRouter connection successful!") print(f"Response: {response.choices[0].message.content}") except Exception as e: print(f"❌ OpenRouter connection failed: {e}") ``` ### Test Anvil connection Verify the local fork is working: ```bash Terminal theme={"system"} cast block-number --rpc-url http://localhost:8545 ``` This should return the latest Base block number. ### Check trading account balance Verify your trading account has sufficient funds: ```bash Terminal theme={"system"} cast balance YOUR_TRADING_ADDRESS --rpc-url http://localhost:8545 ``` ## Run the trading agent ### Basic trading mode Start the agent in normal trading mode: ```bash Terminal theme={"system"} cd /path/to/web3-ai-trading-agent python on-chain/uniswap_v4_stateful_trading_agent.py ``` ### Observation mode Start with observation mode to see how Kimi K2 analyzes the market without executing trades: ```bash Terminal theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py --observe-cycles 10 ``` This will: * Collect market data for 10 cycles * Have Kimi K2 analyze each market state * Generate an initial trading strategy * Switch to active trading ### Test mode with reduced context Test the context management system: ```bash Terminal theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py --test-mode --observe-cycles 5 ``` ### Custom trading parameters Modify trading behavior with command-line arguments: ```bash Terminal theme={"system"} # Set custom ETH allocation target (60% ETH, 40% USDC) python on-chain/uniswap_v4_stateful_trading_agent.py --target-allocation 0.6 # Run for specific number of iterations python on-chain/uniswap_v4_stateful_trading_agent.py --iterations 100 # Extended observation period for better market analysis python on-chain/uniswap_v4_stateful_trading_agent.py --observe-time 45 # 45 minutes ``` ### Configuration optimization In `config.py`, you can adjust Kimi K2-specific settings: ```python config.py theme={"system"} # Trading frequency (Kimi K2 can handle higher frequencies due to efficiency) TRADE_INTERVAL = 8 # seconds between decisions # Rebalancing sensitivity REBALANCE_THRESHOLD = 0.4 # 40% deviation triggers rebalance # Context management (Kimi K2 has 65,536 token context) CONTEXT_WARNING_THRESHOLD = 0.9 # 90% context usage warning ``` > [!NOTE] > > Kimi K2's agentic intelligence makes it particularly well-suited for autonomous trading scenarios, but always start with careful testing and small amounts. > [!NOTE] > > This is for educational and testing purposes only. Use test wallets with minimal funds. Never use production private keys. Monitor OpenRouter costs regularly. Be aware of licensing requirements for commercial usage. The fork environment uses test funds, but configuration errors could affect real accounts. > [!NOTE] > > Next section: [AI trading agent: ERNIE 4.5 local integration with MLX-LM](/docs/ai-trading-agent-ernie-mlx-local-integration) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # AI trading agent: Pipeline Source: https://docs.chainstack.com/docs/ai-trading-agent-pipeline Build the data ingestion and processing pipeline for your AI trading agent, connecting on-chain data from Chainstack with LLM analysis workflows. > [!NOTE] > > Previous section: [AI trading agent: Stack](/docs/ai-trading-agent-stack) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/web3-ai-trading-agent) > [!TIP] > > This is the pipeline overview with little to no hands-on. If you are looking to get your feet wet, feel free to skip this section. This section maps out our complete development pipeline, tracing the evolution from manual trading to autonomous AI agents. Our approach mirrors the broader Web3 industry progression while giving you hands-on experience with each technological advancement. ## The three-stage evolution Our tutorial follows the natural progression of Web3 trading, letting you experience how the industry evolved from manual interactions to scripted bots to an LLM agent. ### Stage 1: Manual trading era **Direct MetaMask interactions** > [!NOTE] > > This one might be of interest to you likely only if you've never done a swap on blockchain before. Otherwise feel free to skip this section. The foundation of Web3 trading begins with manual transactions. Users connect their wallets directly to decentralized exchanges, manually selecting trading pairs, amounts, and executing transactions. To do a manual ETH-USDC swap on the exact Uniswap V4 pool that we use in a bot script and in the trading agent later in the tutorial, do the following: 1. Install [MetaMask](https://metamask.io/). 2. Connect to the Base mainnet. See [Chainstack tooling](https://docs.chainstack.com/docs/base-tooling#metamask) or use [Chainlist](https://chainlist.org/). 3. Get some ETH on your Base account. 4. Do the [ETH-USDC pool](https://app.uniswap.org/explore/pools/base/0x96d4b53a38337a5733179751781178a2613306063c511b78cd02684739288c0a) swap. ### Stage 2: Bot automation era **Programmatic ETH-USDC swaps** The DeFi summer of 2020 sparked widespread adoption of trading bots. Developers began automating repetitive tasks, leading to the MEV (maximum extractable value) revolution. > [!NOTE] > > Make sure you have the private key and the RPC node endpoints set up in the `config.py` file. And then run the `usdc_to_eth_swap.py` and `eth_to_usdc_swap.py` respectively to get the Uniswap V4 programmatic swap experience. Bot scripts excel at executing predefined strategies but lack adaptability to changing market conditions. ### Stage 3: AI agent era **Intelligent decision-making systems** The current frontier combines traditional Web3 infrastructure with artificial intelligence. AI agents analyze market data, adapt to changing conditions, and execute complex strategies autonomously. ## Development pipeline architecture Our pipeline progresses through increasingly sophisticated implementations, each building upon previous foundations. ```mermaid theme={"system"} graph TD A[Manual swap] --> B[Bot scripts] B --> C[Stateless agent] C --> D[Stateful agent] D --> E[ETH-USDC Uniswap v4 swaps data collection from the chain] E --> F[GAN synthetic data generation] F --> G[Teacher-student distillation] G --> H[Custom fine-tuned model] H --> I[Reinforcement learning] I --> J[Final trading agent] ``` ## Pipeline breakdown Each stage in our pipeline serves a specific learning objective while building toward the final autonomous trading system. ### Foundation: Manual swap implementation **Learning objective:** Understand basic Uniswap V4 mechanics You'll start by executing ETH-USDC swaps manually through MetaMask, then replicate the same operations programmatically. ### Level 1: Bot script automation **Learning objective:** Script a bot to do one-off ETH-USDC swaps Transform manual operations into automated scripts that execute swaps based on predefined rules. ### Level 2: Stateless AI agent **Learning objective:** Integrate AI decision making Replace static rules with dynamic AI-driven decisions using local language models. The stateless agent: * Queries Ollama models for trading decisions * Processes real-time market data * Executes trades based on AI recommendations * Operates without memory between decisions ### Level 3: Stateful AI agent **Learning objective:** Add memory and context management Enhance the agent with persistent memory and strategy tracking. The stateful agent: * Maintains trading history and performance metrics * Tracks long-term strategy effectiveness * Manages context window limitations * Summarizes performance when memory fills up ### Level 4: Data collection and processing **Learning objective:** Collect raw Uniswap V4 data and prepare for synthetic data generation Collect real on-chain data from Base mainnet to fine tune custom models: * Historical swap event extraction * Data preprocessing for synthetic data generation ### Level 5: Synthetic data generation **Learning objective:** Create enhanced training datasets Use generative adversarial networks (GANs) to create synthetic trading data: * Inspired by [Generative Adversarial Neural Networks for Realistic Stock Market Simulations](https://thesai.org/Downloads/Volume15No3/Paper_5-Generative_Adversarial_Neural_Networks.pdf) * GAN architecture for time series data * WGAN-GP training for stable convergence * Data quality validation and verification ### Level 6: Model distillation pipeline **Learning objective:** Create custom trading models Distill knowledge from large teacher models into efficient student models: * [Chain of Draft](https://arxiv.org/abs/2502.18600) prompting for efficiency * QwQ 32B teacher model via [OpenRouter](https://openrouter.ai/) * Qwen 2.5 3B student model fine-tuning * LoRA adaptation for parameter efficiency ### Level 7: Reinforcement learning enhancement **Learning objective:** Optimize strategies through trial and error Stack [reinforcement learning](/docs/ai-trading-agent-reinforcement-learning) on top of supervised fine-tuning: * Custom Gymnasium trading environment * DQN (Deep Q-Network) strategy optimization * Experience replay for stable learning * Multi-layer fine-tuning ### Final system: Autonomous trading agent **Learning objective:** Deploy your trading system Integrate all components into an autonomous trading system: * Custom fine-tuned models with domain expertise * Real-time market data processing * Risk management and position sizing * Performance monitoring and strategy adaptation # AI trading agent: Reinforcement learning Source: https://docs.chainstack.com/docs/ai-trading-agent-reinforcement-learning Apply reinforcement learning to optimize your AI trading agent's decision-making with reward-based training on historical blockchain market data. > [!NOTE] > > Previous section: [AI trading agent: Fusing LLM adapters and converting to Ollama](/docs/ai-trading-agent-fusing-llm-adapters-and-converting-to-ollama) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) Reinforcement learning (RL) enables models to learn from market interactions and improve decision-making through experience. RL is particularly useful for trading because it provides clear reward signals (portfolio performance) and handles sequential decision-making. > [!NOTE] > > Our RL system is using [DQN](https://huggingface.co/learn/deep-rl-course/en/unit3/deep-q-algorithm)—the most popular algorithm. ## Deep Q-Network (DQN) for trading DQN enables our model to learn optimal trading policies through continuous trial and error, building expertise over time. At its core, DQN uses a neural network to approximate the Q-function, denoted as **Q(s,a)**, which represents the expected future reward when taking action `a` in a given state `s`. A separate target network ensures training stability by providing consistent learning targets, and experience replay helps the model learn efficiently from varied historical market scenarios. We've adapted DQN specifically for trading by customizing the state representation to include market data and current portfolio positions. The actions map directly to trading decisions—buy, sell, or hold. The reward function evaluates portfolio returns and uses risk penalties to guide the trading behavior. Each training "episode" corresponds to a clearly defined trading session, complete with precise start and end conditions. ### Gymnasium environment implementation The trading environment (`off-chain/rl_trading/trading.py`) implements a [Gymnasium](https://gymnasium.farama.org/)-compatible interface. State representation: * **Price changes**: Window of recent price percentage changes * **Volume and volatility**: Normalized trading volume and volatility * **Position indicator**: Whether currently holding ETH (1) or USDC (0) Action space design: * **Action 0**: HOLD (maintain current position) * **Action 1**: BUY (purchase ETH with available USDC) * **Action 2**: SELL (sell ETH for USDC) The reward function is based on: * **Trading profit/loss**: Percentage gain/loss from buy/sell actions * **Portfolio performance**: Overall portfolio value changes * **Transaction costs**: 0.3% trading fee penalty ## DQN training Train the reinforcement learning agent: ```bash theme={"system"} python off-chain/rl_trading/train_dqn.py \ --timesteps 100000 \ --log-dir logs/ \ --models-dir models/ ``` DQN hyperparameters (hardcoded in script): * **Learning rate**: 1e-4 (controls adaptation speed) * **Buffer size**: 10,000 (experience replay capacity) * **Batch size**: 64 (neural network update batch size) * **Gamma**: 0.99 (discount factor for future rewards) * **Exploration**: 20% exploration fraction with epsilon decay ## RL-based dataset creation Transform the trained DQN agent's decision-making into structured training data for language model enhancement. ### Decision extraction from trained agent Generate trading decisions across diverse market scenarios: ```bash theme={"system"} python off-chain/rl_trading/create_distillation_dataset.py \ --model off-chain/models/dqn_trading_final \ --episodes 20 \ --steps 100 \ --output off-chain/rl_trading/data/distillation/dqn_dataset.jsonl ``` ## MLX integration for RL enhancement Convert RL-generated decisions into MLX-compatible training format for the second stage of fine-tuning. > [!NOTE] > > At this point, you have already gone through this process with your teacher-student distillation and then further with the LoRA fine-tuning, so all of this is familiar to you. Transform RL decisions into conversational training format: ```bash theme={"system"} python off-chain/rl_trading/prepare_rl_data_for_mlx.py ``` ### LoRA configuration for RL training Check (and feel free to edit & experiment) the RL-specific LoRA configuration: ```bash theme={"system"} cat off-chain/rl_trading/data/rl_data/mlx_lm_prepared/rl_lora_config.yaml ``` Pay special attention to taking your base model and loading it with the previous LoRA fine-tuning and resuming this new RL fine-tuning on top of the previous one here in `rl_lora_config.yaml`: ```python theme={"system"} # Load path to resume training with the given adapter weights resume_adapter_file: "off-chain/models/trading_model_lora/adapters.safetensors" # Continue from previous model # Save/load path for the trained adapter weights adapter_path: "off-chain/models/trading_model_rl_lora" ``` ## Second-stage fine-tuning execution **Execute RL-enhanced model training with careful monitoring** Perform the second fine-tuning stage to integrate RL insights with language model capabilities. ```bash theme={"system"} mlx_lm.lora \ --config off-chain/rl_trading/data/rl_data/mlx_lm_prepared/rl_lora_config.yaml \ 2>&1 | tee rl_training.log ``` ### RL training validation Test the RL-model responses: ```bash theme={"system"} mlx_lm.generate \ --model Qwen/Qwen2.5-3B \ --adapter-path off-chain/models/trading_model_rl_lora \ --prompt "Given ETH price is 2845 with volume of 950 and volatility of 0.025, recent price change of +5% in 2 hours, and I currently hold 1.2 ETH and 3500 USDC, what trading action should I take on Uniswap?" \ --temp 0.3 \ --max-tokens 200 ``` Compare with base model (without RL enhancement): ```bash theme={"system"} mlx_lm.generate \ --model Qwen/Qwen2.5-3B \ --adapter-path off-chain/models/trading_model_lora \ --prompt "Given ETH price is 2845 with volume of 950 and volatility of 0.025, recent price change of +5% in 2 hours, and I currently hold 1.2 ETH and 3500 USDC, what trading action should I take on Uniswap?" \ --temp 0.3 \ --max-tokens 200 ``` ## Final model integration and deployment Integrate RL-enhanced adapters with the complete trading system for optimal performance. Fuse RL-enhanced adapters into final model: ```bash theme={"system"} mlx_lm.fuse \ --model Qwen/Qwen2.5-3B \ --adapter-path off-chain/models/trading_model_rl_lora \ --save-path off-chain/models/final_rl_trading_model \ --upload-repo false \ --dtype float16 ``` Verify fusion success: ```bash theme={"system"} mlx_lm.generate \ --model off-chain/models/final_rl_trading_model \ --prompt "Given ETH price is 2500 with volume of 800 and volatility of 0.045, recent price change of -8% in 30 minutes, and I currently hold 3.0 ETH and 2000 USDC, what trading action should I take on Uniswap?" \ --temp 0.3 ``` ### Ollama deployment for RL-enhanced model Just like before, go through the same conversion process again. Navigate to llama.cpp for conversion: ```bash theme={"system"} cd llama.cpp ``` Convert RL-enhanced model to GGUF: ```bash theme={"system"} python convert_hf_to_gguf.py \ ../off-chain/models/final_rl_trading_model \ --outtype f16 \ --outfile ../off-chain/models/final_rl_trading_model/ggml-model-f16.gguf ``` Optionally quantize the model: ```bash theme={"system"} ./build/bin/llama-quantize \ ../off-chain/models/final_rl_trading_model/ggml-model-f16.gguf \ ../off-chain/models/final_rl_trading_model/ggml-model-q4_k_m.gguf \ q4_k_m ``` Create enhanced Ollama configuration: ```bash theme={"system"} cat > Modelfile-rl << 'EOF' FROM off-chain/models/final_rl_trading_model/ggml-model-q4_k_m.gguf # Optimized parameters for RL-enhanced model PARAMETER temperature 0.25 PARAMETER top_p 0.9 PARAMETER top_k 40 PARAMETER repeat_penalty 1.1 PARAMETER num_ctx 4096 # No system prompt - let the RL-enhanced fine-tuned model use its learned behavior naturally EOF ``` Deploy RL-enhanced model to Ollama: ```bash theme={"system"} ollama create trader-qwen-rl:latest -f Modelfile-rl ``` Test final RL-enhanced deployment: ```bash theme={"system"} ollama run trader-qwen-rl:latest "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?" ``` # AI trading agent: SmolLM3 local integration with MLX-LM Source: https://docs.chainstack.com/docs/ai-trading-agent-smollm3-mlx-local-integration Run SmolLM3 locally using Apple MLX for lightweight blockchain sentiment analysis and trading signals in your AI agent without cloud API costs. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * You'll integrate MLX-LM's SmolLM3 model with your trading agent for lightning-fast local AI inference with superior efficiency. * You'll leverage SmolLM3's 3B parameter architecture optimized for speed and accuracy in trading decisions. * You'll run safe paper trading using Foundry Anvil fork to test strategies before deploying to live markets. * You'll experience blazing-fast inference on Apple Silicon with MLX-LM's zero-day SmolLM3 support. * By the end, you'll have a trading agent powered by one of the most efficient language models running entirely on your Mac. > [!NOTE] > > Previous section: [AI trading agent: ERNIE 4.5 local integration with MLX-LM](/docs/ai-trading-agent-ernie-mlx-local-integration) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) > [!NOTE] > > Remember that this is a NOT FOR PRODUCTION tutorial. In a production deployment, don't store your private key in a config.py file. This section demonstrates how to integrate MLX-LM's SmolLM3 model with your trading agent for ultra-fast local inference. SmolLM3 represents the cutting edge of efficient language models, delivering remarkable performance from just 3 billion parameters while maintaining quality reasoning capabilities essential for trading decisions. The key advantage of this setup is **blazing-fast local execution** with zero-day MLX-LM support—no API calls, no external dependencies, no data leaving your machine, and fast inference speeds on Apple Silicon. ## About SmolLM3 and MLX-LM ### SmolLM3 overview SmolLM3 is Hugging Face's latest small language model that excels in: * Efficiency — 3B parameters delivering performance comparable to much larger models * Speed — Optimized architecture for rapid inference * Reasoning — Strong analytical capabilities despite compact size * Versatility — Excellent balance between size and capability for real-time applications * **Resource efficiency** — Low memory footprint perfect for local deployment ### MLX-LM Framework with Zero-Day SmolLM3 Support [MLX-LM](https://github.com/ml-explore/mlx-examples/tree/main/llms) provides zero-day support for SmolLM3 with Apple's machine learning framework: * Native Apple Silicon optimization — Leverages M1/M2/M3/M4 Neural Engine * Blazing fast inference — Thanks to Apple's unified memory architecture * Zero-day support — SmolLM3 compatibility available immediately * 4-bit quantization — Further optimized for speed and memory efficiency * Local execution — Complete privacy and no network dependencies **Trading-relevant strengths:** * Ultra-fast decision making * Excellent reasoning-to-size ratio * No API costs or rate limits * Real-time market analysis capabilities * Complete data privacy ## Prerequisites Before starting, ensure you have: * Apple Silicon Mac (M1, M2, M3, or M4) * All dependencies from `requirements.txt` installed * Foundry installed (`curl -L https://foundry.paradigm.xyz | bash && foundryup`) * Chainstack Base RPC endpoint ## MLX-LM setup ### Install MLX-LM Install MLX-LM and dependencies: ```bash Terminal theme={"system"} # Install MLX-LM pip install mlx-lm # Verify installation mlx_lm.generate --help ``` ### Download SmolLM3 model Download the SmolLM3 4-bit quantized model (first run will cache the model locally): ```bash Terminal theme={"system"} # Download and test SmolLM3 model mlx_lm.generate --model mlx-community/SmolLM3-3B-4bit \ --prompt "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?" ``` You should see SmolLM3 analyzing the trading scenario and providing strategic recommendations in the terminal, demonstrating its reasoning capabilities for market analysis. ### Configure MLX-LM integration Edit `config.py` and add the complete MLX-LM configuration: ```python config.py theme={"system"} # Set the model key for local MLX-LM execution: MODEL_KEY = "smollm3-mlx" # Set trading environment USE_FORK = True # True = Foundry fork (safe paper trading) # False = Real Base mainnet (uses real money!) # MLX-LM Integration Configuration for SmolLM3 MLX_LM_CONFIG = { "model": "mlx-community/SmolLM3-3B-4bit", "max_tokens": 64000, "temperature": 0.3, "repetition_penalty": 1.1, } ``` > [!NOTE] > > The SmolLM3 model is approximately 1.7GB and will be automatically downloaded and cached locally on first use. The 4-bit quantization significantly reduces memory usage while maintaining performance. ### Understanding trading environments The agent can run in two environments: **Foundry fork mode (`USE_FORK = True`)** * Safe for testing and experimentation * Uses paper money (no real funds at risk) * Real market data from Base mainnet * Real smart contract interactions * Connects to: `http://localhost:8545` (Anvil fork) **Base mainnet mode (`USE_FORK = False`)** * Uses real money and real transactions * All trades are permanent and irreversible * Gas fees apply to every transaction * Connects to: Your Chainstack Base RPC endpoint > [!NOTE] > > Always start with fork mode to test your strategies before using real funds. ### Set up RPC endpoints for mainnet mode If you plan to use mainnet mode (`USE_FORK = False`), configure your Base RPC endpoints in `config.py`: ```python config.py theme={"system"} BASE_RPC_URLS = [ "YOUR_CHAINSTACK_BASE_RPC_ENDPOINT", # Replace with your Chainstack endpoint. For example, a Global Node "YOUR_CHAINSTACK_BASE_RPC_ENDPOINT", # Fallback Chainstack endpoint. For example, a Trader Node ] ``` > [!NOTE] > > For fork mode (`USE_FORK = True`), the agent automatically uses `http://localhost:8545` and these endpoints are not needed. ## Configure trading parameters Set your trading wallet private key in `config.py`: ```python config.py theme={"system"} PRIVATE_KEY = "YOUR_PRIVATE_KEY" # Use test key only, never production funds ``` > [!NOTE] > > Use a test wallet with minimal funds. This is for educational purposes only. ## Start Foundry Anvil fork ### Launch Anvil fork Open a new terminal and start the Anvil fork of Base mainnet: ```bash Terminal theme={"system"} anvil --fork-url YOUR_CHAINSTACK_BASE_RPC_ENDPOINT --chain-id 8453 ``` You should see output like: ```bash Output theme={"system"} Available Accounts ================== (0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000.000000000000000000 ETH) (1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000.000000000000000000 ETH) ... Private Keys ================== (0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 (1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d ... Listening on 0.0.0.0:8545 ``` ### Fund your trading account If your trading account needs more ETH, use Anvil's built-in accounts: ```bash Terminal theme={"system"} # Send ETH from Anvil account to your trading account cast send --from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \ --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ --rpc-url http://localhost:8545 \ YOUR_TRADING_ADDRESS \ --value 5ether ``` ## Run the trading agent ### Basic trading mode Start the agent in normal trading mode: ```bash Terminal theme={"system"} cd /path/to/web3-ai-trading-agent python on-chain/uniswap_v4_stateful_trading_agent.py ``` ### Observation mode Start with observation mode to see how SmolLM3 analyzes the market without executing trades: ```bash Terminal theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py --observe-cycles 10 ``` This will: * Collect market data for 10 cycles * Have SmolLM3 analyze each market state * Generate an initial trading strategy * Switch to active trading ### Custom trading parameters optimized for SmolLM3 Modify trading behavior to leverage SmolLM3's speed: ```bash Terminal theme={"system"} # Fast-paced trading with 60% ETH allocation python on-chain/uniswap_v4_stateful_trading_agent.py --target-allocation 0.6 --trade-interval 2 # High-frequency mode for 200 iterations python on-chain/uniswap_v4_stateful_trading_agent.py --iterations 200 --trade-interval 1 # Extended observation with rapid analysis cycles python on-chain/uniswap_v4_stateful_trading_agent.py --observe-time 15 --trade-interval 3 ``` ### Configuration optimization for SmolLM3 In `config.py`, you can adjust SmolLM3-specific settings to maximize performance: ```python config.py theme={"system"} # High-frequency trading (SmolLM3's speed enables faster decisions) TRADE_INTERVAL = 2 # seconds between decisions (ultra-fast inference) # Rebalancing sensitivity optimized for rapid decisions REBALANCE_THRESHOLD = 0.25 # 25% deviation triggers rebalance # SmolLM3-specific parameters for optimal performance MLX_INFERENCE_CONFIG = { "max_tokens": 512, # Shorter responses for lightning-fast decisions "temperature": 0.5, # Balanced for consistent yet dynamic trading "top_p": 0.85, # Focused sampling for better trading decisions "repetition_penalty": 1.05, # Slight penalty to avoid repetitive strategies } # Performance monitoring ENABLE_PERFORMANCE_LOGGING = True LOG_INFERENCE_TIMES = True ``` > [!NOTE] > > SmolLM3's combination of compact size and powerful capabilities makes it ideal for real-time trading applications where speed and efficiency are paramount. > [!NOTE] > > SmolLM3's efficient architecture combined with MLX-LM's Apple Silicon optimization creates the perfect environment for high-frequency, low-latency trading applications. > [!WARNING] > > **Important**: This is for educational and testing purposes only. Use test wallets with minimal funds. Never use production private keys. Monitor system resources during trading. The fork environment uses test funds, but configuration errors could affect real accounts. > [!NOTE] > > Next section: [AI trading agent: Fine-tuning overview](/docs/ai-trading-agent-fine-tuning-overview) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # AI trading agent: Stack Source: https://docs.chainstack.com/docs/ai-trading-agent-stack Set up the complete AI trading agent tech stack including Python environment, Chainstack blockchain access, LLM providers, and data processing tools. > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) This section outlines the complete technology stack for building a Web3 AI trading agent. We prioritize local-first development, giving you full control over your infrastructure while maintaining the security and transparency that Web3 developers expect. ## Hardware requirements The recommended setup provides optimal performance for machine learning workflows while keeping everything local: * **MacBook Pro M3 Pro with 18GB RAM** — optimal for Apple MLX-LM training and inference. That's my machine, so feel free to experiment. * **Alternative hardware** — any machine with adequate GPU support and at least 16GB RAM. You may want to swap MLX-LM to Unsloth if you are not going with Mac hardware. > [!NOTE] > > The MacBook M3 configuration offers several advantages for this project. The unified memory architecture efficiently handles large language models, while the Metal Performance Shaders (MPS) acceleration provides fast training times for our GANs and LoRA fine-tuning. For non-Apple hardware, ensure your system has sufficient VRAM (8GB minimum) for local model inference and training. You can substitute MLX-LM with alternatives like [Unsloth](https://unsloth.ai/). ## Technology stack overview The stack follows Web3 principles of local execution & control. As many components as possible run on your machine, with minimal external dependencies. ### Blockchain infrastructure **Base blockchain**\ [Base](https://base.org/) serves as our Layer 2 execution environment. Deployed & maintained by Coinbase, Base offers low transaction costs and high throughput, making it ideal for frequent trading operations. The network's EVM compatibility ensures seamless integration with existing Ethereum tooling. **Uniswap V4**\ [Uniswap V4](https://docs.uniswap.org/contracts/v4/) is the latest evolution in automated market makers (AMM) and the [singleton contract architecture](https://docs.uniswap.org/contracts/v4/concepts/v4-vs-v3). > [!NOTE] > > If you are a Web3 user or developer and familiar with V3, the singleton design means that we are going to use the pool ID for token pairs instead of a typical separate V3 pool contract. **Foundry development framework**\ [Foundry](https://book.getfoundry.sh/) provides our local blockchain development environment. We use Foundry to fork Base mainnet, creating a local testing environment with real market data, top up our address if necessary with paper ETH. This approach lets you: * Test strategies without spending real funds, aka paper trade * Reproduce exact on-chain conditions * Debug transactions with detailed tracing if necessary **Chainstack nodes**\ [Chainstack](https://chainstack.com/) delivers enterprise-grade [Base RPC endpoints](https://chainstack.com/build-better-with-base/): * Sub-second response times for real-time trading * 99.99% uptime for critical operations * Dedicated Nodes for consistent performance * Global edge locations for minimal latency ### AI/LLM stack **Apple MLX-LM**\ [MLX-LM](https://github.com/ml-explore/mlx-lm) is Apple's machine learning framework optimized for Apple Silicon. MLX-LM handles our LoRA fine-tuning with memory-efficient implementations designed for unified memory architectures. Key benefits include: * Native Apple Silicon optimization * Memory-efficient training algorithms * Seamless integration with Hugging Face models * Support for quantized model inference **Ollama local inference**\ [Ollama](https://ollama.com/) manages local large language model inference. Ollama provides a simple API for running models locally without external dependencies. This ensures: * Complete data privacy * Zero API costs for inference * Offline operation capability * Consistent response times **Gymnasium reinforcement learning**\ [Gymnasium](https://gymnasium.farama.org/) (formerly OpenAI Gym) provides our reinforcement learning environment. We use Gymnasium to create custom trading environments that simulate market conditions and reward profitable strategies. **PyTorch neural networks**\ [PyTorch](https://pytorch.org/) powers our generative adversarial networks for synthetic data generation. PyTorch's dynamic computation graphs make it pretty good for experimenting with GAN architectures and training procedures. ### Models Our model pipeline uses a teacher-student approach with progressively smaller, more specialized models: **Fin-R1**\ Fin-R1 is a financial domain-specific model based on DeepSeek-R1 architecture. Pre-trained on financial data, Fin-R1 provides sophisticated reasoning about market conditions and trading strategies. **QwQ 32B (Distillation teacher)**\ QwQ serves as our distillation teacher via OpenRouter. With 32 billion parameters, QwQ provides detailed reasoning that we compress into smaller, more efficient models. > [!NOTE] > > Larger models like QwQ 32B can be run via [OpenRouter](https://openrouter.ai/). **Qwen 2.5 3B (Student model)**\ Qwen 2.5 3B serves as our trainable student model. This 3-billion parameter model runs efficiently on consumer hardware while maintaining strong performance after fine-tuning. > [!NOTE] > > Remember that there are almost 2 million models on [Hugging Face](https://huggingface.co/models) and new models are published daily, so shop around and experiment. ## Installation and setup Follow these steps to prepare your development environment. Each step builds upon the previous one, so complete them in order. ### 1. Repository setup Clone the project repository and navigate to the project directory: ```bash theme={"system"} git clone cd ai_trading_agent_publish_repo ``` ### 2. Python environment Install the required Python dependencies. The requirements include all necessary packages for blockchain interaction, machine learning, and data processing: ```bash theme={"system"} pip install -r requirements.txt ``` The requirements.txt includes: * **Web3 libraries** — [web3.py](https://pypi.org/project/web3/), [eth-abi](https://pypi.org/project/eth-abi/), [eth-account](https://pypi.org/project/eth-account/), [uniswap-universal-router-decoder](https://pypi.org/project/uniswap-universal-router-decoder/) for blockchain interaction * **ML frameworks** — [torch](https://pypi.org/project/torch/), [mlx](https://pypi.org/project/mlx/), [mlx-lm](https://pypi.org/project/mlx-lm/) for model training * **Data processing** — pandas, numpy for data manipulation * **[Reinforcement learning](/docs/ai-trading-agent-reinforcement-learning)** — gymnasium, stable-baselines3 for RL training ### 3. Foundry installation Install Foundry for blockchain development and testing: ```bash theme={"system"} curl -L https://foundry.paradigm.xyz | bash foundryup ``` Foundry installation includes: * **anvil** — local Ethereum node for testing * **forge** — smart contract compilation and testing * **cast** — command-line tool for blockchain interaction > [!NOTE] > > You can also checkout the Chainstack Developer Portal: [Base tooling](https://docs.chainstack.com/docs/base-tooling#foundry) for an entry on Foundry with Chainstack nodes. ### 4. Ollama setup [Download & install Ollama](https://ollama.com/download) for local model inference. Check Ollama help: ```bash theme={"system"} ollama help ``` Example of checking the local model card (llm details): ```bash theme={"system"} % ollama list NAME ID SIZE MODIFIED hf.co/Mungert/Fin-R1-GGUF:latest 5050b9253527 4.7 GB 22 seconds ago % ollama show hf.co/Mungert/Fin-R1-GGUF:latest Model architecture qwen2 parameters 7.6B context length 32768 embedding length 3584 quantization unknown Capabilities completion tools insert ``` ### 5. Model downloads Download the language models you want to serve through Ollama to the agent. For example, the [Fin-R1 model](https://huggingface.co/Mungert/Fin-R1-GGUF): ```bash theme={"system"} ollama pull hf.co/Mungert/Fin-R1-GGUF ``` Also check out the [Ollama library](https://ollama.com/library) for ready-to-run models. For example, the [Qwen 2.5:3B model](https://ollama.com/library/qwen2.5:3b): ```bash theme={"system"} ollama pull qwen2.5:3b ``` > [!NOTE] > > In general, again, I encourage you to shop around on Hugging Face & Ollama and experiment. There are usually different [quantizations](https://www.youtube.com/watch?v=K75j8MkwgJ0) and community format conversions of the same model. Examples (that get outdated very quickly in this space): * **Fin-R1** — specialized for financial analysis and trading decisions * **Qwen 2.5 3B** — lightweight model suitable for fine-tuning * **Phi4 14B** — balanced performance and resource requirements > [!NOTE] > > Phi4 14B hogs my MacBook Pro M3 Pro 18 GB RAM quite a bit; for your reference on billions of parameters numbers. ## Environment verification Verify your installation by checking each component: Check Python dependencies: ```bash theme={"system"} python -c "import web3, torch, mlx, mlx_lm, uniswap_universal_router_decoder; print('Dependencies installed successfully')" ``` Verify Foundry installation: ```bash theme={"system"} anvil --version ``` Confirm Ollama is running: ```bash theme={"system"} curl http://localhost:11434/api/version ``` List available models: ```bash theme={"system"} ollama list ``` Your environment is ready when all commands execute without errors and return expected output. ## Configuration overview The project uses a centralized configuration system in `config.py`. Key configuration areas include: * **RPC endpoints** — Chainstack Base node connections * **Model settings** — Ollama and MLX model specifications * **Trading parameters** — rebalancing thresholds and intervals * **Security settings** — private keys and API credentials > [!NOTE] > > Remember that this is a NOT FOR PRODUCTION tutorial. In a production deployment, don't store your private key in a config.py file. # AI trading agent: Stateful agent Source: https://docs.chainstack.com/docs/ai-trading-agent-stateful-agent Build a stateful AI trading agent that maintains context across analysis sessions for consistent blockchain market tracking and signal generation. > [!NOTE] > > Previous section: [AI trading agent: Stateless agent](/docs/ai-trading-agent-stateless-agent) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) This section introduces a memory-enabled trading agent that learns from trading history to make data-driven decisions. Unlike the stateless agent that treats each decision independently, a stateful agent analyzes past trade performance, identifies successful patterns, and adapts strategies based on historical outcomes. ## Learning capabilities The stateful agent implements several learning mechanisms: **Historical performance analysis** * Tracks profit/loss for each trade type (ETH->USDC vs USDC->ETH) * Calculates success rates and identifies most profitable strategies * Analyzes recent performance trends to detect improving or declining performance **Market pattern recognition** * Identifies price levels where trades were most successful * Correlates market volatility with trading outcomes * Detects market conditions similar to past successful trades **Context-aware decision making** * Includes recent trade history in LLM prompts * Provides performance insights: "ETH selling trades averaged +2.3% profit (5 trades)" * Highlights similar market conditions: "Similar conditions: 3 trades, avg profit +1.8%" **Intelligent memory management** * Preserves key learning insights during context summarization * Retains top-performing trades and recent trading history * Maintains performance metrics across context resets ## Technical implementation The stateful agent implements sophisticated technical mechanisms to manage memory, context windows, and learning persistence. ### Context window management **Token estimation and monitoring** The agent continuously tracks context usage through advanced token estimation: ```python theme={"system"} # Real-time context monitoring prompt_token_count = self._estimate_token_count(user_query) # Safely calculate context tokens with null check context_token_count = 0 for decision in self.context.trading_decisions: if decision.reasoning: context_token_count += self._estimate_token_count(decision.reasoning) # Add market state tokens market_state_tokens = len(self.context.market_states) * 20 # Calculate total usage total_estimated_tokens = prompt_token_count + context_token_count + market_state_tokens context_usage_ratio = total_estimated_tokens / self.context_capacity ``` **Threshold-based summarization** When context usage approaches the configured threshold, automatic summarization triggers: * **Warning threshold**: 90% of context capacity (configurable via `CONTEXT_WARNING_THRESHOLD`) * **Automatic trigger**: Summarization activates when threshold exceeded * **Cooldown period**: 2-minute between summarizations (configurable via `SUMMARIZATION_COOLDOWN`) * **Recursive protection**: Prevents summarization loops during processing **Adaptive prompt management** The agent dynamically adjusts prompt complexity based on available context space: ```python theme={"system"} if self.last_context_usage.get('ratio', 0) > 0.7: # Use simplified prompt when context is getting full user_query = f"Given ETH price... {historical_context}" else: # Use full enhanced prompt when context allows user_query = f"Given ETH price... TRADING HISTORY... PERFORMANCE INSIGHTS... MARKET PATTERNS..." ``` ### Configuration integration **Model-specific context handling** The agent automatically adapts to different model capabilities through `config.py`: ```python theme={"system"} # Dynamic context capacity based on selected model AVAILABLE_MODELS = { 'qwen-trader': { 'model': 'trader-qwen:latest', 'context_capacity': 32768 }, 'fin-r1': { 'model': 'hf.co/Mungert/Fin-R1-GGUF', 'context_capacity': 32768 } } # Agent automatically uses appropriate capacity self.context_capacity = get_context_capacity(MODEL_KEY, test_mode) ``` **Configuration-driven behavior** All context management parameters respect configuration settings: * **Context warning threshold**: `CONTEXT_WARNING_THRESHOLD = 0.9` (90%) * **Summarization cooldown**: `SUMMARIZATION_COOLDOWN = 2 * 60` (2 minutes) * **Test mode**: Reduced context capacity for testing summarization logic * **Model selection**: Automatic adaptation to selected model's capabilities > [!NOTE] > > For troubleshooting, to force an actual transaction even if the LLM model advises to hold based on the market observation, you can set `REBALANCE_THRESHOLD = 0` to force a trade without asking an LLM instance. ### Observation mode capabilities **Data collection without trading** The agent supports observation-only mode for market analysis and strategy development. You can start the stateful agent in observation mode for a number of cycles and then it'll switch to active trading but will act based on the collected observation data. Collect market data for 5000 tokens before trading: ```bash theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py --observe 5000 ``` Observe for 30 minutes before trading ```bash theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py --observe-time 30 ``` Collect 50 observation cycles before trading ```bash theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py --observe-cycles 50 ``` ### Memory optimization techniques **Learning insight compression** To prevent context explosion, the agent employs sophisticated compression: ```python theme={"system"} # Insight compression algorithm most_valuable_insights = sorted(learning_insights, key=len, reverse=True)[:2] for insight in most_valuable_insights: compact_insight = insight[:200] + "..." if len(insight) > 200 else insight ``` **Selective data retention** During context summarization, the agent preserves only the most valuable information: * **Learning insights**: Top 2 most valuable patterns (compressed to 200 characters) * **Performance metrics**: Last 3 results for each trade type * **Market states**: Most recent 2 market conditions * **Trading decisions**: Best performing trade + most recent trade * **Strategy information**: Current strategy parameters and timing **Context explosion prevention** Multiple safeguards prevent uncontrolled memory growth: * **Compression limits**: All insights truncated to manageable sizes * **Retention limits**: Fixed maximum items preserved per category * **Cooldown enforcement**: Minimum time between summarization events * **Recursive protection**: Flags prevent summarization during summarization * **Priority-based selection**: Keeps most valuable data, discards redundant information **Memory efficiency monitoring** > [!NOTE] > > The agent provides real-time visibility into memory usage in the terminal printouts. ## Core stateful components ### Memory management system **Trading history database** The agent maintains comprehensive trading records in memory using the `TradingDecision` on market states and trading decisions. ### Context window management **Intelligent memory allocation** The system optimizes memory usage based on the model's context capacity and the configured thresholds in `config.py`. The agent automatically determines optimal memory allocation using the existing configuration parameters. **Adaptive summarization algorithm** When approaching context limits, the agent compresses data through the `_summarize_and_restart_context()` method. ### Strategy persistence engine **Basic strategy tracking** The agent maintains simple strategy information in `TradingContext`. **Strategy functionality** The system provides basic strategy management: * **Strategy generation** — creates initial strategy based on observation mode * **Strategy timing** — tracks duration and elapsed time using MIN/MAX\_STRATEGY\_DURATION * **Performance tracking** — separates rebalancing ROI from LLM trading ROI * **Strategy display** — shows current strategy information in portfolio table ### Basic analytics engine **Simple performance tracking** The agent tracks basic performance metrics through `TradingContext` for rebalancing script PnL and LLM-driven PnL. ## Configuration and deployment Stateful agents require careful configuration to balance memory usage, performance tracking, and strategic consistency. ## Step-by-step stateful agent deployment Deploy your memory-enabled trading agent with proper initialization and monitoring. ### 1. Environment preparation Ensure your base environment is ready: Verify Ollama is running with adequate memory: ```bash theme={"system"} ollama serve ``` Check available system memory On Linux: ```bash theme={"system"} free -h ``` On macOS: ```bash theme={"system"} memory_pressure ``` Confirm Foundry fork is active: ```bash theme={"system"} cast block-number --rpc-url http://localhost:8545 ``` ### 2. Stateful agent initialization Launch the stateful trading agent with various configuration options: **Basic trading mode:** ```bash theme={"system"} python on-chain/uniswap_v4_stateful_trading_agent.py ``` **Observation mode options:** ```bash theme={"system"} # Collect market data for 5000 tokens before trading python on-chain/uniswap_v4_stateful_trading_agent.py --observe 5000 # Observe for 30 minutes before trading python on-chain/uniswap_v4_stateful_trading_agent.py --observe-time 30 # Collect 50 observation cycles before trading python on-chain/uniswap_v4_stateful_trading_agent.py --observe-cycles 50 ``` **Additional configuration options:** ```bash theme={"system"} # Test mode with reduced context capacity python on-chain/uniswap_v4_stateful_trading_agent.py --test-mode # Custom target allocation (60% ETH, 40% USDC) python on-chain/uniswap_v4_stateful_trading_agent.py --target-allocation 0.6 # Limited number of trading iterations python on-chain/uniswap_v4_stateful_trading_agent.py --iterations 100 ``` ## Next steps With your stateful agent successfully managing memory and learning from trading history, you're ready to explore advanced model integrations for enhanced decision-making capabilities. > [!NOTE] > > Next section: [AI trading agent: Grok-4 integration with OpenRouter](/docs/ai-trading-agent-grok4-openrouter-integration) # AI trading agent: Stateless agent Source: https://docs.chainstack.com/docs/ai-trading-agent-stateless-agent Build a stateless AI trading agent for independent blockchain market analysis calls with no session memory, ideal for serverless deployment patterns. > [!NOTE] > > Previous section: [AI trading agent implementation](/docs/ai-trading-agent-implementation) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) This section introduces your first AI-powered trading agent. The stateless agent combines local language models with real-time market data to make autonomous trading decisions on Uniswap V4. Unlike traditional trading bots with hardcoded rules, this agent adapts its strategy based on current market conditions using your locally running LLM model. ## Understanding stateless agent architecture A stateless agent operates without persistent memory between trading decisions, treating each market evaluation as an independent event. ### Stateless vs stateful design **Stateless operation principles** A stateless agent processes each trading decision independently: * **No historical context** — each decision starts with a clean slate * **Current market focus** — analyzes only present conditions * **Independent reasoning** — no bias from previous trades * **Fresh perspective** — uninfluenced by past successes or failures This approach offers several advantages for learning & experimenting: * **Simplified debugging** — easier to trace decision logic * **Predictable behavior** — consistent responses to similar market conditions * **Reduced complexity** — fewer variables affect decision making * **No context window overflow** — no warm-up period required **Trade-offs and limitations** Stateless design sacrifices some capabilities: * **No learning from experience** — cannot improve from past mistakes * **Missing trend analysis** — cannot identify longer-term patterns * **No strategy persistence** — cannot maintain consistent approaches ## Core stateless components ### Ollama language model integration **Local AI decision engine** **Ollama** [Ollama](https://ollama.ai/) provides the intelligence layer for trading decisions. **MLX-LM** > [!NOTE] > > You can skip Ollama and run directly off [MLX-LM](https://github.com/ml-explore/mlx-lm) by setting `USE_MLX_MODEL = True` in `config.py`. Here's what you get with the local LLM: * **No LLM API dependence** — no external LLM API calls; you run it all locally * **Consistent latency** — predictable response times for time-sensitive decisions * **Cost efficiency** — zero per-request charges for AI inference ### Rebalancing logic system The [rebalancing system](https://www.investopedia.com/articles/stocks/11/rebalancing-strategies.asp) maintains target portfolio proportions through automated trading: **50/50 allocation strategy** The default configuration maintains equal ETH and USDC holdings: * **Reduced volatility** — diversification across two assets * **Capture opportunities** — profit from price movements in either direction * **Risk management** — prevents overexposure to single asset **Dynamic rebalancing triggers** The system monitors portfolio drift and triggers rebalancing when: ```python theme={"system"} # Configuration in config.py REBALANCE_THRESHOLD = 0.5 # 50% deviation triggers rebalancing DEFAULT_ETH_ALLOCATION = 0.5 # Target 50% ETH, 50% USDC ``` **Rebalancing decision flow:** 1. **Portfolio analysis** — calculate current ETH-USDC ratio 2. **Deviation measurement** — compare against target allocation 3. **Threshold evaluation** — determine if rebalancing is required 4. **If rebalancing needed** — execute trade directly without LLM consultation 5. **If no rebalancing needed** — query LLM for trading decisions > [!NOTE] > > The default `REBALANCE_THRESHOLD = 0.5` (50% deviation) is set high to disable automatic rebalancing and route most decisions through the LLM for learning purposes. ### Real-time market data collection **Data collection through Chainstack nodes** The agent gathers live market data through your Chainstack Base mainnet node: **Pool state monitoring** Data collected every trading cycle: ```python theme={"system"} market_data = { 'eth_price': eth_usdc_price, 'price_change_pct_10m': price_change_percentage_10min, 'volume_eth_10m': eth_volume_10min, 'volume_usdc_10m': usdc_volume_10min, 'swap_count_10m': number_of_swaps_10min, 'timestamp': current_timestamp } ``` **Data sources:** * **ETH price** — current ETH/USDC exchange rate from pool * **10-minute metrics** — recent swap events analysis for volume and price changes; you can change the time cycle in `uniswap_v4_stateless_trading_agent.py` * **Swap activity** — transaction count and volume over last 10 minutes ### Automated swap execution **Uniswap V4 integration** The execution engine handles all blockchain interactions: * **Transaction construction** — builds optimal swap parameters * **Gas optimization** — calculates efficient gas prices and limits * **Slippage protection** — prevents excessive price impact * **Error handling** — retries failed transactions with adjusted parameters ## Configuration and deployment Edit your `config.py` file with these: ```python theme={"system"} # Trading behavior configuration REBALANCE_THRESHOLD = 0.5 # Forces LLM consultation on every trade DEFAULT_ETH_ALLOCATION = 0.5 # Target 50% ETH, 50% USDC TRADE_INTERVAL = 10 # 10 seconds between decisions # Model selection MODEL_KEY = "fin-r1" # Choose from AVAILABLE_MODELS USE_MLX_MODEL = False # Start with Ollama # Security settings PRIVATE_KEY = "YOUR_PRIVATE_KEY" # Trading wallet private key BASE_RPC_URL = "https://base-mainnet.core.chainstack.com/AUTH_KEY" ``` ## Step-by-step deployment Follow this sequence to launch your stateless trading agent. ### 1. Ollama service initialization Ensure Ollama is running and responsive: ```bash theme={"system"} ollama serve ``` **Verification steps:** ```bash theme={"system"} # Test Ollama connectivity curl http://localhost:11434/api/version # Verify model availability ollama list # Test model response ollama run hf.co/Mungert/Fin-R1-GGUF:latest "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?" ``` > [!NOTE] > > Again, feel free to use other models instead of Fin-R1 if you find it hallucinates or you do not like the responses in general. ### 2. Foundry environment setup Launch your local blockchain fork in a separate terminal: ```bash theme={"system"} anvil --fork-url https://base-mainnet.core.chainstack.com/AUTH_KEY --chain-id 8453 ``` **Environment verification:** Confirm fork is active: ```bash theme={"system"} cast block-number --rpc-url http://localhost:8545 ``` Check test account balance (returned in Wei): ```bash theme={"system"} cast balance YOUR_BASE_ADDRESS --rpc-url http://localhost:8545 ``` Verify USDC balance (returned in hex; convert at [Chainstack Web3 tools](https://web3tools.chainstacklabs.com/hexadecimal-decimal)): ```bash theme={"system"} cast call 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 "balanceOf(address)" YOUR_BASE_ADDRESS --rpc-url http://localhost:8545 ``` ### 3. Agent execution Launch the stateless trading agent: ```bash theme={"system"} python on-chain/uniswap_v4_stateless_trading_agent.py ``` The agent provides detailed logs and the LLM outputs for each trading cycle. You will also see the trading (Uniswap V4 swaps) transaction hashes in the output and in your Foundry Anvil terminal instance. > [!NOTE] > > If you are getting a message that the model failed to respond with `Error getting LLM decision`, test your model's response time manually and increase the `TRADE_INTERVAL` in `config.py` if your LLM takes longer than the configured interval to respond. You can test response time with: Ollama: ```bash theme={"system"} time ollama run YOUR_MODEL_NAME "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?" ``` To get the list of models, run `ollama list`. MLX-LM: ```bash theme={"system"} time mlx_lm.generate --model YOUR_MODEL_NAME --prompt "Given ETH price is $2506.92 with volume of 999.43 and volatility of 0.045, recent price change of 35.7765 ticks, and I currently hold 3.746 ETH and 9507.14 USDC, what trading action should I take on Uniswap?" --temp 0.3 ``` To get the list of models, run `mlx_lm.manage --scan --pattern ""`. If responses consistently take longer than your `TRADE_INTERVAL` setting, increase the interval to allow sufficient processing time. **Model response quality** > [!NOTE] > > If you are getting inconsistent or poor trading decisions: > > * Experiment with different model temperatures (0.1-0.7) in the agent script > * Shop around for models at [https://ollama.com/library/](https://ollama.com/library/) or [https://huggingface.co/models](https://huggingface.co/models) > * Adjust prompt engineering in the agent script > * Verify market data quality and completeness with `fetch_pool_data.py`, `fetch_pool_stats.py`, `fetch_pools_stats_from_fork.py` # Aptos methods Source: https://docs.chainstack.com/docs/aptos-methods Complete reference of supported Aptos API methods on Chainstack nodes, including accounts, blocks, events, transactions, and view function calls. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which Aptos methods are full vs archive, see [Request units — Aptos method scope](/docs/request-units#aptos-method-scope). | Method | Availability | Comment | | - | - | - | | /v1 | | | | /v1/accounts/\{address} | | | | /v1/accounts/\{address}/events/\{creation\_number} | | | | /v1/accounts/\{address}/events/\{event\_handle}/\{field\_name} | | | | /v1/accounts/\{address}/module/\{module\_name} | | | | /v1/accounts/\{address}/modules | | | | /v1/accounts/\{address}/resource/\{resource\_type} | | | | /v1/accounts/\{address}/resources | | | | /v1/accounts/\{address}/transactions | | | | /v1/blocks/by\_height/\{block\_height} | | | | /v1/blocks/by\_version/\{version} | | | | /v1/estimate\_gas\_price | | | | /v1/healthy | | | | /v1/spec | | | | /v1/submit\_transactions | | | | /v1/tables/\{table\_handle}/item | | | | /v1/tables/\{table\_handle}/raw\_item | | | | /v1/transactions | | | | /v1/transactions/batch | | | | /v1/transactions/by\_hash/\{txn\_hash} | | | | /v1/transactions/by\_version/\{txn\_version} | | | | /v1/transactions/encode\_submission | | | | /v1/transactions/simulate | | | | /v1/view | | | # Aptos tooling Source: https://docs.chainstack.com/docs/aptos-tooling Set up the Aptos CLI, TypeScript SDK, and development tools to build and deploy Move modules on Aptos through your Chainstack node endpoints. Get started with a [reliable Aptos RPC endpoint](https://chainstack.com/build-better-with-aptos/) to use the tools below. ## Martian wallet You can set your [Martian wallet](https://martianwallet.xyz/) to interact through your Aptos nodes deployed with Chainstack. 1. Open your Martian wallet and click the network selector. 2. In the network selector, click **ADD CUSTOM NETWORK**. 3. In the **Enter Node Url** field, enter the endpoint. See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). 4. In the **Enter Faucet Url** field, enter the [Aptos testnet faucet](https://aptoslabs.com/testnet-faucet) URL. 5. Click **ADD NETWORK**. ## REST API Interact with your Aptos node using the [Aptos Node API](https://fullnode.devnet.aptoslabs.com/v1/spec#/). Use your Chainstack Aptos REST endpoint. Example to get block information by block height: ```bash cURL theme={"system"} curl --request GET \ --url YOUR_CHAINSTACK_ENDPOINT/v1/blocks/by_height/2047048\ --header 'Content-Type: application/json' ``` where YOUR\_CHAINSTACK\_ENDPOINT is your HTTPS endpoint protected either with the key or password # Aptos: Publish a module to save and retrieve messages Source: https://docs.chainstack.com/docs/aptos-tutorial-publish-a-module-to-save-and-retrieve-a-message-on-aptos Publish a Move module on Aptos that stores and retrieves on-chain messages using the Aptos CLI and a Chainstack node on devnet for deployment. **TLDR:** * Aptos uses the Move language and “published” modules in place of traditional smart contracts. * This tutorial shows you how to set up an Aptos node, initialize a Move project, and publish a simple module that stores a string on-chain. * You’ll run through the basics of module creation, compilation, testing, and then sending transactions to set and retrieve data. * Use the Aptos CLI for everything from local testing to publishing, and wrap up by querying the on-chain data with Aptos’ REST API. ## Main article Aptos uses its own terminology for widely-known Web3 entities. Smart contracts are called Modules and are written in the [Move language](https://move-language.github.io/move/). Modules are also not deployed but *published* on the Aptos chain. The objective of this tutorial is to familiarize you with the Aptos network, the Move language and modules written in it. In the end of this tutorial, you will be able to publish, test, and interact with Move modules in Aptos. Specifically, in this tutorial, you will: * Initialize an Aptos project using the Aptos CLI. * Publish a module on the Aptos testnet. * Interact with the module to save a message. * Use the Aptos REST API to retrieve the message. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Aptos RPC endpoint](https://chainstack.com/build-better-with-aptos/). * [Martian Aptos wallet](https://martianwallet.xyz/) to receive testnet Aptos token (APT). * [Aptos CLI](https://github.com/aptos-labs/aptos-core) to compile, publish, and interact with the Move module. ## Overview To get from zero to publishing your string via the module to Aptos testnet, do the following: 1. With Chainstack, create a public chain project (A public chain project- a project to join public networks). 2. With Chainstack, join Aptos testnet. 3. With Chainstack, access your Aptos node credentials. 4. Set up your Martian wallet to work through the Chainstack Aptos node. 5. Fund your account through the [Aptos testnet faucet](https://aptoslabs.com/testnet-faucet). 6. Install the [Aptos CLI](https://aptos.dev/tools/aptos-cli/install-cli/). 7. Create a Move project. 8. Create and configure your Aptos project. 9. Create a module in the Move language. 10. Compile and test the Move module. 11. Publish the Move module. 12. Save and retrieve a message on the Aptos chain. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Aptos testnet See [Join a public network](/docs/manage-your-networks). ### Get node access and credentials See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Set up Martian wallet See [Aptos tooling: Martian wallet](/docs/aptos-tooling#martian-wallet). ### Fund your account Your account needs to pay fees in testnet APT to publish the module and interact with it. Fund your account with the [Aptos testnet faucet](https://aptoslabs.com/testnet-faucet). ### Install the Aptos CLI You need the Aptos CLI to interact with your Move module. Set up the [Aptos CLI](https://aptos.dev/tools/aptos-cli/install-cli/). ### Create a Move project 1. In your project directory, create a Move project: ```bash Shell theme={"system"} aptos move init --name save-message ``` where `save-message` — name of the package. This creates a `sources` directory and a `Move.toml` file. 2. Open your `Move.toml` file and edit it to add `[addresses]` and `[dev-addresses]`, where: * `dev = "_"` — your default Aptos account. * `dev = "0xC0FFEE"` — an alternative Aptos account for tests. Example: ```toml TOML theme={"system"} [package] name = 'save-message' version = '1.0.0' [addresses] dev = "_" [dev-addresses] dev = "0xC0FFEE" [dependencies.AptosFramework] git = 'https://github.com/aptos-labs/aptos-core.git' rev = 'main' subdir = 'aptos-move/framework/aptos-framework' ``` > [!WARNING] > > Note that packages have one-time names. If you want to re-publish the package, you must change its name. ### Create and configure an Aptos project 1. In your project directory, run `aptos init > custom`. This will start a configuration process, during which you need to set up your Chainstack endpoint and Martian wallet private key. Adding the private key will retrieve your Aptos public address automatically. 2. Add your [Aptos node endpoint](/docs/manage-your-node#view-node-access-and-credentials) deployed with Chainstack. 3. At the faucet URL request, type `skip` since you have already funded your account on the previous step. 4. Paste your Martian wallet private key to finish configuring your project. The key is used to send transactions and retrieve your public address. Example of a successful result: ```bash Shell theme={"system"} Aptos CLI is now set up for account ...4474 as profile default! Run `aptos --help` for more information about commands { "Result": "Success" } ``` As a result, you get a `.aptos` directory with a `config.yaml` file inside it. In `config.yaml`, you will find your project setup. ### Create a Move module In your Move project directory, navigate to the `sources` directory. Create your Move module file `message.move` which allows you to call the `set_message` function and save a string on-chain: ```js JavaScript theme={"system"} module dev::message { use std::error; use std::signer; use std::string; use aptos_framework::account; use aptos_framework::event; //:!:>resource struct MessageHolder has key { message: string::String, message_change_events: event::EventHandle, } //<:!:resource struct MessageChangeEvent has drop, store { from_message: string::String, to_message: string::String, } /// There is no message present const ENO_MESSAGE: u64 = 0; public fun get_message(addr: address): string::String acquires MessageHolder { assert!(exists(addr), error::not_found(ENO_MESSAGE)); *&borrow_global(addr).message } public entry fun set_message(account: signer, message: string::String) acquires MessageHolder { let account_addr = signer::address_of(&account); if (!exists(account_addr)) { move_to(&account, MessageHolder { message, message_change_events: account::new_event_handle(&account), }) } else { let old_message_holder = borrow_global_mut(account_addr); let from_message = *&old_message_holder.message; event::emit_event(&mut old_message_holder.message_change_events, MessageChangeEvent { from_message, to_message: copy message, }); old_message_holder.message = message; } } #[test(account = @0x1)] public entry fun sender_can_set_message(account: signer) acquires MessageHolder { let addr = signer::address_of(&account); aptos_framework::account::create_account_for_test(addr); set_message(account, string::utf8(b"Hello Chainstack dev")); assert!( get_message(addr) == string::utf8(b"Hello Chainstack dev"), ENO_MESSAGE ); } } ``` ### Compile and test the Move module 1. To compile your Move module, run: ```bash Shell theme={"system"} aptos move compile --named-addresses dev=default ``` 2. After the module compiled, run a build-in test which checks if the `set_message` and `get_message` functions work: ```bash Shell theme={"system"} aptos move test ``` ### Publish the Move module 1. Publish your compiled and tested Move module by running: ```bash Shell theme={"system"} aptos move publish --named-addresses dev=default ``` 2. Type `yes` to confirm publishing the transaction on the Aptos chain. The module will publish and the terminal will return the module information. You can use the transaction hash to retrieve transaction details. To do so, run: ```bash cURL theme={"system"} curl --location --request GET 'YOUR_CHAINSTACK_ENDPOINT/v1/transactions/by_hash/0x815cecb01a962331ca34904653a26715e6cd8e631d2d1b7da17b593adda1ea65' \ --header 'Content-Type: application/json' ``` where YOUR\_CHAINSTACK\_ENDPOINT is your Aptos node endpoint you used earlier. ### Save and retrieve a message on the Aptos chain 1. To save a message on the Aptos chain, run: ```bash Shell theme={"system"} aptos move run --function-id 'default::message::set_message' --args 'string:Hello Chainstack dev' ``` where: * `run` — a Move command to call functions * `function-id` — a function to call * `args` — arguments of the function 2. Type `yes` to confirm publishing the transaction on the Aptos chain. 3. Retrieve the published message via the REST API by running: ```bash cURL theme={"system"} curl --location --request GET 'YOUR_CHAINSTACK_ENDPOINT/v1/accounts/c0e0ce57eaf9680ae67252fb3126f25aa86bb098b05f7b72cf0cf0de57c72a7f/resource/0xc0e0ce57eaf9680ae67252fb3126f25aa86bb098b05f7b72cf0cf0de57c72a7f::message::MessageHolder' \ --header 'Content-Type: application/json' ``` where YOUR\_CHAINSTACK\_ENDPOINT is your Aptos node endpoint you used earlier. Successful response example: ```Json JSON theme={"system"} { "type": "0xc0e0ce57eaf9680ae67252fb3126f25aa86bb098b05f7b72cf0cf0de57c72a7f::message::MessageHolder", "data": { "message": "Hello Chainstack dev", "message_change_events": { "counter": "0", "guid": { "id": { "addr": "0xc0e0ce57eaf9680ae67252fb3126f25aa86bb098b05f7b72cf0cf0de57c72a7f", "creation_num": "4" } } } } } ``` ## Conclusion This tutorial guided you through the basics of creating, publishing, and testing a simple module that saves a string on the Aptos chain. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Arbitrum methods Source: https://docs.chainstack.com/docs/arbitrum-methods Complete reference of supported Arbitrum JSON-RPC methods on Chainstack, including method availability, debug, and trace API support. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Arbitrum API call examples](/reference/arbitrum-getting-started). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_simulateV1 | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | eth\_sendRawTransactionSync | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_accountRange | | | | debug\_dumpBlock | | not available on Global Nodes and Trader Nodes | | debug\_getAccessibleState | | not available on Global Nodes and Trader Nodes | | debug\_getBadBlocks | | | | debug\_getModifiedAccountsByHash | | | | debug\_getModifiedAccountsByNumber | | | | debug\_getRawBlock | | | | debug\_getRawHeader | | | | debug\_getRawReceipts | | | | debug\_getRawTransaction | | | | debug\_getTrieFlushInterval | | | | debug\_intermediateRoots | | | | debug\_preimage | | | | debug\_printBlock | | | | debug\_storageRangeAt | | | | debug\_traceBlock | | | | debug\_traceBadBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | arbtrace\_block | | Pre-Nitro blocks only | | arbtrace\_call | | Pre-Nitro blocks only | | arbtrace\_callMany | | Pre-Nitro blocks only | | arbtrace\_filter | | Pre-Nitro blocks only | | arbtrace\_get | | Pre-Nitro blocks only | | arbtrace\_replayBlockTransactions | | Pre-Nitro blocks only | | arbtrace\_replayTransaction | | Pre-Nitro blocks only | | arbtrace\_transaction | | Pre-Nitro blocks only | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Arbitrum tooling Source: https://docs.chainstack.com/docs/arbitrum-tooling Set up MetaMask, Hardhat, Foundry, and development tools to build and deploy smart contracts on Arbitrum through your Chainstack RPC node endpoints. Get started with a [reliable Arbitrum RPC endpoint](https://chainstack.com/build-better-with-arbitrum/) to use the tools below. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Arbitrum nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](/docs/arbitrum-tooling#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Arbitrum nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Arbitrum nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Php theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Arbitrum nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint * USERNAME — your node access username * PASSWORD — your node access password See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Arbitrum nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Arbitrum network ID: * Mainnet: `42161` * Testnet: `421614` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint * NETWORK\_ID — Arbitrum network ID: * Mainnet: `42161` * Testnet: `421614` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Brownie 1. Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). 2. Use the `brownie networks add` command with the node endpoint: ```shell Shell theme={"system"} brownie networks add Arbitrum ID name="NETWORK_NAME" host= YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `chainstack-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password * NETWORK\_ID — Arbitrum network ID: * Mainnet: `42161` * Testnet: `421614` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Arbitrum: L1 to L2 messaging smart contract Source: https://docs.chainstack.com/docs/arbitrum-tutorial-l1-to-l2-messaging-smart-contract Send cross-chain messages from Ethereum L1 to Arbitrum L2 using the Arbitrum SDK and Chainstack nodes with a smart contract deployed via Hardhat. > [!WARNING] > > ### Deprecation notice > > As the Goerli testnet has been deprecated, this guide is for historical reference. Sending a message from the Ethereum chain (L1) to the Arbitrum chain (L2) does not involve the state challenge period and is as fast as the block confirmation time on L1 and L2 combined. In this tutorial, you will: * Deploy greeter contracts on Ethereum and on Arbitrum. * Send a message from the greeter contract deployed on Ethereum (L1) to the greeter contract deployed on Arbitrum (L2). ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Arbitrum RPC endpoint](https://chainstack.com/build-better-with-arbitrum/). * [MetaMask](https://metamask.io/) to fund your account on L2 with GoerliETH. ## Overview To get from zero to your first L1 to L2 message, do the following: 1. With Chainstack, create a public chain project. 2. With Chainstack, join the Ethereum Goerli testnet. 3. With Chainstack, join the Arbitrum Goerli testnet. 4. Set up your MetaMask to work through the Chainstack Ethereum and Arbitrum nodes. 5. Fund your account through a faucet on the Ethereum Goerli testnet and on the Arbitrum Goerli testnet. 6. Run the tutorial script to deploy the contracts on L1 and L2 and send the message from L1 to L2. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Ethereum and Arbitrum Goerli testnets Deploy a node on the Ethereum Goerli testnet and a node on the Arbitrum Goerli testnet. See [Join a public network](/docs/manage-your-networks). ### Get the access and credentials to your deployed nodes See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Set up MetaMask See [Arbitrum tooling: MetaMask](/docs/arbitrum-tooling#metamask). ### Fund your account Your account will need Goerli ether on both the Ethereum Goerli testnet and the Arbitrum Goerli testnet as you will deploy a contract on each of the chains. * Ethereum Goerli faucet * Arbitrum Goerli faucet: see [Nitro Goerli Rollup](https://developer.offchainlabs.com/public-chains) in the Arbitrum documentation The default Arbitrum Goerli faucet may fund your account with 0.001 GoerliETH, which is not enough to deploy the greeter contract on L2. If you do not have enough GoerliETH on L2, you may bridge some more from the Ethereum Goerli testnet using the [Arbitrum bridge](https://bridge.arbitrum.io/). ### Clone and prepare the tutorials repository You will use the [Offchain Labs tutorials repository](https://github.com/OffchainLabs/arbitrum-tutorials) to deploy the contracts and send the message. Clone the repository: ```bash Shell theme={"system"} git clone https://github.com/OffchainLabs/arbitrum-tutorials.git ``` Change to `arbitrum-tutorials/packages/greeter`. Install dependencies by running `yarn`. Set up the `.env` file by renaming the sample one in `arbitrum-tutorials/packages/greeter`: ```bash Shell theme={"system"} cp .env-sample .env ``` In the `.env` file, add your account key and the endpoints: * DEVNET\_PRIVKEY — the private key of your account that has GoerliETH both on the Ethereum Goerli testnet and the Arbitrum Goerli testnet. * L2RPC — the Chainstack HTTPS endpoint of your Arbitrum node deployed on the Arbitrum Goerli testnet. * L1RPC — the Chainstack HTTPS endpoint of your Ethereum node deployed on the Ethereum Goerli testnet. Example: ```env env theme={"system"} DEVNET_PRIVKEY=YOUR_DEVNET_PRIVATE_KEY L2RPC=YOUR_CHAINSTACK_ENDPOINT L1RPC=YOUR_CHAINSTACK_ENDPOINT ``` ### Deploy the contract and send the message from L1 to L2 You are now all set to run the tutorial script that will deploy the greeter contracts and send a message from L1 to L2. In `arbitrum-tutorials/packages/greeter`, run: ```bash Shell theme={"system"} yarn run greeter ``` The script will: * Deploy the L1 greeter contract on the Ethereum Goerli testnet. Example: [0x9B4F541D6A82Beb594Ee2A1EfF14d88f2898176c](https://goerli.etherscan.io/address/0x9B4F541D6A82Beb594Ee2A1EfF14d88f2898176c). * Deploy the L2 greeter contract on the Arbitrum Goerli testnet. Example: [0x890443aB733bd527F0036aEd3E249358a30Ff3ce](https://goerli-rollup-explorer.arbitrum.io/address/0x890443aB733bd527F0036aEd3E249358a30Ff3ce). * On the L1 contract, [set the L2 contract address](https://goerli.etherscan.io/tx/0xbd20609976a96ce791eae71dae0e87a254f542eab1ab400ce8b4681cc4f6b5aa). * On the L2 contract, [set the L1 contract address](https://goerli-rollup-explorer.arbitrum.io/tx/0x98dcfec500561985cdaf0f3933f1b361b3106edc055e0a2644c0f67396596d42/internal-transactions). * Retrieve the current gas costs for the transaction off the [ArbRetryableTx contract on L2](https://goerli-rollup-explorer.arbitrum.io/address/0x000000000000000000000000000000000000006E/read-contract#address-tabs). See also Arbitrum documentation: [Messaging Between Layers](https://developer.offchainlabs.com/arbos/l1-to-l2-messaging). * Using the retrieved gas cost values, [submit the message transaction on L1](https://goerli.etherscan.io/tx/0xa39ecbb53844d009dc121825c26b0608def2c4117d81a6ebeb6000fcf304ac9e). The transaction will send the message to the [inbox contract on L1](https://goerli.etherscan.io/address/0x6BEbC4925716945D46F0Ec336D5C2564F419682C#readProxyContract). See also Arbitrum documentation: [Contract addresses](https://developer.offchainlabs.com/useful-addresses). * The transaction will then be submitted as a retryable ticket by the [ArbRetryableTx contract on L2](https://goerli-rollup-explorer.arbitrum.io/address/0x000000000000000000000000000000000000006E/). [Example](https://goerli-rollup-explorer.arbitrum.io/tx/0xac1f89c9d449145aaa6a715bfb7a678009654191b379c03d20bd0a27b8f6968f). * Then the retryable ticket will be redeemed and change the state in the greeter contract on L2 with the message from the greeter contract on L1. [Example](https://goerli-rollup-explorer.arbitrum.io/tx/0x6c8dd56c1ef93064b7b219154327361c051588dfadf716cc23e9d5e3ed610814). ## Conclusion This tutorial guided you through the basics of creating and deploying a simple greeter contract that sends a message from the Ethereum chain to the Arbitrum chain. The tutorial also provided the examples and an explanation of the step-by-step state changes and the contracts involved in the L1 to L2 messaging. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Arc methods Source: https://docs.chainstack.com/docs/arc-methods Complete reference of Arc JSON-RPC methods supported on Chainstack, with per-method availability for the eth, debug, trace, txpool, net, web3, and arc namespaces. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Arc API call examples](/reference/arc-getting-started). Arc is an EVM-compatible layer 1 from Circle. It pairs the [Reth](https://github.com/paradigmxyz/reth) execution client with [Malachite](https://github.com/circlefin/malachite) BFT consensus, and it uses USDC for gas. Chainstack serves Arc Mainnet (chain ID `5042`) and Arc Testnet (chain ID `5042002`). Both networks run the same [arc-node](https://github.com/circlefin/arc-node) software, which is what enforces the restrictions described on this page. The availability below applies to Arc Mainnet and Arc Testnet on Global Nodes. ## Arc namespace Arc adds a custom `arc_*` namespace on top of the standard Reth surface: * [`arc_getVersion`](/reference/arc-getversion) — returns the execution layer build information: `git_version`, `git_commit`, `git_short_hash`, and `cargo_version`. * [`arc_getCertificate`](/reference/arc-getcertificate) — takes a block height and returns the consensus commit certificate for that height: the round, the block hash, and the validator signatures. This is the finality proof for a block. > [!NOTE] > > `rpc_modules` does not list `arc` in its response, because the namespace is registered outside the standard module registry. The methods work regardless — do not use `rpc_modules` to decide whether they are available. ## Pending transactions are not available Arc disables pending-transaction visibility. Both `eth_newPendingTransactionFilter` and `eth_subscribe("newPendingTransactions")` return `-32001`, with `Pending transaction filters are not allowed` and `Subscriptions to pending transactions are not allowed` respectively. Two more methods are blocked for the same reason, and these return `null` rather than an error, so they fail quietly: * `eth_getBlockByNumber("pending")` — other block tags work normally. * `eth_getTransactionBySenderAndNonce` — it answers from the pool. All of this is enforced by Arc's own node software rather than by Chainstack. The [arc-node](https://github.com/circlefin/arc-node) RPC middleware describes the intent as preventing a request that "would leak pending transaction data". See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access) for how this compares with the other EVM chains. `txpool_status` works, so you can read the pool's pending and queued transaction counts. `txpool_content`, `txpool_contentFrom`, and `txpool_inspect` return `-32601`, because Global Nodes and Trader Nodes do not serve them on any EVM chain. ## Block time and archive billing Arc produces a block roughly every 0.5 seconds. The standard EVM rule — 127 or more blocks behind the tip bills as archive — therefore covers only about a minute of history, against roughly 25 minutes on Ethereum. On Arc, expect most historical queries to bill as archive. ## Legacy debug methods Reth registers a number of Geth-era `debug_*` methods but returns `null` for them rather than an error. They are marked as unavailable below, because a `null` response carries no data. Use `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceCall` instead, or `eth_getStorageAt` in place of `debug_storageRangeAt` — see [Using eth\_getStorageAt instead of debug\_storageRangeAt on Reth](/docs/using-eth_getstorageat-instead-of-debug_storagerangeat-on-reth). ## Method availability > [!NOTE] > > `eth_createAccessList` needs an explicit `gas` value in the call object. Without one it returns `-32000 intrinsic gas too high`. | Method | Availability | Comment | | - | - | - | | arc\_getCertificate | | | | arc\_getVersion | | | | debug\_accountRange | | registered but returns `null` on Reth | | debug\_chaindbProperty | | returns `-32601` | | debug\_codeByHash | | registered but returns `null` on Reth | | debug\_dumpBlock | | returns `-32601` | | debug\_getAccessibleState | | returns `-32601` | | debug\_getBadBlocks | | returns an empty array | | debug\_getModifiedAccountsByHash | | registered but returns `null` on Reth | | debug\_getModifiedAccountsByNumber | | registered but returns `null` on Reth | | debug\_getRawBlock | | | | debug\_getRawHeader | | | | debug\_getRawReceipts | | | | debug\_getRawTransaction | | | | debug\_getTrieFlushInterval | | not implemented by Reth | | debug\_intermediateRoots | | registered but returns `null` on Reth | | debug\_preimage | | registered but returns `null` on Reth | | debug\_printBlock | | registered but returns `null` on Reth | | debug\_storageRangeAt | | registered but returns `null` on Reth | | debug\_traceBadBlock | | registered but returns `null` on Reth | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | eth\_accounts | | returns an empty array — no unlocked accounts on a hosted node | | eth\_blobBaseFee | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_coinbase | | not implemented by Reth — returns `-32603 unimplemented` | | eth\_createAccessList | | pass an explicit `gas` value, otherwise it returns `-32000 intrinsic gas too high` | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getHeaderByHash | | | | eth\_getHeaderByNumber | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getRawTransactionByHash | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionBySenderAndNonce | | Arc returns `null` — it reads the pool, which Arc does not expose | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleByBlockHashAndIndex | | always null — Arc has no uncles (BFT consensus) | | eth\_getUncleByBlockNumberAndIndex | | always null — Arc has no uncles (BFT consensus) | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_hashrate | | always `0x0` | | eth\_maxPriorityFeePerGas | | | | eth\_mining | | not implemented by Reth — returns `-32603 unimplemented` | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | pending-transaction visibility is disabled on Arc | | eth\_protocolVersion | | | | eth\_sendRawTransaction | | | | eth\_sendRawTransactionSync | | | | eth\_simulateV1 | | | | eth\_subscribe | | WSS. `newHeads` and `logs` only — `newPendingTransactions` returns `-32001` | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | WSS | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | rpc\_modules | | | | trace\_block | | | | trace\_call | | | | trace\_callMany | | | | trace\_filter | | | | trace\_get | | | | trace\_rawTransaction | | | | trace\_replayBlockTransactions | | | | trace\_replayTransaction | | | | trace\_transaction | | | | txpool\_content | | not available on Global Nodes and Trader Nodes — see [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access) | | txpool\_contentFrom | | not available on Global Nodes and Trader Nodes | | txpool\_inspect | | not available on Global Nodes and Trader Nodes | | txpool\_status | | | | web3\_clientVersion | | | | web3\_sha3 | | | # Arc tooling Source: https://docs.chainstack.com/docs/arc-tooling Connect MetaMask, ethers.js, viem, and web3.py to an Arc Mainnet or Arc Testnet node on Chainstack. Chain configuration, the USDC gas token, and the custom arc_* methods. Arc is EVM-compatible, so the standard Ethereum libraries work against an Arc node without modification. The differences are in the chain configuration rather than in the code. ## Chain configuration Arc Mainnet and Arc Testnet have different chain IDs and block explorers, and only Arc Testnet has a faucet. The native gas currency, its decimals, and the Multicall3 address are the same on both networks. > [tab] Mainnet > > | Property | Value | > | - | - | > | Network | Arc Mainnet | > | Chain ID | `5042` | > | Native gas currency | USDC | > | Decimals | `18` | > | RPC URL | your Chainstack [Arc endpoint](/docs/manage-your-node#view-node-access-and-credentials) | > | Multicall3 | `0xcA11bde05977b3631167028862bE2a173976CA11` | > | Block explorer | [explorer.arc.io](https://explorer.arc.io) | > [tab] Testnet > > | Property | Value | > | - | - | > | Network | Arc Testnet | > | Chain ID | `5042002` | > | Native gas currency | USDC | > | Decimals | `18` | > | RPC URL | your Chainstack [Arc endpoint](/docs/manage-your-node#view-node-access-and-credentials) | > | Multicall3 | `0xcA11bde05977b3631167028862bE2a173976CA11` | > | Block explorer | [explorer.testnet.arc.io](https://explorer.testnet.arc.io) | > | Faucet | [faucet.circle.com](https://faucet.circle.com) | > [!WARNING] > > Arc pays gas in USDC, but the native currency carries **18 decimals**, not the 6 that the USDC ERC-20 token uses. Balances are wei-denominated exactly as on Ethereum, so `formatEther` and `from_wei(..., "ether")` are the correct helpers. Treating the gas balance as 6-decimal USDC puts you out by a factor of 1012. See [Arc methods](/docs/arc-methods) for per-method availability and [the API reference](/reference/arc-getting-started) for interactive examples. ## MetaMask Add Arc as a custom network. On Chainstack, get your [Arc endpoint](/docs/manage-your-node#view-node-access-and-credentials), then in MetaMask select **Add a custom network** and fill in: * Network name — Arc Mainnet or Arc Testnet * Default RPC URL — your Chainstack Arc endpoint * Chain ID — `5042` for mainnet, `5042002` for testnet * Currency symbol — USDC The block explorer field is optional in MetaMask. For Arc Mainnet, use [explorer.arc.io](https://explorer.arc.io). For Arc Testnet, use [explorer.testnet.arc.io](https://explorer.testnet.arc.io). ## ethers.js Install [ethers.js](https://docs.ethers.org/): ```shell Shell theme={"system"} npm install ethers ``` ```javascript index.js theme={"system"} const { JsonRpcProvider, formatEther } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function main() { const network = await provider.getNetwork(); console.log("Chain ID:", network.chainId.toString()); const block = await provider.getBlockNumber(); console.log("Block:", block); const balance = await provider.getBalance("0x19d7CF8eA0CE468417d1edA365a222a9dCBC7D27"); console.log("Balance:", formatEther(balance), "USDC"); } main(); ``` Arc's custom methods have no ethers helper, so call them with `send`: ```javascript index.js theme={"system"} const certificate = await provider.send("arc_getCertificate", [55000000]); console.log("Round:", certificate.round, "signatures:", certificate.signatures.length); ``` ## viem Arc is not in viem's bundled chain list, so define it with `defineChain`: ```shell Shell theme={"system"} npm install viem ``` ```javascript index.mjs theme={"system"} import { createPublicClient, http, defineChain, formatEther } from "viem"; export const arcMainnet = defineChain({ id: 5042, name: "Arc", nativeCurrency: { decimals: 18, name: "USDC", symbol: "USDC" }, rpcUrls: { default: { http: ["CHAINSTACK_NODE_URL"] } }, contracts: { multicall3: { address: "0xcA11bde05977b3631167028862bE2a173976CA11" }, }, }); export const arcTestnet = defineChain({ id: 5042002, name: "Arc Testnet", testnet: true, nativeCurrency: { decimals: 18, name: "USDC", symbol: "USDC" }, rpcUrls: { default: { http: ["CHAINSTACK_NODE_URL"] } }, contracts: { multicall3: { address: "0xcA11bde05977b3631167028862bE2a173976CA11" }, }, }); const client = createPublicClient({ chain: arcMainnet, transport: http() }); console.log("Chain ID:", await client.getChainId()); console.log("Block:", await client.getBlockNumber()); const balance = await client.getBalance({ address: "0x19d7CF8eA0CE468417d1edA365a222a9dCBC7D27", }); console.log("Balance:", formatEther(balance), "USDC"); ``` For the `arc_*` methods, use `client.request`: ```javascript index.mjs theme={"system"} const certificate = await client.request({ method: "arc_getCertificate", params: [55000000], }); ``` ## web3.py Install [web3.py](https://web3py.readthedocs.io/): ```shell Shell theme={"system"} pip install web3 ``` ```python main.py theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider("CHAINSTACK_NODE_URL")) print("Connected:", web3.is_connected()) print("Chain ID:", web3.eth.chain_id) print("Block:", web3.eth.block_number) address = Web3.to_checksum_address("0x19d7cf8ea0ce468417d1eda365a222a9dcbc7d27") balance = web3.eth.get_balance(address) print("Balance:", web3.from_wei(balance, "ether"), "USDC") ``` > [!NOTE] > > web3.py rejects lowercase addresses with `InvalidAddress`. Wrap any address you did not get from the library itself in `Web3.to_checksum_address()`, as above. The `arc_*` methods are not in the `web3.eth` namespace, so send them through the provider: ```python main.py theme={"system"} version = web3.provider.make_request("arc_getVersion", [])["result"] print("Node version:", version["git_version"]) certificate = web3.provider.make_request("arc_getCertificate", [55000000])["result"] print("Round:", certificate["round"], "signatures:", len(certificate["signatures"])) ``` ## Two Arc behaviors that affect tooling **Only replay-protected transactions are accepted.** Arc rejects pre-EIP-155 transactions on the RPC submission path with `-32000 only replay-protected (EIP-155) transactions allowed over RPC`. Every current library signs with the chain ID by default, so this only bites if you construct raw transactions by hand or use a very old signer. **Pending state is not readable.** Subscribing to pending transactions fails, and `eth_getBlockByNumber("pending")` returns `null` rather than an error. Code that polls the pending block to estimate the next state will silently receive nothing. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). # Authentication methods available on Chainstack Source: https://docs.chainstack.com/docs/authentication-methods-for-different-scenarios Choose the right Chainstack authentication method for your use case, comparing API key headers, URL-embedded keys, and bearer token approaches. **TLDR** * The Chainstack platform API uses a bearer token (API key) passed in the header for requests. * Blockchain node requests on Chainstack often use either the auth token appended to the URL or basic authentication (username and password). A multichain auth token can replace the auth token across several nodes. * Always keep API keys, usernames, and passwords private to avoid unauthorized node access. * Choose an authentication method based on your security needs and infrastructure constraints – e.g., basic auth for simplicity or key-based for more robust usage. ## Main article This guide provides a comprehensive overview of the different authentication methods that Chainstack offers. Our goal is to guide you in understanding these options and to assist in selecting the most appropriate method tailored to your specific use cases. Let's explore the authentication methods available and how they apply to various scenarios. ## Authentication methods API authentication is a crucial factor in application programming interface (API) development, used to verify the identities of applications or users utilizing the API. Different kinds of authentication methods are available in the API world; let's briefly explore the four primary methods used for API authentication. 1. **API keys**. These are the simplest form of API authentication. The client includes an API key, a unique identifier, in the header or as a parameter in the URL. The server matches the key to a corresponding key in its database and, if it matches, grants access. Although easy to implement, the API key can be misused if intercepted. 2. **Basic authentication**. This method involves sending a user ID and password with each API request. The credentials are Base-64 encoded but not encrypted, making it easily decipherable by anyone who intercepts the transmission. Basic authentication should always be used over HTTPS to add an additional layer of security. 3. **Digest authentication**. This is a step up from basic authentication, where the client sends a hashed (or digested) version of the password. It's more secure than basic authentication because even if an attacker intercepts the hashed password, they cannot use it to make API requests. 4. **OAuth (open authorization)**. OAuth is more complex but secure. It enables third-party applications to make requests on behalf of a user without needing their password. OAuth2, the latest version, uses short-lived access tokens rather than user credentials for authentication. 5. **JWT (JSON web tokens)**. JWT is a token-based authentication that allows for stateless authentication. An encoded string of characters represents a payload of data, which often includes issued at time (iat), expiration time (exp), and not before (nbf) statements. Here's a quick comparison table: | Method | Security | Complexity | | - | - | - | | API Key | Low | Low | | Basic authentication | Medium (if used over HTTPS) | Low | | Digest authentication | Medium | Medium | | OAuth | High | High | | JWT | High | Medium | Remember that choosing the right authentication method for your API depends on your specific use case, including your security needs and the resources available for implementation. > [!NOTE] > > The Chainstack platform api only accepts an API key as a bearer token as a form of authentication, so we’ll focus on this in the next section. ### Header authentication (bearer token) Header authentication with a bearer token is a common method employed in API requests. This approach involves attaching an authorization header with a bearer token in each HTTP request to the server. This token is a cryptic string, ensuring that data access is only granted to the token's bearer, thus giving this authentication method its name. In the context of the Chainstack platform, it's crucial to note that header authentication using a bearer token is fully supported for the [platform API requests](/reference/platform-api-getting-started). This means users can authenticate their API calls on the platform by simply including the bearer token in their request headers. However, bearer token authentication is currently unavailable when it comes to blockchain APIs. Blockchain nodes typically don't provide traditional user-based authentication. Chainstack uses API keys or similar mechanisms to authenticate requests to the hosted nodes. However, these are not traditional bearer tokens. The following is an example of how to send a header authenticated to the Chainstack platform API using a bearer token. > [!NOTE] > > Check out the Docs to learn [how to generate your Chainstack API key](/reference/platform-api-getting-started#create-api-key). ```bash cURL theme={"system"} curl --request GET \ --url https://api.chainstack.com/v1/organization/ \ --header 'accept: application/json' \ --header 'authorization: Bearer YOUR_CHAINSTACK_API_KEY' ``` This example calls the `Get Organization name and ID` API, which returns the organization name and ID belonging to the API key. Edit `YOUR_CHAINSTACK_API_KEY` with the API key you get from the Chainstack console. Example response: ```shell Shell theme={"system"} { "id": "RG-123-456", "name": "Cool Organization" } ``` ## Selecting the appropriate authentication method When choosing an authentication method for your scenario, consider the following points: * **Use case and purpose**. Identify the specific use case and the purpose of the API requests. Understanding the requirements will guide you in selecting the most suitable authentication method. * **Security and complexity**. Evaluate the level of security and complexity required for your API requests. Basic authentication provides a straightforward approach, whereas API keys offer a secure way to manage different URLs and chains. * **Compatibility and flexibility**. Determine the compatibility of the authentication method with your existing systems and the flexibility it provides for future expansion. ## Authenticating blockchain requests to a node Chainstack offers several ways to authenticate requests to a node: an auth token in the endpoint URL, a multichain auth token that replaces the auth token across several nodes, a username and password, and an x-token for gRPC. ### Access via auth token You can use the endpoint with an auth token like you can find in your Chainstack console: * HTTPS endpoint example: `https://ethereum-mainnet.core.chainstack.com/YOUR_AUTH_TOKEN` * WebSocket endpoint example: `wss://ethereum-mainnet.core.chainstack.com/ws/YOUR_AUTH_TOKEN` You can use a POST request like the following to access the HTTPS endpoint via a curl command. This example shows how to [retrieve the client version](/reference/ethereum-clientversion), one of the standard Ethereum JSON-RPC methods. ```bash cURL theme={"system"} curl -X POST --location 'https://ethereum-mainnet.core.chainstack.com/YOUR_AUTH_TOKEN' \ --header 'Content-Type: application/json' \ --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}' ``` > [!WARNING] > > Make sure to replace the placeholder endpoint with your endpoint. ### Access via multichain auth token A multichain auth token is one auth token that works in place of the node's auth token on every node attached to a multichain endpoint. The multichain auth token starts with `mt_` and goes in the same position in the URL as the node's auth token: * HTTPS endpoint example: `https://ethereum-mainnet.core.chainstack.com/YOUR_MULTICHAIN_AUTH_TOKEN` * WebSocket endpoint example: `wss://ethereum-mainnet.core.chainstack.com/YOUR_MULTICHAIN_AUTH_TOKEN` The node's own auth token keeps working alongside the multichain auth token. A request with the multichain auth token to a node that isn't attached to the multichain endpoint returns `401 Unauthorized`. To create a multichain endpoint and attach nodes, see [Use Multichain API](/docs/multichain-api). ### Password-protected access For the blockchain API requests, you can also use basic authentication: * HTTPS endpoint: `https://ethereum-mainnet.core.chainstack.com` * WSS endpoint: `wss://ethereum-mainnet.core.chainstack.com/ws` * Username: `YOUR_USER_NAME` * Password: `YOUR_PASSWORD` > [!NOTE] > > You can find your [username and password credentials](/docs/manage-your-node#view-node-access-and-credentials) in the Chainstack console. For password-protected access, you include the username and password in your `curl` command like so: ```bash cURL theme={"system"} curl -X POST \ -u YOUR_USER_NAME:YOUR_PASSWORD \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}' \ https://ethereum-mainnet.core.chainstack.com ``` In this command, `-u YOUR_USER_NAME:YOUR_PASSWORD` includes your username and password for basic authentication. Please replace `YOUR_PASSWORD` with your actual password. > [!NOTE] > > Keeping your API key and username/password secure is critical to prevent unauthorized access to your blockchain node. To replace a node's auth token, username and password, and x-token without downtime, rotate the node credentials. See [Rotate node credentials](/docs/manage-your-node#rotate-node-credentials). ### gRPC access via x-token For protocols that support gRPC endpoints (such as [TRON](/docs/tron-tooling#grpc-api), [Sui](/docs/sui-tooling#grpc-api), and [Solana Geyser](/docs/yellowstone-grpc-geyser-plugin)), Chainstack uses x-token authentication. Unlike HTTPS endpoints where the auth token is part of the URL, gRPC endpoints require passing the token in the request metadata. Your gRPC credentials in the Chainstack console include: * **gRPC endpoint** — the host and port, e.g., `tron-mainnet.core.chainstack.com:443` or `sui-mainnet.core.chainstack.com:443`. For Solana Geyser it is a separate endpoint listed under **Yellowstone gRPC**. The console shows it as `https://yellowstone-solana-mainnet.core.chainstack.com:443` on Global Nodes and as a bare hostname with no scheme or port on Dedicated Nodes * **x-token** — your authentication token. This is the same value as the node's auth token, the one that appears as the last path segment of the HTTPS endpoint, so if you already have the RPC endpoint working you already have the x-token When you [rotate the node credentials](/docs/manage-your-node#rotate-node-credentials), the x-token changes with them. Some clients want the scheme and some reject it: `@triton-one/yellowstone-grpc` (TypeScript) and the Rust Yellowstone client take `https://HOST:443`, while the Python `grpc` package, Go, and `grpcurl` take `HOST:443`. Check your client before assuming either form. To authenticate gRPC requests, pass the x-token as metadata in your gRPC client: ```bash grpcurl theme={"system"} grpcurl -H "x-token: YOUR_X_TOKEN" \ sui-mainnet.core.chainstack.com:443 \ list ``` ```python Python theme={"system"} import grpc channel = grpc.secure_channel( 'sui-mainnet.core.chainstack.com:443', grpc.ssl_channel_credentials() ) # Pass x-token in metadata for each call metadata = [('x-token', 'YOUR_X_TOKEN')] response = stub.YourMethod(request, metadata=metadata) ``` ```javascript Node.js theme={"system"} const grpc = require('@grpc/grpc-js'); const metadata = new grpc.Metadata(); metadata.add('x-token', 'YOUR_X_TOKEN'); // Pass metadata when making calls client.yourMethod(request, metadata, callback); ``` > [!WARNING] > > Keep your x-token secure. Anyone with your x-token can make requests to your gRPC endpoint. ## Conclusion The realm of API authentication is vast, encompassing a variety of methods, each with its distinct security levels and complexities. From the simplest API Keys to the secure OAuth and JWT methods, the choice depends largely on your specific use case and the resources you have available for implementation. In the context of Chainstack, platform API requests are authenticated through a bearer token. However, when it comes to blockchain APIs, bearer token authentication is currently not available, and API keys or basic authentication are the preferred methods. Regardless of your chosen method, it is crucial to remember that the security of your API access points is paramount. Always safeguard your API keys, usernames, and passwords to prevent unauthorized access. Also, consider your use case's specific needs and requirements, including the level of security and complexity required for your API requests and the compatibility and flexibility of the chosen authentication method with your existing systems. Remember, the ultimate goal of API authentication is to facilitate secure access to services, ensure user data privacy, and prevent unauthorized access. By understanding and effectively implementing the right authentication methods, you are well on your way to achieving these goals in your software development endeavors. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Available node methods Source: https://docs.chainstack.com/docs/available-node-methods Browse all supported API methods across Chainstack protocols including Ethereum, Solana, Bitcoin, and other EVM and non-EVM blockchain networks. Check the table subpages for each of the protocols to see the method availability. If there's anything that you are missing, always feel free to [reach out to us](https://support.chainstack.com/). We also provide additional node customizability on request. See also the interactive [API call examples](/reference/blockchain-apis). # Avalanche Granite network upgrade Source: https://docs.chainstack.com/docs/avalanche-granite-upgrade Understand the Avalanche Granite upgrade including ACP-181, ACP-204, and ACP-226 proposals that improve network performance and validator operations. **TLDR:** * Granite upgrade activates on Fuji testnet October 29, 2025, with Mainnet following after successful Fuji deployment. * Three Avalanche Community Proposals (ACPs) activate: ACP-181 (P-Chain Epoched Views), ACP-204 (secp256r1 curve support), ACP-226 (Dynamic block times). * New `proposervm` namespace APIs available on all chains (P-chain, X-chain, C-chain) — accessible but not shown in Chainstack UI. * Breaking changes only affect library consumers (libevm hooks) and plugin developers (version 44) — standard RPC users unaffected. > [!TIP] > > No breaking changes for standard RPC users. All existing RPC methods continue to work as expected. > [!NOTE] > > Granite is not the latest Avalanche upgrade. The [Helicon upgrade](/docs/avalanche-helicon-upgrade) follows it and does remove RPC methods — check your application against it. If you're using Chainstack nodes or other standard RPC endpoints, you don't need to make any changes to your applications. ## Main article The Granite upgrade is a major network upgrade for Avalanche that activates on Fuji testnet on October 29, with Mainnet activation following after successful Fuji deployment. > [!TIP] > > ### Chainstack nodes are Granite-ready > > Chainstack will roll out the Granite upgrade following the Avalanche upgrade timeline: Fuji testnet first, then Mainnet. All nodes on Chainstack are prepared for the respective network upgrades. ## Three Avalanche Community Proposals (ACPs) The Granite upgrade implements three ACPs that improve P-Chain operations, expand cryptographic support, and optimize block timing. ### ACP-181: P-Chain Epoched Views ACP-181 introduces epoched views for the P-Chain, improving validator set management and consensus efficiency. This proposal restructures how the P-Chain manages validator state across epochs, enabling better coordination and reducing overhead in validator set transitions. See the full proposal: [ACP-181: P-Chain Epoched Views](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/181-p-chain-epoched-views) ### ACP-204: Precompile for secp256r1 Curve Support ACP-204 adds precompile support for the secp256r1 elliptic curve, expanding Avalanche's cryptographic capabilities. The secp256r1 curve (also known as P-256) is widely used in various cryptographic applications, including TLS/SSL certificates and secure hardware modules. This addition enables smart contracts to verify secp256r1 signatures natively, opening up new integration possibilities with existing systems that use this curve. See the full proposal: [ACP-204: Precompile for secp256r1 Curve Support](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/204-secp256r1-precompile) ### ACP-226: Dynamic Minimum Block Times ACP-226 introduces dynamic minimum block times with delay excess calculations, allowing more flexible block production timing. Previously, block times were static. This proposal enables the network to adjust block timing dynamically based on network conditions, improving overall efficiency while maintaining security guarantees. > [!NOTE] > > **New block header fields**: ACP-226 adds `TimeMilliseconds` and `MinDelayExcess` fields to C-Chain block headers (implemented in coreth v0.15.4-rc.4). These appear as additional fields in block responses from methods like `eth_getBlockByNumber`. The existing `timestamp` field remains unchanged for backward compatibility. Standard RPC clients that ignore unknown JSON fields are unaffected. See the full proposal: [ACP-226: Dynamic Minimum Block Times](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/226-dynamic-minimum-block-times) ## New API endpoints The Granite upgrade introduces several new API endpoints across the `proposervm` and `platform` namespaces. ### `proposervm` namespace The `proposervm` namespace is now available on all Avalanche primary chains (P-chain, X-chain, C-chain) with two new methods: * `GetProposedHeight` — retrieves the currently proposed block height. * `GetCurrentEpoch` — returns the current epoch information. Or using chain aliases: * P-chain: `/ext/bc/P/proposervm` * C-chain: `/ext/bc/C/proposervm` * X-chain: `/ext/bc/X/proposervm` > [!NOTE] > > The `proposervm` namespace is fully supported on Chainstack nodes but is not displayed in the Chainstack UI. You can access it directly via the endpoints above. ### `platform` namespace A new `platform` method is available: * `GetAllValidatorsAt` — retrieves all validators at a specific height This enables more efficient queries of the complete validator set at any given point in the chain's history. ## Stateless block types Granite introduces stateless block types for the new consensus mechanism, reducing the amount of state that needs to be maintained during block processing. This architectural change lays the groundwork for future scalability improvements while maintaining backward compatibility with existing operations. ## What this means for developers If you're building on Avalanche using Chainstack nodes: **No action required** — your existing applications will continue working without changes. **New opportunities** — the `proposervm` namespace provides additional monitoring and observability capabilities for your applications. **Future-ready** — the Granite upgrade positions Avalanche for enhanced P-Chain operations and improved block timing flexibility. # Avalanche Helicon network upgrade Source: https://docs.chainstack.com/docs/avalanche-helicon-upgrade What the Avalanche Helicon upgrade (AvalancheGo v1.15.0) changes for RPC users — removed admin, warp, and personal namespaces, new named block resolution on the C-Chain, and a dynamic minimum gas price. **TLDR:** * Helicon activated on Fuji Testnet on July 28, 2026 and on Avalanche Mainnet on September 22, 2026 at 15:00 UTC, with [AvalancheGo v1.15.0](https://github.com/ava-labs/avalanchego/releases/tag/v1.15.0). * Six Avalanche Community Proposals (ACPs) activate: ACP-194 (continuous execution), ACP-236 (auto-renewed staking), ACP-267 (uptime requirement increase), ACP-273 (reduce minimum staking duration), ACP-283 (dynamic minimum gas price), ACP-285 (reduce minimum consumption rate). * Helicon removes C-Chain RPC methods — the `admin`, `warp`, and `personal` namespaces, three `eth` methods, and eight non-trace `debug` methods. * On the C-Chain, `latest` and `pending` now resolve to the last executed block, and `safe` and `finalized` to the last settled block. * [AvalancheGo v1.15.2](https://github.com/ava-labs/avalanchego/releases/tag/v1.15.2) removes `avax.getAtomicTxStatus`, which v1.15.0 deprecated. Use `avax.getAtomicTx`. > [!WARNING] > > Helicon is a breaking upgrade for RPC users, unlike the [Granite upgrade](/docs/avalanche-granite-upgrade). A call to `eth_accounts` or to one of the removed `debug` methods fails on an upgraded node. Check your application against the removed methods listed on this page. ## Main article Helicon is a network upgrade for Avalanche that changes how the C-Chain executes blocks and prices gas, and that removes a set of C-Chain RPC methods. Fuji Testnet activated Helicon on July 28, 2026, and Avalanche Mainnet activated it on September 22, 2026 at 15:00 UTC. > [!TIP] > > ### Chainstack nodes run Helicon > > Chainstack Avalanche nodes run AvalancheGo v1.15.2 on both Fuji Testnet and Avalanche Mainnet. `eth_call`, `eth_getLogs`, `eth_sendRawTransaction`, `eth_getBlockByNumber`, the `debug_trace*` methods, and the X-Chain and P-Chain APIs are unaffected. For the full supported surface, see [Avalanche methods](/docs/avalanche-methods). ## Removed API namespaces Helicon removes three C-Chain namespaces entirely. A request to any method in these namespaces returns a method-not-found error on an upgraded node: * `admin` — node administration methods such as `admin_peers` and `admin_nodeInfo`. These were never served on Chainstack nodes. * `warp` — Avalanche Warp Messaging methods. * `personal` — account management methods. ## Removed C-Chain RPC methods Helicon removes eleven individual C-Chain methods on top of the removed namespaces. Three `eth` methods that report node-local accounts: * `eth_accounts` * `eth_coinbase` * `eth_etherbase` Eight `debug` methods for state and block inspection: * `debug_accountRange` * `debug_dumpBlock` * `debug_getAccessibleState` * `debug_getBadBlocks` * `debug_getModifiedAccountsByHash` * `debug_getModifiedAccountsByNumber` * `debug_preimage` * `debug_storageRangeAt` The `debug_trace*` methods — `debug_traceBlockByNumber`, `debug_traceBlockByHash`, `debug_traceTransaction`, `debug_traceCall`, and `debug_traceBlock` — are not affected. Deploy your node in [archive mode](/docs/protocols-modes-and-types#modes) to call them, as described in [Debug and trace APIs](/docs/debug-and-trace-apis). > [!TIP] > > If your application reads contract storage with `debug_storageRangeAt`, you can replicate the query with [`eth_getStorageAt`](/reference/avalanche-getstorageat) against known slot positions. See [eth\_getStorageAt vs debug\_storageRangeAt on Reth](/docs/using-eth_getstorageat-instead-of-debug_storagerangeat-on-reth) for a working script — the same approach applies to Avalanche. ## Named block resolution on the C-Chain ACP-194 makes C-Chain block execution continuous, so a block can be accepted before it is executed. That splits apart what the named block tags mean, and the tags resolve as follows on an upgraded node: | Tag | Resolves to | | - | - | | `latest` | Last executed block | | `pending` | Last executed block | | `safe` | Last settled block | | `finalized` | Last settled block | The practical effect is that `latest` can lag block acceptance by the execution queue depth, and `safe` and `finalized` now return the same block. An application that reads state at `latest` and expects it to match the newest accepted block height needs to account for that gap — compare the height returned by [`eth_getBlockByNumber`](/reference/avalanche-getblockbynumber) with `latest` against the height you get from a block subscription. ## Dynamic minimum gas price ACP-283 replaces the fixed minimum C-Chain gas price with one that adjusts to network conditions. Read the current value with [`eth_gasPrice`](/reference/avalanche-getgasprice) or `eth_feeHistory` rather than hardcoding a floor, and let `eth_estimateGas` size your gas limit. ## What AvalancheGo v1.15.2 changes [AvalancheGo v1.15.2](https://github.com/ava-labs/avalanchego/releases/tag/v1.15.2) removes one C-Chain method that Helicon deprecated, and adds two P-Chain validator fields: * `avax.getAtomicTxStatus` — removed. Use `avax.getAtomicTx` at the C-Chain `/ext/bc/C/avax` endpoint, which returns the transaction together with its block height. For a transaction that isn't accepted, `avax.getAtomicTx` returns an error, where `avax.getAtomicTxStatus` returned a status such as `Unknown`. * `platform.getCurrentValidators` — now returns `restakedValidationRewards` and `restakedDelegateeRewards` for validators whose stake renews automatically under ACP-236. ## The six ACPs Helicon activates six proposals. Four change validator economics and operations on the P-Chain, and two change C-Chain execution and pricing: * [ACP-194: continuous execution](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/194-continuous-execution) — decouples C-Chain block acceptance from block execution. * [ACP-236: auto-renewed staking](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/236-auto-renewed-staking) — lets a validator's stake renew without a new staking transaction. * [ACP-267: uptime requirement increase](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/267-uptime-requirement-increase) — raises the uptime a validator must meet to earn rewards. * [ACP-273: reduce minimum staking duration](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/273-reduce-minimum-staking-duration) — shortens the minimum validator staking period. * [ACP-283: dynamic minimum gas price](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/283-dynamic-minimum-gas-price) — makes the C-Chain minimum gas price responsive to demand. * [ACP-285: reduce minimum consumption rate](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/285-reduce-minimum-consumption-rate) — lowers the minimum staking reward consumption rate. ## What this means for developers If you build on Avalanche using Chainstack nodes: **Check your calls** — replace the removed `debug` methods and drop any `eth_accounts`, `eth_coinbase`, or `eth_etherbase` call. A node endpoint has no accounts of its own, so these methods returned an empty or zero result before Helicon removed them. **Re-read your block tags** — if your indexer or bot treats `latest` as the newest accepted block, measure the gap against a block subscription and decide whether your application can tolerate it. **Stop hardcoding a gas floor** — query the minimum gas price at call time. **Switch atomic transaction lookups** — replace `avax.getAtomicTxStatus` with `avax.getAtomicTx`, and handle the error it returns for a transaction that isn't accepted yet. # Avalanche methods Source: https://docs.chainstack.com/docs/avalanche-methods Complete reference of supported Avalanche C-Chain and P-Chain JSON-RPC methods on Chainstack, including method availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Avalanche API call examples](/reference/avalanche-getting-started). ## Contract chain (C-Chain) > [!NOTE] > > This table reflects the C-Chain surface after the [Helicon upgrade](/docs/avalanche-helicon-upgrade), which removed the `admin`, `warp`, and `personal` namespaces, three `eth` methods, and eight non-trace `debug` methods. Helicon activated on Fuji Testnet on July 28, 2026 and on Avalanche Mainnet on September 22, 2026 at 15:00 UTC. AvalancheGo v1.15.2 also removes `avax.getAtomicTxStatus`. | Method | Availability | Comment | | - | - | - | | eth\_accounts | | Removed by the [Helicon upgrade](/docs/avalanche-helicon-upgrade) | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_get\_asset\_balance | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | txpool\_content | | | | txpool\_inspect | | | | txpool\_contentFrom | | | | txpool\_status | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_getBadBlocks | | Removed by the [Helicon upgrade](/docs/avalanche-helicon-upgrade) | | debug\_storageRangeAt | | Removed by the [Helicon upgrade](/docs/avalanche-helicon-upgrade) — use [eth\_getStorageAt instead](/docs/using-eth_getstorageat-instead-of-debug_storagerangeat-on-reth) | | debug\_getTrieFlushInterval | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | avax.getAtomicTx | | Served at the `/ext/bc/C/avax` endpoint | | avax.getAtomicTxStatus | | Removed in [AvalancheGo v1.15.2](/docs/avalanche-helicon-upgrade) — use `avax.getAtomicTx` | | avax.getUTXOs | | Served at the `/ext/bc/C/avax` endpoint | | avax.issueTx | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | ## Exchange chain (X-Chain) | Method | Availability | Comment | | - | - | - | | avm.buildGenesis | | | | avm.getAddressTxs | | | | avm.getAllBalances | | | | avm.getAssetDescription | | | | avm.getBalance | | | | avm.getBlockByHeight | | | | avm.getHeight | | | | avm.getTx | | | | avm.getTxStatus | | | | avm.getUtxOs | | | | avm.issueTx | | | ## Platform chain (P-Chain) | Method | Availability | Comment | | - | - | - | | platform.getBalance | | | | platform.getAllValidatorsAt | | | | platform.getBlockchainStatus | | | | platform.getBlockchains | | | | platform.getCurrentSupply | | | | platform.getCurrentValidators | | Returns `restakedValidationRewards` and `restakedDelegateeRewards` for auto-renewed validators | | platform.getFeeConfig | | | | platform.getFeeState | | | | platform.getHeight | | | | platform.getMinStake | | | | platform.getRewardUtxOs | | | | platform.getStake | | | | platform.getStakingAssetId | | | | platform.getSubnets | | | | platform.getTimestamp | | | | platform.getTotalStake | | | | platform.getTx | | | | platform.getTxStatus | | | | platform.getUtxOs | | | | platform.getValidatorsAt | | | | platform.issueTx | | | | platform.sampleValidators | | | | platform.validatedBy | | | | platform.validates | | | ## proposervm namespace Available on all chains (P-chain, X-chain, C-chain). | Method | Availability | Comment | | - | - | - | | proposervm.GetCurrentEpoch | | | | proposervm.GetProposedHeight | | | # Avalanche tooling Source: https://docs.chainstack.com/docs/avalanche-tooling Set up MetaMask, Hardhat, Foundry, and development tools to build and deploy smart contracts on Avalanche C-Chain through Chainstack RPC endpoints. Get started with a [reliable Avalanche RPC endpoint](https://chainstack.com/build-better-with-avalanche/) to use the tools below. ## Endpoint structure Avalanche runs three built-in chains (C, X, P) on the same node. Your Chainstack base endpoint serves all three — to target a specific chain, append the chain-specific URI **before** your auth key. The URL pattern is: ``` https://avalanche-mainnet.core.chainstack.com// ``` | Chain | Purpose | HTTPS URI | WSS URI | | - | - | - | - | | **C-Chain** | EVM smart contracts (`eth_*` methods) | `/ext/bc/C/rpc` | `/ext/bc/C/ws` | | **X-Chain** | Native asset transfers (UTXO) | `/ext/bc/X` | `/ext/bc/X/events` | | **P-Chain** | Validator and subnet management | `/ext/P` | — | > [!NOTE] > > The most common reason an Avalanche endpoint "doesn't work" is forgetting the chain URI. The node access page in the Chainstack console shows the full URL for each chain — copy from there to avoid path errors. ### C-Chain HTTPS To call EVM methods, use the C-Chain HTTPS endpoint: ```bash cURL theme={"system"} curl -X POST \ 'https://avalanche-mainnet.core.chainstack.com/ext/bc/C/rpc/AUTH_KEY' \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` ### C-Chain WebSocket For real-time subscriptions, connect over WSS to the C-Chain WSS endpoint: ```bash wscat theme={"system"} wscat -c wss://avalanche-mainnet.core.chainstack.com/ext/bc/C/ws/AUTH_KEY > {"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"],"id":1} ``` ## C-Chain ### MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ### Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Avalanche nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ### Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](/docs/avalanche-tooling#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ### web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Avalanche nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). #### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). #### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ### web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Avalanche nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Key Protected theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ### web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Avalanche nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint * USERNAME — your node access username * PASSWORD — your node access password See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ### ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Avalanche nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Avalanche C-Chain network ID: * Mainnet: `43114` * Fuji Testnet: `43113` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). #### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — Avalanche C-Chain network ID: * Mainnet: `43114` * Fuji Testnet: `43113` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### Brownie 1. Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). 2. Use the `brownie networks add` command with the node endpoint: ```bash Shell theme={"system"} brownie networks add Avalanche ID name="NETWORK_NAME" host= YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `chainstack-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password * NETWORK\_ID — Avalanche C-Chain network ID: * Mainnet: `43114` * Fuji Testnet: `43113` Example to run the deployment script: ```bash Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ### Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. #### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```bash Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password #### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```bash Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password ## X-Chain ### JSON-RPC API Interact with the X-Chain through your Avalanche nodes using JSON-RPC API. Use [curl](https://curl.haxx.se) or [Postman](https://www.getpostman.com) to invoke Avalanche X-Chain API methods. Example below demonstrates how to get AVAX balance of an address through your Avalanche node HTTPS endpoint on the X-Chain mainnet: ```bash Curl theme={"system"} curl -X POST --data '{ "jsonrpc":"2.0", "id" : 1, "method" :"avm.getBalance", "params" :{ "address":"X-avax1slt2dhfu6a6qezcn5sgtagumq8ag8we75f84sw", "assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z" } }' -H 'content-type:application/json;' YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password ### AvalancheJS 1. Install [AvalancheJS](https://github.com/ava-labs/avalanchejs): ```bash theme={"system"} npm install @avalabs/avalanchejs ``` 2. Create a chain API client with your node's base endpoint and authenticate with the username and password from your node's [access and credentials](/docs/manage-your-node#view-node-access-and-credentials). AvalancheJS appends the chain path, such as `/ext/bc/X`, to the base endpoint, so pass the endpoint without the auth token. Example to get the AVAX balance of an address on the X-Chain Mainnet: ```javascript JavaScript theme={"system"} import { avm } from "@avalabs/avalanchejs"; const BASE_ENDPOINT = "https://avalanche-mainnet.core.chainstack.com"; const USERNAME = "YOUR_USERNAME"; const PASSWORD = "YOUR_PASSWORD"; const xchain = new avm.AVMApi(BASE_ENDPOINT); xchain.setFetchOptions({ headers: { Authorization: "Basic " + btoa(`${USERNAME}:${PASSWORD}`) }, }); const main = async () => { const balances = await xchain.getAllBalances({ address: "X-avax1k30tskunzxr2tmapy8p4y0ujn2802yr3743679", }); console.log(balances); }; main(); ``` # Avalanche: Aave V3 flash loan with Hardhat Source: https://docs.chainstack.com/docs/avalanche-tutorial-aavev3-flash-loans-with-hardhat Execute Aave V3 flash loans on Avalanche C-Chain using Hardhat and a Chainstack RPC node for zero-collateral borrowing and DeFi arbitrage testing. **TLDR:** * You’ll configure a Hardhat project to interact with Aave V3 flash loans on Avalanche’s Fuji testnet. * You’ll use Chainstack for your Avalanche node endpoint and deploy a custom FlashLoan contract. * You’ll borrow USDC, then repay it plus fees in a single transaction, demonstrating a flash loan’s instant, collateral-free mechanics. * By the end, you’ll have a working Aave flash loan flow on a testnet environment ready for deeper custom logic. ## Flash loans A flash loan is a type of loan that can be obtained instantly and without any collateral, unlike traditional loans that require time-consuming application processes and collateral such as property or assets. This type of loan is available through Aave, a decentralized lending platform, where borrowers can borrow any amount they need and repay it within a single transaction. The loan is secured by the borrower's smart contract and is only valid for the duration of that transaction. If the borrower cannot repay the loan and the associated fees within the same transaction, the loan is automatically canceled, and the transaction is reverted. Flash loans are often used in the context of cryptocurrency trading and arbitrage, as they enable traders to obtain funds quickly and cheaply to take advantage of market opportunities. For detailed documentation, see Aave Developers: [Flash Loans](https://docs.aave.com/developers/guides/flash-loans). The objective of this tutorial is to make you familiar with the Avalanche C-Chain, the [Hardhat framework](/docs/avalanche-tooling#hardhat), and the [Aave flash loans](https://docs.aave.com/faq/flash-loans). Specifically, in this tutorial, you will: * Deploy an Avalanche node on the Fuji testnet. * Create a flash loan project using Hardhat. * Run a flash loan on the Fuji testnet through an Avalanche node deployed with Chainstack. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Avalanche RPC endpoint](https://chainstack.com/build-better-with-avalanche/) on Chainstack's [Avalanche node infrastructure](https://chainstack.com/protocols/). * [Node.js](https://nodejs.org/en/) as the JavaScript framework. * [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#overview) to create, deploy, and interact with contracts. ## Dependencies * Hardhat: ^2.12.7 * @aave/core-v3: ^1.17.2 * dotenv: ^16.0.3 ## Overview This tutorial shows you how to request a flash loan on the Fuji testnet to borrow USDC. To get from zero to an executed Aave V3 flash loan on the Avalanche Fuji C-Chain testnet, do the following: 1. With Chainstack, create a public chain project. 2. With Chainstack, join the Avalanche Fuji testnet. 3. With Chainstack, access your Avalanche node endpoint. 4. With Hardhat, create and set up an Aave flash loan project. 5. With Hardhat, execute the flash loan through your Avalanche node. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Avalanche Fuji testnet See [Join a public network](/docs/manage-your-networks). ### Get your Avalanche node endpoint See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Fund your wallet Before diving into the flash loan project, make sure to top up your wallet with testnet AVAX and USDC tokens. Use the following faucets: * [Aave faucet](https://app.aave.com/faucet/) for USDC. Make sure you are on the Avalanche Market. * [Fuji faucet](https://faucet.avax.network/) for AVAX. ### Install HardHat See [Installing Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#installation). ### Create a Hardhat project Create a new directory for your project, then run the following from a terminal: ```shell Shell theme={"system"} npx hardhat ``` This will launch the Hardhat CLI, which will prompt you to choose a starter project. For this project, answer **yes** to the following: * **Create a JavaScript project** * **Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)?** ### Install the required dependencies This project uses the [aave/core-v3](https://github.com/aave/aave-v3-core) package for the smart contracts and the [dotenv](https://github.com/motdotla/dotenv) package to safely use environment variables. Run the following command in your root directory to install: ```shell Shell theme={"system"} npm i @aave/core-v3 dotenv ``` ### Create a .env file In your project's root directory, create a new file and name it `.env`. Here is where you will set up the environment variables for your Chainststack Avalanche Fuji endpoint and your wallet's private key. ```Text .env theme={"system"} PRIVATE_KEY="YOUR_WALLET_PRIVATE_KEY" FUJI_CHAINSTACK="YOUR_CHAINSTACK_ENDPOINT" ``` Save it after you added your information. ### Edit the Hardhat configuration file You will find a file named `hardhat.config.js` in the root directory. This file is used to configure various settings for your Hardhat projects, such as the network you want to deploy your contracts on, the compilers you want to use, and the plugins you want to enable. Delete the default code in the file and replace it with the following: ```javascript hardhat.config.js theme={"system"} require("@nomicfoundation/hardhat-toolbox"); require("dotenv").config(); /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: "0.8.10", networks: { fuji: { url: process.env.FUJI_CHAINSTACK, accounts: [process.env.PRIVATE_KEY], }, }, }; ``` Let's break down what each part of the file does: * `require("@nomicfoundation/hardhat-toolbox");` imports the Hardhat Toolbox plugin, which provides several useful tools and utilities for Hardhat projects. * `require("dotenv").config();` loads environment variables from a `.env` file using the `dotenv` package. * `module.exports = { ... }` exports a JavaScript object containing the configuration for the Hardhat project. * `solidity: "0.8.10",` sets the Solidity compiler version to 0.8.10. * `networks: { ... }` defines the network configurations for the Hardhat project. In this case, it defines a network called `fuji` that connects to the Avalanche Fuji blockchain network. * `fuji: { ... }` defines the configuration for the `fuji` network. * `url: process.env.FUJI_CHAINSTACK,` sets the URL for the Fuji network using the `FUJI_CHAINSTACK` environment variable. * `accounts: [process.env.PRIVATE_KEY],` sets the accounts for the `fuji` network using the `PRIVATE_KEY` environment variable. This will allow the Hardhat project to deploy contracts and interact with the Fuji network using the specified private key. ### Create the flash loan smart contract In the root directory, you will find a directory named `contracts` with a sample contract in it. Rename this contract to `FlashLoan.sol` and replace its code with the following: ```sol FlashLoan.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import {FlashLoanSimpleReceiverBase} from "@aave/core-v3/contracts/flashloan/base/FlashLoanSimpleReceiverBase.sol"; import {IPoolAddressesProvider} from "@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol"; import {IERC20} from "@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol"; /** * @title FlashLoan * @dev A contract that demonstrates how to use Aave's flash loans. This contract can borrow any token from the Aave lending pool, perform custom logic with the borrowed funds, and repay the loan plus interest in a single transaction. */ contract FlashLoan is FlashLoanSimpleReceiverBase { address payable public owner; // The owner of this contract, who can withdraw funds. /** * @dev Constructor function that sets the address provider for the Aave lending pool and the contract owner. * @param _addressProvider The address provider for the Aave lending pool. */ constructor(address _addressProvider) FlashLoanSimpleReceiverBase(IPoolAddressesProvider(_addressProvider)) { owner = payable(msg.sender); // Set the contract owner to the creator of this contract. } /** * @dev This function is called after this contract receives a flash loan. It executes custom logic with the borrowed funds and repays the loan plus interest to the Aave lending pool. * @param asset The token being borrowed. * @param amount The amount of the token being borrowed. * @param premium The fee paid to the Aave lending pool. * @param initiator The address that initiated the flash loan. * @param params Additional parameters for the flash loan. * @return true to indicate that the flash loan has been repaid. */ function executeOperation( address asset, uint256 amount, uint256 premium, address initiator, bytes calldata params ) external override returns (bool) { // This function is called by the Aave lending pool contract after this contract receives the flash loan. // The asset parameter represents the token being borrowed, amount is the amount borrowed, and premium is the fee paid to the pool. // 👇 Your custom logic for the flash loan should be implemented here 👇 /** YOUR CUSTOM LOGIC HERE */ // 👆 Your custom logic for the flash loan should be implemented above here 👆 // Approve the lending pool contract to pull funds from this contract to pay back the flash loan. uint256 amountOwed = amount + premium; IERC20(asset).approve(address(POOL), amountOwed); return true; // Return true to indicate that the flash loan has been repaid. } /** * @dev Function to request a flash loan for a specified token and amount. * receiverAddress The address of this contract, which will receive the flash loan. * @param _token The token to be borrowed. * @param _amount The amount of the token to be borrowed. * params No additional parameters are needed. * referralCode No referral code is used. */ function requestFlashLoan(address _token, uint256 _amount) public onlyOwner { address receiverAddress = address(this); address asset = _token; uint256 amount = _amount; bytes memory params = ""; uint16 referralCode = 0; // Call the Aave lending pool contract to initiate the flash loan. POOL.flashLoanSimple( receiverAddress, asset, amount, params, referralCode ); } /** * @dev Get the balance of a specific token in this contract. * @param _tokenAddress The address of the token to check the balance of. * @return The balance of the specified token in this contract. */ function getBalance(address _tokenAddress) external view returns (uint256) { return IERC20(_tokenAddress).balanceOf(address(this)); } /** * @dev Withdraw a specific token from this contract to the contract owner's address. * @param _tokenAddress The address of the token to withdraw. */ function withdraw(address _tokenAddress) external onlyOwner { IERC20 token = IERC20(_tokenAddress); // Create an instance of the token contract. token.transfer(msg.sender, token.balanceOf(address(this))); // Transfer the token balance to the contract owner. } /** * @dev Modifier to ensure that only the contract owner can call a specific function. */ modifier onlyOwner() { require( msg.sender == owner, "You are not the owner!" ); _; } /** * @dev Fallback function to receive ETH payments. */ receive() external payable {} } ``` > [!NOTE] > > ### Default flash loan logic > > This smart contract receives the flash loan but performs no further actions on it. You will need to add your own logic. This smart contract is heavily commented on to explain its inner workings, but you can find more details on the [Aave docs](https://docs.aave.com/developers/guides/flash-loans). ### Create the deploying and interacting script In the `scripts` directory inside the root of your project, you will find a file named `deploy.js`. Replace its content with the following: ```javascript deploy.js theme={"system"} const { ethers } = require("hardhat"); // Contract addresses and other values const AVA_FUJI_POOL_PROVIDER = "0x220c6A7D868FC38ECB47d5E69b99e9906300286A"; const USDC_ADDRESS = "0x6a17716Ce178e84835cfA73AbdB71cb455032456"; const USDC_DECIMALS = 6; const FLASHLOAN_AMOUNT = ethers.utils.parseUnits("1000", USDC_DECIMALS); // USDC tranfer function ABI const USDC_ABI = ["function transfer(address to, uint256 value) external returns (bool)"]; async function main() { try { console.log("Deploying FlashLoan contract..."); const FlashLoan = await ethers.getContractFactory("FlashLoan"); const flashLoan = await FlashLoan.deploy(AVA_FUJI_POOL_PROVIDER); await flashLoan.deployed(); console.log(`FlashLoan contract deployed at: ${flashLoan.address}`); console.log(`View contract at: https://testnet.snowtrace.io/address/${flashLoan.address}`); console.log("---------------------------------------------------------------\n"); // Transfer USDC to the FlashLoan contract const erc20 = new ethers.Contract(USDC_ADDRESS, USDC_ABI, ethers.provider.getSigner()); const amount = ethers.utils.parseUnits("5", USDC_DECIMALS); console.log(`Transferring ${amount / 1e6} USDC to the FlashLoan contract...`); const transferErc20 = await erc20.transfer(flashLoan.address, amount); console.log(`Transferred ${amount / 1e6} USDC tokens to the FlashLoan contract`); console.log("Waiting for 1 block to verify the transfer..."); await transferErc20.wait(1); // Wait 1 block for the transaction to be verified to update the balance console.log(`---------------------------------------------------------------\n`); // Check USDC balance of the FlashLoan contract const usdcBalance = await flashLoan.getBalance(USDC_ADDRESS); console.log(`USDC balance of the FlashLoan contract is: ${usdcBalance / 1e6} USDC`); console.log("---------------------------------------------------------------\n"); // Call flash loan console.log(`Requesting a flash loan of ${FLASHLOAN_AMOUNT / 1e6} USDC...`); const flashloanTx = await flashLoan.requestFlashLoan(USDC_ADDRESS, FLASHLOAN_AMOUNT); console.log("Flash loan executed!"); console.log(`View transaction at: https://testnet.snowtrace.io/tx/${flashloanTx.hash}`); await flashloanTx.wait(1); // Wait 1 block for the transaction to be verified to update the balance console.log("---------------------------------------------------------------\n"); // Withdraw remaining USDC const remainingUSDC = await flashLoan.getBalance(USDC_ADDRESS); console.log(`Withdrawing ${remainingUSDC / 1e6} USDC from the FlashLoan contract...`); const withdrawFunds = await flashLoan.withdraw(USDC_ADDRESS); await withdrawFunds.wait(1); // Wait 1 block for the transaction to be verified console.log(`Funds sent!`) console.log(`View transaction at: https://testnet.snowtrace.io/tx/${withdrawFunds.hash}`); } catch (error) { console.error(error); process.exitCode = 1; } } main(); ``` This code is a script that deploys a `FlashLoan` smart contract and uses it to request a flash loan of 1,000 USDC tokens. The script first sets some constants, including the addresses of the Aave pool provider and the USDC token on the Fuji testnet. Verify that the addresses are up to date on the [Aave docs](https://docs.aave.com/developers/deployed-contracts/v3-testnet-addresses#contract-name-changes-from-v2-greater-than-v3) by finding the addresses for `PoolAddressesProvider-Avalanche`and `USDC-TestnetMintableERC20-Avalanche`. The amount of USDC to be borrowed is also declared here. It then deploys the `FlashLoan` contract and transfers 5 USDC tokens to the contract from the deployer's account. To request flash loans, the smart contract must hold some of the tokens that you are planning to borrow; these tokens are used to repay the fee. On the V3 version, the fee is a fixed percentage, and you can find the updated fee value on the [Aave docs](https://docs.aave.com/developers/guides/flash-loans#flash-loan-fee). Next, the script checks the USDC balance of the `FlashLoan` contract, this is only for displaying it to the user, but you can easily implement some logic to stop the process if the funds to repay the borrowing fee are too low. It then requests a flash loan of 1,000 USDC tokens. Once the loan is executed, the remaining USDC tokens in the contract are withdrawn. The [Aave documentation](https://docs.aave.com/developers/guides/flash-loans#2.-calling-flashloan-or-flashloansimple) recommends not leaving any funds in the smart contract to avoid possible misuse by an attacker. The script uses the Hardhat development framework and the `ethers.js` library to interact with the blockchain network and the `FlashLoan` contract. It also prints out messages to the console at various points in the script's execution to provide information about the progress of the `FlashLoan` operation. ### Run the flash loan To run the flash loan on the Fuji network, execute the following command in the console from your root directory: ```shell Shell theme={"system"} npx hardhat run --network fuji scripts/deploy.js ``` This command will compile the smart contracts, deploy the FlashLoan contract and execute the operation The result in the console will look like the following: ```shell Shell theme={"system"} Deploying FlashLoan contract... FlashLoan contract deployed at: 0x77609a96E67455EcbBb3d8AD38567511dc587C54 View contract at: https://testnet.snowtrace.io/address/0x77609a96E67455EcbBb3d8AD38567511dc587C54 --------------------------------------------------------------- Transferring 5 USDC to the FlashLoan contract... Transferred 5 USDC tokens to the FlashLoan contract Waiting for 1 block to verify the transfer... --------------------------------------------------------------- USDC balance of the FlashLoan contract is: 5 USDC --------------------------------------------------------------- Requesting a flash loan of 1000 USDC... Flash loan executed! View transaction at: https://testnet.snowtrace.io/tx/0xdcc41e8eec65f798aae643b99036f595137090fa777a729ce15c1a0397b247fa --------------------------------------------------------------- Withdrawing 4.5 USDC from the FlashLoan contract... Funds sent! View transaction at: https://testnet.snowtrace.io/tx/0x759fbd6c513e59f48001566b86d1358c329519f6e6096ffb1748ab3ff0b6f97c ``` > [!NOTE] > > ### Possible compiler warnings > > Note that you might receive two warnings from the Solidity compiler about two `Unused function parameter`. You can ignore the warnings as they do not stop the compiler or the execution of the flash loan. This is happening because the function is being overridden and the parameters are needed to keep the same function's signature. You can see how a completed deployment and flash loan looks like on the Fuji explorer by checking the following transactions: > [card] [Deployed FlashLoan contract](https://testnet.snowtrace.io/address/0x77609a96E67455EcbBb3d8AD38567511dc587C54) > [card] [Execute flash loan](https://testnet.snowtrace.io/tx/0xdcc41e8eec65f798aae643b99036f595137090fa777a729ce15c1a0397b247fa) > [card] [Withdraw funds](https://testnet.snowtrace.io/tx/0x759fbd6c513e59f48001566b86d1358c329519f6e6096ffb1748ab3ff0b6f97c) ## Conclusion This tutorial guided you through setting up Hardhat to work with Chainstack nodes and creating a project to run your own flash loan transaction on the Avalanche network. This tutorial uses a testnet; however, the exact same instructions and sequence will work on the mainnet as well. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Base: Accept B20 payments Source: https://docs.chainstack.com/docs/base-accept-b20-payments Accept B20 token payments on Base through a Chainstack node — tag each payment with an order ID using the memo field, then reconcile from on-chain events. **TLDR:** * B20 is an ERC-20 superset, so any app that already accepts ERC-20 accepts B20 with no code changes. * B20 adds a memo: `transferWithMemo` attaches a `bytes32` order ID to a payment and emits a `Memo` event right after the standard `Transfer`. * You'll tag a payment with an order ID, then reconcile it by reading the `Memo` event through your Chainstack node. ## Main article [B20](/docs/base-b20-token-standard) is Base's native, ERC-20-compatible token standard. Standard `transfer`, `transferFrom`, `approve`, `balanceOf`, and `permit` all work, so existing ERC-20 payment code accepts B20 unchanged. What B20 adds for payments is the **memo**: `transferWithMemo` works like `transfer` but also attaches a `bytes32` reference — such as an order ID — and emits a `Memo` event immediately after the standard `Transfer`. Reading that event ties each on-chain payment to an order in your system, which you do through your Chainstack Base node. This guide tags a payment with an order ID and reconciles it from events. It uses Base Sepolia; the same ABI and event flow works on Base mainnet, where B20 is live. ## Prerequisites * A [Chainstack account](https://console.chainstack.com/) and a Base node — see [deploy a node](/docs/manage-your-networks). * A B20 token to accept — see [Base: deploy a B20 token](/docs/base-tutorial-deploy-a-b20-token). The payer must hold a balance. * [Base's Foundry build](https://github.com/base/base-anvil) (`base-cast`) for the CLI examples, or [viem](https://viem.sh) for the app examples. ## Tag a payment with an order ID `transferWithMemo(to, amount, memo)` sends the tokens and records the order ID. Encode the order ID as `bytes32`: ```bash base-cast theme={"system"} # order ID as bytes32 MEMO=$(base-cast format-bytes32-string "order-42") # pay 10 tokens to the merchant, tagged with the order ID base-cast send $TOKEN "transferWithMemo(address,uint256,bytes32)" \ $MERCHANT 10000000000000000000 $MEMO \ --rpc-url $RPC_URL --private-key $PRIVATE_KEY ``` ```js viem theme={"system"} import { parseUnits, stringToHex } from 'viem'; const ABI = [ { type: 'function', name: 'decimals', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint8' }] }, { type: 'function', name: 'transferWithMemo', stateMutability: 'nonpayable', inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }, { name: 'memo', type: 'bytes32' }], outputs: [{ type: 'bool' }] }, ]; // B20 decimals range from 6 to 18 — read them, don't assume. const decimals = await publicClient.readContract({ address: TOKEN, abi: ABI, functionName: 'decimals' }); const hash = await walletClient.writeContract({ address: TOKEN, abi: ABI, functionName: 'transferWithMemo', args: [MERCHANT, parseUnits('10', decimals), stringToHex('order-42', { size: 32 })], }); ``` > [!NOTE] > > B20 decimals range from 6 to 18 — read `decimals()` rather than assuming 18. ## Reconcile payments through your Chainstack node On the merchant side, read the `Memo` event to match each incoming payment to its order. The event is `Memo(address indexed caller, bytes32 indexed memo)`, and it sits immediately after the payment's `Transfer` in the same transaction. ```bash base-cast theme={"system"} # read recent Memo events on the token through your Chainstack node base-cast logs --address $TOKEN "Memo(address,bytes32)" \ --from-block --rpc-url $RPC_URL # decode an order ID from a Memo topic base-cast parse-bytes32-string 0x6f726465722d3432000000000000000000000000000000000000000000000000 # -> "order-42" ``` ```js viem theme={"system"} import { hexToString, parseEventLogs } from 'viem'; const MEMO_ABI = [{ type: 'event', name: 'Memo', inputs: [ { name: 'caller', type: 'address', indexed: true }, { name: 'memo', type: 'bytes32', indexed: true }, ] }]; // after a payment, pull the order ID from the receipt const receipt = await publicClient.waitForTransactionReceipt({ hash }); const [memo] = parseEventLogs({ abi: MEMO_ABI, logs: receipt.logs, eventName: 'Memo' }); const orderId = hexToString(memo.args.memo, { size: 32 }).replace(/\0+$/, ''); // "order-42" // or watch for incoming payments live publicClient.watchContractEvent({ address: TOKEN, abi: MEMO_ABI, eventName: 'Memo', onLogs: (logs) => logs.forEach((l) => markOrderPaid(hexToString(l.args.memo, { size: 32 }).replace(/\0+$/, ''))), }); ``` > [!NOTE] > > For per-payment confirmation, read the order ID from the transaction receipt — it already carries the `Memo` log. When scanning for past payments with `getLogs`, query confirmed blocks rather than the exact chain head: a load-balanced endpoint can briefly serve a node a block behind, so a range at the head can error until the node catches up. To collect with an allowance instead of a direct transfer, use `transferFromWithMemo` — it emits the same `Memo` event. ## Handle B20-specific reverts A B20 transfer can revert where a plain ERC-20 would not. Surface these so a failed payment is visible, not silent: * **`PolicyForbids`** — the sender or recipient isn't authorized by the token's transfer policy. Most tokens are open by default, but a regulated issuer can gate transfers with an allowlist or blocklist. * **`ContractPaused`** — the issuer paused the token's `TRANSFER` feature. Simulate before sending so you can show the reason instead of a failed transaction: ```js viem theme={"system"} await publicClient.simulateContract({ account, address: TOKEN, abi: ABI, functionName: 'transferWithMemo', args: [MERCHANT, parseUnits('10', decimals), stringToHex('order-42', { size: 32 })], }); ``` ## What you built You accepted a B20 payment tagged with an order ID and reconciled it from on-chain events through your Chainstack node — the foundation of an on-chain checkout that ties payments to orders without trusting off-chain signals. ## See also * [Base B20 token standard](/docs/base-b20-token-standard) * [Base: deploy a B20 token](/docs/base-tutorial-deploy-a-b20-token) # Base B20 token standard Source: https://docs.chainstack.com/docs/base-b20-token-standard B20 is Base's native, ERC-20-compatible token standard, implemented as a precompile with built-in roles, transfer policies, and supply controls. Learn how it works and how to use it on Chainstack. B20 is Base's native token standard, introduced with the [Beryl upgrade](https://docs.base.org/base-chain/specs/upgrades/beryl/overview) (Base mainnet activation June 25, 2026). It is an ERC-20 superset implemented as a native precompile rather than a smart contract: it keeps full ERC-20 compatibility while building roles, transfer policies, supply caps, pausing, memos, and `permit` directly into the chain. You create a B20 token by calling the singleton B20 factory precompile — there is no token contract to write, deploy, or audit. ## B20 vs. ERC-20 | | ERC-20 | B20 | | - | - | - | | Implementation | Smart contract you write and deploy | Native precompile | | Creation | Deploy your own bytecode | One `createB20` call to the factory | | Compliance features | Build them yourself | Roles, transfer policies, freeze-and-seize, pause, supply cap, and memos built in | | Compatibility | — | Full ERC-20 selector parity — a drop-in for wallets, explorers, and indexers | | Cost and throughput | Standard EVM execution | Cheaper and higher-throughput (native) | Because B20 implements the full ERC-20 surface, existing wallets, explorers, and indexers treat a B20 token like any other ERC-20. ## How B20 works All B20 tokens are created through the singleton B20 factory precompile. A single call configures and creates the token in one transaction: ```solidity theme={"system"} createB20(variant, salt, params, initCalls) ``` * `variant` — `ASSET` or `STABLECOIN`. * `salt` — caller-chosen entropy that fixes the token's deterministic address. * `params` — ABI-encoded name, symbol, initial admin, and variant-specific data: decimals for an Asset or a currency code for a Stablecoin. * `initCalls` — an optional batch of configuration calls (grant roles, set the supply cap, bind policies) applied atomically at creation. ### Precompile addresses | Precompile | Address | | - | - | | B20 factory | `0xB20f000000000000000000000000000000000000` | | Policy registry | `0x8453000000000000000000000000000000000002` | | Activation registry | `0x8453000000000000000000000000000000000001` | Token addresses are deterministic and encode the variant in the address itself: `[10-byte B20 prefix][1-byte variant][9-byte hash of deployer and salt]`. Tokens created by the factory start with `0xB200…`; the factory itself is `0xB20f…`. You can predict a token's address before creating it with `getB20Address(variant, deployer, salt)`. ## Variants | Variant | Decimals | Adds | | - | - | - | | **Asset** | 6–18, set at creation | Rebase multiplier, on-chain announcements, batched issuance, extra metadata | | **Stablecoin** | 6, fixed | Immutable ISO-style currency code (for example, `USD`) | ## Built-in features B20 ships a compliance and operations toolkit that an ERC-20 leaves you to build: * **Role-based access control** — `DEFAULT_ADMIN_ROLE`, `MINT_ROLE`, `BURN_ROLE`, `BURN_BLOCKED_ROLE`, `PAUSE_ROLE`, `UNPAUSE_ROLE`, and `METADATA_ROLE`. The Asset variant adds `OPERATOR_ROLE`. A token can be made permanently admin-less. * **Transfer policies** — each token binds allowlist or blocklist policies from the policy registry to per-actor scopes (transfer sender, transfer receiver, mint receiver). \[!WARNING] Every policy scope defaults to `ALWAYS_ALLOW` at creation. An unconfigured B20 is fully open — constrain it intentionally in `initCalls`. * **Freeze-and-seize** — `burnBlocked` burns from a policy-blocked account, the path regulated issuers need. * **Supply cap** — an optional cap enforced on every mint. * **Granular pause** — pause `TRANSFER`, `MINT`, or `BURN` independently, with separate pause and unpause roles. * **Memos** — attach a `bytes32` reference (a payment ID or settlement tag) to transfers, mints, and burns. Each is emitted as a `Memo` event for off-chain reconciliation. * **ERC-2612 `permit`** and **ERC-7572 `contractURI`** are built in. ## Availability B20 ships with the Beryl upgrade: | Network | Beryl activation | | - | - | | Base Sepolia | June 18, 2026 | | Base mainnet | June 25, 2026, 18:00 UTC | Base mainnet and Base Sepolia both serve the B20 precompiles. New token creation remains gated by the Activation Registry at runtime, so check the target variant before broadcasting a factory call. The [deployment tutorial](/docs/base-tutorial-deploy-a-b20-token) includes the preflight command. Chainstack Base nodes serve the same precompiles, so you can create and query B20 tokens through your Chainstack Base endpoint. ## Use B20 on Chainstack **1. Deploy a Base node** [Deploy a Base node](/docs/manage-your-networks) on Chainstack and copy your endpoint. **2. Create a token** Call the B20 factory from a script or directly with `cast`. See the [Deploy a B20 token](/docs/base-tutorial-deploy-a-b20-token) tutorial for the full walkthrough. ## See also * [Base: deploy a B20 token](/docs/base-tutorial-deploy-a-b20-token) — Chainstack tutorial * [B20 token standard specification](https://docs.base.org/base-chain/specs/upgrades/beryl/b20) — Base * [base-std](https://github.com/base/base-std) — Solidity interfaces and helpers for B20 # Base methods Source: https://docs.chainstack.com/docs/base-methods Complete reference of supported Base JSON-RPC methods on Chainstack nodes, including eth, debug, and trace namespace availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Base API call examples](/reference/base-api-reference). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | Deprecated | | eth\_blockNumber | | | | eth\_call | | | | eth\_callMany | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_simulateV1 | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | Returns empty — mempool is private to the sequencer | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_subscribe newFlashblockTransactions | | Flashblocks WS, Base Mainnet only | | eth\_subscribe pendingLogs | | Flashblocks WS, Base Mainnet only | | eth\_subscribe newFlashblocks | | Flashblocks WS, Base Mainnet only | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | erigon\_blockNumber | | | | erigon\_forks | | | | erigon\_getBlockByTimestamp | | | | erigon\_getBlockReceiptsByBlockHash | | | | erigon\_getHeaderByHash | | | | erigon\_getHeaderByNumber | | | | erigon\_getLatestLogs | | | | erigon\_getLogsByHash | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | Not available on Reth | | debug\_getTrieFlushInterval | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | trace\_block | | | | trace\_call | | | | trace\_callMany | | | | trace\_filter | | | | trace\_rawTransaction | | | | trace\_replayBlockTransactions | | | | trace\_replayTransaction | | | | trace\_transaction | | | | optimism\_outputAtBlock | | | | optimism\_syncStatus | | | | optimism\_rollupConfig | | | | optimism\_version | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Stream real-time transactions on Base with Flashblocks Source: https://docs.chainstack.com/docs/base-streaming-transactions-flashblocks Base has no public mempool, so pending-transaction filters return empty. Stream transactions in real time over WebSocket with Flashblocks, with tested Python and JavaScript examples. > [!WARNING] > > Base's [Denim upgrade](https://docs.base.org/upgrades/denim/overview) replaces Flashblocks with canonical 200ms blocks. Denim activated on Base Sepolia on Oct 21, 2026, at 18:00 (UTC), so Flashblocks subscriptions and pending state are no longer available on Base Sepolia. Base Mainnet serves Flashblocks until Denim activates there in November 2026, which is Base's current target. To move your integration to `newHeads`, `logs`, and regular RPC calls, see Base's [migration guide](https://docs.base.org/upgrades/denim/migrate-from-flashblocks). Base has no public mempool — pending transactions are held privately by the sequencer, so `eth_newPendingTransactionFilter` and `eth_subscribe("newPendingTransactions")` return empty. To watch transactions in real time on Base, subscribe to Flashblocks: the sequencer streams pre-confirmed sub-blocks roughly every 200 ms over WebSocket. This guide streams individual transactions, filtered event logs, and the in-progress block, with tested Python and JavaScript. > [!NOTE] > > Flashblocks is enabled by default on Chainstack Base endpoints. For how it works, see [Flashblocks on Base](/docs/flashblocks-on-base). ## Prerequisites * A Base mainnet WebSocket endpoint. On Chainstack, copy the WSS endpoint from your Base node's **Access and credentials** tab — it looks like `wss://base-mainnet.core.chainstack.com/`. The examples below use `YOUR_CHAINSTACK_WSS_ENDPOINT` as a placeholder. * Python 3.8+ with the `websockets` library (`pip install websockets`), or Node.js 22+ (the `WebSocket` client is built in, so there are no dependencies to install). ## Why pending-transaction filters return empty on Base Base is an OP Stack chain with a single sequencer. Transactions go to the sequencer's private mempool and are never gossiped to RPC nodes, so a node has no public pending transactions to report. `eth_newPendingTransactionFilter` and `eth_subscribe("newPendingTransactions")` are accepted but always return empty — this is by design, not a node error, and it is the same on every Base RPC provider. See [Mempool configurations](/docs/mempool-configuration) for the per-protocol breakdown. Flashblocks fills this gap from the other side: instead of unconfirmed mempool transactions, it streams transactions the sequencer has already pre-confirmed into a 200 ms sub-block. ## Stream individual transactions `eth_subscribe("newFlashblockTransactions")` pushes each transaction as the sequencer pre-confirms it — one WebSocket message per transaction, roughly every 200 ms in batches. By default each message is a transaction hash. Pass `true` as a second parameter to receive the full transaction object instead. ```python Python theme={"system"} import asyncio import json import websockets WSS = "YOUR_CHAINSTACK_WSS_ENDPOINT" # wss://base-mainnet.core.chainstack.com/ async def stream_transactions(): async with websockets.connect(WSS) as ws: await ws.send(json.dumps({ "jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newFlashblockTransactions"], # add True for full tx objects })) await ws.recv() # subscription id async for raw in ws: message = json.loads(raw) if message.get("method") == "eth_subscription": tx_hash = message["params"]["result"] print(tx_hash) asyncio.run(stream_transactions()) ``` ```javascript JavaScript theme={"system"} // Node.js 22+ has a built-in WebSocket — no dependencies required. const WSS = "YOUR_CHAINSTACK_WSS_ENDPOINT"; // wss://base-mainnet.core.chainstack.com/ const ws = new WebSocket(WSS); ws.addEventListener("open", () => { ws.send(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_subscribe", params: ["newFlashblockTransactions"], // add true for full tx objects })); }); ws.addEventListener("message", (event) => { const message = JSON.parse(event.data); if (message.method === "eth_subscription") { console.log(message.params.result); } }); ``` You get a continuous stream of transaction hashes: ```text theme={"system"} 0x0cfe50baafea669279002091724b8b63c38e26a63283bbd276c5f4aa98b4df95 0x6f924d44e1b02b71072032b26a263d8ed70e4d943ea4b7aef9d36950b13908a0 0xe9a7f6b0db41bb499326bdf6cc5b67015c0c2e59bc0ab0d798b11ce953576af4 ``` ### Full transaction objects Pass `true` to receive the full transaction with its receipt fields (`logs`, `status`, `gasUsed`) embedded — useful when you want to act on the transaction without a follow-up [`eth_getTransactionReceipt`](/reference/base-gettransactionreceipt) call: ```python theme={"system"} "params": ["newFlashblockTransactions", True] ``` ```json theme={"system"} { "hash": "0xe9a7f6b0db41bb499326bdf6cc5b67015c0c2e59bc0ab0d798b11ce953576af4", "blockNumber": "0x2da0b71", "blockHash": null, "from": "0x065d75c4550f27c2665716569c105a785edf64ee", "to": "0x681e908b8ab57c49c74d770f369754ccc3e1ae09", "value": "0x0", "status": "0x1", "type": "0x2" } ``` `blockHash` is `null` because the transaction is pre-confirmed, not yet sealed into a final block. `blockNumber` is the block the sequencer is currently building. ## Stream filtered event logs `eth_subscribe("pendingLogs")` streams event logs from pre-confirmed transactions, filtered by `address` and `topics` the same way as `eth_getLogs`. This example follows USDC transfers on Base: ```python theme={"system"} import asyncio import json import websockets WSS = "YOUR_CHAINSTACK_WSS_ENDPOINT" USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" TRANSFER = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" async def stream_logs(): async with websockets.connect(WSS) as ws: await ws.send(json.dumps({ "jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["pendingLogs", {"address": USDC, "topics": [TRANSFER]}], })) await ws.recv() # subscription id async for raw in ws: message = json.loads(raw) if message.get("method") == "eth_subscription": log = message["params"]["result"] print(log["transactionHash"], log["data"]) asyncio.run(stream_logs()) ``` Each log carries the standard fields, with `blockHash` set to zero until the block seals: ```json theme={"system"} { "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "topics": [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x000000000000000000000000b7bb38bf6c9f947b1a192199cc2b152cda3bf1ef", "0x0000000000000000000000008f10b468b06c6fd214b65f87778827f7d113f996" ], "data": "0x000000000000000000000000000000000000000000000000000000000550d0b5", "blockNumber": "0x2da0b7d", "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "transactionHash": "0xc5043e57489bbfe79f2002412d958ddca923d3fff432ae..." } ``` ## Stream the in-progress block `eth_subscribe("newFlashblocks")` pushes the block the sequencer is currently building as a standard block object, refreshed roughly every 200 ms. Across updates the block `number` stays the same while `transactions` grows, and `hash` and `stateRoot` are zero until the block seals — it is the `pending` block, streamed: ```python theme={"system"} block = message["params"]["result"] print(int(block["number"], 16), len(block["transactions"])) ``` ```text theme={"system"} 47844267 1 47844267 51 47844267 131 47844267 252 47844267 308 47844268 1 ``` The block number repeats while transactions accumulate, then advances to the next block — each 2-second block is built from about 10 Flashblocks. ## Read pre-confirmed state over HTTP If you need request-response access rather than a stream, the `pending` block tag reflects the latest Flashblock on Chainstack Base endpoints. `eth_getBlockByNumber("pending")`, `eth_getTransactionReceipt`, and `eth_call` with the `pending` tag all return pre-confirmed state: ```bash theme={"system"} curl -s -X POST YOUR_CHAINSTACK_HTTPS_ENDPOINT \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["pending",false]}' ``` The `pending` block is one block ahead of `latest`, with a zero `hash` and fewer transactions, because the sequencer is still building it. ## Production considerations * Pre-confirmations are not final. Flashblock state can change until the 2-second block seals, so for settlement confirm the transaction against `latest` or a finalized block. * Empty hashes are expected. `blockHash` is `null` on streamed transactions and zero on logs and Flashblocks until the block seals — read the final hash from the sealed block. * The stream is best-effort. Flashblocks can arrive in bursts or pause briefly; if the stream stops, fall back to the finalized block from the node. * Re-subscribe on reconnect. A subscription ends when the WebSocket drops, so re-send the `eth_subscribe` request after reconnecting. * Throughput tracks network activity. In tests against Base mainnet, `newFlashblockTransactions` delivered roughly 130–330 transactions per second. ## Related * [Flashblocks on Base](/docs/flashblocks-on-base) — how Flashblocks works * [Mempool configurations](/docs/mempool-configuration) — mempool availability by protocol * [`eth_subscribe newFlashblockTransactions`](/reference/base-subscribe-newflashblocktransactions) * [`eth_subscribe pendingLogs`](/reference/base-subscribe-pendinglogs) * [`eth_subscribe newFlashblocks`](/reference/base-subscribe-newflashblocks) # Base tooling Source: https://docs.chainstack.com/docs/base-tooling Complete Base tooling guide for Chainstack nodes. Use Geth, MetaMask, Hardhat, ethers.js, GraphQL, Foundry, and more to build dApps on Base blockchain. Get started with a [reliable Base RPC endpoint](https://chainstack.com/build-better-with-base/) to use the tools below. ## Geth Interact with your Base node using [Geth](https://geth.ethereum.org/docs/getting-started). 1. Install [Geth](https://github.com/ethereum/go-ethereum). 2. Use `geth attach` command with the node endpoint. ```shell Shell theme={"system"} geth attach YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). 3. Invoke any methods from [Web3 JavaScript API](https://web3js.readthedocs.io/). Example below demonstrates how to get the balance of an address in wei value and convert it to ether value: ```js JavaScript theme={"system"} > web3.fromWei(web3.eth.getBalance("0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae")) 642538.078574759898951277 ``` ## GraphQL You can use GraphQL on a Dedicated Node on the [paid plans](https://chainstack.com/pricing/). ### UI You can query data using the graphical interface. 1. On Chainstack, navigate to your dedicated Base node. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). 2. Hover over **GraphQL IDE URL** and click **Open**. 3. In the graphical interface that opens, run a GraphQL query. Example to get the latest block number: ```graphql GraphQL theme={"system"} { block { number } } ``` ### node.js You can build a web app to query data using node.js and [axios](https://www.npmjs.com/package/axios): ```javascript Javascript theme={"system"} const axios = require("axios"); const main = async () => { try { const result = await axios.post("YOUR_CHAINSTACK_ENDPOINT", { query: ` { block { number } } `, }); console.log(result.data); } catch (error) { console.error(error); } }; main(); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node GraphQL endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * `query` — your GraphQL query. In this case, to get the latest block number. See also [Using GraphQL with EVM-compatible nodes](https://support.chainstack.com/hc/en-us/articles/4409604331161-Using-GraphQL-with-EVM-compatible-nodes). ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Base nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. For a detailed tutorial with Remix IDE, see [Trust fund account with Remix](/docs/ethereum-tutorial-trust-fund-account-with-remix). ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Base nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number: ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Base nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Key Protected theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Base nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Base nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: "YOUR_CHAINSTACK_ENDPOINT", }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: "YOUR_CHAINSTACK_ENDPOINT", user: "USERNAME", password: "PASSWORD", }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Base network ID: * Mainnet: `8453` * Sepolia: `84532` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider( "YOUR_CHAINSTACK_ENDPOINT", NETWORK_ID ); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — Base network ID: * Mainnet: `8453` * Sepolia: `84532` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Brownie 1. Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). 2. Use the `brownie networks add` command with the node endpoint. For example, Base mainnet: ```shell Shell theme={"system"} brownie networks add Base ID name="NETWORK_NAME" host= YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `chainstack-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password * NETWORK\_ID — Base network ID: * Mainnet: `8453` * Sepolia: `84532` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```bash Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```bash Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Base: Deploy a B20 token Source: https://docs.chainstack.com/docs/base-tutorial-deploy-a-b20-token Deploy a B20 token on Base through a Chainstack node — create an Asset token with the B20 factory precompile, mint supply, and verify the balance on-chain. **TLDR:** * You'll deploy a Chainstack Base node and install Base's Foundry build (`base-forge`). * You'll create a B20 Asset token by calling the B20 factory precompile in a single transaction, with admin, minter, and supply cap set atomically. * You'll mint the initial supply and verify the balance on-chain through your Chainstack endpoint. * B20 is an ERC-20 superset, so the result works with any ERC-20 tooling. ## Main article [B20](/docs/base-b20-token-standard) is Base's native token standard, introduced with the Beryl upgrade. Instead of writing and deploying an ERC-20 contract, you create a token by calling the singleton B20 factory precompile — roles, supply caps, pausing, transfer policies, memos, and `permit` are built into the chain. This tutorial creates an Asset token, mints its initial supply, and verifies the balance, all through a Chainstack Base node. It supports Base mainnet and Base Sepolia. Start on Base Sepolia to test the full flow without risking real funds, then use the same factory call on mainnet. ## Prerequisites * A [Chainstack account](https://console.chainstack.com/) and a Base node for your target network — see [deploy a node](/docs/manage-your-networks). Copy the node's HTTPS endpoint from its access and credentials. * [Base's Foundry build](https://github.com/base/base-anvil) (`base-forge`, `base-cast`). Standard `forge` can't simulate the B20 precompiles locally and aborts with `call to non-contract address`. * ETH for gas on your target network. For Base Sepolia, see [Base network faucets](https://docs.base.org/base-chain/network-information/network-faucets). ## Step 1. Install Base's Foundry build ```bash theme={"system"} curl -L https://raw.githubusercontent.com/base/base-anvil/HEAD/foundryup/install | bash base-foundryup --install v1.1.0 ``` > [!NOTE] > > `base-forge` installs alongside standard Foundry without overwriting it. Use `base-forge` and `base-cast` for the commands below. ## Step 2. Set up the project ```bash theme={"system"} base-forge init b20-quickstart && cd b20-quickstart base-forge install base/base-std --no-git ``` Add the remappings and the `base = true` flag to `foundry.toml`, under `[profile.default]`. The `base = true` flag tells `base-forge` to run the B20 precompiles inside its local EVM, so the deploy script can simulate the factory call: ```toml theme={"system"} base = true remappings = [ "base-std/=lib/base-std/src/", "base-std-test/=lib/base-std/test/", ] ``` ## Step 3. Choose a network Create a `.env` file in the project directory with the Chainstack endpoint for your target network: > [tab] Base mainnet > > ```bash theme={"system"} > export RPC_URL="YOUR_CHAINSTACK_BASE_MAINNET_ENDPOINT" > export PRIVATE_KEY="0x..." > export ACCOUNT_ADDRESS="0x..." > export CHAIN_ID="8453" > ``` > > > [!WARNING] > > > > Mainnet transactions spend real ETH and create permanent state. Verify the token name, symbol, admin, roles, supply cap, and salt before broadcasting. > [tab] Base Sepolia > > ```bash theme={"system"} > export RPC_URL="YOUR_CHAINSTACK_BASE_SEPOLIA_ENDPOINT" > export PRIVATE_KEY="0x..." > export ACCOUNT_ADDRESS="0x..." > export CHAIN_ID="84532" > ``` Confirm the node is reachable and the account is funded: ```bash theme={"system"} source .env base-cast balance $ACCOUNT_ADDRESS --rpc-url $RPC_URL ``` > [!TIP] > > The command prints a non-zero balance. This account signs the deploy and the mint, and receives the minted supply. ## Step 4. Verify B20 activation B20 token creation is gated by the Activation Registry at runtime. Check the Asset feature on the selected network before broadcasting: ```bash theme={"system"} REGISTRY=0x8453000000000000000000000000000000000001 ASSET_FEATURE=$(base-cast keccak "base.b20_asset") base-cast call $REGISTRY "isActivated(bytes32)(bool)" $ASSET_FEATURE --rpc-url $RPC_URL # true ``` > [!NOTE] > > For the Stablecoin variant, check `base.b20_stablecoin` instead. A `false` result means a factory call for that variant will revert with `FeatureNotActivated`. ## Step 5. Write the create script The factory's single entry point is `createB20(variant, salt, params, initCalls)`. Use `B20FactoryLib` to encode `params` and `initCalls` in the canonical form the precompile expects. Create `script/CreateToken.s.sol`: ```solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Script, console} from "forge-std/Script.sol"; import {B20Constants} from "base-std/lib/B20Constants.sol"; import {B20FactoryLib} from "base-std/lib/B20FactoryLib.sol"; import {IB20Factory} from "base-std/interfaces/IB20Factory.sol"; import {StdPrecompiles} from "base-std/StdPrecompiles.sol"; contract CreateToken is Script { function run() external returns (address token) { // For the quickstart, one account is admin + minter. address account = vm.envAddress("ACCOUNT_ADDRESS"); bytes32 salt = keccak256("my-first-b20"); // Name, symbol, initial DEFAULT_ADMIN_ROLE holder, decimals (6-18). bytes memory params = B20FactoryLib.encodeAssetCreateParams("My Token", "MYT", account, 18); // Configuration applied atomically at creation. bytes[] memory initCalls = new bytes[](2); initCalls[0] = B20FactoryLib.encodeGrantRole(B20Constants.MINT_ROLE, account); initCalls[1] = B20FactoryLib.encodeUpdateSupplyCap(1_000_000e18); vm.startBroadcast(); token = StdPrecompiles.B20_FACTORY.createB20(IB20Factory.B20Variant.ASSET, salt, params, initCalls); vm.stopBroadcast(); console.log("B20 token created at:", token); } } ``` > [!NOTE] > > Asset decimals are fixed at creation and must be in `[6, 18]`. The supply cap is optional; the no-cap sentinel is `type(uint128).max`. > [accordion] Want a stablecoin instead? > > Use the `STABLECOIN` variant and its params encoder. A stablecoin fixes decimals at 6 and carries an immutable ISO currency code (uppercase `A`–`Z`): > > ```solidity theme={"system"} > bytes memory params = B20FactoryLib.encodeStablecoinCreateParams("My USD", "MUSD", account, "USD"); > > token = StdPrecompiles.B20_FACTORY.createB20(IB20Factory.B20Variant.STABLECOIN, salt, params, initCalls); > ``` > > Roles, supply cap, minting, and verification work identically. ## Step 6. Deploy through your Chainstack node ```bash theme={"system"} source .env base-forge script script/CreateToken.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast ``` On success the script logs the new token's address, which starts `0xB200…`: ```text theme={"system"} == Logs == B20 token created at: 0xB200... ``` Save the address for the next step: ```bash theme={"system"} TOKEN_ADDRESS=$(jq -er '.returns.token.value' \ broadcast/CreateToken.s.sol/$CHAIN_ID/run-latest.json) \ && echo "export TOKEN_ADDRESS=$TOKEN_ADDRESS" >> .env \ && source .env \ && echo "TOKEN_ADDRESS=$TOKEN_ADDRESS" ``` ## Step 7. Mint and verify Minting requires `MINT_ROLE`, which `initCalls` granted to your account: ```bash theme={"system"} base-cast send $TOKEN_ADDRESS "mint(address,uint256)" $ACCOUNT_ADDRESS 1000000000000000000000 \ --rpc-url $RPC_URL --private-key $PRIVATE_KEY base-cast call $TOKEN_ADDRESS "balanceOf(address)(uint256)" $ACCOUNT_ADDRESS --rpc-url $RPC_URL # 1000000000000000000000 [1e21] ``` > [!TIP] > > The token holds minted supply on-chain. Search `$TOKEN_ADDRESS` on [basescan.org](https://basescan.org) for mainnet or [sepolia.basescan.org](https://sepolia.basescan.org) for Base Sepolia. ## Alternative: deploy directly with `cast` Because your Chainstack Base node runs the B20 precompiles, you can create a basic token without `base-forge` — standard Foundry's `cast` is enough, since the factory call executes on the node rather than in a local simulation: ```bash theme={"system"} # Encode the Asset params (version 1, name, symbol, admin, decimals) PARAMS=$(cast abi-encode "f((uint8,string,string,address,uint8))" \ "(1,My Token,MYT,$ACCOUNT_ADDRESS,18)") # Call the factory; initCalls is an empty array here cast send 0xB20f000000000000000000000000000000000000 \ "createB20(uint8,bytes32,bytes,bytes[])(address)" \ 0 $(cast keccak "my-first-b20") "$PARAMS" "[]" \ --rpc-url $RPC_URL --private-key $PRIVATE_KEY ``` Predict the token's address with a read call (no gas): ```bash theme={"system"} cast call 0xB20f000000000000000000000000000000000000 \ "getB20Address(uint8,address,bytes32)(address)" \ 0 $ACCOUNT_ADDRESS $(cast keccak "my-first-b20") --rpc-url $RPC_URL ``` > [!NOTE] > > The `cast` path creates a minimal token (no roles or supply cap). To configure roles, the supply cap, and policies atomically at creation, use the `base-forge` script above with `B20FactoryLib`, which also guarantees the canonical encoding the precompile requires. ## What you built In this tutorial you: * Created a B20 Asset token with a single `createB20` call through your Chainstack Base node * Configured its admin, minter, and supply cap atomically with `initCalls` * Minted supply and verified the balance on-chain You did all of this without writing, deploying, or auditing a token contract. ## Next steps * Learn the full standard in [Base B20 token standard](/docs/base-b20-token-standard). * Gate transfers or mints with policy registry allowlists and blocklists, add granular pause, or issue a stablecoin variant. See the [B20 specification](https://docs.base.org/base-chain/specs/upgrades/beryl/b20). # Base: Deploy an ERC-721 contract with Hardhat Source: https://docs.chainstack.com/docs/base-tutorial-deploy-an-erc-721-contract-with-hardhat Deploy an ERC-721 NFT smart contract on Base Sepolia testnet using Hardhat, OpenZeppelin, and a Chainstack node endpoint for contract verification. **TLDR:** * You’ll bridge Sepolia ETH to the Base Sepolia Testnet, taking advantage of Base’s L2 benefits. * You’ll configure a Hardhat project and deploy an ERC-721 NFT contract to Base Testnet. * You’ll set environment variables for your Chainstack endpoint and private key. * You’ll verify and explore your contract on the Base Sepolia block explorer. ## Main article Base is an Ethereum layer 2 (L2) solution built atop the Ethereum blockchain and incubated within Coinbase. As an L2 chain, Base provides enhanced security, stability, and scalability, instrumental for the efficient operation of decentralized applications. It supports the deployment of any Ethereum Virtual Machine (EVM) codebase, allowing for an efficient transition of users and assets from Ethereum layer 1 (L1), Coinbase, and other interoperable blockchains. Using Base's L2 solution substantially reduces the cost associated with operating within the EVM environment. Base provides early access to advanced Ethereum features such as account abstraction (ERC-4337), developer APIs for gasless transactions, and smart contract wallets, broadening the scope of possibilities for developers. Base launched on Optimism's open-source [OP Stack](https://github.com/ethereum-optimism/optimism), meaning it ran Optimism's node software and shared the Superchain's upgrade schedule. With the Azul upgrade (mainnet May 28, 2026), Base switched to its own node clients — [`base-reth-node`](https://github.com/base/base) (execution) and [`base-consensus`](https://github.com/base/base) (consensus) — and now sets its own upgrade schedule. This is an under-the-hood infrastructure change: Base remains fully EVM-compatible, so you deploy and verify contracts exactly as you would on Ethereum. As a product scaled by Coinbase, Base facilitates the integration of decentralized applications with Coinbase's extensive suite of products and distribution channels. This integration includes seamless interfacing with Coinbase, efficient fiat onramps, and access to a vast user base within the Coinbase ecosystem. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Base RPC endpoint](https://chainstack.com/build-better-with-base/) * [node.js](https://nodejs.org/en) as the JavaScript framework ## Dependencies * Hardhat: ^2.17.0 * @nomicfoundation/hardhat-toolbox: ^3.0.0 * dotenv: ^16.0.3 ## Overview In this tutorial, we will go over how to bridge funds between Ethereum Sepolia and the Base Sepolia testnets and deploy a smart contract to the Base Sepolia Testnet using Hardhat. Here is a brief overview of the tutorial: 1. With Chainstack, create a public chain project. 2. With Chainstack, join the Base Testnet. 3. With Chainstack, access your nodes' credentials. 4. Bridge funds between the Sepolia Testnet and the Base Testnet. 5. Create a Hardhat project using node.js. 6. Install the required dependencies. 7. Create a `.env` file to store the secrets. 8. Edit the Hardhat config file. 9. Write the smart contract. 10. Write and run the deployment script. 11. Deploy smart contracts to the Base Testnet. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Ethereum Sepolia Testnet and the Base Testnet See [Join a public network](/docs/manage-your-networks). ### Get your Base Testnet node endpoint See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Fund your wallet Before diving into the project, make sure to top up your wallet with Sepolia ether. You can use the Chainstack Sepolia faucet]\([https://faucet.chainstack.com](https://faucet.chainstack.com)). ### Bridging Sepolia ETH to Base Testnet Using the [Base bridge](https://bridge.base.org/), we can easily move assets between Ethereum (L1) and Base (L2). To bridge assets between L1 and L2, the user has to lock up any amount of those assets in the original network using the Base bridge. An equivalent amount of wrapped tokens are then minted in the other chain. ### Create a Hardhat project Create a new directory for your project, then run the following from a terminal: ```shell Shell theme={"system"} npm init --y ``` This will create a new Node project. > [!NOTE] > > Check out [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project) to learn more about creating a Node project. Then run the following to install Hardhat: ```shell Shell theme={"system"} npm install --save-dev hardhat ``` When Hardhat is installed, initialize a Hardhat project: ```shell Shell theme={"system"} npx hardhat ``` This will launch the Hardhat CLI, which will prompt you to configure a starter project. For this tutorial, create a JavaScript project and click **Yes** on all the prompts Hardhat offers. ### Set up environment variables This project uses the [dotenv](https://github.com/motdotla/dotenv) package to use environment variables safely. Run the following command in your root directory to install the dotenv package: ```shell Shell theme={"system"} npm install dotenv ``` In your project's root directory, create a new file and name it `.env`. Here, you will set up the environment variables for your Chainststack endpoint and your wallet's private key. ```sh .env theme={"system"} CHAINSTACK_ENDPOINT="YOUR_CHAINSTACK_ENDPOINT" PRIVATE_KEY="YOUR_WALLET_PRIVATE_KEY" ``` Save the file after you add your information. ### Edit the Hardhat configuration file You will find a file named `hardhat.config.js` in the root directory. This file is used to configure various settings for your Hardhat projects, such as the network you want to deploy your contracts on, the compilers you want to use, and the plugins you want to enable. Delete the default code in the file and replace it with the following: ```js hardhat.config.js theme={"system"} require("@nomicfoundation/hardhat-toolbox"); require('dotenv').config(); module.exports = { solidity: "0.8.18", defaultNetwork: "base_testnet", networks: { base_testnet: { url: `${process.env.CHAINSTACK_ENDPOINT}`, accounts: [process.env.YOUR_PRIVATE_KEY] }, }, }; ``` Let's break down what each part of the file does: * `require("@nomicfoundation/hardhat-toolbox");` imports the Hardhat Toolbox plugin, which provides several useful tools and utilities for Hardhat projects. * `require("dotenv").config();` loads environment variables from a `.env` file using the `dotenv` package. * `module.exports = { ... }` exports a JavaScript object containing the configuration for the Hardhat project. * `solidity: "0.8.18",` sets the Solidity compiler version to 0.8.18. * `networks: { ... }` defines the network configurations for the Hardhat project. * `defaultNetwork: { ... }` defines the default network that Hardhat will use. * `base_testnet: { ... }` defines the configuration for the `base` network. * `url: ${process.env.CHAINSTACK_ENDPOINT},` sets the RPC URL for the Base network. * `accounts: [process.env.YOUR_PRIVATE_KEY],` sets the accounts for the `base` network using the `PRIVATE_KEY` environment variable. This will allow the Hardhat project to deploy contracts and interact with the Base Testnet using the specified private key. ### Develop an ERC-721 smart contract with OpenZeppelin Now it's time to make our NFT smart contract. You can use the example from here or the [OpenZeppelin Wizard](https://wizard.openzeppelin.com/#erc721) to create a new one. In the root directory, you will find a directory named `contracts`. Create a new file named `CBS.sol`, and paste the following code inside it: ```sol CBS.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract ChainBase is ERC721, ERC721Burnable, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; constructor() ERC721("ChainBase", "CBS") {} function safeMint(address to) public onlyOwner { uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); } } ``` This smart contract named `ChainBase`is an implementation of an ERC-721 token. ERC-721 is a standard for non-fungible tokens on the Ethereum blockchain, meaning each token has a unique value and is not interchangeable with any other token. ERC-721 tokens are often used for digital collectibles or assets. Then install the OpenZeppelin package in your project: ```shell Shell theme={"system"} npm install @openzeppelin/contracts ``` ### Create and run the deployment script In the `scripts` directory inside the root of your project, you will find a file named `deploy.js`. Replace its content with the following: ```js deploy.js theme={"system"} const { ethers } = require("hardhat"); async function main() { console.log("Deploying your contract, please Wait..."); const NFT = await ethers.deployContract("ChainBase"); await NFT.waitForDeployment(); console.log("NFT Contract deployed to:", NFT.target); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); }); ``` This is a simple deploy script that deploys the `ChainBase` smart contract to the Base Sepolia Testnet, and returns the address of the newly deployed contract in the terminal. You can search for your contract on the [Base Sepolia explorer](https://base-sepolia.blockscout.com/). To run this script, execute the following command in the terminal: ```shell Shell theme={"system"} npx hardhat run --network base_testnet scripts/deploy.js ``` You will get a similar response in the console: ```shell Shell theme={"system"} $ npx hardhat run --network base_testnet scripts/deploy.js Deploying your contract, please Wait... NFT Contract deployed to: 0x2CfAf4441995344451F10054eE25d4df286768F7 ``` ## Conclusion This tutorial guided you through bridging funds between Ethereum Sepolia Testnet and Base Sepolia Testnet. We also deployed a smart contract to the Base Sepolia Testnet using Hardhat. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Berachain: On-chain data quickstart with Python Source: https://docs.chainstack.com/docs/berachain-on-chain-data-quickstart-with-python Fetch and analyze Berachain on-chain data using Python and web3.py with a Chainstack RPC endpoint for blocks, transactions, and contract interactions. **TLDR:** * Demonstrates connecting to Berachain Mainnet using Python and Web3.py via Chainstack endpoints * Shows how to query ERC-20 tokens, core Berachain tokens (BGT, HONEY), and monitor live transfers * Covers Proof-of-Liquidity concepts and real-time blockchain data monitoring * Provides practical scripts for read-only blockchain interactions without requiring private keys ## Overview Berachain is an innovative EVM-compatible blockchain that implements Proof-of-Liquidity (PoL) consensus. Unlike traditional Proof-of-Stake systems, Berachain's PoL mechanism aligns network incentives through BGT (Berachain Governance Token) emissions and liquidity provision. This tutorial will walk you through building practical Python applications that interact with Berachain Mainnet using Chainstack's reliable RPC infrastructure. We'll explore everything from basic connectivity to advanced monitoring capabilities, all while maintaining a read-only approach that's perfect for learning and experimentation. ## Prerequisites * [web3.py](https://web3py.readthedocs.io/) * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Berachain RPC endpoint](https://chainstack.com/build-better-with-berachain/) ## Understanding Berachain architecture Before writing code, let's understand key Berachain concepts: ### Core tokens * **BERA** - Native gas token, similar to ETH on Ethereum * **BGT** - Governance token that powers Proof-of-Liquidity * **HONEY** - Algorithmic stablecoin native to Berachain ### Key contracts * **WBERA** (`0x6969696969696969696969696969696969696969`) - Wrapped BERA token * **BGT** (`0x656b95E550C07a9ffe548bd4085c72418Ceb1dba`) - Governance token * **HONEY** (`0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce`) - Stablecoin * **BeraChef** (`0xdf960E8F3F19C481dDE769edEDD439ea1a63426a`) - Proof-of-Liquidity manager ## Basic connection and network information Let's start with a simple connection script that verifies our setup and displays basic network information. Create `connection.py`: ```python theme={"system"} from web3 import Web3 import textwrap # Connect to Berachain Mainnet via Chainstack RPC_URL = "YOUR_CHAINSTACK_ENDPOINT" w3 = Web3(Web3.HTTPProvider(RPC_URL)) assert w3.is_connected(), "❌ Check your RPC URL 🔌" # Display connection info print(textwrap.dedent(f""" ✅ Connected to Berachain Mainnet • Chain ID : {w3.eth.chain_id} • Latest Block: {w3.eth.block_number} • Base Fee : {w3.from_wei(w3.eth.fee_history(1,'latest')['baseFeePerGas'][-1],'gwei'):.3f} Gwei """)) ``` Key concepts demonstrated: * Establishing Web3 connection to Berachain * Verifying connectivity with `is_connected()` * Querying basic network information (chain ID, latest block, gas fees) * Working with wei-to-gwei conversions ## ERC-20 token interactions Next, let's interact with ERC-20 tokens through node calls, starting with WBERA (Wrapped BERA). Create `wbera_query.py`: ```python theme={"system"} from web3 import Web3 from eth_utils import to_checksum_address # Connect to Berachain Mainnet via Chainstack RPC_URL = "YOUR_CHAINSTACK_ENDPOINT" w3 = Web3(Web3.HTTPProvider(RPC_URL)) # WBERA token address on Mainnet wbera = to_checksum_address("0x6969696969696969696969696969696969696969") def get_token_info(token_address): """Get basic token information using low-level calls""" # Query token symbol symbol_data = w3.eth.call({ "to": token_address, "data": w3.keccak(text="symbol()")[:4] }) # Query token name name_data = w3.eth.call({ "to": token_address, "data": w3.keccak(text="name()")[:4] }) # Query decimals decimals_data = w3.eth.call({ "to": token_address, "data": w3.keccak(text="decimals()")[:4] }) # Query total supply supply_data = w3.eth.call({ "to": token_address, "data": w3.keccak(text="totalSupply()")[:4] }) # Parse responses symbol = symbol_data.rstrip(b"\x00").decode() name = name_data.rstrip(b"\x00").decode() decimals = int.from_bytes(decimals_data, "big") supply = int.from_bytes(supply_data, "big") / (10 ** decimals) return { "symbol": symbol, "name": name, "decimals": decimals, "total_supply": supply } # Get WBERA information wbera_info = get_token_info(wbera) print(f"🪙 Token: {wbera_info['name']} ({wbera_info['symbol']})") print(f"📊 Decimals: {wbera_info['decimals']}") print(f"📈 Total Supply: {wbera_info['total_supply']:,.2f}") ``` Key concepts demonstrated: * Using `w3.keccak()` to generate function selectors * Making contract calls with `w3.eth.call()` * Parsing contract responses and handling different data types * Working with ERC-20 standard functions ## Core Berachain tokens Let's explore Berachain's core tokens: BGT and HONEY. ```python theme={"system"} from web3 import Web3 from eth_utils import to_checksum_address # Connect to Berachain Mainnet via Chainstack RPC_URL = "YOUR_CHAINSTACK_ENDPOINT" w3 = Web3(Web3.HTTPProvider(RPC_URL)) # Core Berachain token addresses bgt = to_checksum_address("0x656b95E550C07a9ffe548bd4085c72418Ceb1dba") # BGT - governance/PoL honey = to_checksum_address("0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce") # HONEY - stablecoin def get_token_supply(token_address): """Get total supply for a token""" sig_total = w3.keccak(text="totalSupply()")[:4] supply_raw = int.from_bytes(w3.eth.call({"to": token_address, "data": sig_total}), "big") return supply_raw / 1e18 # Query total supplies bgt_supply = get_token_supply(bgt) honey_supply = get_token_supply(honey) print("🐻 Berachain Core Token Supplies") print("=" * 35) print(f"🗳️ BGT (Governance): {bgt_supply:,.0f}") print(f"🍯 HONEY (Stablecoin): {honey_supply:,.0f}") # Additional insights print(f"\n📊 Insights:") print(f"• BGT powers Proof-of-Liquidity consensus") print(f"• HONEY is overcollateralized by BERA") print(f"• Current BGT/HONEY ratio: {bgt_supply/honey_supply:.3f}") ``` Key concepts demonstrated: * Querying multiple token contracts efficiently * Understanding Berachain's tokenomics * Creating reusable functions for common operations ## Live transfer monitoring One of the most powerful features is monitoring live blockchain activity. Let's track WBERA transfers in real-time. ```python theme={"system"} from web3 import Web3 from eth_utils import to_checksum_address import time # Connect to Berachain Mainnet via Chainstack RPC_URL = "YOUR_CHAINSTACK_ENDPOINT" w3 = Web3(Web3.HTTPProvider(RPC_URL)) # WBERA token setup wbera = to_checksum_address("0x6969696969696969696969696969696969696969") decimals = 18 print("🐻 WBERA Live Transfer Monitor") print("=" * 40) print(f"📍 WBERA Contract: {wbera}") # ERC-20 Transfer event signature TRANSFER_SIG = w3.keccak(text="Transfer(address,address,uint256)").hex() try: latest = w3.eth.block_number print(f"🔗 Latest Block: {latest}") # Query recent transfer logs block_range = 1000 from_block = latest - block_range print(f"🔍 Scanning blocks {from_block} to {latest} ({block_range} blocks)") logs = w3.eth.get_logs({ "fromBlock": from_block, "toBlock": latest, "address": wbera, "topics": [TRANSFER_SIG] }) print(f"✅ Found {len(logs)} WBERA transfers in the last {block_range} blocks") if logs: print("\n📋 Recent transfers:") for i, lg in enumerate(logs[-5:]): # Show 5 most recent try: # Extract addresses from topics from_addr = "0x" + lg["topics"][1].hex()[-40:] to_addr = "0x" + lg["topics"][2].hex()[-40:] # Extract amount from data if isinstance(lg["data"], str): data_hex = lg["data"][2:] if lg["data"].startswith('0x') else lg["data"] amount = int(data_hex, 16) / 10**decimals else: amount = int.from_bytes(lg["data"], "big") / 10**decimals # Get block info for timestamp block_info = w3.eth.get_block(lg["blockNumber"]) timestamp = time.strftime('%H:%M:%S', time.localtime(block_info["timestamp"])) print(f" {i+1}. Block {lg['blockNumber']} @ {timestamp}") print(f" {from_addr[:10]}...{from_addr[-6:]} → {to_addr[:10]}...{to_addr[-6:]}") print(f" 💰 {amount:.6f} WBERA") print() except Exception as e: print(f" ❌ Error parsing transfer {i+1}: {e}") except Exception as e: print(f"❌ Error querying transfers: {e}") ``` Key concepts demonstrated: * Event signature generation with `w3.keccak()` * Querying historical logs with `w3.eth.get_logs()` * Parsing event data from topics and data fields * Real-time blockchain monitoring techniques ## Advanced dashboard with rich formatting Finally, let's create a beautiful, comprehensive dashboard using the Rich library. ```python theme={"system"} from web3 import Web3 from eth_utils import to_checksum_address from rich.console import Console from rich.table import Table from rich.panel import Panel import time # Connect to Berachain Mainnet via Chainstack RPC_URL = "YOUR_CHAINSTACK_ENDPOINT" # Initialize console = Console() w3 = Web3(Web3.HTTPProvider(RPC_URL)) # Contract addresses wbera = to_checksum_address("0x6969696969696969696969696969696969696969") bgt = to_checksum_address("0x656b95E550C07a9ffe548bd4085c72418Ceb1dba") honey = to_checksum_address("0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce") def get_network_info(): """Get basic network information""" fee_history = w3.eth.fee_history(1, 'latest') return { 'chain_id': w3.eth.chain_id, 'block_number': w3.eth.block_number, 'base_fee': w3.from_wei(fee_history['baseFeePerGas'][-1], 'gwei') } def get_token_supply(address): """Get total supply for a token""" sig_total = w3.keccak(text="totalSupply()")[:4] supply_raw = int.from_bytes(w3.eth.call({"to": address, "data": sig_total}), "big") return supply_raw / 1e18 def main(): console.print("\n🐻 [bold cyan]Berachain Mainnet Dashboard[/bold cyan] 🚀", justify="center") # Network Info Panel network = get_network_info() network_text = f""" [green]✅ Connected to Berachain Mainnet[/green] [blue]• Chain ID:[/blue] {network['chain_id']} [blue]• Latest Block:[/blue] {network['block_number']:,} [blue]• Base Fee:[/blue] {network['base_fee']:.3f} Gwei """ console.print(Panel(network_text.strip(), title="🔗 Network Status", border_style="green")) # Token Supplies Table table = Table(title="📊 Token Supplies") table.add_column("Token", style="cyan", no_wrap=True) table.add_column("Symbol", style="magenta") table.add_column("Total Supply", style="green", justify="right") table.add_column("Type", style="yellow") # Get token supplies bgt_supply = get_token_supply(bgt) honey_supply = get_token_supply(honey) table.add_row("BGT", "BGT", f"{bgt_supply:,.0f}", "Governance/PoL") table.add_row("HONEY", "HONEY", f"{honey_supply:,.0f}", "Stablecoin") console.print(table) # Footer console.print("\n[dim]🔄 Refresh this dashboard by running the script again![/dim]", justify="center") if __name__ == "__main__": main() ``` ## Conclusion This tutorial provided a comprehensive introduction to Berachain development using Python and Chainstack's reliable infrastructure. You've learned how to: * Connect to Berachain Mainnet using web3.py * Query ERC-20 tokens and core Berachain assets * Monitor live blockchain activity and transfers * Build beautiful terminal dashboards with Rich * Understand Berachain's unique Proof-of-Liquidity system The read-only approach demonstrated here is perfect for learning, monitoring, and building analytics applications. As you continue your Berachain development journey, these foundational skills will serve as the building blocks for more complex applications. > [!NOTE] > > ### See also > > > [card] [Berachain Documentation](https://docs.berachain.com) > > > [card] [Web3.py Documentation](https://web3py.readthedocs.io/) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Berachain tooling Source: https://docs.chainstack.com/docs/berachain-tooling Set up MetaMask, Hardhat, Foundry, and development tools to build and deploy smart contracts on Berachain through your Chainstack RPC node endpoints. Get started with a [reliable Berachain RPC endpoint](https://chainstack.com/build-better-with-berachain/) to use the tools below. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Berachain nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Berachain nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.JsonRpcProvider(urlInfo.url, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * NETWORK\_ID — Berachain network ID: * Mainnet: `80094` * Bepolia Testnet: `80069` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Berachain nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Best practices for error handling in API requests Source: https://docs.chainstack.com/docs/best-practices-for-error-handling-in-api-requests Implement robust error handling and load balancing across multiple Chainstack RPC endpoints using JavaScript to build reliable blockchain applications. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR** * Explains how Chainstack’s [Global Node](/docs/global-elastic-node) feature can boost your DApp’s reliability by balancing traffic automatically based on user location. * Demonstrates a JavaScript load balancer script using multiple Chainstack endpoints, distributing requests across different regions to avoid single-point failures. * Shows examples with both web3.js and ethers.js, detailing how to fail over to the next endpoint if one fails. * Concludes that both Global Nodes and custom load-balancing approaches help ensure your blockchain app can handle high traffic and unexpected downtimes. ## Main article In the world of API requests, error handling is not just a best practice—it's a necessity. Effectively handling HTTP status codes is crucial for ensuring smooth and reliable communication between clients and servers. Whether you're a seasoned developer or just starting out, understanding how to automate the retrieval of response codes from any request can help you build more robust applications, implement effective retry logic, and create comprehensive error backlogs. This guide will walk you through the best practices for error handling in API requests, with a focus on handling HTTP status codes and implementing retry logic. ### Importance of handling HTTP status codes HTTP status codes are the server's way of telling the client about the status of the operation it requested. They play a vital role in API requests as they can indicate success, failure, or need for further action. By properly handling these status codes, you can ensure your application responds appropriately to each possible outcome of an API request. This can significantly enhance the user experience and the overall performance of your application. ### Overview of HTTP status codes HTTP status codes are grouped into five major categories, each representing a specific class of responses. These include: * **1xx** (informational) — the request has been received and understood, and the client should continue the process. * **2xx** (success) — the action was successfully received, understood, and accepted. * **3xx** (redirection) — the client must take additional action to complete the request. * **4xx** (client errors) — the request contains bad syntax or cannot be fulfilled. * **5xx** (server errors) — the server failed to fulfill an apparently valid request. Understanding these status codes and how to handle them is the first step toward effective error handling in API requests. In the following sections, we'll dive deeper into how to retrieve and handle these status codes in your Python code and how to implement a retry logic for temporary failures. ## Practical example Before we can handle HTTP status codes, we first need to know how to retrieve them. In Python, this can be done using the `status_code` attribute of the response object. This attribute holds the status code that the server returned for the HTTP request.  Let's consider a scenario where we're interested in getting the logs of the latest block. We can do this using the following Python code: ```python Python theme={"system"} import json import requests node_url = 'YOUR_CHAINSTACK_ENDPOINT' headers = { "Content-Type": "application/json" } payload = { "jsonrpc": "2.0", "method": "eth_getLogs", "params": [ { "fromBlock": "latest", "toBlock": "latest", } ], "id": 1 } response = requests.post(node_url, headers=headers, json=payload) print(response.text) ``` If the above code is successfully run, it will output the logs for the latest block. This means that the response code received by the client (you, who made the request) was equal to 200. To retrieve the response code of the request presented above, we can simply use the following: ```python Python theme={"system"} # Considering this request: response = requests.post(node_url, headers=headers, json=payload) # Here's how we can get the response code for such request: response_code = response.status_code print('status code:', response_code) ``` This will store the HTTP status code of the response in the `response_code` variable. Now that we know how to retrieve the status code of a response, we can move on to handling these codes and analyzing error responses. ### Analyzing error responses In addition to dealing with response codes, it's also important to analyze other information in the response to understand and deal with errors. This can be particularly useful when the server returns a 4xx or 5xx status code, indicating a client or server error. For instance, let's consider a possible response for a `eth_getLogs` request that contains an error content in the output: ``` {"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"failed to get logs for block #192001 (0xa388fd..65beb8)"}} ``` In this case, the server returned a JSON object with an `error` field, which contains further information about the error that occurred. We can extract this information in our Python code like this: ``` response = requests.post(node_url, headers=headers, json=payload) response_code = response.status_code print('status code:', response_code) if response_code == 200: response_data = json.loads(response.text) if 'error' in response_data: error_content = response_data['error'] print('Error:', error_content) ``` In this code, we first check if the response's status code is 200, indicating a successful request. If it is, we parse the JSON content of the response and check if it contains an `error` field. If it does, we store the content of this field in the `error_content` variable. This information can be used to implement a retry logic and keep a record of whenever those errors happen in time. ### Importance of implementing retry logic Incorporating retry logic into your code can significantly enhance the reliability of your application. By leveraging the tools and techniques we have discussed, you can implement a retry mechanism that automatically handles temporary failures and retries the request when necessary. This can reduce the impact of temporary failures on you, increase system availability, and ensure data integrity. In the worst-case scenario, this enables you to keep track of the errors you face with precise timestamps for such incidents. Implementing retry logic is particularly important when dealing with 5xx server errors. These errors indicate a problem with the server and are often temporary. By implementing a retry logic, your application can automatically retry the request after a short delay, giving the server a chance to recover. This can significantly improve the user experience by reducing the number of failed requests the user has to deal with. ### Implementing retry logic in code Now that we understand the importance of implementing retry logic let's dive into how to implement it in our Python code. Our retry logic aims to automatically retry the request when a temporary failure occurs. This can be a 5xx server error, a connection error, or any other type of error that we deem temporary. Here's an example of how to implement retry logic in Python using both the response code and error messages to determine when to retry a request: ``` import json import time import requests node_url = 'YOUR_CHAINSTACK_ENDPOINT' headers = { "Content-Type": "application/json" } payload = { "jsonrpc": "2.0", "method": "eth_getLogs", "params": [ {"fromBlock": "latest", "toBlock": "latest"} ], "id": 1 } # Max retries retries = 5 delay = 1 # Seconds def get_logs(): for i in range(retries): response = requests.post(node_url, headers=headers, json=payload) if response.status_code != 200: print(f"Request failed with status code {response.status_code}. Retrying attempt {i+1}...") time.sleep(delay) continue response_data = json.loads(response.text) if 'error' in response_data: print(f"There was an error in attempt {i+1}: {response_data['error']}") time.sleep(delay) continue logs = response_data.get("result", []) if len(logs) > 0: block_number = int(logs[0]['blockNumber'], 16) print(f"Block number from eth_getLogs call: {block_number} in attempt {i+1}") print('Processing the event logs...') return print(f"Result is empty for this block in attempt {i+1}") time.sleep(delay) get_logs() ``` The retry logic is governed by a for loop that runs up to a predefined maximum number of attempts (the `retries` variable). For each iteration of the loop, which represents an attempt to fetch the logs, the code performs the following steps: 1. A `POST` request is sent to the Ethereum node with the defined headers and payload. 2. If the HTTP status code of the response is not 200 (indicating a successful request), the code prints a message indicating that the request failed and the current attempt number. Then, it waits for the specified delay period (the `delay` variable) before proceeding to the next iteration of the loop. This delay provides a pause before retrying, which can be helpful in cases where the server might be temporarily overloaded or experiencing other transient issues. 3. If the status code is 200 (indicating a successful request), the response is parsed into JSON format and checked for an `error` key. If `error` is present, the code prints a message with the error details and the current attempt number, waits for the specified delay period, and proceeds to the next iteration of the loop. This handles cases where the request was technically successful, but the response indicates an error condition that might be resolved with a retry. 4. If there's no `error` key in the response but the `result` is empty, the code prints a message indicating this fact and the current attempt number, waits for the specified delay period, and proceeds to the next iteration of the loop. This handles situations where the request was successful and didn't result in an error but didn't provide any logs to process. If the function hasn't returned by the end of the loop (meaning it hasn't successfully processed a set of logs), it will have retried the request the maximum number of times. At this point, the function will exit, and the code will continue, effectively giving up on fetching logs after exhausting all the allowed attempts. ### Using response code and error messages in retry logic As you can see in the above example, we use both the response code and error messages in our retry logic. The response code allows us to determine whether the request was successful, while the error messages provide more detailed information about what went wrong. By using both of these pieces of information, we can make our retry logic more intelligent and effective. For example, we can decide to retry the request immediately if the error message indicates a temporary problem with the server or wait for a longer delay if the error message indicates a more serious problem. In addition, by logging the error messages, we can keep a record of the errors that occurred, which can be useful for debugging and improving our application. ## Handling 429 Too Many Requests `HTTP 429 Too Many Requests` means your client exceeded the rate limit for the endpoint. The fix is to slow your request rate down — preferably proactively, so the limit never trips. ### Where the limit comes from The exact per-second limit depends on your plan and the node type. See [Requests per second (RPS) plan limits](/docs/rps-plan-limits) and [Throughput guidelines](/docs/limits) — including [Solana method-specific limits](/docs/limits#solana-method-limits) and [EVM range limits](/docs/limits#evm-range-limits). ### Confirm the 429 is real Before changing your code, check your node's metrics in the Chainstack console (**Projects → Node → Metrics**). If you see 429s in your debug logs but **not** on the Metrics page, the limit isn't being hit on the Chainstack side — something between your client and the endpoint is generating the 429s. A known case: older versions of the Solana Python SDK return 429-shaped errors that don't correspond to real rate-limit hits. ### Respect Retry-After If a 429 response includes the standard [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header, wait at least that long before retrying. Pair it with the exponential backoff pattern from the previous section as the fallback. ```javascript JavaScript theme={"system"} async function callWith429Backoff(url, body, maxRetries = 5) { for (let attempt = 0; attempt <= maxRetries; attempt++) { const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (res.status !== 429) return res; const retryAfter = parseFloat(res.headers.get("retry-after")) || (2 ** attempt); await new Promise(r => setTimeout(r, retryAfter * 1000)); } throw new Error("Rate-limited after retries"); } ``` ```python Python theme={"system"} import time, requests def call_with_429_backoff(url, payload, max_retries=5): for attempt in range(max_retries + 1): r = requests.post(url, json=payload) if r.status_code != 429: return r delay = float(r.headers.get("Retry-After", 2 ** attempt)) time.sleep(delay) raise RuntimeError("Rate-limited after retries") ``` ### Throttle proactively Rather than retrying after the fact, cap your client's request rate so the limit is never hit. Battle-tested options: * **Python** — [`ratelimit`](https://pypi.org/project/ratelimit/) decorator, or `asyncio.Semaphore` with `asyncio.sleep`. * **Node.js** — [`axios-rate-limit`](https://www.npmjs.com/package/axios-rate-limit), [`bottleneck`](https://www.npmjs.com/package/bottleneck), or [`p-throttle`](https://www.npmjs.com/package/p-throttle). * **Go** — [`uber-go/ratelimit`](https://github.com/uber-go/ratelimit) (leaky-bucket) or `golang.org/x/time/rate` (token bucket). Pair the throttle with a small concurrency cap and the exponential-backoff retry above. That combination handles both routine bursts and the occasional spike. ### Reduce the request volume itself When throttling alone isn't enough, the call pattern is usually the lever: * **Batch JSON-RPC requests** — combine multiple calls into one HTTP request when the protocol supports batch arrays. * **Use `eth_getLogs` over filters** for catch-up reads, not `eth_getBlockByNumber` in a loop. * **Switch to subscriptions** for streaming data (`eth_subscribe` over WebSocket) instead of polling on every block. * **Use a [Trader Node](/docs/trader-node)** or move to an [Unlimited Node](/docs/unlimited-node) tier if your sustained throughput is consistently above your plan's RPS cap — an Unlimited Node converts per-request billing into a flat RPS-tier fee. * **Enterprise custom RPS.** Enterprise customers can request a custom RPS limit (including for method-specific caps like Solana `getProgramAccounts`) by [contacting support](https://chainstack.com/contact/). ## Common problems and gotchas While handling HTTP status codes and implementing retry logic can significantly improve the reliability of your application, there are a few common problems and gotchas that you should be aware of. ### Importance of effective retry logics and robust error backlogs Another common problem is the lack of effective retry logic and robust error backlogs. Without these, your application may not be able to recover from temporary failures, resulting in poor user experience and potential data loss. An effective retry logic should take into account the nature of the error and adjust its behavior accordingly. For example, if the error is temporary (such as a 5xx server error), the retry logic should wait for a short delay before retrying the request. If the error is permanent (such as a 4xx client error), the retry logic should not retry the request and should log the error and notify the user. A robust error backlog, on the other hand, can help you keep track of the errors that occur in your application, allowing you to debug and fix issues more effectively. It can also provide valuable insights into the performance and reliability of your application, helping you identify areas for improvement. ## Conclusion Handling HTTP status codes and implementing retry logic are crucial aspects of working with API requests. They ensure smooth and reliable communication between clients and servers and enhance your applications' overall performance and resilience. As the volume and complexity of data continue to increase, the importance of these practices cannot be overstated. Remember, the key to effective error handling is understanding the different types of HTTP status codes and how to handle them. This includes knowing how to retrieve these codes, analyze error responses, and implement robust retry logic. By doing so, you can build applications that are capable of handling temporary failures and maintaining data integrity, even in the face of increasing data volume and complexity. However, it's also important to be aware of the common challenges and gotchas associated with these practices. This includes dealing with the constantly growing data in Web3, implementing effective retry logic, and maintaining robust error backlogs. By being aware of these challenges and knowing how to handle them, you can ensure that your applications remain reliable and resilient, no matter what comes their way. In conclusion, while error handling in API requests can be complex, it's an essential skill for any developer working with APIs. By following the best practices outlined in this guide, you can ensure that your applications are well-equipped to handle any errors that may occur, resulting in a better user experience and a more reliable application. # Best practices for securing your Chainstack endpoint Source: https://docs.chainstack.com/docs/best-practices-for-securing-your-chainstack-endpoint Security checklist for your Chainstack RPC node: enable 2FA, use HTTPS/WSS, rotate API keys, restrict access with allowlists, and keep dependencies up to date. **TLDR:** * Lock down your **account** first: 2FA, scoped roles, key rotation. * Lock down your **endpoint** second: HTTPS/WSS only, access rules (IP and Origin allowlists), basic-auth or key-auth — never both leaked. * Lock down your **code** third: secrets via env vars or a secret manager, never on the frontend, keep dependencies patched. This is a checklist. Each item links into the deeper page that walks through the implementation. ## 1. Enable 2FA on your Chainstack account If your account password leaks, 2FA is what stops an attacker from logging in. Turn it on before anything else. * In the [Chainstack console](https://console.chainstack.com/), go to **Settings → Security → Enable 2FA**. * Use an authenticator app (Authy, 1Password, etc.) — SMS is the weaker option. ## 2. Use scoped roles for team members Don't share the owner account. Invite teammates with the least-privilege role they need. * See [Manage your organization](/docs/manage-your-organization) for roles (Admin, Editor, Viewer) and how to invite members. * Audit the member list when people change teams or leave. ## 3. Always connect over HTTPS / WSS Plain HTTP exposes endpoint URLs and request bodies on the wire. Every Chainstack endpoint supports TLS — use the HTTPS / WSS URLs only. If your client throws `CERTIFICATE_VERIFY_FAILED`, fix the CA bundle, don't disable verification in production. See [Common SSL issues on Python and how to fix it](/docs/common-ssl-issues-on-python-and-how-to-fix-it). ## 4. Pick the right auth method and protect it Chainstack endpoints support three authentication styles: * **Key authentication** — the auth token is part of the endpoint URL (`https://.core.chainstack.com/`). Convenient, but anyone who sees the URL has access. * **Multichain auth token** — one auth token in the URL of every node attached to a [multichain endpoint](/docs/multichain-api). A leaked multichain auth token exposes every attached node, and you can't rotate it, so use a separate multichain endpoint per environment and replace a leaked multichain auth token by creating a new multichain endpoint. * **Basic authentication** — a username and password sent in the `Authorization` header. See [Authentication methods for different scenarios](/docs/authentication-methods-for-different-scenarios) for the full comparison and when to pick which. Whichever you pick: * Treat keys, usernames, and passwords as secrets — never commit them to git, never paste them in screenshots or tickets. * Rotate them on a schedule and immediately after any suspected leak. A rotation replaces the node's auth token, username and password, and x-token together, with no downtime. See [Rotate node credentials](/docs/manage-your-node#rotate-node-credentials). ## 5. Restrict who can reach your endpoint Access rules add a second perimeter on top of authentication. * **IP allowlist** — only specific source IPs can call the endpoint. Use it for backend services with stable egress IPs. * **Origin allowlist** — only specific `Origin` headers (your dapp's domain) are accepted from browser-side code. * **HTTP referrer rules** — for browser fetches from particular pages. See [Access rules](/docs/access-rules) for the full setup. Available on [Global Nodes](/docs/global-elastic-node). ## 6. Never expose secrets on the frontend A wallet, dapp, or browser extension that calls an RPC endpoint directly leaks that endpoint to every visitor. The standard fix is to proxy through your backend: * Frontend calls your backend. * Backend calls Chainstack with the real key. * Backend can additionally rate-limit and validate before forwarding. For environment-variable handling, dotenv vaults, and enterprise secret managers, see [How to store your Web3 DApp secrets](/docs/how-to-store-your-web3-dapp-secrets-guide-to-environment-variables). ## 7. Keep dependencies patched Third-party libraries are a common breach vector. Track CVEs for your wallet/RPC/web3 dependencies and update on a regular cadence. Avoid pinning to abandoned forks. ## 8. Be cautious with third-party tools Browser extensions, RPC proxies, and "free" dashboards that ask for your endpoint URL or wallet are a common phishing surface. Only use tools you've vetted; never paste production endpoint URLs or seed phrases into them. ## See also * [Manage your organization](/docs/manage-your-organization) — 2FA, roles, SSO * [Access rules](/docs/access-rules) — IP / Origin / referrer allowlists * [Authentication methods for different scenarios](/docs/authentication-methods-for-different-scenarios) * [How to store your Web3 DApp secrets](/docs/how-to-store-your-web3-dapp-secrets-guide-to-environment-variables) * [Common SSL issues on Python and how to fix it](/docs/common-ssl-issues-on-python-and-how-to-fix-it) # Billing thresholds Source: https://docs.chainstack.com/docs/billing-thresholds Learn how Chainstack automatic charge thresholds work to manage your usage, prevent service interruptions, and control your billing and payments. Chainstack uses billing thresholds to help manage usage-based charges and ensure service continuity. This system automatically triggers payments when your accumulated usage reaches certain thresholds, preventing excessive unpaid usage while maintaining uninterrupted access to your services. ## How billing thresholds work With metered billing, you're typically charged at the end of your billing cycle. However, if your accumulated usage reaches a billing threshold before the cycle ends, an automatic charge will be processed immediately. This helps: * Prevent large unpaid balances from accumulating * Ensure continuous service availability * Protect both customers and Chainstack from unexpected overages > [!NOTE] > > Billing thresholds apply to metered extra usage charges, including API requests, Dedicated Node hours, and Warp transactions that exceed your plan's included allowances. ## Threshold levels by subscription plan Each subscription plan has progressive billing thresholds. Once you reach a threshold, you'll be charged immediately for the accumulated usage, and the next threshold becomes active. > [tab] Developer > > | Step | Threshold amount | > | - | - | > | 1 | \$5 | > | 2 | \$10 | > | 3 | \$25 | > | 4 | \$50 | > | 5 | \$100 | > | 6 | \$400 | > | 7 | \$600 | > | 8 | \$1,000 | > [tab] Growth > > | Step | Threshold amount | > | - | - | > | 1 | \$50 | > | 2 | \$100 | > | 3 | \$400 | > | 4 | \$600 | > | 5 | \$1,000 | > [tab] Pro > > | Step | Threshold amount | > | - | - | > | 1 | \$100 | > | 2 | \$400 | > | 3 | \$600 | > | 4 | \$1,000 | > [tab] Business > > | Step | Threshold amount | > | - | - | > | 1 | \$100 | > | 2 | \$400 | > | 3 | \$600 | > | 4 | \$1,000 | > [tab] Enterprise > > | Step | Threshold amount | > | - | - | > | 1 | \$100 | > | 2 | \$400 | > | 3 | \$600 | > | 4 | \$1,000 | ### How thresholds progress The thresholds work progressively: 1. **First threshold** — when your usage reaches the first threshold amount for your plan, you're charged immediately 2. **Subsequent thresholds** — after each charge, the next higher threshold becomes active 3. **Final threshold** — once you reach the highest threshold for your plan, subsequent charges occur each time you accumulate that same amount again > [!NOTE] > > On the Developer plan, you'll be charged at `$5`, then at `$10` (additional `$5` from the first threshold), then at `$25` (additional `$15`), and so on. Once you reach the `$1,000` threshold, you'll be charged every time you accumulate another `$1,000` in usage. ## Payment processing When a billing threshold is reached: 1. **Automatic charge** — your payment method is charged immediately for the accumulated usage 2. **Balance priority** — your crypto balance is used first if available 3. **Credit card backup** — if your crypto balance is insufficient, your credit card is charged for the remaining amount 4. **Billing cycle continues** — your regular billing cycle remains unchanged; the threshold charge simply collects payment for usage to date > [!WARNING] > > Ensure your payment methods are up to date to avoid service interruptions. Failed threshold payments may result in service suspension. ## Managing high usage If you anticipate high usage that may trigger multiple thresholds: * **Top up your crypto balance** — prepay to avoid frequent credit card charges * **Monitor your usage** — track your current usage in the [Billing](https://console.chainstack.com/user/settings/billing) section * **Contact support** — for custom threshold arrangements or enterprise billing needs ## Frequently asked questions > [accordion] Do billing thresholds reset each month? > > Yes, thresholds reset at the beginning of each billing cycle. You'll start again from the first threshold level for your plan. > [accordion] Can I change my billing thresholds? > > Thresholds are tied to your subscription plan. Upgrading your plan will automatically update your thresholds to the new plan's levels. For custom threshold requirements, Enterprise customers can contact support. > [accordion] What happens if a threshold payment fails? > > If a threshold payment fails, you'll receive a notification and should update your payment method immediately. Continued failed payments may result in service suspension according to our [payment failure policy](/docs/manage-your-billing#settle-failed-payments). > [accordion] Are subscription fees subject to thresholds? > > No, billing thresholds apply only to metered usage charges. Your base subscription fee is always charged at the regular billing cycle. ## See also * [Manage your billing](/docs/manage-your-billing) * [Chainstack pricing](https://chainstack.com/pricing/) * [Terms of Service](https://chainstack.com/tos/) # Bitcoin methods Source: https://docs.chainstack.com/docs/bitcoin-methods Complete reference of supported Bitcoin JSON-RPC methods on Chainstack nodes, covering blockchain queries, raw transactions, and network information. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Our pricing is simple: 1 RU per request. Bitcoin has no archive class — see [Request units](/docs/request-units#full-vs-archive-by-protocol). > [!NOTE] > > On smart-contract-enabled chains, a Full node keeps recent state; an Archive node adds all historical state from genesis. Bitcoin has no per-block historical state — chain state is the UTXO set, derived from the chain itself — so there is no separate Archive class. Chainstack Bitcoin nodes are non-pruned with `txindex` enabled: every block and every transaction from genesis is queryable. Full does not mean pruned. > > Historical address queries (transaction history, balance at a past block) need an indexer like [electrs](https://github.com/Blockstream/electrs) — available as a [Bitcoin Dedicated Node customization](/docs/features-availability-across-subscription-plans#node-customization). [Contact us](https://chainstack.com/contact/) to enable it. See also [interactive Bitcoin API call examples](/reference/bitcoin-api-reference). ## Method availability The following table shows method availability for Chainstack Bitcoin nodes. It applies to Bitcoin Mainnet, Testnet (testnet3), Signet Testnet, and Testnet4. For how the testnets differ, see [Bitcoin testnets](/docs/bitcoin-testnets). * **Available** — Immediately accessible on Global, Trader, and Unlimited Bitcoin nodes * **Customization required** — Available on dedicated Bitcoin nodes with additional customization * **Not available** — Cannot be activated on any Chainstack Bitcoin node > [!NOTE] > > To access methods requiring customization, you can request a Dedicated Node directly from the Chainstack console UI. A Chainstack Support representative will contact you to assist with enabling the required methods. | Method | Availability | Comment | | - | - | - | | getbestblockhash | | | | getblock | | | | getblockchaininfo | | | | getblockfilter | | | | getblockhash | | | | getblockheader | | | | getblockstats | | | | getchaintips | | | | getchaintxstats | | | | getdifficulty | | | | getmempoolancestors | | | | getmempooldescendants | | | | getmempoolentry | | | | getmempoolinfo | | | | getrawmempool | | | | gettxoutsetinfo | | | | gettxout | | | | verifychain | | | | gettxoutproof | | | | preciousblock | | | | pruneblockchain | | | | savemempool | | | | scantxoutset | | | | verifytxoutproof | | | | uptime | | | | getmemoryinfo | | | | getrpcinfo | | | | help | | | | logging | | | | stop | | | | generateblock | | | | generatetoaddress | | | | generatetodescriptor | | | | getblocktemplate | | | | getmininginfo | | | | getnetworkhashps | | | | prioritisetransaction | | | | submitblock | | | | submitheader | | | | getpeerinfo | | | | getnetworkinfo | | | | getconnectioncount | | | | getnettotals | | | | listbanned | | not available on Global or Trader Nodes | | ping | | not available on Global or Trader Nodes | | addnode | | | | disconnectnode | | | | getnodeaddresses | | | | setnetworkactive | | not available on Global or Trader Nodes | | setban | | | | analyzepsbt | | | | createpsbt | | | | combinepsbt | | | | createrawtransaction | | | | combinerawtransaction | | | | decodepsbt | | | | decoderawtransaction | | | | decodescript | | | | finalizepsbt | | | | fundrawtransaction | | | | getrawtransaction | | | | sendrawtransaction | | | | signrawtransactionwithkey | | | | testmempoolaccept | | | | createmultisig | | | | deriveaddresses | | | | estimatesmartfee | | | | getindexinfo | | | | signmessagewithprivkey | | | | validateaddress | | | | verifymessage | | | | createwallet | | | | listwalletdir | | | | loadwallet | | | | listtransactions | | | | abandontransaction | | | | abortrescan | | | | bumpfee | | | | dumpprivkey | | | | getaddressesbylabel | | | | getbalance | | | | getbalances | | | | getnewaddress | | | | getrawchangeaddress | | | | getreceivedbyaddress | | | | getreceivedbylabel | | | | gettransaction | | | | importaddress | | | | importdescriptors | | | | importmulti | | | | importprivkey | | | | importprunedfunds | | | | importpubkey | | | | importwallet | | | | keypoolrefill | | | | listaddressgroupings | | | | listlabels | | | | listlockunspent | | | | listreceivedbyaddress | | | | listsinceblock | | | | listunspent | | | | lockunspent | | | | psbtbumpfee | | | | removeprunedfunds | | | | rescanblockchain | | | | send | | | | sendmany | | | | sendtoaddress | | | | sethdseed | | | | setlabel | | | | settxfee | | | | setwalletflag | | | | signmessage | | | | signrawtransactionwithwallet | | | | unloadwallet | | | | upgradewallet | | | | walletcreatefundedpsbt | | | | walletlock | | | | walletpassphrase | | | | walletpassphrasechange | | | | walletprocesspsbt | | | | getzmqnotifications | | | | enumeratesigners | | | # Bitcoin testnets: Testnet, Signet Testnet, and Testnet4 Source: https://docs.chainstack.com/docs/bitcoin-testnets Compare Testnet (testnet3), Signet Testnet, and Testnet4, the Bitcoin test networks on Chainstack, with network parameters, address formats, fee estimation on Testnet4, and a checklist for moving from Testnet (testnet3) to Testnet4. Chainstack serves three Bitcoin test networks on Global Nodes: Testnet (testnet3), Signet Testnet, and Testnet4. Use Signet Testnet or Testnet4 for new work. Bitcoin Core marks testnet3 as deprecated, and Testnet4 replaces it with consensus rules that stop CPU miners from flooding the chain with blocks. Every Bitcoin network on Chainstack, including the testnets, runs Bitcoin Core non-pruned with `txindex` enabled and serves the same JSON-RPC methods. See [Bitcoin methods](/docs/bitcoin-methods) for availability and [Bitcoin tooling](/docs/bitcoin-tooling) for connection examples. ## Which Bitcoin testnet to use Testnet4 is the general-purpose public testnet that replaces Testnet (testnet3). Signet Testnet is the testnet that unauthorized miners cannot flood with blocks, because every block needs a signature from the signing keys. | Network | Who produces blocks | Block production | Use it for | | - | - | - | - | | Testnet (testnet3) | Anyone, by proof of work | Deprecated in Bitcoin Core since version 28.0. Prone to block storms, which have pushed the chain past 5 million blocks. | Existing integrations that have not moved to Testnet4 | | Signet Testnet | Holders of the default signet signing keys, by proof of work plus a signature ([BIP325](https://github.com/bitcoin/bips/blob/master/bip-0325.mediawiki)) | Blocks target a 10-minute interval, and only the signing keys can add blocks, so the chain has no block storms | Integration tests and demos that need a stable chain | | Testnet4 | Anyone, by proof of work | Follows [BIP94](https://github.com/bitcoin/bips/blob/master/bip-0094.mediawiki). A block mined more than 20 minutes after the previous one may use the minimum difficulty, and blocks often carry only the coinbase transaction. | Wallets, exchanges, and services that need a public proof-of-work testnet in place of Testnet (testnet3) | ## Network parameters The `chain` value and the genesis block hash identify each network. The Bitcoin Core options and default ports apply when you run your own Bitcoin Core next to your Chainstack node, for example for a local wallet. A Chainstack endpoint is HTTPS on port 443 for every network. | Parameter | Testnet (testnet3) | Signet Testnet | Testnet4 | | - | - | - | - | | Network name on Chainstack | Testnet | Signet Testnet | Testnet4 | | `chain` in `getblockchaininfo` | `test` | `signet` | `testnet4` | | Genesis block hash | `000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943` | `00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6` | `00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043` | | Bitcoin Core option | `-testnet` | `-signet` | `-testnet4` | | Config file section | `[test]` | `[signet]` | `[testnet4]` | | Default P2P port | `18333` | `38333` | `48333` | | Default RPC port | `18332` | `38332` | `48332` | | Block explorer | [mempool.space/testnet](https://mempool.space/testnet) | [mempool.space/signet](https://mempool.space/signet) | [mempool.space/testnet4](https://mempool.space/testnet4) | > [!NOTE] > > `bitcoin-cli` connects over plain HTTP only. Bitcoin Core removed RPC TLS support in version 0.12, so `bitcoin-cli` cannot call a Chainstack HTTPS endpoint. Use curl or an HTTP client library, as shown in [Bitcoin tooling](/docs/bitcoin-tooling#testnet-signet-testnet-and-testnet4). ## Why an address does not identify its testnet Testnet (testnet3), Signet Testnet, and Testnet4 use the same address and key encodings, so the same address string is valid on all three networks, with a separate balance on each chain. | Encoding | Prefix on all three testnets | | - | - | | Native SegWit and Taproot (bech32 and bech32m) | `tb1` | | Legacy (P2PKH) | `m` or `n` | | Script hash (P2SH) | `2` | | Extended keys | `tpub` and `tprv` | To confirm which network an endpoint serves, read `chain` from `getblockchaininfo` when your application starts: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"1.0","id":1,"method":"getblockchaininfo","params":[]}' ``` A Signet Testnet endpoint returns `"chain": "signet"`, and a Testnet4 endpoint returns `"chain": "testnet4"`. Bitcoin libraries follow the same encodings. In bitcoinjs-lib, `networks.testnet` produces addresses for all three testnets. In python-bitcoinlib, `SelectParams("testnet")` does the same. Neither library has a separate Testnet4 setting. ## Fee estimation on Testnet4 Testnet4 blocks often carry only the coinbase transaction, so `estimatesmartfee` often lacks data for short confirmation targets and returns `"errors": ["Insufficient data or no feerate found"]` instead of a `feerate`. Longer targets, such as 144 or 1008 blocks, usually return a fee rate. When `estimatesmartfee` returns no estimate, fall back to `mempoolminfee` from `getmempoolinfo`, the lowest fee rate the node accepts into its mempool. Signet Testnet blocks carry regular traffic, and `estimatesmartfee` returns a fee rate for short targets there. ## Move from Testnet (testnet3) to Testnet4 Testnet4 keeps the consensus rules of Testnet (testnet3) and adds the BIP94 rules, so an application that reads and writes through a node's JSON-RPC API needs only the Testnet4 network parameters. Software that validates blocks itself must also enforce the BIP94 rules, or it can follow a chain that Testnet4 nodes reject. To move an integration: 1. Deploy a Bitcoin Testnet4 [Global Node](/docs/global-elastic-node) on Chainstack and replace your Testnet (testnet3) endpoint with it. 2. Check that `chain` is `testnet4` when your application starts. 3. Fund new Testnet4 addresses. Testnet (testnet3) coins do not exist on Testnet4, so every balance starts at zero. 4. Keep your address and key handling. The encodings are the same, so library network settings and the testnet derivation coin type `1` don't change. 5. Discard stored Testnet (testnet3) block heights, block hashes, and transaction IDs. None of them exist on Testnet4, and its chain is far shorter. 6. Add a fee fallback for short confirmation targets. See [Fee estimation on Testnet4](#fee-estimation-on-testnet4). 7. If you run your own Bitcoin Core as well, upgrade it to version 28.0 or later, replace `-testnet` with `-testnet4`, and move testnet settings from the `[test]` config section to `[testnet4]`. ## Testnet faucets and explorers [mempool.space](https://mempool.space) runs block explorers for Testnet (testnet3), Signet Testnet, and Testnet4, with the links in [Network parameters](#network-parameters). Signet Testnet and Testnet4 coins come from community-run faucets. For current faucets, see the [Bitcoin Wiki Signet page](https://en.bitcoin.it/wiki/Signet) for Signet Testnet and the [testnet4.dev](https://www.testnet4.dev/) list for Testnet4. Signet Testnet blocks need a signature from the signing keys, so faucets are the practical way to get Signet Testnet coins. ## FAQ ### Is Testnet (testnet3) going away? Bitcoin Core marks testnet3 as deprecated and states that support "will be removed in an upcoming release". As of Bitcoin Core 31.1, no release has removed it and no removal version is set. Chainstack serves Testnet (testnet3) on Global Nodes. ### Do Signet Testnet and Testnet4 support the same methods as Bitcoin Mainnet? Yes. The Bitcoin JSON-RPC methods available on Chainstack are the same on Mainnet, Testnet (testnet3), Signet Testnet, and Testnet4. See [Bitcoin methods](/docs/bitcoin-methods). ### Can I reuse a Testnet (testnet3) address on Testnet4? The address string is valid on Testnet4, because the encodings are the same, but its Testnet (testnet3) balance does not carry over. Fund the address on Testnet4 before you spend from it. ### Is historical data available on the testnets? Yes. Chainstack Bitcoin testnet nodes keep every block from genesis and have `txindex` enabled, so `getrawtransaction` returns any transaction by its ID except the genesis block coinbase, which Bitcoin Core does not treat as an ordinary transaction. Read the genesis coinbase with `getblock` at verbosity `2`. Historical address queries need an address indexer such as electrs. See [Bitcoin methods](/docs/bitcoin-methods). ### Does Chainstack offer regtest? No. Regtest is a private network that you run locally with `bitcoind -regtest`. It has no shared chain to connect to. # Bitcoin tooling Source: https://docs.chainstack.com/docs/bitcoin-tooling Connect to a Chainstack Bitcoin node over JSON-RPC with curl, Python, and JavaScript on Mainnet, Testnet (testnet3), Signet Testnet, and Testnet4. Get started with a [reliable Bitcoin RPC endpoint](https://chainstack.com/build-better-with-bitcoin/) to use the tools below. Chainstack Bitcoin nodes serve the Bitcoin Core [JSON-RPC API](https://developer.bitcoin.org/reference/rpc/) over HTTPS. Send requests with curl or any HTTP client. The same calls work on Mainnet, Testnet (testnet3), Signet Testnet, and Testnet4. ## JSON-RPC API To get basic network information from your Bitcoin node HTTPS endpoint, call `getblockchaininfo`: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"1.0","id":1,"method":"getblockchaininfo","params":[]}' ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). To call methods from Postman, use the [Bitcoin RPC methods Postman collection](/reference/bitcoin-rpc-methods-postman-collection). `bitcoin-cli` connects over plain HTTP only, so it cannot call a Chainstack HTTPS endpoint. Use curl or an HTTP client library. ## Python Install [requests](https://pypi.org/project/requests/): ```shell Shell theme={"system"} pip install requests ``` ```python main.py theme={"system"} import requests ENDPOINT = "YOUR_CHAINSTACK_ENDPOINT" def rpc(method, params=None): response = requests.post( ENDPOINT, json={"jsonrpc": "1.0", "id": 1, "method": method, "params": params or []}, timeout=30, ) if "application/json" not in response.headers.get("Content-Type", ""): raise RuntimeError(f"HTTP {response.status_code}: {response.text.strip()}") body = response.json() if body.get("error"): raise RuntimeError(body["error"]["message"]) return body["result"] info = rpc("getblockchaininfo") print("Chain:", info["chain"]) print("Blocks:", info["blocks"]) fee = rpc("estimatesmartfee", [6]) if "feerate" in fee: print("Fee rate (BTC/kvB):", fee["feerate"]) else: mempool = rpc("getmempoolinfo") print("No estimate for 6 blocks; minimum fee (BTC/kvB):", mempool["mempoolminfee"]) ``` On Mainnet, the `main.py` example prints `Chain: main`, the block height, and a fee rate for confirmation within 6 blocks. For a password-protected endpoint, put the credentials in the URL, as in `https://USERNAME:PASSWORD@HOSTNAME`, and requests sends them as HTTP basic authentication. > [!NOTE] > > python-bitcoinlib 0.12.2 rejects HTTPS URLs in `bitcoin.rpc.Proxy` with `Unsupported URL scheme 'https'`, so send JSON-RPC to a Chainstack endpoint with an HTTP client such as requests. ## JavaScript Install [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib) with its key-pair dependencies. The example needs Node.js 20 or later, which `ecpair` requires, and uses the built-in `fetch`. ```shell Shell theme={"system"} npm install bitcoinjs-lib ecpair tiny-secp256k1 ``` ```javascript index.mjs theme={"system"} import * as bitcoin from "bitcoinjs-lib"; import { ECPairFactory } from "ecpair"; import * as ecc from "tiny-secp256k1"; const ENDPOINT = "YOUR_CHAINSTACK_ENDPOINT"; async function rpc(method, params = []) { const response = await fetch(ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "1.0", id: 1, method, params }), signal: AbortSignal.timeout(30000), }); if (!response.headers.get("content-type")?.includes("application/json")) { throw new Error(`HTTP ${response.status}: ${(await response.text()).trim()}`); } const body = await response.json(); if (body.error) throw new Error(body.error.message); return body.result; } const { chain, blocks } = await rpc("getblockchaininfo"); console.log("Chain:", chain, "Blocks:", blocks); const network = chain === "main" ? bitcoin.networks.bitcoin : bitcoin.networks.testnet; const ECPair = ECPairFactory(ecc); const keyPair = ECPair.makeRandom({ network }); const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network }); console.log("Address:", address); const result = await rpc("validateaddress", [address]); console.log("Valid on", chain + ":", result.isvalid); ``` The `index.mjs` example picks the bitcoinjs-lib network from `chain`, generates a new native SegWit address for it, and checks the address with `validateaddress`. On Mainnet, it prints `Chain: main`, a `bc1q` address, and `Valid on main: true`. The built-in `fetch` rejects a URL that includes credentials, with `TypeError: Request cannot be constructed from a URL that includes credentials`. For a password-protected endpoint, send the credentials in an `Authorization` header instead: ```javascript auth.mjs theme={"system"} const ENDPOINT = "https://HOSTNAME"; const AUTHORIZATION = "Basic " + Buffer.from("USERNAME:PASSWORD").toString("base64"); const response = await fetch(ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", Authorization: AUTHORIZATION }, body: JSON.stringify({ jsonrpc: "1.0", id: 1, method: "getblockcount", params: [] }), }); console.log("Blocks:", (await response.json()).result); ``` ## Testnet, Signet Testnet, and Testnet4 Testnet (testnet3), Signet Testnet, and Testnet4 endpoints take the same JSON-RPC calls as Bitcoin Mainnet, so the Python and JavaScript examples run on them unchanged. Before your application sends transactions, check that `chain` in `getblockchaininfo` is `test`, `signet`, or `testnet4`, because testnet addresses look the same on every testnet. For network parameters and how the testnets differ, see [Bitcoin testnets](/docs/bitcoin-testnets). * bitcoinjs-lib has no separate Signet Testnet or Testnet4 network. `networks.testnet` produces `tb1` addresses that are valid on all three testnets, which is the network the `index.mjs` example selects for any `chain` other than `main`. * On Testnet4, `estimatesmartfee` often has no estimate for short targets, so the `main.py` example falls back to the node's minimum mempool fee. See [Fee estimation on Testnet4](/docs/bitcoin-testnets#fee-estimation-on-testnet4). # Blob transactions the hard way Source: https://docs.chainstack.com/docs/blob-transactions-the-hard-way Construct and send EIP-4844 blob transactions manually on Ethereum using ethers.js and c-kzg, covering blob encoding, commitment proofs, and gas fees. **TLDR:** * Walks you through crafting and broadcasting blob (type-3) transactions on Ethereum, including creating 128 KB blobs and verifying the KZG commitment and blob versioned hash. * Explains how blob data is stored on the consensus layer for 18 days, while the blob versioned hash lives on the execution layer. * Dives into EIP-4788 & EIP-4844, detailing how to map execution-layer blocks to consensus-layer slots to fetch and verify blob data. * Provides full Python scripts for a "bare bones" approach to building, sending, and verifying blob transactions from start to finish. ## Main article Or *almost* the hard way as we are going to use a bit of Python. The focus of this guide is on keeping the [Dencun and EIP-4844 talk](/docs/ethereum-dencun-rundown-with-examples) to a minimum and to give you hands-on exposure to blob transactions, which is a good way to supplement the prior general knowledge on the topic. > [!NOTE] > > Chainstack Ethereum nodes follow the consensus client defaults for blob data availability. With Lighthouse, blob sidecars are retained for roughly 18 days (per EIP-4844). > > [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required. You can sign up with your GitHub, X, Google, or Microsoft account. ## Introduction ### EIPS involved The two EIPs for this guide are: * [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) — each block on the execution layer now contains the parent beacon's block root. * [EIP-4844](https://www.eip4844.com/) — the compressed transaction data from rollups is now stored on the consensus layer for 18 days. There's more to each of these EIPs, but we focus on what we need to know for this guide. ### Problem A quick reminder on the problem that the blob transactions solve. With the increased activity on the Ethereum mainnet, the limited throughput leads up to the increased transaction fees (and costs as ETH appreciates in value). To solve this, a new chain is launched. The new chain has faster block times and a centralized unit (the sequencer) processing the transactions and blocks. To have some of the Ethereum network security & decentralization, the new chain takes all of its transactions as complete transaction data, compresses them (or rolls them up, hence rollups) and posts the data in batches to the Ethereum network as smart contract call data. Posting as call data is costly, but you now have the costs split between all the transactions on the rollup chain that were compressed and posted as one transaction to the Ethereum network. As an example, have a look at the Arbitrum batch [517392](https://arbiscan.io/batch/517392?isnitro=true) — there are 809 transactions in the batch; these transactions were paid for in ETH on the Arbitrum mainnet. The ETH from the transactions in the batch was used to fund [one transaction](https://etherscan.io/tx/0x7a5c81cbbc07c68abf0b9d91bb8c6d121aaf3b25b49cd4f047603b3fa9d39910) on the Ethereum mainnet where it posted the entire compressed data from the 809 transactions as call data to a contract. In a simplified way, now you have the cost of this one Ethereum transaction split between the actors of the 809 Arbitrum transactions. This makes each of the 809 transactions cheaper. The two problems with this are: * The EVM was never designed for this use case and posting compressed transaction data in batches as call data to smart contracts is expensive. * The call data is unnecessary and expensive to merely store data. ### Solution Enter blob transactions — the simple and sane solution. Blob data is moved to be stored on the consensus layer. Blob data is no longer processed by the EVM, which means it can be much bigger in size and there is no associated EVM processing gas costs. | | Blockspace | Blobspace | | - | - | - | | Seen by all nodes | Yes | Yes | | Storage | Execution layer | Consensus layer | | EVM access | Yes | No | | Longevity | Forever | 18 days | | Cost | Expensive | Cheap | Table credits: [Consensys](https://consensys.io/blog/ethereum-evolved-dencun-upgrade-part-5-eip-4844) . A few quick things on blob transactions: * Blob transactions are type-3 transactions on the execution layer. * When submitted on the execution layer, blob transactions go through the usual lifecycle (node > mempool > block) but the actual blob data is separately gossiped to the consensus layer. * Blob data is not validated in the rollup chain context by the Ethereum chain. * 18 days is theoretically sufficient for all the network participants to agree on the rollup chain state. * It is ultimately the job of the respective rollup chain mechanisms to store the blob data beyond 18 days and ensure its availability (aka Data Availability). * Even though the blob data is stored for 18 days, the KZG commitment for each blob — which is basically a fancy hash of blob data — is stored forever. This means that if you recover a blob that's been lost, you can prove it's the same blob through the respective KZG commitment. ### Transaction types As mentioned, blob transactions are type-3 transactions and at the time of this post are the latest transaction type. * **Type-0** aka Legacy — original Ethereum transactions with the parameters `nonce`, `gasPrice`, `gasLimit`, `to`, `value`, `data`, `v`, `r`, `s`. * **Type-1** aka Access list — adds the `accessList` parameter to the original set. Introduced by [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930). * **Type-2** aka [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) — introduces priority fees by adding `maxPriorityFeePerGas` and `maxFeePerGas`. * **Type-3** aka Blobs — adds two new parameters: * `maxFeePerBlobGas` — max fee per blob gas to store the blob * `blobVersionedHashes` — a blob pointer, which is a hash of the blob's KZG commitment As you see, Ethereum transactions add more parameters with each new type. ## Walkthrough Now let's have a hands-on walkthrough. In Python. Here's what we are going to do: * Create our own blob data * Send a type-3 transaction with our blob data on Sepolia * Retrieve our blob data from the network * Compute the KZG commitment for our blob data and verify it's the same as computed by the node * Compute the blob versioned hash from the KZG commitment and verify it's the same as computed by the node * Find all type-3 transactions in a block * Put it all together and run a script that: * Extracts all type-3 transactions from a block * Retrieves the blob data from the network * Retrieves the KZG commitment from the network * Retrieves the blob versioned hash from the network * Locally computes the KZG commitment and the blob versioned hash * Checks if the locally computed KZG commitment and the blob versioned has match the ones retrieved from the network (aka computed by the nodes) ### Prerequisites 1. Log in to your [Chainstack account](https://console.chainstack.com/) and get a [reliable Ethereum RPC endpoint](https://chainstack.com/build-better-with-ethereum/) for Sepolia. You can get by with a full node for this exercise. 2. Install [eth-abi](https://pypi.org/project/eth-abi/). 3. Install or update [web3.py](https://pypi.org/project/web3/). Note that it's important you have the latest version as it includes support for the type-3 transactions. 4. Install or update [eth-account](https://pypi.org/project/eth-account/). Same here — it's important you have the latest version as it includes support for the type-3 transactions. 5. Install [ckzg](https://pypi.org/project/ckzg/) — this computes the KZG commitments. 6. Fund your account with SepoliaETH. You can do this through the [Chainstack faucet](https://faucet.chainstack.com/). 7. Create a `.env` file with the following variables: ``` EXECUTION_LAYER_URL= CONSENSUS_LAYER_URL= PRIVATE_KEY= ``` [GitHub repository](https://github.com/chainstacklabs/blob-transactions-the-hard-way) for all the scripts. ### Create blob data Each blob is always is a fixed 128 KB in size. You are always paying for a 128 KB slot space. For our hands-on exercise, this means that if our blob data is less than 128 KB, we need to pad it to 128 KB. We are sending a blob that says `Chainstack`, so we pad it to the fixed size. ```python Python theme={"system"} import os from eth_abi import abi def create_blob_data(text): # Encode the text using Ethereum ABI encoding for a string encoded_text = abi.encode(["string"], [text]) # Calculate the required padding to make the blob size exactly 131072 bytes or 128 KB required_padding = 131072 - (len(encoded_text) % 131072) # Create the BLOB_DATA with the correct padding BLOB_DATA = (b"\x00" * required_padding) + encoded_text return BLOB_DATA def main(): text = "Chainstack" # If you change this, make sure you update the padding # Create blob data blob_data = create_blob_data(text) # Print the blob data in hexadecimal format print("Blob Data (Hex):") print(blob_data.hex()) if __name__ == "__main__": main() ``` For convenience, redirect the output to a file as it's going to be huge: ```shell Shell theme={"system"} python create_blob_data.py > blob.txt ``` What you have now is your blob data. As a reminder, it's completely arbitrary what we put into it — it can be [2837 transactions](https://arbiscan.io/batch/617502?isnitro=true) rolled into one object or it can the string `Chainstack` in hex and padded to 128 KB (as in our case). ### Send a type-3 transaction Now that we know what blob data is and how to create one, let's actually put it on-chain. Let's first run the script and then have a look at the transaction committed to the chain. ```python Python theme={"system"} import os from eth_abi import abi from eth_account import Account from eth_utils import to_hex from web3 import Web3, HTTPProvider from dotenv import load_dotenv load_dotenv() w3 = Web3(HTTPProvider(os.getenv("EXECUTION_LAYER_URL"))) """ Add the web3py middleware to ensure compatibility with Erigon 2 as the eth_estimateGas only expects one argument. The block number or block hash was dropped in https://github.com/ledgerwatch/erigon/releases/tag/v2.60.1 """ def erigon_compatibility_middleware(make_request, w3): def middleware(method, params): if method == 'eth_estimateGas' and len(params) > 1: # Modify the params to include only the transaction object params = params[:1] return make_request(method, params) return middleware w3.middleware_onion.add(erigon_compatibility_middleware) text = "Chainstack" encoded_text = abi.encode(["string"], [text]) # Calculate the required padding to make the blob size exactly 131072 bytes required_padding = 131072 - (len(encoded_text) % 131072) # Create the BLOB_DATA with the correct padding BLOB_DATA = (b"\x00" * required_padding) + encoded_text pkey = os.environ.get("PRIVATE_KEY") acct = w3.eth.account.from_key(pkey) tx = { "type": 3, # Type-3 transaction "chainId": 11155111, # Hoodi 560048; Sepolia 11155111; Holesky 17000 (deprecated on Oct 31, 2025) "from": acct.address, "to": "0x0000000000000000000000000000000000000000", # Does not matter what account you send it to "value": 0, "maxFeePerGas": 10**12, "maxPriorityFeePerGas": 10**12, "maxFeePerBlobGas": to_hex(10**12), # Note the new type-3 parameter for blobs "nonce": w3.eth.get_transaction_count(acct.address), } # Now you can estimate gas as usual gas_estimate = w3.eth.estimate_gas(tx) tx["gas"] = gas_estimate # Proceed with the rest of your script signed = acct.sign_transaction(tx, blobs=[BLOB_DATA]) tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction) tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) print(f"TX receipt: {tx_receipt}") ``` Note the comments in the script. Especially note how we are handling the Erigon compatibility here. Erigon introduced a breaking change in [v2.60.1](https://github.com/ledgerwatch/erigon/releases/tag/v2.60.1) — unlike Geth, the `eth_estimateGas` call in Erigon moving forward does not take in the block number or block hash as an argument. Running this will print out the transaction receipt. Here's an example of this transaction on the Sepolia etherscan: [0x5a74bd72aeeb99e874e58b927f9a5c96665278a36b61bed69a4b09597b02edce](https://sepolia.etherscan.io/tx/0x5a74bd72aeeb99e874e58b927f9a5c96665278a36b61bed69a4b09597b02edce) On etherscan when viewing the transaction, hit **Blobs**. You will see: * Commitment — the KZG commitment of the blob data. We are going to touch on this later in this article. * Blob Versioned Hash — basically a hash of the blob, serving as pointer on the execution layer to the blob on the consensus layer. Clicking the Blob Versioned Hash value will show the blob data. You can check if it's the same as the one we created previously and saved as `blob.txt`. It should be the same. Etherscan, being a great explorer, is providing you the data both from the consensus layer and the execution layer. What you need to remember here is: * Blob data — only stored on the consensus layer. * KZG commitment — only stored on the consensus layer. * Blob Versioned Hash — only stored on the execution layer. Here's the same blob transaction from our example as `Index 0` in the [Sepolia Beacon chain explorer](https://sepolia.beaconcha.in/block/6090748#blobs). A look at the actual cycle of our type-3 transaction: 1. We create the blob data and submit it as a type-3 transaction to a node. The `to` address of the transaction doesn't really matter as our goal is to put the blob data on the consensus layer. 2. The node picks up our transaction in its entirety, including the complete blob data, and computes the KZG commitment for the blob data and the blob versioned hash from the KZG commitment. 3. The node then propagates the transaction (without the blob data) with the blob versioned hash value to the mempool on the execution layer. The node also gossips the actual blob data over P2P to other nodes. 4. The transaction gets picked up from the mempool as usual and committed in a block on the execution layer. The blob data is stored on the consensus layer. The blob versioned hash on the execution layer is acting as pointer to the blob on the consensus layer. Now we have the type-3 transaction with the blob versioned hash forever living on the execution layer and the actual blob data stored for 18 days on the consensus layer. ### Retrieve blob data from the network Let's retrieve the blob data from the network. Since we are working with the consensus layer now, make sure you now use the consensus client endpoint from your Chainstack node details. Our example transaction is included in [block 6090748](https://sepolia.etherscan.io/txs?block=6090748\&isblob=true) on the execution layer. The call we are going to use is [Retrieve blobs by block ID](/reference/getblobsbyblockid). To provide a specific pointer for the blob data on the consensus layer, we can use one of the following:`head`, `genesis`, `finalized`, or `slot_number`. The original [Beacon chain API spec](https://ethereum.github.io/beacon-APIs/#/Beacon/getBlobSidecars) also includes `hex encoded blockRoot with 0x prefix`, but Chainstack nodes do not support this for finalized blocks for efficiency. And we don't need this anyway as you will see later. Note that the call does not support the execution layer block number, you can only provide the respective slot number on the consensus layer. Programmatically, we'll deal with this later. For now, we'll just pick the respective slot number from the Sepolia Beacon chain explorer: [slot 5203463](https://sepolia.beaconcha.in/slot/5203463). Here's our call ```shell Shell theme={"system"} curl --request GET \ --url CONSENSUS_LAYER_URL/eth/v1/beacon/blobs/5203463 \ --header 'accept: application/json' | jq "." ``` This will retrieve all the four blobs referenced by the four type-3 transactions in [block 6090748](https://sepolia.etherscan.io/txs?block=6090748\&isblob=true) on the execution layer. One of them is our blob data. ### Compute the KZG commitment Let's compute the KZG commitment for our blob data and make sure it's the same one as computed by the node. When we did the `sidecars` call in the previous section, we received 4 different blobs with respective KZG commitments. Let's now verify that one them is ours and computed correctly by independently taking our own created blob data and getting its KZG commitment. #### What is a KZG commitment But first — what is a KZG commitment? In simple words, this is a fancy (and extremely secure) way of creating a hash of a blob. You might have been one of the 141,416 contributors who were a part of the [KZG ceremony](https://ceremony.ethereum.org/) by providing your input. What was created as a result of this ceremony was the file `trusted_setup.txt` that the KZG commitment takes as one of the inputs. Examples: * In [Geth](https://github.com/ethereum/go-ethereum/blob/3687c34cfc9eba1b2c29209d27d272a72c4de3af/crypto/kzg4844/trusted_setup.json) * In [Nimbus](https://github.com/status-im/nimbus-eth2/blob/d2a07514541ffe6ee02a2ec7272ce7a315131e04/beacon_chain/conf.nim#L1487) * In [Erigon](https://github.com/ledgerwatch/erigon/blob/ff0da3dd47eb691dc5aefddb114a261779288778/cmd/utils/flags.go#L117) * In [Nethermind](https://github.com/NethermindEth/nethermind/blob/4fee4bfe1a72433cff37d62319e718462f78ca9a/src/Nethermind/Nethermind.Crypto/kzg_trusted_setup.txt) * In [c-kzg-4844](https://github.com/ethereum/c-kzg-4844/blob/main/src/trusted_setup.txt) The last one is the KZG implementation that we are actually going to use. #### Compute the KZG commitment The Python library for [c-kzg-4844](https://github.com/ethereum/c-kzg-4844/blob/main/src/trusted_setup.txt) is [ckzg](https://pypi.org/project/ckzg/). We are going to use the library, take our blob data that we created with `create_blob_data.py` earlier and saved as `blob.txt`, and take the [trusted\_setup.txt](https://github.com/ethereum/c-kzg-4844/blob/main/src/trusted_setup.txt) file. ```python Python theme={"system"} import ckzg def bytes_from_hex(hexstring): return bytes.fromhex(hexstring.replace("0x", "")) if __name__ == "__main__": ts = ckzg.load_trusted_setup("trusted_setup.txt") with open("blob.txt", "r") as file: blob_hex = file.read().strip() blob = bytes_from_hex(blob_hex) # Compute KZG commitment commitment = ckzg.blob_to_kzg_commitment(blob, ts) # Print the commitment in hexadecimal format print("KZG Commitment:", commitment.hex()) ``` This will print the KZG commitment for our blob. For our example, it should be `9493a713dd89eb7fe295efd62545bb93bca395a84d18ecfa2c6c650cddc844ad4c1935cbe7d6830967df9d33c5a2e230` If you look up the KZG commitment in the data you retrieved (or look up in the explorer), you will see that our independently computed KZG for our independently created blob data matches the on-chain one. ### Compute blob versioned hash from the KZG commitment This one is less fancy, let's do the hash of our KZG commitment and see if it matches the versioned hash from the transaction on the execution layer. ```python Python theme={"system"} import hashlib # Given KZG commitment kzg_commitment = "9493a713dd89eb7fe295efd62545bb93bca395a84d18ecfa2c6c650cddc844ad4c1935cbe7d6830967df9d33c5a2e230" # Remove the '0x' prefix if present if kzg_commitment.startswith("0x"): kzg_commitment = kzg_commitment[2:] # Convert the KZG commitment to bytes kzg_commitment_bytes = bytes.fromhex(kzg_commitment) # Compute the SHA-256 hash of the KZG commitment sha256_hash = hashlib.sha256(kzg_commitment_bytes).digest() # Prepend the version byte (0x01) to the last 31 bytes of the SHA-256 hash version_byte = b'\x01' blob_versioned_hash = version_byte + sha256_hash[1:] # Convert to hexadecimal for display blob_versioned_hash_hex = blob_versioned_hash.hex() # Print the result print(f"Blob versioned hash: 0x{blob_versioned_hash_hex}") ``` Remember that the blob versioned hash is stored on the execution layer. To retrieve it, you can do a simple [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash). Example for our transaction on Sepolia: ```shell Shell theme={"system"} curl --request POST \ --url EXECUTION_LAYER_URL \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "eth_getTransactionByHash", "params": [ "0x5a74bd72aeeb99e874e58b927f9a5c96665278a36b61bed69a4b09597b02edce" ] }' | jq '.result.blobVersionedHashes' ``` Check if the retrieved one matches the locally computed one. It should match. ### Find all type-3 transactions in a block This one simple with the latest version of [web3py](https://pypi.org/project/web3/). Make sure you have it updated. ```python Python theme={"system"} import os from web3 import Web3 from web3 import Web3, HTTPProvider from dotenv import load_dotenv load_dotenv() w3 = Web3(HTTPProvider(os.getenv("EXECUTION_LAYER_URL"))) # Specify the block number you want to check block_number = 6090748 block = w3.eth.get_block(block_number, full_transactions=True) # Iterate through transactions and check for type-3 transactions for tx in block.transactions: if tx.type == 3: # Type 3 refers to blob transactions print("Transaction Hash:", tx.hash.hex()) ``` ### Putting it all together As a the final one, let's put all parts together and have a script that does the following: * Extracts all type-3 transactions from a block * Retrieves the blob data from the network * Retrieves the KZG commitment from the network * Retrieves the blob versioned hash from the network * Locally computes the KZG commitment and the blob versioned hash * Checks if the locally computed KZG commitment and the blob versioned matches the ones retrieved from the network (aka computed by the nodes) What you may notice is there's no very direct association of the block number on the execution layer with the respective slot number on the consensus layer. We do need the slot number on the consensus layer, however, to be able to do a `sidecar` call and retrieve the blobs associated with the type-3 transactions that we detect on the execution layer. This is where the [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) will help us. The EIP introduced the first real way for the execution layer to have access to the consensus layer by having the parent block root of the consensus layer. Each block on the execution layer now has the parent (previous) slot root of the consensus layer in it stored as `parentBeaconBlockRoot`. We'll retrieve this parent slot root, get the slot number associated with root with the `eth/v1/beacon/headers/{parent_beacon_block_root_hex}` call on the consensus layer, and then do a simple `+ 1` to get the slot number that we need. ```python Python theme={"system"} import os import requests from web3 import Web3, HTTPProvider from dotenv import load_dotenv import ckzg import hashlib load_dotenv() # Connect to the Ethereum Execution Layer w3 = Web3(HTTPProvider(os.getenv("EXECUTION_LAYER_URL"))) # Specify the block number you want to check block_number = 6090748 block = w3.eth.get_block(block_number, full_transactions=True) # Find type-3 transactions type_3_tx_hashes = [tx.hash.hex() for tx in block.transactions if tx.type == 3] # Store blob versioned hashes in a dictionary blob_versioned_hashes_dict = {} for tx_hash in type_3_tx_hashes: tx_details = w3.eth.get_transaction(tx_hash) blob_versioned_hashes = tx_details.get('blobVersionedHashes', []) if blob_versioned_hashes: blob_versioned_hashes_dict[tx_hash] = blob_versioned_hashes[0].hex() # Extract the parentBeaconBlockRoot from the block data parent_beacon_block_root = block['parentBeaconBlockRoot'] # Convert byte string to hexadecimal string parent_beacon_block_root_hex = parent_beacon_block_root.hex() # Ensure it starts with '0x' if not parent_beacon_block_root_hex.startswith('0x'): parent_beacon_block_root_hex = '0x' + parent_beacon_block_root_hex # Print the parentBeaconBlockRoot for visibility print("parentBeaconBlockRoot being queried:", parent_beacon_block_root_hex) # Use parentBeaconBlockRoot for further queries headers_url = f"{os.getenv('CONSENSUS_LAYER_URL')}/eth/v1/beacon/headers/{parent_beacon_block_root_hex}" header_response = requests.get(headers_url) if header_response.status_code != 200: print("Failed to fetch data:", header_response.status_code) print(header_response.text) exit() header_data = header_response.json() if 'data' not in header_data: print("Unexpected response format:", header_data) exit() slot_number = int(header_data['data']['header']['message']['slot']) + 1 # Retrieve blobs blobs_url = f"{os.getenv('CONSENSUS_LAYER_URL')}/eth/v1/beacon/blobs/{slot_number}" blobs_response = requests.get(blobs_url).json() blobs = blobs_response['data'] # Process each blob results = [] for i, tx_hash in enumerate(type_3_tx_hashes): blob = blobs[i] print(f"Retrieved KZG commitment for transaction {tx_hash}: {blob['kzg_commitment']}") blob_data_hex = blob['blob'] # Save blob data to a file with open(f"blob{i}.txt", "w") as file: file.write(blob_data_hex) # Load blob data from the file and ensure it's correct with open(f"blob{i}.txt", "r") as file: blob_hex = file.read().strip() blob_data = bytes.fromhex(blob_hex.replace("0x", "")) # Ensure consistent handling print(f"Blob data file for transaction {tx_hash}: blob{i}.txt") # Load trusted setup ts = ckzg.load_trusted_setup("trusted_setup.txt") # Compute KZG commitment commitment = ckzg.blob_to_kzg_commitment(blob_data, ts) print(f"Locally computed KZG commitment for transaction {tx_hash}: {commitment.hex()}") # Compute versioned hash sha256_hash = hashlib.sha256(commitment).digest() versioned_hash = b'\x01' + sha256_hash[1:] # Compare with network data, ignoring the '0x' prefix network_commitment = blob['kzg_commitment'] local_commitment_hex = '0x' + commitment.hex() commitment_match = local_commitment_hex == network_commitment print(f"KZG commitment match for transaction {tx_hash}: {commitment_match}") # Use the stored blob versioned hashes during blob processing network_versioned_hash = blob_versioned_hashes_dict.get(tx_hash, "No blob versioned hash found") print(f"Network versioned hash for transaction {tx_hash}: {network_versioned_hash}") print(f"Blob data file for transaction {tx_hash}: blob{i}.txt") print(f"Locally computed KZG commitment for transaction {tx_hash}: {commitment.hex()}") print(f"Locally computed versioned hash for transaction {tx_hash}: {versioned_hash.hex()}") print() results.append({ 'transaction_hash': tx_hash, 'commitment': commitment.hex(), 'versioned_hash': versioned_hash.hex(), 'commitment_match': commitment_match, 'versioned_hash_match': versioned_hash.hex() == network_versioned_hash }) print("### SUMMARY ###") print(f"Block {block_number}, Slot {slot_number}") print("Type-3 transactions:", type_3_tx_hashes) print() for result in results: print(f"TX:{result['transaction_hash']}:") print(f"KZG: {result['commitment']}") print(f"Versioned hash: {result['versioned_hash']}") print(f"Locally computed match for the retrieved blob:") print(f"KZG commitment: {result['commitment_match']}") print(f"Versioned hash: {result['versioned_hash_match']}") print() ``` Make sure you have `trusted_setup.txt` in the same directory with the script. This will print the results and tell you if the independently computed KZG commitment and blob versioned hash match the ones retrieved off the network. Example for our transaction: ```shell Shell theme={"system"} TX:5a74bd72aeeb99e874e58b927f9a5c96665278a36b61bed69a4b09597b02edce: KZG: 9493a713dd89eb7fe295efd62545bb93bca395a84d18ecfa2c6c650cddc844ad4c1935cbe7d6830967df9d33c5a2e230 Versioned hash: 01afb6777db05b1376ccb3d0c1e842d437d2a8ad9c0acfb8247edab425fee61c Locally computed match for the retrieved blob: KZG commitment: True Versioned hash: True ``` ## Conclusion This hands-on the hard way guide walked you through creating, detecting, and verifying the type-3 transactions (aka blob transactions). It also showed you some of the pitfalls and how to navigate them like getting slot number associated with the execution layer blob transaction, being able to retrieve the blobs through the consensus layer client, blob data being stored for 18 days, and so on. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # BNB Smart Chain: Lorentz hardfork Source: https://docs.chainstack.com/docs/bnb-lorentz-hardfork Details on the BNB Smart Chain Lorentz hardfork activating BEP-520 for 1.5-second block intervals. Chainstack nodes are ready at activation. ## Main article Lorentz is an upgrade hardfork on the BNB Smart Chain mainnet that activates on April 29, 2025. Lorentz introduces one single change: [BEP-520: Short Block Interval Phase One: 1.5 seconds](https://github.com/bnb-chain/BEPs/blob/master/BEPs/BEP-520.md) > [!TIP] > > ### Chainstack nodes are Lorentz-ready > > Chainstack nodes are prepared for the Lorentz network upgrade. Here's what you need to know: * Block time halved from 3 seconds to 1.5 seconds * Gas limit per block halved from 140 million to 70 million * Epoch extended from 200 blocks to 500 blocks * Consecutive block validation per validator increased from 4 blocks to 8 blocks The most important changes that may affect you as a dApp developer are gas limit per block and faster block times. The gas limit per block is an obvious one—slashing 140 million gas to 70 million gas means you can fit fewer heavy compute transactions in a block. Now let's focus on faster block times ## 1.5 second block time Going from 3 seconds to 1.5 seconds means you will need to be ingesting the blocks at 2x the pre-fork speed. Chainstack infrastructure is handling the nodes, so just make sure your client-side service is ready for that. Now let's have a look at the block structure. ### Lorentz block structure Compare post-fork and post-node-upgrade: ```javascript theme={"system"} "baseFeePerGas": "0x0", "blobGasUsed": "0x0", "difficulty": "0x2", "excessBlobGas": "0x0", "extraData": "0xd883010509846765746888676f312e32332e37856c696e7578000000c011f0d2f8b27fb860a72b39a61672bf9410fecd9432d18ab0285d4475cc22583b71cf485b0c35d4109d83576db2dd1244484e36a6d9133f46159499e3db3db964b9dd4b9b2c7e185dbc2e3716df7b77befbd89ee2df8dd3d66c54f4a1fad622e1480dc019e35373c8f84c840303fa92a0db41c2b264c7bf23960962bc8e3d92f3538671c28f705760f0d700b17982d0fc840303fa93a0be106c47b19b9cc0341e36e6635202c3ed9b8548511f5f6c9bff56e9168f41b5801c415589572c27ab7d9ba60074e2148896ec1c10881891a52f43ead9084310fa58d5d44846a51c87a42d0f94194df1dc6e226747a693e64694bf9dce51610cd600", "gasLimit": "0x42c1d80", "gasUsed": "0xe464f9", "hash": "0xe58794d2ee429567b8107cbad4a67eb70929616748c4a0f4d76df436e5c56f6b", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "milliTimestamp": "0x1965c14398c", "miner": "0xd447b49cd040d20bc21e49ffea6487f5638e4346", "mixHash": "0x00000000000000000000000000000000000000000000000000000000000001f4", "nonce": "0x0000000000000000", "number": "0x303fa94", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0xbe106c47b19b9cc0341e36e6635202c3ed9b8548511f5f6c9bff56e9168f41b5", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0x37f", "stateRoot": "0x7c7abdcf4b9b0e660e5a3002f9946be0be54b1e99e3208e621598b443f254e37", "timestamp": "0x6807302f", "totalDifficulty": "0x6043378", "transactions": [], "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "uncles": [], "withdrawals": [], "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" ``` And pre-fork and pre-node-upgrade: ```javascript theme={"system"} "baseFeePerGas": "0x0", "blobGasUsed": "0x0", "difficulty": "0x2", "excessBlobGas": "0x0", "extraData": "0xd883010508846765746888676f312e32342e30856c696e7578000000ce18f5d3f8b5831effffb8609291fcd043ec871f0f8c00a11ef5a4e74164698519f3b391ed741010d736594d024b346d36092761a54899cdee613aa5063c028128f9a26884426ae237ea09ff38735645774849ccaf3e644655dcecf65e723ea971cb7aeeab8a25dd1185fa52f84c8402e52cffa04a0bda48a3d029c68c6663306c341213aad0ba3ff5d16f8c3251a59895641b4a8402e52d00a05e4a10dd4dcaa30a1eb6c194ae42abbda4f49632a0bbd37e4d6b48b45c9093f48030faf0179c11484aa23403d9619926beda9cb93ea4fc6e40e5e03ab46b64839b2474871bc03cd01bb57497c3fd7c97af75a42f7095dda366761ec21014bdb6df01", "gasLimit": "0x8583b00", "gasUsed": "0xe464f9", "hash": "0xe58794d2ee429567b8107cbad4a67eb70929616748c4a0f4d76df436e5c56f6b", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0xd447b49cd040d20bc21e49ffea6487f5638e4346", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x303fa94", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0xbe106c47b19b9cc0341e36e6635202c3ed9b8548511f5f6c9bff56e9168f41b5", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0x37f", "stateRoot": "0x7c7abdcf4b9b0e660e5a3002f9946be0be54b1e99e3208e621598b443f254e37", "timestamp": "0x6807302f", "totalDifficulty": "0x6043378", "transactions": [], "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "uncles": [], "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" ``` | Field | Pre-fork (v1.5.7) | Post-fork (v1.5.11 / Lorentz) | Comment | | - | - | - | - | | `gasLimit ` | 140,000,000 gas (0x8583b00) | 70,000,000 gas (0x42c1d80) | Block gas limit is halved, but blocks are produced twice as often (every 1.5s instead of 3s). This maintains the same overall throughput while improving transaction confirmation times. | | `milliTimestamp` | Not present | Added (e.g., 0x1965c14398c) | Introduced by BEP-520 to support 1.5-second block spacing without breaking the EVM's second-based timestamp system. | | `mixHash` | All zeros (unused in PoSA; legacy field from PoW) | Low 2 bytes contain sub-second remainder (0x01f4 = 500ms, 0x0000 = 0ms, etc.) | Allows clients to reconstruct the full millisecond timestamp while maintaining the same header size. | | `requestsHash` | Not present | Present (e3b0...b855 = empty SHA-256) | Introduced in [EIP-7685](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7685.md) and implemented on BNB Smart Chains as part of [BEP-466](https://github.com/bnb-chain/BEPs/blob/master/BEPs/BEP-466.md) and exposed with the upgrade node version. | Let's have a look at `milliTimestamp`. The `milliTimestamp` field is introduced to keep the original `timestamp` in integer seconds. With the switch from 3 seconds to 1.5 seconds, the value becomes more granular, allowing for precise millisecond-level timing while maintaining backward compatibility with the existing second-based timestamp system. The original `timestamp` value is preserved post-fork, and the previously unused `mixHash` value now contains the last bytes of the sub-second timestamp to complement the original `timestamp` value. This means you can retrieve the post-fork block timestamp either via the `milliTimestamp` field or via the `timestamp` + `mixHash` calculation. The `timestamp` + `mixHash` calculation algorithm: 1. Convert timestamp to decimal seconds: `0x6807302f` > 1745301551 s 2. Multiply by 1000 to get milliseconds: 1745301551 × 1000 > 1745301551000 ms 3. Extract the ms remainder from `mixHash`: `0x…01f4` > `0x1f4` > 500 ms 4. Add them together: 1745301551000 + 500 > 1745301551500 ms 5. Convert back to hex if desired `0x1965c14398c` Or in Python: ```python theme={"system"} timestamp = int("0x6807302f", 16) # 1745301551 s ms = int("0x1f4", 16) # 500 ms milli_ts = timestamp * 1000 + ms # 1745301551500 hex(milli_ts) # '0x1965c14398c' ``` And there you have it. Unless your service heavily relies on block numbers or block timestamps, you don't have much to worry about. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # BNB Smart Chain methods Source: https://docs.chainstack.com/docs/bnb-smart-chain-methods Complete reference of supported BNB Smart Chain JSON-RPC methods on Chainstack, including eth, debug, trace, and net namespace availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive BNB Smart Chain API call examples](/reference/getting-started-bnb-chain). On BNB Smart Chain Global Nodes, `eth_getProof` returns proofs only for blocks less than 127 behind the tip. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | txpool\_content | | | | txpool\_inspect | | | | txpool\_contentFrom | | | | txpool\_status | | | | web3\_clientVersion | | | | web3\_sha3 | | | | erigon\_blockNumber | | | | erigon\_forks | | | | erigon\_getBlockByTimestamp | | | | erigon\_getBlockReceiptsByBlockHash | | | | erigon\_getHeaderByHash | | | | erigon\_getHeaderByNumber | | | | erigon\_getLatestLogs | | | | erigon\_getLogsByHash | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_getTrieFlushInterval | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | trace\_block | | | | trace\_call | | | | trace\_callMany | | | | trace\_filter | | | | trace\_rawTransaction | | | | trace\_replayBlockTransactions | | | | trace\_replayTransaction | | | | trace\_transaction | | | | parlia\_getValidators | | | | parlia\_getSnapshot | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # BNB Smart Chain Trader Nodes with Warp transactions Source: https://docs.chainstack.com/docs/bnb-trader-nodes Use BNB Smart Chain Trader Nodes with Warp transactions for fast propagation through bloXroute's relay network, bypassing standard mempool delays. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). > [!NOTE] > > The Warp transactions feature is available starting from the [paid plans](https://chainstack.com/pricing/). > > Warp transactions (not requests, just the transactions) consumed are billed separately. For details, see [Pricing](https://chainstack.com/pricing/) with the [extra usage](/docs/manage-your-billing#manage-the-extra-usage-setting) setting enabled. ## Benefits with Chainstack Chainstack nodes with Warp transactions combine reliable nodes for pre-transaction operations with bloXroute's high-speed propagation for the transaction itself. Switching to a Chainstack node with Warp transactions (available on both Trader Nodes and Global Nodes) is as simple as changing the endpoint in your code. All calls go through Chainstack's infrastructure, except for `eth_sendRawTransaction`, which is routed directly to bloXroute. To enable Warp transactions, install the [Warp transactions](/docs/warp-transactions) add-on on your Trader or Global Node's **Add-ons** tab. ## Sample use case 1. A token liquidity is deployed on BNB Smart Chain. 2. You receive a signal through your BNB Smart Chain node by monitoring the PancakeSwap contracts. 3. Your bot acts on the signal, sending a transaction via a Chainstack node with Warp transactions enabled. 4. Transaction is quickly propagated, increasing chances of winning the race. ## Availability and usage * Available from the [paid plans](https://chainstack.com/pricing). * Warp transactions (not requests, just the transactions) consumed are billed separately. For details, see [Pricing](https://chainstack.com/pricing/) with the [extra usage](/docs/manage-your-billing#manage-the-extra-usage-setting) setting enabled. * Only `eth_sendRawTransaction` calls are consumed as Warp transactions. * Deploy nodes close to your bot for best performance. See also [Sending Trader Node Warp transaction with web3.js, ethers.js, web3.py, and ethClient.go](/docs/sending-warp-transaction-with-web3js-ethersjs-web3py-and-ethclientgo). # BNB Smart Chain tooling Source: https://docs.chainstack.com/docs/bsc-tooling Set up MetaMask, Hardhat, Foundry, and BNB Smart Chain client to build and deploy contracts on BSC through your Chainstack RPC node endpoints. Get started with a [reliable BNB Smart Chain RPC endpoint](https://chainstack.com/build-better-with-bnb-smart-chain/) to use the tools below. ## BNB Smart Chain client Interact with your BNB Smart Chain node using [BNB Smart Chain client](https://docs.bnbchain.org/docs/validator/fullnode). 1. Install [BNB Smart Chain client](https://docs.bnbchain.org/docs/validator/fullnode). 2. Use `geth attach` command with the node endpoint. ```bash Shell theme={"system"} geth attach YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). 3. Invoke any methods from [Web3 JavaScript API](https://web3js.readthedocs.io/). Example below demonstrates how to get the balance of an address in wei value and convert it to ether value: ```javascript Javascript theme={"system"} > web3.fromWei(web3.eth.getBalance("0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae")) 642538.078574759898951277 ``` ## GraphQL You can use GraphQL on a Dedicated Node (Dedicated Node - a public network node deployed exclusively for the customer. You exclusively use such nodes for your production purposes. With a Dedicated Node, you pay for the compute and storage resources used by the node and do not pay for JSON-RPC requests to the node.) on the [paid plans](https://chainstack.com/pricing/). ### UI You can query data using the graphical interface. 1. On Chainstack, navigate to your dedicated BNB Smart Chain node. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). 2. Hover over **GraphQL IDE URL** and click **Open**. 3. In the graphical interface that opens, run a GraphQL query. Example to get the latest block number: ```graphql GraphQL theme={"system"} { block { number } } ``` ### Node.js You can build a web app to query data using node.js and [axios](https://www.npmjs.com/package/axios): ```javascript Javascript theme={"system"} const axios = require('axios'); const main = async () => { try { const result = await axios.post( 'YOUR_CHAINSTACK_ENDPOINT', { query: ` { block { number } } ` } ); console.log(result.data); } catch(error) { console.error(error); } } main(); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node GraphQL endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * `query` — your GraphQL query. In this case, to get the latest block number. See also [Using GraphQL with EVM-compatible nodes](https://support.chainstack.com/hc/en-us/articles/4409604331161-Using-GraphQL-with-EVM-compatible-nodes). ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your BNB Smart Chain nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and BNB Smart Chain nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number: ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and BNB Smart Chain nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Key Protected theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and BNB Smart Chain nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and BNB Smart Chain nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — BNB Smart Chain network ID: * Mainnet: `56` * Testnet: `97` See [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — BNB Smart Chain network ID: * Mainnet: `56` * Testnet: `97` See [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Brownie 1. Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). 2. Use the `brownie networks add` command with the node endpoint: ```shell Shell theme={"system"} brownie networks add "BNB Smart Chain" ID name="NETWORK_NAME" host=YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `bsc-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint * NETWORK\_ID — BNB Smart Chain network ID: * Mainnet: `56` * Testnet: `97` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # BNB Smart Chain: BEP-1155 with Truffle & OpenZeppelin Source: https://docs.chainstack.com/docs/bsc-tutorial-bep-1155-contract-with-truffle-and-openzeppelin Learn to create BEP-1155 multi-token contracts with Truffle and OpenZeppelin on BNB Smart Chain. Deploy fungible and non-fungible tokens using Chainstack nodes. **TLDR:** * You’ll create a BEP-1155 contract combining fungible and non-fungible tokens in one deployment. * You’ll rely on OpenZeppelin libraries and Truffle to compile, deploy, and verify your tokens on BNB Smart Chain testnet via Chainstack. * You’ll flatten your code for a smooth verification on BscScan. * By the end, you’ll have fungible tokens (0) and NFT tokens (1) all in a single smart contract. ## Main article BEP-1155 is the multi-token standard for smart contracts that combines the fungibility of BEP-20 and the non-fungibility of BEP-721 in one contract. With a single BEP-1155 contract, you can deploy an ecosystem that has both fungible tokens (currency) and non-fungible tokens (NFTs). In this tutorial, you will: * Create a BEP-1155 contract that has a supply of fungible tokens and one non-fungible token. * Deploy the contract on the BNB Smart Chain testnet through a node deployed with Chainstack. * Interact with the deployed contract. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable BNB Smart Chain RPC endpoint](https://chainstack.com/build-better-with-bnb-smart-chain/). * [Truffle Suite](https://trufflesuite.com/) to create and deploy contracts. * [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts/4.x/) to use the audited [ERC-1155 libraries](https://docs.openzeppelin.com/contracts/4.x/erc1155) to create your BEP-1155 contract. ## Overview To get from zero to a deployed BEP-1155 contract on the BNB Smart Chain testnet, do the following: 1. With Chainstack, create a public chain project (public chain project- a project to join public networks). 2. With Chainstack, join the BNB Smart Chain testnet. 3. With Chainstack, access your BNB Smart Chain node credentials. 4. With OpenZeppelin, create a BEP-1155 contract. 5. With Truffle, compile and deploy the contract through your BNB Smart Chain node. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the BNB Smart Chain testnet See [Join a public network](/docs/manage-your-networks). ### Get your BNB Smart Chain node access and credentials See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Install OpenZeppelin Contracts See [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts/4.x/). ### Install Truffle Suite See [Truffle Suite: Installation](https://trufflesuite.com/docs/truffle/how-to/install/). ### Create the contract 1. In your contract directory, initialize Truffle: ```bash Shell theme={"system"} truffle init ``` This will generate the Truffle boilerplate structure: ```bash Shell theme={"system"} . ├── contracts │ └── .gitkeep ├── migrations │ └── .gitkeep ├── test │ └── .gitkeep └── truffle-config.js ``` 2. Go to the `contracts` directory. In the directory, create your BEP-1155 contract: `BNBSmartChain1155.sol`. ```solidity solidity theme={"system"} //SPDX-License-Identifier: MIT pragma solidity ^0.8; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; contract BNBSmartChain1155 is ERC1155 { uint256 public constant FUNGIBLE = 0; uint256 public constant NON_FUNGIBLE = 1; constructor() ERC1155("JSON_URI") { _mint(msg.sender, FUNGIBLE, 100, ""); _mint(msg.sender, NON_FUNGIBLE, 1, ""); } } ``` The contract implementation is the following: * The contract uses OpenZeppelin audited [ERC-1155 contract templates](https://docs.openzeppelin.com/contracts/4.x/erc1155). * The contract creates two tokens: 100 fungible units of the currency called `FUNGIBLE` and 1 non-fungible unit called `NON-FUNGIBLE`. In the BEP-1155 standard, setting a token issuance to `1` makes it non-fungible. * The contract also has `JSON_URI` which is a locator for the metadata of your tokens hosted externally. For example, ``. See [EIP-1155](https://github.com/ethereum/eips/issues/1155) for details. 3. Create `2_deploy_contracts.js` in the `migrations` directory. ```js JavaScript theme={"system"} module.exports = function(deployer) { var BNBSmartChain1155 = artifacts.require("./BNBSmartChain1155.sol"); deployer.deploy(BNBSmartChain1155); }; ``` This will create the contract deployment instructions for Truffle. ### Compile and deploy the contract 1. Install `HDWalletProvider`. [HDWalletProvider](https://github.com/trufflesuite/truffle/tree/develop/packages/hdwallet-provider) is Truffle's separate npm package used to sign transactions. Run: ```bash Shell theme={"system"} npm install @truffle/hdwallet-provider ``` 2. Edit `truffle-config.js` to add: * `HDWalletProvider` * Your BNB Smart Chain node access and credentials * Your BNB Smart Chain account that you will use to deploy the contract ```js JavaScript theme={"system"} const HDWalletProvider = require("@truffle/hdwallet-provider"); const private_key = 'PRIVATE_KEY'; module.exports = { networks: { testnet: { provider: () => new HDWalletProvider(private_key, "YOUR_CHAINSTACK_ENDPOINT"), network_id: 97, confirmations: 3, timeoutBlocks: 200, skipDryRun: true } }, compilers: { solc: { version: "0.8.2", } } }; ``` where * `testnet` — any network name that you will pass to the `truffle migrate --network` command. * `HDWalletProvider` — Truffle's custom provider to sign transactions. * PRIVATE\_KEY — the private key of your BNB Smart Chain account that will deploy the contract. The account must have enough BNB funds to run the deployment. See also [BNB Smart Chain Faucet](https://faucet.chainstack.com/bnb-testnet-faucet). * YOUR\_CHAINSTACK\_ENDPOINT — your Chainstack node HTTPS endpoint. See also [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) and [BNB Smart Chain tooling](/docs/bsc-tooling). * `network_id` — the network ID of the BNB Smart Chain network: mainnet is `56`, testnet is `97`. * `solc` — the Solidity compiler version that Truffle must use. OpenZeppelin contracts have a higher version Solidity compiler requirement than the default Truffle installation, hence you must provide a specific compiler version. 3. Run: ```bash Shell theme={"system"} truffle migrate --network testnet ``` This will engage `2_deploy_contracts.js` and deploy the contract to the BNB Smart Chain testnet as specified in `truffle-config.js`. ### Interact with the contract Once your contract is deployed, you can view it online at [BscScan testnet](https://testnet.bscscan.com/). Network explorers, including BscScan, do not display the NFT standards by default, so you will have to perform additional steps to check the balance of your issued tokens. Namely, you must verify the contract on BscScan to interact with it online. ### Flatten your contract code Since your BEP-1155 contract uses imported OpenZeppelin libraries, you must put all the imports into one `.sol` file to make BscScan be able to verify it. 1. Install [Truffle Flattener](https://www.npmjs.com/package/truffle-flattener). Run: ```bash Shell theme={"system"} npm install truffle-flattener ``` 2. Flatten the contract. In the `contracts` directory, run: ```bash Shell theme={"system"} npx truffle-flattener BNBSmartChain1155.sol > FlatBNBSmartChain1155.sol ``` 3. Clean up the licensing information. The flattened contract will have the same licensing note imported from each of the files. Multiple licensing notes in one file break the BscScan verification, so you have to leave one licensing note for the entirety of the flattened contract. The easiest way to clean up is to search for the `SPDX` mentions in the file and remove all of them except for the very first one. ### Verify the deployed contract on BscScan At this point, you have your flattened and cleaned-up contract ready for the BscScan verification. 1. Go to [BscScan testnet](https://testnet.bscscan.com/). 2. Find your deployed contract. The address of your contract should have been printed by Truffle at the end of the deployment in the `contract address` field. 3. On the contract page on BscScan, click **Contract** > **Verify and Publish**. 4. In **Compiler Type**, select **Solidity (Single file)**. 5. In **Compiler Version**, select **v0.8.2**. This is the version this tutorial used to compile the contract. 6. Click **Continue**. 7. Keep the **Optimization** option set to **No** as Truffle does not use optimization by default. 8. Paste the entirety of your flattened `.sol` contract in the **Enter the Solidity Contract Code below** field. 9. Click **Verify and Publish**. BscScan will take a few seconds to compile your contract, verify, and publish it. ### Check the balance Now that your BEP-1155 contract is verified, you can check your balance of the issued tokens on BscScan. 1. On BscScan, on your contract, click **Contract**. 2. Click **Read Contract**. 3. Scroll to the **balanceOf** field. 4. In the **account** field, provide the address of the account you used to deploy the contract. 5. In the **id** field, put `0` to check your fungible balance and put `1` to check your non-fungible balance. ## Conclusion This tutorial guided you through the basics of creating and deploying a contract in the BEP-1155 multi-token standard. The BEP-1155 is useful in that it can deploy an ecosystem of currencies and NFTs in one go—you can add however many fungible and non-fungible tokens to your contract. You also verified your deployed contract online and interacted with it. This tutorial uses testnet, however, the exact same instructions and sequence will work on the mainnet as well. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Celo: Build a voting dApp with Foundry and Next.js Source: https://docs.chainstack.com/docs/celo-build-a-simple-voting-dapp-with-foundry-nextjs-and-web3js Build a voting dApp on Celo with a Solidity contract deployed via Foundry and a React/Next.js frontend using web3.js and MetaMask for interaction. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR:** * You’ll build a simple voting DApp on Celo: a Solidity contract (Voting.sol) deployed with Foundry, and a React/Next.js front-end with web3.js to interact via MetaMask. * The Solidity contract defines two initial candidates (Luna, Orion) and basic voting logic, tested with Foundry’s test suite. * The front end fetches candidate data, checks the chain/network, and allows users to cast votes. * This serves as a baseline for deeper improvements (security, Sybil protection, etc.) before production. ## Introduction to Celo Celo is an open-source blockchain ecosystem that makes decentralized financial (DeFi) tools and services accessible to anyone with a smartphone. It is designed to support financial inclusion and provide a platform for decentralized applications (DApps) with a particular emphasis on mobile usability. > [!WARNING] > > ### NFP > > Not for production (NFP) obviously. Feel free to take the source and modify to your needs. > > We assume no responsibility for the code. Moreover, this is a very rough unaudited contract. ## Project overview: Simple voting DApp In this tutorial, we'll build a simple voting DApp on the Celo blockchain. The project involves deploying a smart contract using Foundry and creating a simple user interface with Next.js and web3.js to interact with MetaMask. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Celo RPC endpoint](https://chainstack.com/build-better-with-celo/) * [Foundry](https://book.getfoundry.sh/) for smart contract development * [Next.js](https://nextjs.org/) for the front-end framework * [web3.js](https://docs.web3js.org/) for interacting with the blockchain * Some Celo tokens ## Step-by-step ### Get a Celo node ### Smart contract development Use Foundry to develop, compile, and deploy a simple voting smart contract. Install Foundry on your machine; you can [follow the instructions in the Foundry book](https://book.getfoundry.sh/getting-started/installation). Once installed, create a new directory for your project and initialize a Foundry project. ``` forge init foundry ``` This will create a Foundry project with the following layout: ``` . ├── README.md ├── foundry.toml ├── lib │   └── forge-std │   ├── LICENSE-APACHE │   ├── LICENSE-MIT │   ├── README.md │   ├── foundry.toml │   ├── package.json │   ├── scripts │   ├── src │   └── test ├── script │   └── Counter.s.sol ├── src │   └── Counter.sol └── test └── Counter.t.sol ``` In the `src` directory, rename the sample smart contract to `Voting.sol` and paste the following contract: > [!WARNING] > > ### Disclaimer > > This Solidity smart contract is provided as an educational example and is not intended for production use. The code is simplified for clarity and lacks several critical features required for a secure and efficient production-grade application. ```sol Voting.sol theme={"system"} // SPDX-License-Identifier: MIT // Celo only supports up to 0.8.19 due to the new Push0 opcode pragma solidity >=0.8.0 <=0.8.19; /// @title A simple voting contract /// @notice This contract allows users to vote for pre-defined candidates /// @dev This contract uses mappings to store candidate and voter information contract Voting { struct Candidate { string name; uint voteCount; } mapping(uint => Candidate) public candidates; mapping(address => bool) public voters; uint public candidatesCount; event CandidateAdded(uint indexed candidateId, string name); event Voted(address indexed voter, uint indexed candidateId); /// @notice Constructor to initialize the contract with default candidates constructor() { addCandidate("Luna"); addCandidate("Orion"); } /// @notice Adds a new candidate to the candidates list /// @dev This function is private and can only be called within the contract /// @param _name The name of the candidate to add function addCandidate(string memory _name) private { candidatesCount++; candidates[candidatesCount] = Candidate(_name, 0); emit CandidateAdded(candidatesCount, _name); } /// @notice Allows a user to vote for a candidate /// @dev This function checks if the voter has already voted and if the candidate ID is valid /// @param _candidateId The ID of the candidate to vote for function vote(uint _candidateId) public { require(!voters[msg.sender], "Already voted."); require( _candidateId > 0 && _candidateId <= candidatesCount, "Invalid candidate." ); voters[msg.sender] = true; candidates[_candidateId].voteCount++; emit Voted(msg.sender, _candidateId); } /// @notice Returns the name and vote count of a candidate /// @dev This function retrieves candidate information based on the candidate ID /// @param _candidateId The ID of the candidate to retrieve /// @return name The name of the candidate /// @return voteCount The vote count of the candidate function getCandidate( uint _candidateId ) public view returns (string memory name, uint voteCount) { Candidate memory candidate = candidates[_candidateId]; return (candidate.name, candidate.voteCount); } } ``` Follow the comments in the smart contract to understand the implementation; here is a quick breakdown. #### TL;DR smart contract breakdown This Solidity smart contract implements a simple voting system. Here's a concise breakdown: 1. **Contract Name**: `Voting` * Implements a basic voting mechanism. 2. **Key Components**: * **Candidate Struct**: Represents a candidate with a `name` and `voteCount`. * **Mappings**: * `candidates`: Maps a candidate ID to a `Candidate` struct. * `voters`: Maps an address to a boolean indicating if the address has voted. 3. **State Variables**: * `candidatesCount`: Tracks the number of candidates. 4. **Events**: * `CandidateAdded`: Emitted when a new candidate is added. * `Voted`: Emitted when a vote is cast. 5. **Constructor**: * Initializes the contract by adding two default candidates: "Luna" and "Orion". 6. **Functions**: * **`addCandidate`**: * Private function to add a new candidate. * Increments `candidatesCount` and updates the `candidates` mapping. * Emits the `CandidateAdded` event. * **`vote`**: * Allows a user to vote for a candidate. * Check if the voter has already voted and the candidate's ID is valid. * Updates the `voters` mapping to mark the address as having voted. * Increments the candidate's `voteCount`. * Emits the `Voted` event. * **`getCandidate`**: * Returns the name and vote count of a candidate by ID. #### Functionality summary * **Adding Candidates**: Candidates can only be added internally via the `addCandidate` function, which is called in the constructor to add initial candidates. * **Voting**: Users can vote once for a candidate by providing the candidate's ID. The contract ensures each user votes only once and only for valid candidates. * **Retrieving Candidate Info**: Users can get a candidate's name and vote count by providing the candidate's ID. This smart contract is designed for a simple voting scenario where users vote for pre-defined candidates, and the results are publicly accessible. #### Test smart contract Now that we have a contract let's implement a simple test script within Foundry. In the `test` directory, rename the current sample script to `Voting.t.sol` and paste the following script: ```sol Voting.t.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Test} from "../lib/forge-std/src/Test.sol"; import {Voting} from "../src/Voting.sol"; contract VotingTest is Test { Voting voting; // The setUp function runs before each test function // It creates a new instance of the Voting contract to ensure each test starts with a fresh contract function setUp() public { voting = new Voting(); } // This test checks that a voter cannot vote more than once function testCannotVoteTwice() public { // Cast the first vote for candidate with ID 1 voting.vote(1); // Expect the next vote attempt from the same voter to revert with "Already voted." message vm.expectRevert(bytes("Already voted.")); // Attempt to vote again for the same candidate, which should fail voting.vote(1); } // This test checks that a voter cannot vote for an invalid candidate function testCannotVoteInvalidCandidate() public { // Expect the vote attempt for an invalid candidate ID (3) to revert with "Invalid candidate." message // The contract is deployed with 2 candidates vm.expectRevert(bytes("Invalid candidate.")); // Attempt to vote for a non-existent candidate, which should fail voting.vote(3); } } ``` This basic test script checks that an address cannot vote twice and that a user cannot vote for a candidate not on the list. Note how this contract lacks any real Sybil protection; this is an improvement you can add. Move your terminal within the Foundry project and run the test command. ``` forge test ``` This will run the test script and you should see all the tests pass. ``` [⠘] Compiling... [⠔] Compiling 1 files with 0.8.19 [⠑] Solc 0.8.19 finished in 1.69s Compiler run successful! Running 2 tests for test/Voting.t.sol:VotingTest [PASS] testCannotVoteInvalidCandidate() (gas: 13027) [PASS] testCannotVoteTwice() (gas: 57875) Test result: ok. 2 passed; 0 failed; 0 skipped; finished in 5.57ms Ran 1 test suites: 2 tests passed, 0 failed, 0 skipped (2 total tests) ``` #### Deploy the smart contract We have a tested smart contract; let's deploy it on Celo using Foundry and your Chainstack node. If you haven't yet, ensure you have some Celo tokens. Let's compile the smart contract: ``` forge compile ``` Then, we can deploy the contract in one single command using `forge create`: ``` forge create --rpc-url YOUR_CELO_CHAINSTACK_RPC --private-key YOUR_PRIVATE_KEY src/Voting.sol:Voting ``` > [!NOTE] > > Make sure to add your RPC url and your private key to the command editing `YOUR_CELO_CHAINSTACK_RPC` and `YOUR_PRIVATE_KEY`. > > Also note that this is a quick way to deploy and test, but exposing endpoints and private keys in your terminal is not a good security practice; ensure the wallet is used for testing only. This will deploy the smart contract on Celo. ``` [⠆] Compiling... No files changed, compilation skipped Deployer: 0x8f8e7012F8F974707A8F11C7cfFC5d45EfF5c2Ae Deployed to: 0x5564C4fC4842898Cf78B59373D822A32431d9f46 Transaction hash: 0x383821697a1019f36fe2ab4206463d58bf6bea4e226a4a8db8846a8a48d1eac4 ``` The transaction can be seen on the [Celo explorer](https://explorer.celo.org/alfajores/tx/0x383821697a1019f36fe2ab4206463d58bf6bea4e226a4a8db8846a8a48d1eac4); the link will show you an example of this contract deployment. > [!NOTE] > > We'll need the contract ABI and the address where it was deployed for the front end. You can find the ABI in the Foundry project in`out/Voting.sol/Voting.json`. If you didn't make any edits to the contract, you'll find the proper ABI already implemented in the front-end code we'll review in the next section. ### Developing the front end Now that we have deployed the smart contract, we can create a simple front end so that users can interact with it and vote. Let's initiate a Next.js project. You can do this in a different directory. ``` npx create-next-app@latest celo-voting-dapp ``` You can initialize the project with the following options ``` ✔ Would you like to use TypeScript? … No ✔ Would you like to use ESLint? … Yes ✔ Would you like to use Tailwind CSS? … Yes ✔ Would you like to use `src/` directory? … Yes ✔ Would you like to use App Router? (recommended) … Yes ✔ Would you like to customize the default import alias (@/*)? … No ``` Then, move into that directory: ``` cd celo-voting-dapp ``` And install the web3.js package: ``` npm i web3@4.9.0 ``` Then in the Next project, go in `src/app/page.js`and paste the following: > [!NOTE] > > Remember to add your node URL and smart contract address in: > > ``` > > const nodeUrl = "YOUR_CHAINSTACK_CELO_URL"; > > > > // Add the address of your smart contract, you can use this if you don't have one 0x5564C4fC4842898Cf78B59373D822A32431d9f46 > > const contractAddress = "YOUR_DEPLOYED_SMART_CONTRACT"; > ``` > > Also note that exposing your endpoint in the front end like this is not good security practice, but it works for a prototype. ```javascript page.js theme={"system"} "use client"; import { useEffect, useState } from "react"; import { Web3 } from "web3"; // You node URL and chain ID const nodeUrl = "YOUR_CHAINSTACK_CELO_URL"; const chainId = "0xa4ec"; // 42220 Chain ID for Celo // Add the address of your smart contract const contractAddress = "YOUR_DEPLOYED_SMART_CONTRACT"; const contractAbi = [ { type: "constructor", inputs: [], stateMutability: "nonpayable" }, { type: "function", name: "candidates", inputs: [{ name: "", type: "uint256", internalType: "uint256" }], outputs: [ { name: "name", type: "string", internalType: "string" }, { name: "voteCount", type: "uint256", internalType: "uint256" }, ], stateMutability: "view", }, { type: "function", name: "candidatesCount", inputs: [], outputs: [{ name: "", type: "uint256", internalType: "uint256" }], stateMutability: "view", }, { type: "function", name: "getCandidate", inputs: [ { name: "_candidateId", type: "uint256", internalType: "uint256" }, ], outputs: [ { name: "name", type: "string", internalType: "string" }, { name: "voteCount", type: "uint256", internalType: "uint256" }, ], stateMutability: "view", }, { type: "function", name: "vote", inputs: [ { name: "_candidateId", type: "uint256", internalType: "uint256" }, ], outputs: [], stateMutability: "nonpayable", }, { type: "function", name: "voters", inputs: [{ name: "", type: "address", internalType: "address" }], outputs: [{ name: "", type: "bool", internalType: "bool" }], stateMutability: "view", }, { type: "event", name: "CandidateAdded", inputs: [ { name: "candidateId", type: "uint256", indexed: true, internalType: "uint256", }, { name: "name", type: "string", indexed: false, internalType: "string" }, ], anonymous: false, }, { type: "event", name: "Voted", inputs: [ { name: "voter", type: "address", indexed: true, internalType: "address", }, { name: "candidateId", type: "uint256", indexed: true, internalType: "uint256", }, ], anonymous: false, }, ]; export default function Home() { const [candidates, setCandidates] = useState([]); const [account, setAccount] = useState(""); const [isCorrectNetwork, setIsCorrectNetwork] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); useEffect(() => { // Load the user's account when the component mounts loadAccount(); }, []); useEffect(() => { // Load candidates if the user is connected to the correct network if (isCorrectNetwork) { loadCandidates(); } }, [isCorrectNetwork]); const checkNetwork = async (web3) => { // Get the current chain ID const currentChainId = await web3.eth.getChainId(); // Check if the user is connected to the correct network if (currentChainId !== parseInt(chainId, 16)) { // Switch to the correct network if not await switchNetwork(); } else { setIsCorrectNetwork(true); } }; const switchNetwork = async () => { try { // Request to switch the network in MetaMask await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: chainId }], }); setIsCorrectNetwork(true); } catch (switchError) { // Handle the error if the network is not available in MetaMask if (switchError.code === 4902) { setError( "This network is not available in your MetaMask, please add it manually" ); } } }; const loadAccount = async () => { if (window.ethereum) { const web3 = new Web3(window.ethereum); try { // Request the user's accounts from MetaMask await window.ethereum.request({ method: "eth_requestAccounts" }); const accounts = await web3.eth.getAccounts(); // Set the user's account setAccount(accounts[0]); // Check if the user is connected to the correct network await checkNetwork(web3); return accounts[0]; } catch (error) { // Handle the error if the user denies account access setError("User denied account access"); return null; } } else { // Handle the error if MetaMask is not detected setError("MetaMask not detected"); return null; } }; const disconnectAccount = () => { // Reset the user's account and network status setAccount(""); setIsCorrectNetwork(false); }; const loadCandidates = async () => { const web3 = new Web3(nodeUrl); const contract = new web3.eth.Contract(contractAbi, contractAddress); // Get the total number of candidates from the smart contract const candidatesCount = await contract.methods.candidatesCount().call(); const candidatesArray = []; // Fetch each candidate's details from the smart contract for (let i = 1; i <= candidatesCount; i++) { const candidate = await contract.methods.getCandidate(i).call(); candidatesArray.push({ id: i, name: candidate[0], voteCount: parseInt(candidate[1], 10), }); } // Update the state with the list of candidates setCandidates(candidatesArray); setLoading(false); }; const vote = async (candidateId) => { // Load the user's account if not already loaded const account = await loadAccount(); if (!account) return; const web3 = new Web3(window.ethereum); const contract = new web3.eth.Contract(contractAbi, contractAddress); try { // Call the vote function in the smart contract await contract.methods.vote(candidateId).send({ from: account }); // Reload the candidates to update the vote counts loadCandidates(); } catch (error) { // Handle the error if voting fails setError("Error voting: " + error.message); } }; return (

Celo Voting App | Dashboard powered by Chainstack

Connect your account and vote for your candidate!

Each account can only vote once.

{error &&
{error}
} {!account ? (
) : ( <>
Connected: {account}
{isCorrectNetwork ? ( loading ? (
Loading candidates...
) : (
{candidates.map((candidate) => (
{candidate.name}: {candidate.voteCount} votes
))}
) ) : (
Please switch to the Celo Alfajores Testnet to vote.
)} )}
); } ``` #### TL;DR front-end code breakdown This React component implements a frontend interface for a simple voting application that interacts with a Celo blockchain smart contract. Here's a concise breakdown: 1. **Dependencies**: * `Web3` library for blockchain interaction. * React hooks (`useEffect`, `useState`) for managing state and side effects. 2. **Key Variables**: * `nodeUrl`: URL of the Celo blockchain node. * `chainId`: Chain ID for the Celo Mainnet. * `contractAddress`: Address of the deployed voting smart contract. * `contractAbi`: ABI (Application Binary Interface) of the smart contract. 3. **State Variables**: * `candidates`: Array to store candidate details. * `account`: Stores the user's blockchain account address. * `isCorrectNetwork`: Boolean indicating if the user is connected to the correct blockchain network. * `loading`: Boolean indicating if candidate data is being loaded. * `error`: String for storing error messages. 4. **Lifecycle Hooks**: * `useEffect`: Loads the user's account on the component mount and checks the network status. If the user is connected to the correct network, it loads candidates. 5. **Functions**: * **`checkNetwork`**: Checks if the user is connected to the correct blockchain network and switches networks if necessary. * **`switchNetwork`**: Switches the user's MetaMask network to the Celo Mainnet. * **`loadAccount`**: Requests the user's account address from MetaMask and checks the network status. * **`disconnectAccount`**: Resets the account and network status. * **`loadCandidates`**: Loads candidate details from the smart contract and updates the state. * **`vote`**: The user can vote for a candidate by using the smart contract. 6. **UI Elements**: * Displays the application title and instructions. * Connect button to log in with MetaMask. * Displays the connected account address and log-out button. * Displays a list of candidates with their names, vote counts, and vote buttons. * Error messages and network status messages. This frontend code provides a user interface for interacting with the voting smart contract, enabling users to connect their MetaMask accounts, vote for candidates, and view current voting results. ### Run the DApp All the pieces are together now. If you do not change the contract, the ABI in the front end will work. Otherwise, you'll need to add the updated ABI. Run the project with: ``` npm run dev ``` Your front end will be available on `http://localhost:3000`: ``` > celo-tutorial@0.1.0 dev > next dev ▲ Next.js 14.2.3 - Local: http://localhost:3000 ``` Click on the **Login with MetaMask** button to connect your wallet to the DApp. Then, you can vote for a candidate by signing a transaction. ## Conclusion Congratulations! You have successfully built a simple voting DApp on the Celo blockchain. Following this tutorial, you've learned how to develop, test, and deploy a smart contract using Foundry and create a frontend interface with Next.js and web3.js to interact with the contract. This project is a foundational example of getting you started with blockchain development on Celo. Remember to enhance and secure your DApp before deploying it to a production environment. # Celo methods Source: https://docs.chainstack.com/docs/celo-methods Complete reference of supported Celo JSON-RPC methods on Chainstack nodes, including eth, debug, trace, and web3 namespace availability for each call. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_simulateV1 | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Celo tooling Source: https://docs.chainstack.com/docs/celo-tooling Set up MetaMask, Hardhat, Foundry, and other development tools to build and deploy smart contracts on Celo through your Chainstack RPC node endpoints. Get started with a [reliable Celo RPC endpoint](https://chainstack.com/build-better-with-celo/) to use the tools below. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Celo nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Celo nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.JsonRpcProvider(urlInfo.url, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Celo network ID: * Celo Mainnet: `42220` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Celo nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Celo nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Php theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Chainbench: Benchmark your blockchain RPC endpoints Source: https://docs.chainstack.com/docs/chainbench-tutorial-benchmark-your-blockchain-rpc-endpoints Benchmark RPC endpoint performance using Chainbench with configurable test patterns, concurrent requests, and detailed latency reporting on Chainstack. **TLDR:** * You'll learn how to install and use Chainbench, an open-source blockchain infrastructure benchmarking tool built on Locust. * You'll run load tests against your RPC endpoints using built-in profiles for EVM chains and Solana. * You'll discover available methods on endpoints and create custom test profiles. * You'll understand how to use both web UI and headless modes for interactive and automated testing. ## Main article In this tutorial, you will: * Install Chainbench and explore its features. * Run load tests against blockchain RPC endpoints using single methods and profiles. * Use the discover command to check method availability on your nodes. * Create custom profiles for specific testing scenarios. * Run tests in both web UI and headless modes. The tool is open source and available in the [Chainbench GitHub repository](https://github.com/chainstacklabs/chainbench). ## Prerequisites * [Chainstack account](https://console.chainstack.com/user/login) to deploy blockchain RPC endpoints for testing * Python 3.10+ installed on your machine * A blockchain RPC endpoint to test (Chainstack provides [Global Nodes and Dedicated Nodes](https://chainstack.com/pricing/) across 70+ protocols) ## Overview To get started with Chainbench: 1. Install Chainbench using pip. 2. Explore available commands and profiles. 3. Run a single-method load test. 4. Run a profile-based load test. 5. Use the discover command to check method availability. 6. Create a custom profile for your specific use case. 7. Run tests in headless mode for automation. ## Step-by-step ### Install Chainbench Install Chainbench using pip: ```shell Shell theme={"system"} pip install chainbench ``` Verify the installation: ```shell Shell theme={"system"} chainbench --help ``` This displays all available commands and options. ### Alternative installation with Poetry > [!NOTE] > > If you prefer to work with the source code or contribute to the project: > > ```shell Shell theme={"system"} > git clone https://github.com/chainstacklabs/chainbench.git > cd chainbench && poetry install --without dev > poetry run chainbench --help > ``` ### Explore available resources Before running tests, explore what's available: ```shell List profiles theme={"system"} chainbench list profiles ``` This shows all built-in profiles including: * **EVM chains**: Ethereum, BSC, Polygon, Arbitrum, Optimism, Avalanche, Fantom, Base, Gnosis, Oasis, Ronin * **Non-EVM**: Solana, Starknet (partial support) * **Generic EVM profiles**: `evm.light`, `evm.heavy`, `evm.get_logs`, `evm.debug_trace`, `evm.all` ```shell List methods theme={"system"} chainbench list methods ``` This shows all RPC methods you can test individually. ```shell List load shapes theme={"system"} chainbench list shapes ``` This lists extra load shapes you can pass with `--shape`: * **step**: Load increases in discrete steps * **spike**: 10% users → spike to 100% → back to 10% If you do not set `--shape`, Chainbench uses the default ramp-up pattern (users ramp up until the target is reached, then it holds steady for the rest of the test). ### Run a single-method load test Test a single RPC method against your endpoint: > [!WARNING] > > Load testing can generate a lot of requests quickly. Start small (for example, `--users 5` and `--test-time 30s`) and scale up gradually to avoid unexpected costs, rate limits, or node instability. ```shell Shell theme={"system"} chainbench start eth_blockNumber \ --users 50 \ --workers 2 \ --test-time 5m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` where: * `eth_blockNumber` — the RPC method to test * `--users 50` — number of simulated users making concurrent requests * `--workers 2` — number of worker processes (Locust workers) * `--test-time 5m` — test duration (supports s, m, h suffixes) * `--target` — your Chainstack RPC endpoint URL * `--headless` — run without the web UI * `--autoquit` — exit automatically when the test completes Replace `YOUR_CHAINSTACK_ENDPOINT` with your actual endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Example output shows requests per second (RPS), response times, and failure rates. ### Run a profile-based load test Profiles define realistic traffic patterns with multiple methods and weighted distributions. Run a test using the general BSC profile: ```shell Shell theme={"system"} chainbench start \ --profile bsc.general \ --users 100 \ --workers 4 \ --test-time 10m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` For EVM-compatible chains, use the generic profiles: ```shell Light profile theme={"system"} chainbench start \ --profile evm.light \ --users 50 \ --workers 2 \ --test-time 5m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` The `evm.light` profile includes common read methods: * `eth_blockNumber` * `eth_getBalance` * `eth_chainId` * `eth_getBlockByNumber` * `eth_getTransactionByHash` * `eth_getTransactionReceipt` * `web3_clientVersion` ```shell Heavy profile theme={"system"} chainbench start \ --profile evm.heavy \ --users 50 \ --workers 2 \ --test-time 5m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit \ --debug-trace-methods ``` The `evm.heavy` profile includes resource-intensive methods like `eth_getLogs` and debug/trace methods (enabled with `--debug-trace-methods`). ### Use the discover command Before running load tests, verify which methods your endpoint supports: ```shell Shell theme={"system"} chainbench discover YOUR_CHAINSTACK_ENDPOINT --clients geth,erigon ``` The output shows: * ✓ for working methods * ✗ for unsupported methods * Error codes for methods that return errors List available client specifications: ```shell Shell theme={"system"} chainbench list clients ``` This is useful for: * Verifying node configuration before delivery * Comparing method support across different node providers * Quick smoke testing of endpoints ### Run tests in web UI mode For interactive testing with real-time statistics: ```shell Shell theme={"system"} chainbench start \ --profile evm.light \ --workers 2 \ --target YOUR_CHAINSTACK_ENDPOINT ``` This starts a web server on `http://localhost:8089` where you can: * Adjust users, spawn rate, and test duration * View real-time charts and statistics * Start and stop tests interactively * Download test results ### Selecting profiles in the web UI > [!NOTE] > > If you want to pick a profile interactively in the Locust UI, start Chainbench with a profile directory and omit `--profile` and the method argument. > > If you cloned the Chainbench repository, run this command from the repo root: > > ```shell Shell theme={"system"} > chainbench start --workers 1 --target YOUR_CHAINSTACK_ENDPOINT --profile-dir chainbench/profile/evm > ``` > > If you installed Chainbench with `pip`, print the built-in EVM profiles directory: > > ```shell Shell theme={"system"} > python -c "import pathlib, chainbench; print(pathlib.Path(chainbench.__file__).resolve().parent / 'profile' / 'evm')" > ``` > > Then pass the printed path to `--profile-dir`. ### Create a custom profile Create a custom profile for your specific traffic pattern. Save this as `my_profile.py`: ```python my_profile.py theme={"system"} from locust import constant_pacing from chainbench.user import EvmUser class MyCustomProfile(EvmUser): wait_time = constant_pacing(1) # 1 request per second per user rpc_calls = { EvmUser.eth_call: 30, # 30% weight EvmUser.eth_get_logs: 25, # 25% weight EvmUser.eth_block_number: 20, # 20% weight EvmUser.eth_get_balance: 15, # 15% weight EvmUser.eth_chain_id: 10, # 10% weight } tasks = EvmUser.expand_tasks(rpc_calls) ``` Run your custom profile: ```shell Shell theme={"system"} chainbench start \ --profile-path /path/to/my_profile.py \ --users 50 \ --workers 2 \ --test-time 10m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` Or organize profiles in a directory: ```shell Shell theme={"system"} chainbench start \ --profile-dir /path/to/profiles \ --profile my_profile \ --users 50 \ --workers 2 \ --test-time 10m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` ### Configure test data options Chainbench generates realistic test data from the blockchain. Configure data generation: **Test data size:** | Size | Blocks sampled | | - | - | | XS | 10 | | S | 100 (default) | | M | 1,000 | | L | 10,000 | | XL | 100,000 | ```shell Shell theme={"system"} chainbench start \ --profile evm.light \ --size M \ --users 50 \ --workers 2 \ --test-time 5m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` **Use latest blocks:** For nodes with limited history (like full nodes without archive data): ```shell Shell theme={"system"} chainbench start \ --profile evm.light \ --use-latest-blocks \ --size S \ --users 50 \ --workers 2 \ --test-time 5m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` **Custom block range:** ```shell Shell theme={"system"} chainbench start \ --profile evm.light \ --start-block 18000000 \ --end-block 18001000 \ --users 50 \ --workers 2 \ --test-time 5m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` **Reference node for test data:** Use a different node to generate test data (useful when testing a node that might be slower or have restrictions): ```shell Shell theme={"system"} chainbench start \ --profile evm.light \ --ref-url YOUR_REFERENCE_ENDPOINT \ --users 50 \ --workers 2 \ --test-time 5m \ --target YOUR_TEST_ENDPOINT \ --headless \ --autoquit ``` ### Use load pattern shapes Control how load is distributed over time: **Step pattern:** ```shell Shell theme={"system"} chainbench start \ --profile evm.light \ --shape step \ --users 100 \ --spawn-rate 20 \ --workers 2 \ --test-time 10m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` This creates 5 steps (100 users ÷ 20 spawn-rate), each running for 2 minutes (10m ÷ 5 steps). **Spike pattern:** ```shell Shell theme={"system"} chainbench start \ --profile evm.light \ --shape spike \ --users 100 \ --workers 2 \ --test-time 10m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` Runs at 10% users for 40% of time, spikes to 100% for 20%, then back to 10%. ### Monitor sync lag during tests Track how far behind the node is during load testing: ```shell Shell theme={"system"} chainbench start \ --profile evm.light \ --monitor sync-lag-monitor \ --users 50 \ --workers 2 \ --test-time 5m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` ### Run batch requests Test batch JSON-RPC requests: ```shell Shell theme={"system"} chainbench start \ --profile evm.light \ --batch \ --batch-size 10 \ --users 50 \ --workers 2 \ --test-time 5m \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit ``` ### Run tests on a remote server For long-running tests on a server: ```shell Shell theme={"system"} nohup chainbench start \ --profile bsc.general \ --workers 4 \ --users 100 \ --test-time 12h \ --target YOUR_CHAINSTACK_ENDPOINT \ --headless \ --autoquit & ``` Results are saved in `./results/{profile_name}/{timestamp}/`. > [!NOTE] > > Open `report.html` to review latency percentiles, RPS, and failures. Use the CSV files (for example, `report.csv_stats.csv`) if you want to process results programmatically. ## Available EVM methods Chainbench supports testing these EVM methods (among others): | Category | Methods | | - | - | | Block | `eth_blockNumber`, `eth_getBlockByNumber`, `eth_getBlockByHash` | | Transaction | `eth_getTransactionByHash`, `eth_getTransactionReceipt`, `eth_getTransactionCount` | | Account | `eth_getBalance`, `eth_getCode`, `eth_getStorageAt` | | Call | `eth_call`, `eth_estimateGas` | | Logs | `eth_getLogs` | | Debug | `debug_traceTransaction`, `debug_traceCall`, `debug_traceBlockByNumber` | | Trace | `trace_transaction`, `trace_block`, `trace_call` | > [!NOTE] > > ### Debug and trace methods > > Debug and trace methods are excluded by default. Enable them with `--debug-trace-methods`. ## Solana testing Test Solana endpoints with the Solana profile: ```shell Shell theme={"system"} chainbench start \ --profile solana.general \ --users 50 \ --workers 2 \ --test-time 5m \ --target YOUR_SOLANA_ENDPOINT \ --headless \ --autoquit ``` The Solana profile includes methods like: * `getAccountInfo` * `getBlock` * `getTransaction` * `getBalance` * `getTokenAccountsByOwner` * `getMultipleAccounts` * `getSignaturesForAddress` ## Conclusion Chainbench provides a powerful way to benchmark and load test your blockchain infrastructure. Use it to: * Validate node performance before production deployment * Compare performance across different node providers * Test custom traffic patterns matching your application's usage * Monitor node behavior under various load conditions * Automate performance testing in CI/CD pipelines For more information, see the [Chainbench documentation](https://github.com/chainstacklabs/chainbench/blob/main/docs/PROFILE.md) on creating custom profiles. > [!NOTE] > > ### See also > > * [Make your DApp more reliable with Chainstack](/docs/make-your-dapp-more-reliable-with-chainstack) > * [Using eRPC with Chainstack](/docs/using-erpc-with-chainstack-quickstart) > * [Tutorial on how to make your DApp reliable and scalable with Kubernetes](/docs/tutorial-on-how-to-make-your-dapp-reliable-and-scalable-with-kubernetes) # Chainlink: Estimating the price of a call Source: https://docs.chainstack.com/docs/chainlink-estimating-the-price-of-a-call Estimate the gas cost of Chainlink oracle calls on Ethereum using web3.js to calculate request pricing before executing on-chain data feed queries. **TLDR:** * A script in JavaScript using the [Chainlink functions toolkit library](https://www.npmjs.com/package/@chainlink/functions-toolkit) to estimate the call cost before making the actual smart contract call to the DON network and getting frustrated when it fails. ## Overview [Chainlink functions](https://chain.link/functions) is no small feat in Web3 and it's part more than one onboarding guides for developers getting seriously into in the industry. After all, deploying a simple ERC20 token is no longer enough. AI agents do it all the time. Chainlink has a very good functions tutorial: [Chainlink Functions Getting Started](https://docs.chain.link/chainlink-functions/getting-started). It's designed to be very easy and informative. It explains every step and there are videos to follow along too. One thing, however, is a number of onboarding developers fail at the sending the actual request. A lot of the times it fails. This extremely specific article fixes that. Why? Because people get confused at this step, there's no logging or explanation, in their confusion, they don't know whether it's an RPC endpoint or something else. And it involves quite a bit of digging to get understand what actually is going on. Since I was involved in the troubleshooting (or basically had to troubleshoot), I'm putting out this quick script to public in case someone else finds it useful. The scripts prints what the network *thinks* the call needs to cost to be fulfilled and not fail. I put the word *thinks* here in italics because it's not what the actual computation ends up costing, but it's what preventing the call from going through and you the messages like: * The request will most likely fail. Do you want to continue? * `sendRequest` failed * `sendRequest` execution reverted It can be very frustrating if you are following a tutorial to a ***t*** and still can't through for reasons you can't understand. ## Details It costs \$LINK to fulfill the requests on the Chainlink Oracle network, but the examples you are doing are on Sepolia where the dynamic Oracle call price due to it being the testnet infrastructure can fluctuate wildly. While the example in the tutorial might show that having 2 links on your funded subscription account is more that enough, more often than not the actual price estimate can be otherworldly like 16 \$LINK or even hundreds of LINK. And not having enough $LINK on your subscription account is what actually makes the call fail without explaining it to you. Not that the actual fulfillment price will still be something reasonable within the $LINK decimals range (not even one Link). But the amount of \$LINK on you subscription account will make your transaction revert and fail. ## Call estimation script Make sure you have the [Chainlink functions toolkit library](https://www.npmjs.com/package/@chainlink/functions-toolkit) installed. So here's the script: Note it's for Sepolia. You'll need other values for different networks, but you can find them easily in the Chainlink documentation that's generally has always been pretty well maintained: [Chainlink Functions: Supported Networks](https://docs.chain.link/chainlink-functions/supported-networks) . Also you can change `subscriptionId` to your subscription, but for the default Chainlink tutorial it doesn't really matter. And provide and an actual endpoint for the query to go through here `CHAINSTACK_SEPOLIA_RPC`. ```node estimateCall.js theme={"system"} import { SubscriptionManager } from '@chainlink/functions-toolkit' import { ethers } from 'ethers' async function estimateRequestCost() { try { const provider = new ethers.providers.JsonRpcProvider('CHAINSTACK_SEPOLIA_RPC') const signer = ethers.Wallet.createRandom().connect(provider) const subscriptionManager = new SubscriptionManager({ signer, linkTokenAddress: '0x779877A7B0D9E8603169DdbD7836e478b4624789', // Sepolia LINK address functionsRouterAddress: '0xb83E47C2bC239B3bf370bc41e1459A34b41238D0', // Sepolia Router address }) await subscriptionManager.initialize() const estimatedCostInJuels = await subscriptionManager.estimateFunctionsRequestCost({ subscriptionId: 4062, callbackGasLimit: 300000, gasPriceWei: BigInt(20000000000), // 20 gwei donId: 'fun-ethereum-sepolia-1' }) const estimatedCostInLink = ethers.utils.formatEther(estimatedCostInJuels.toString()) console.log(`Estimated cost: ${estimatedCostInLink} LINK`) } catch (error) { console.error('Error:', error) } } estimateRequestCost() ``` For example, at the time posting this article: ```shell Shell theme={"system"} % node estimateCost.js Estimated cost: 14.358219259338930318 LINK ``` Which would mean I'd need to have 14+ \$LINK on my subscription account for the [sendRequest](https://docs.chain.link/chainlink-functions/getting-started#run-the-example) to be successful. ## Conclusion Cost estimation is a crucial step in working with Chainlink Functions. This script helps you understand the \$LINK token costs associated with your Functions requests before deployment, allowing for better resource planning and optimization. It may be flaky on Sepolia, but it's still a must-have survival skill for prod. Happy learning! ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Blockchain APIs guides Source: https://docs.chainstack.com/docs/chainstack-blockchain-apis-guides The following guides provide valuable resources for those looking to harness the power of Blockchain APIs to build decentralized applications. These guides provide not only theoretical knowledge but also showcase practical examples, enabling developers to deepen their understanding of these concepts and apply them effectively in their projects. By mastering these tools and techniques, developers can build innovative blockchain-based applications, streamline their development process, and make the most of the decentralized ecosystem. ## Guides list > [card] [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) > [card] [Expanding your blockchain horizons: The eth\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) > [card] [Deep dive into Merkle proofs and eth\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) > [card] [Geth vs Erigon: Deep dive into RPC methods on Ethereum clients](/docs/geth-vs-erigon-deep-dive-into-rpc-methods-on-ethereum-clients) > [card] [Ethereum logs tutorial series: Logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters) > [card] [Mastering custom JavaScript tracing for Ethereum virtual machine](/docs/mastering-custom-javascript-tracing-for-ethereum-virtual-machine) > [card] [Develop a battleship game using zero-knowledge concepts on Ethereum](/docs/develop-a-battleship-game-using-zero-knowledge-concepts-on-ethereum) > [card] [Sending Warp transaction with web3.js, ethers.js, web3.py, and ethClient.go](/docs/sending-warp-transaction-with-web3js-ethersjs-web3py-and-ethclientgo) > [card] [Harnessing Chainlink oracles with Chainstack: Fetching real-time crypto prices from Ethereum](/docs/harnessing-chainlink-oracles-with-chainstack-fetching-real-time-crypto-prices-from-ethereum) > [card] [Fetching transfer events with getPastEvents for a BAYC NFT](/docs/fetching-transfer-events-with-getpastevents-for-a-bayc-nft) > [card] [Understanding Ethereum's 'Filter not found' error and how to fix it](/docs/understanding-ethereums-filter-not-found-error-and-how-to-fix-it) > [card] [Introducing Bun: The future of JavaScript runtimes](/docs/introducing-bun-the-future-of-javascript-runtimes) > [card] [Fetching transactions to and from a specific address with eth\_getBlockByNumber](/docs/fetching-transactions-to-and-from-a-specific-address-with-eth_getblockbynumber) > [card] [cryo: Your gateway to blockchain data](/docs/cryo-your-gateway-to-blockchain-data) > [card] [cryo + Chainstack: A developer's guide to blockchain data mastery with Python](/docs/cryo-with-chainstack-and-python) > [card] [Exploring Bitcoin Transactions with `getrawtransaction`](/docs/exploring-bitcoin-transactions-with-getrawtransaction) > [card] [Integrating Multi-Endpoint Shared Configuration (MESC) with Chainstack: A comprehensive guide](/docs/mesc-and-chainstack) > [card] [Ethers.js: Enhancing blockchain data reliability with FallbackProvider](/docs/enhancing-blockchain-data-reliability-with-ethers-fallbackprovider) > [card] [Web3.js: How to use the new Chainstack plugin](/docs/web3js-how-to-use-the-new-chainstack-plugin) > [card] [ethers.js ChainstackProvider: How to BUIDL a multi-chain wallet balance aggregator](/docs/ethersjs-chainstackprovider-how-to-multi-chain-wallet-balance-aggregator) # Chainstack ChatGPT plugin overview Source: https://docs.chainstack.com/docs/chainstack-chat-gpt-plugin-introduction Use the Chainstack ChatGPT plugin to query blockchain data through natural language, fetching balances, transactions, and token info conversationally. > [!NOTE] > > ### The new Chainstack GPT > > The Chainstack GPT is now available on ChatGPT using GPT4. Try it out here: [Chainstack GPT](https://chat.openai.com/g/g-ueMUdZAzr-chainstack-gpt) ## Natural language Web3 infrastructure management The Chainstack ChatGPT plugin takes this a step further by providing seamless integration with the Web3 infrastructure workflow. This integration enables developers to manage their projects, establish networks, deploy or remove nodes, and even request funds directly through the [Chainstack Faucet](/docs/faucets) API, all within the ChatGPT environment. This not only enhances the capabilities of ChatGPT but also simplifies and streamlines the process of interacting with the Chainstack platform, offering a more efficient and intuitive user experience. ## Install the Chainstack ChatGPT plugin To install the Chainstack plugin, simply search for **Chainstack** in the plugin store in ChatGPT. ## What can the Chainstack ChatGPT plugin do? The Chainstack plugin provides various functionalities to interact with different EVM (Ethereum Virtual Machine) based blockchains. Below you can find a breakdown of its capabilities. ### 1. Querying blockchain data * **Get the latest block number** — retrieve the latest block number from the specified chain. * **Get balance** — fetch the balance of a given account at the latest block. * **Get base gas fee** — retrieve the base gas fee from an EVM chain. * **Blockchain call** — make a specific method call on a chosen protocol with user-specified parameters. * **Get transaction summary** — retrieve earliest and latest transactions, among other information, by address. ### 2. Conversion utilities * **Hex to decimal** — convert a hexadecimal value to a decimal value. * **Decimal to hex** — convert a decimal value to a hexadecimal value. * **From wei** — convert a wei value to an ether value. ### 3. ENS (Ethereum Name Service) resolution * **ENS to address** — resolve an ENS name to an Ethereum address. ### 4. Token and NFT management * **Get token balances** — retrieve token balances for a given chain name and wallet address. * **Get NFTs** — retrieve all NFTs for a given chain name and wallet address. * **Get single NFT** — retrieve a single NFT for a given chain name, wallet address, and token ID. ### 5. Retrieve docs information * **Chainstack docs** — scrape Chainstack docs for platform info and blockchain development guides. ## How to use the Chainstack ChatGPT plugin Once you have installed the plugin from the store, simply ask ChatGPT to interact with the Chainstack platform, the faucet, or a blockchain. The blockchains available out of the box are: * Ethereum Mainnet * Polygon Mainnet * Avalanche Mainnet * Arbitrum Mainnet * Gnosis Chain Mainnet You can also provide an endpoint for any EVM chain, and ChatGPT will use it. ### Interaction examples Here you can find some interaction examples and how you can leverage the power of ChatGPT with Chainstack and the blockchain. #### Check accounts balance You can ask for checking balances specifying accounts and blockchain. The plugin supports ENS as well. > [!NOTE] > > **User**: What is the balance of jaredfromsubway.eth on Ethereum? > > **ChatGPT**: The Ethereum balance for the ENS address jaredfromsubway.eth (resolved to 0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13) is 180.158912073898873256 ETH. #### Blockchain calls You can also ask for general blockchain calls. For example, retrieve a list of transactions in a block. ChatGPT will attempt to make conversions on its own, which are not always accurate. > [!NOTE] > > **User**: Can you convert block 43184850 to hexadecimal and show me the transactions hashes of this block on Polygon? Please keep the transaction details flag off. > > **ChatGPT**: The block number 43184850 in decimal is represented as 0x292f2d2 in hexadecimal. Here is the list of transaction hashes from that block on the Polygon network: > > 0x33e4e2167bd8787d480543022b5a9dd577e3dd48d05a958521e7295d88f47021 0x092709a880a8df3fa10e60dfd124e0da81310e4e3b588e1231ddb5aefd42f0d4 0x6f2726aff25220395816ca9058c165bdcd03c15f9109a643355cd0b5c01ccba8 0x8cba7ca23f86113b8820c7c3ca2339968326834dadc7a983118273bd309bfdae 0xa4f9fd3f88403cc2e4e41a337b7ddc1034cb9cabcb76ceea78f038fe8545e811 > > . > > . In this case, ChatGPT will first convert the value to hexadecimal, then call `eth_getBlockByNumber` with the correct parameters. #### Check the chain ID for an endpoint > [!NOTE] > > **User**: Can you check the chain ID using this endpoint? [https://ethereum-mainnet.core.chainstack.com/YOUR\_AUTH\_TOKEN](https://ethereum-mainnet.core.chainstack.com/YOUR_AUTH_TOKEN) > > **ChatGPT**: The chain ID for the specified endpoint [https://ethereum-mainnet.core.chainstack.com/YOUR\_AUTH\_TOKEN](https://ethereum-mainnet.core.chainstack.com/YOUR_AUTH_TOKEN) is 0x1 #### Get the base gas fee > [!NOTE] > > **User**: What is the base gas fee on Ethereum? > > **ChatGPT**: The base gas fee on the Ethereum network is currently 38.28 Gwei. > [!NOTE] > > Note that while ChatGPT is highly skilled at understanding requests and interacting with plugins, it is recommended to provide comprehensive prompts. If you encounter an error, feel free to ask again giving more details. ## Try the Chainstack plugin Head over to [ChatGPT](https://chat.openai.com/) and install the Chainstack plugin from the OpenAI store. # Chainstack Compare dashboard Source: https://docs.chainstack.com/docs/chainstack-compare-dashboard Analyze and compare blockchain RPC node performance metrics across providers using the Chainstack Compare dashboard with visual latency benchmarks. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR**: * Chainstack Compare provides real-time RPC provider performance — availability, latency (P50/P95/P99), success rates, and transaction confirmation — for major providers across multiple blockchains and regions. * Built with Vercel (data collection) and Grafana Cloud (dashboards), it refreshes data every 1–3 minutes and keeps 14 days of history. * The landing page ranks providers at a glance per network; one more click opens the full Grafana dashboard with per-method and per-region breakdowns. * The ranking system prioritizes fast, reliable providers, while special metrics (e.g., transaction landing on Solana) help fine-tune your choice of provider. * Fork the GitHub repository for full technical details and customization of this open dashboard solution. ## Problem Choosing the right RPC provider for your blockchain or trading project can be challenging. Performance varies by region and protocol, and it changes over time. How do you make an informed decision? [Chainstack Compare](https://compare.chainstack.com/) solves this problem by providing real-time performance data for major RPC providers across different regions and blockchains. The dashboard has been built with the help of Vercel (Pro plan) and Grafana Cloud (Free plan). You can either use the dashboard hosted by Chainstack or fork the [GitHub repository](https://github.com/chainstacklabs/chainstack-rpc-dashboard-functions) and customize it to your needs. > [!NOTE] > > ### Run nodes on Chainstack > > [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required. You can sign up with your GitHub, X, Google, or Microsoft account. ## Solution overview The solution consists of the two main components: data collection services and dashboard. The data collection service sends API calls to all providers, measures response times, and pushes collected data to the dashboard every 1–3 minutes. The data collection service only records response times and marks requests based on whether they were successful or not. All calculations, including averages and other aggregated values, are performed by the dashboard. ### Quick start 1. Visit [Chainstack Compare](https://compare.chainstack.com/) 2. Select your blockchain network — Ethereum, Solana, BNB Chain, Arbitrum, Base, TON, Hyperliquid, or Monad 3. Review the provider ranking, availability, and latency (P50/P95/P99) per region, and switch between the 24h and 7d windows 4. For per-method breakdowns and success rates, open the linked Grafana dashboard ### What you can do With Chainstack Compare Dashboard, you can: 1. Compare RPC provider availability and latency (P50/P95/P99) across regions 2. Monitor response times and success rates 3. Open per-method breakdowns in the Grafana dashboard 4. Monitor transaction confirmation metrics (Solana only) The dashboard currently supports: * Blockchains: Ethereum, Solana, BNB Chain, Arbitrum, Base, TON, Hyperliquid, Monad * Regions: United States, Germany, Singapore * Providers (vary by network): Alchemy, Chainstack, QuickNode, Helius, dRPC, TonCenter > [!NOTE] > > ### Data and paid tiers > > All metrics, except for the transaction landing metrics, are updated every 1–3 minutes, with 14 days of historical data available. The transaction landing metrics are updated every 15 minutes. > > For the dashboard, we used paid tiers of the mentioned providers, except for TonCenter where we used the free tier. ### Special features #### Provider ranking We evaluate RPC providers based on their speed (response time) and reliability (success rate) across three regions to rank them in each region and globally. Transaction landing metrics are not considered for this ranking. 1. The lower the score, the better the provider 2. Even small success rate drops (99% vs 97%) significantly impact the final score 3. Performance is measured equally across all regions The formula used for ranking is presented below. Score = 1 ÷ ((1/ResponseTime) × (SuccessRate3)) ### Solana: transaction landing We send standardized test transactions every 15 min across the Solana network to measure two metrics: * **Slot Latency** The time it takes for a transaction to be confirmed on the network, measured in slots. Lower slot latency means faster transaction finality. * **Landing Rate** The percentage of transactions that successfully confirm within our testing parameters. This metric helps identify reliable providers for your specific region. Each test transaction: * Uses the Solana Memo Program for consistent, lightweight operations * Includes fixed priority fees (200,000 microlamports/CU) * Has the 55 sec timeout For more technical details on the transaction landing metric, please check the code [here](https://github.com/chainstacklabs/chainstack-rpc-dashboard-functions/blob/master/metrics/solana_landing_rate.py). We added a few enhanced endpoints for providers which offer such services. Enhanced enpoints are used only for measuring the transaction confirmation speed. | Provider name | Default endpoint | Enhanced endpoint | | - | - | - | | Alchemy | Yes | No | | Chainstack | Yes | Yes, [enhanced endpoint](/docs/trader-node) with bloXroute technology | | Helius | Yes | Yes, staked connection | | Quicknode | Yes | No | ## How it works ### Vercel: data collection services We chose Vercel as our hosting solution due to its simplicity and time-to-production. The [data collection services](https://github.com/chainstacklabs/chainstack-rpc-dashboard-functions) are Vercel serverless functions deployed to multiple regions. Metrics for each blockchain are collected by a dedicated function which is triggered by Vercel cron jobs every 1–3 minutes. Once data is collected, it is pushed to a Grafana Cloud Prometheus instance. The service has the following performance thresholds: * Response timeout: 55 seconds * Block delay threshold: 55 seconds Failures include: * Timeouts and excessive block delays * Non-200 HTTP responses * JSON-RPC error responses ### Grafana Cloud: dashboards Grafana Cloud with its hosted Prometheus instance providers hassle-free services. Thanks to it, we can focus on the dashboard quality, rather than on supporting a Prometheus instance. Grafana Cloud stores dashboard configurations as JSON files, which makes it easier to support and improve them. Data transformation required for some charts is performed with the help of Grafana Data [Transformation feature](https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/transform-data/) and [PromQL](https://prometheus.io/docs/prometheus/latest/querying/basics/). Historical data is available for the last 14 days. ## For developers ![Chainstack Compare Dashboard](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/4d2f558febc43bc352454f3a5dcc2463148dcae1a4931f9bf75deebebbb4b2ec-image.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=e14e2bff8863dd8341eefc32cf5985ba) For developers interested in the technical implementation and contribution to the project, please refer to our [GitHub repository](https://github.com/chainstacklabs/chainstack-rpc-dashboard-functions), which contains: 1. Complete technical documentation 2. Setup instructions 3. Configuration guidelines ## FAQ > [accordion] What providers do you monitor? > > Alchemy, Chainstack, QuickNode, Helius, dRPC, and TonCenter. Paid tiers (except for TonCenter). Provider coverage varies by network. > [accordion] Why do I need this dashboard? > > It helps choose the right RPC provider based on real data and monitor their performance across regions. > [accordion] How often is data updated? > > Every 1–3 minutes; the landing page refreshes every 3 minutes. Transaction landing metrics update every 15 minutes. > [accordion] What counts as a failed request? > > Any response slower than 55 seconds, non-200 status codes, or responses containing error messages (as per JSON-RPC specification). For blocks, delays over 55 seconds count as failures. > [accordion] Can I see historical data? > > Yes. The landing page shows 24h and 7d views, and the Grafana dashboard keeps 14 days of performance history. > [accordion] How do you collect the metrics? > > We use serverless functions in three regions, measuring response times and success rates from each location. > [accordion] What API methods do you test? > > We focus on commonly used methods like balance checks, transaction simulation, and block queries. Each blockchain has its specific set of tested methods. > [accordion] What's the difference between global and regional views? > > Global shows aggregated performance across all regions, while regional views provide detailed metrics for specific locations. ## Conclusion Chainstack Compare Dashboard provides Web3 developers and users with a comprehensive tool for monitoring RPC provider performance across different regions. By offering real-time metrics on response times, success rates, block delays, and transaction landing, the dashboard enables its users to make data-driven infrastructure decisions. ### About author > [card] Anton Sauchyk > > ![Anton Sauchyk](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1817926677730664448/3nNn0T2p_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1d13d3584862f1be3a84fa3f8e38fea7) > > Developer Advocate @ Chainstack > \ > Multiple years of software development and Web3 expertise > > [https://github.com/smypmsa](https://github.com/smypmsa) > > [https://x.com/sensuniama](https://x.com/sensuniama) > > [https://www.linkedin.com/in/anton-sauchyk/](https://www.linkedin.com/in/anton-sauchyk/) # Chainstack DLP tool overview Source: https://docs.chainstack.com/docs/chainstack-data-leak-protection-tool-introduction Scan your codebase for exposed blockchain private keys, seed phrases, and API credentials using the Chainstack data leak protection scanning tool. The Chainstack DLP browser extension is designed to enhance the privacy and security of your interactions with ChatGPT. This extension works by redacting potentially sensitive information before it's sent to ChatGPT for processing, including names, addresses, API keys, JWTs, etc. > [!NOTE] > > Currently, **Chainstack DLP** is available either locally from the source code or through installation on the [Chrome Web Store](https://chrome.google.com/webstore/detail/chainstack-dlp/miihnlobablmmphiogghcpeebijgpgce?hl=en\&authuser=0). ## Processing sensitive data To function as a genuine data loss prevention (DLP) tool, Chainstack DLP performs all processing locally. This means the extension does not rely on any external APIs, ensuring that your data never leaves your local environment. Trust is a critical factor in data security, and to uphold this trust, we've made our tool 100% open-source. This transparency allows you to verify the security measures we've implemented. You can install the extension directly from the store, or, for those who prefer, you can also install it locally using this repository. Chainstack DLP tool employs regular expression patterns to detect potentially sensitive data, such as API keys, credit card numbers, JWTs, etc. Additionally, it utilizes the [compromise package](https://github.com/spencermountain/compromise) V 14.9.0, a robust JavaScript library for natural language processing (NLP). This library aids in the identification of personal and business identifiers, including names, addresses, and company names. ## Source code The source code of the Chainstack DLP tool is 100% open source and is available on the Chainstack Labs GitHub account. > [card] [Chainstack DLP repository](https://github.com/chainstacklabs/chainstack-dlp-browser-extension) ## Usage The Chainstack DLP introduces two buttons and a small preview window above the ChatGPT input bar. The `↕` button serves two functions: expanding and minimizing the preview window. Meanwhile, the clear button deletes all content within the ChatGPT input bar. A popup window is provided to manage the redaction tool. Users can enable or disable the tool and select or unselect specific types of data to redact. However, particularly sensitive data such as credit card patterns, JSON Web Tokens, Ethereum private keys, and phone numbers cannot be disabled. Any changes made within the popup window will update live. However, you must empty the ChatGPT input bar and reenter the content to see these changes. A preview is available to check the content before submitting it to ChatGPT. If something appears malfunctioning, reloading the page should resolve the issue. # Chainstack Marketplace Source: https://docs.chainstack.com/docs/chainstack-marketplace-tutorials Step-by-step tutorials for integrating Chainstack Marketplace tools into your blockchain applications, including API setup and authentication guides. Whether you're a seasoned user or new to the platform, these guides will offer valuable insights and help you get the most out of your experience with the Chainstack Marketplace. ## Guides list > [card] [Mastering JSON web tokens: How to implement secure user authentication](/docs/tutorial-mastering-jwt-how-to-implement-secure-user-authentication) # Chainstack MCP server Source: https://docs.chainstack.com/docs/chainstack-mcp-server Chainstack MCP server for AI agents — deploy blockchain nodes, search docs, request testnet faucet tokens, fetch pricing, and contact the team. ## Quick start Type or paste this into any AI agent: ``` get mcp.chainstack.com ``` The agent will fetch the onboarding page, understand what the server offers, and walk you through setup. That's it. *** The **Chainstack MCP server** gives AI agents direct access to the Chainstack platform — deploy blockchain nodes, search documentation, request testnet faucet tokens, fetch live pricing, contact the Chainstack team, and check platform status, all from your coding environment. It works with Claude Code, Cursor, Codex, Windsurf, Gemini CLI, GitHub Copilot, Antigravity, Claude.ai, and ChatGPT. [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) is an open standard that lets AI applications connect to external tools and data sources. The Chainstack MCP server is a remote Streamable HTTP server — no local setup required. ## Tools **No API key needed:** | Tool | Description | | - | - | | `search_docs` | Search Chainstack documentation — blockchain RPC methods, deployment guides, code examples | | `get_doc_page` | Fetch the full content of a documentation page | | `get_platform_status` | Check platform status, active incidents, and network health | | `contact_chainstack` | Submit a message to the Chainstack team — sales, support, or general inquiries | | `get_chainstack_pricing` | Fetch a live Chainstack pricing snapshot — plan tiers, feature matrix, add-ons, dedicated-node catalog | **API key required** (get one at [console.chainstack.com/user/settings/api-keys](https://console.chainstack.com/user/settings/api-keys)). All tools appear in the agent's tool list regardless of auth — the tools below execute only when the key is configured. Calls without it return an auth error. | Tool | Description | | - | - | | `get_organization` | Get organization name and ID | | `get_deployment_options` | List available blockchain/cloud/region combinations | | `list_projects` | List all projects | | `create_project` | Create a new project | | `get_project` | Get project details | | `update_project` | Update a project's name or description | | `delete_project` | Delete a project | | `list_nodes` | List all nodes with status and endpoints | | `get_node` | Get a node's full details including RPC endpoints | | `create_node` | Deploy a new blockchain node | | `update_node` | Rename a node | | `delete_node` | Delete a node | | `list_node_access_rules` | List a node's [access rules](/docs/access-rules) and what they currently enforce | | `create_node_access_rule` | Add an IP address or allowed origin rule to a Global Node | | `update_node_access_rule` | Change an access rule's value, or activate or deactivate it | | `delete_node_access_rule` | Delete an access rule | | `set_node_access_rules_status` | Activate or deactivate every access rule on a node | | `request_testnet_funds` | Top up a testnet address from the [Chainstack faucet](/docs/faucets) — Sepolia, Hoodi, Base Sepolia, Amoy, BNB testnet, Solana devnet, and more | ## Option 1: Install as a skill Save the SKILL.md to your agent's skills directory. The agent calls tools via HTTP on demand — no MCP registration, no tools forced into context. | Agent | Skills directory | | - | - | | Claude Code / Cowork | `~/.claude/skills/chainstack/` | | Codex / Gemini CLI / Antigravity / Copilot | `~/.agents/skills/chainstack/` | | Cursor | `~/.cursor/skills/chainstack/` | | Windsurf | `~/.codeium/windsurf/skills/chainstack/` | ```bash theme={"system"} # Claude Code mkdir -p ~/.claude/skills/chainstack curl -o ~/.claude/skills/chainstack/SKILL.md https://mcp.chainstack.com/skill # Codex, Gemini CLI, Antigravity, Copilot (shared directory) mkdir -p ~/.agents/skills/chainstack curl -o ~/.agents/skills/chainstack/SKILL.md https://mcp.chainstack.com/skill ``` You may need to restart your agent or start a new session for the skill to take effect. The skill contains workflows, tool reference, curl examples, and error handling. To set up your API key for authenticated tools, add it to your shell profile: ```bash theme={"system"} export CHAINSTACK_API_KEY="your-key-from-console.chainstack.com/user/settings/api-keys" ``` ## Option 2: Register as an MCP server Add the server to your agent's configuration. Tools appear in context every session. ### Claude Code ```bash theme={"system"} # All projects claude mcp add --transport http chainstack https://mcp.chainstack.com/mcp -s user # This project only claude mcp add --transport http chainstack https://mcp.chainstack.com/mcp # With API key claude mcp add --transport http chainstack https://mcp.chainstack.com/mcp -s user \ --header "Authorization: Bearer YOUR_API_KEY" ``` ### Codex CLI ```bash theme={"system"} codex mcp add chainstack --url https://mcp.chainstack.com/mcp ``` Or edit `~/.codex/config.toml`: ```toml theme={"system"} [mcp_servers.chainstack] url = "https://mcp.chainstack.com/mcp" [mcp_servers.chainstack.http_headers] Authorization = "Bearer YOUR_API_KEY" ``` ### Cursor Edit `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project): ```json theme={"system"} { "mcpServers": { "chainstack": { "url": "https://mcp.chainstack.com/mcp", "transport": "streamable-http", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` ### Windsurf Edit `~/.codeium/windsurf/mcp_config.json`: ```json theme={"system"} { "mcpServers": { "chainstack": { "serverUrl": "https://mcp.chainstack.com/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` ### VS Code Chainstack is listed in the GitHub MCP Registry, so VS Code can install it for you. There are two ways to do it. **From inside VS Code** — open the Extensions view (**⇧⌘X** on macOS, **Ctrl+Shift+X** on Windows and Linux), search `@mcp chainstack`, and click **Install** on the Chainstack entry. To install into the workspace rather than your user profile, right-click the entry and select **Install in Workspace**, which writes `.vscode/mcp.json`. **From the registry in your browser** — open the [Chainstack entry in the GitHub MCP Registry](https://github.com/mcp/com.chainstack/chainstack) and click **Install** > **Install in VS Code**. VS Code opens and asks you to confirm. Both paths add the server without an API key, which covers every tool that works unauthenticated. To add your key, or to configure by hand, edit `.vscode/mcp.json` (workspace) or run **MCP: Open User Configuration** from the Command Palette (global): ```json theme={"system"} { "servers": { "chainstack": { "type": "http", "url": "https://mcp.chainstack.com/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` > [!NOTE] > > VS Code uses `servers` as the root key, not `mcpServers`. Installed servers appear under **MCP SERVERS - INSTALLED** in the Extensions view, and **MCP: List Servers** in the Command Palette enables, disables, and shows logs for them. To use the tools, open the Chat view in agent mode and click **Configure Tools** to toggle the Chainstack tools on. ### Gemini CLI ```bash theme={"system"} # All projects gemini mcp add -s user --url https://mcp.chainstack.com/mcp chainstack # This project only gemini mcp add --url https://mcp.chainstack.com/mcp chainstack ``` Or edit `~/.gemini/settings.json` (global) or `.gemini/settings.json` (project): ```json theme={"system"} { "mcpServers": { "chainstack": { "httpUrl": "https://mcp.chainstack.com/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` > [!NOTE] > > Gemini CLI uses `httpUrl` for Streamable HTTP endpoints, not `url`. ### Antigravity Edit `~/.gemini/antigravity/mcp_config.json`: ```json theme={"system"} { "mcpServers": { "chainstack": { "serverUrl": "https://mcp.chainstack.com/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` ### Claude.ai Settings > Connectors > Add custom connector > paste `https://mcp.chainstack.com/mcp` ### ChatGPT Settings > Apps & Connectors > Developer mode > Create > paste `https://mcp.chainstack.com/mcp` ## Discovery Visit [mcp.chainstack.com](https://mcp.chainstack.com/) for an agent-readable onboarding page. Point any agent at this URL (e.g., "get mcp.chainstack.com") and it can read the page and self-configure. The server is also listed in two public registries, where it appears as `com.chainstack/chainstack`: * [GitHub MCP Registry](https://github.com/mcp/com.chainstack/chainstack) — the catalog VS Code surfaces in its Extensions view, with one-click install * [MCP Registry](https://registry.modelcontextprotocol.io/?search=chainstack) — the official registry that MCP clients read from ## Endpoints | Path | Description | | - | - | | `/` | Agent-readable discovery and onboarding page | | `/mcp` | MCP transport (Streamable HTTP) | | `/skill` | SKILL.md — full usage guide for on-demand skill mode | | `/healthz` | Liveness probe | | `/readyz` | Readiness probe | ## Report issues Found a bug or something not working as expected? Open an issue: * [Open a new issue](https://github.com/chainstacklabs/mcp-server/issues/new) * [View existing issues](https://github.com/chainstacklabs/mcp-server/issues) Include what tool you called, what you expected, and what happened instead. ## Request features Have an idea for a new tool or improvement? Open a feature request: * [Request a feature](https://github.com/chainstacklabs/mcp-server/issues/new) Describe your use case — what you're trying to accomplish and why the current tools don't cover it. ## Changelog and updates The changelog is maintained in the repository: * [View changelog](https://github.com/chainstacklabs/mcp-server/blob/main/CHANGELOG.md) To get notified of new releases and changes, watch the repository: 1. Go to [chainstacklabs/mcp-server](https://github.com/chainstacklabs/mcp-server) 2. Click **Watch** > **Custom** > select **Releases** 3. You'll receive notifications when new versions are published # Best practices handbook Source: https://docs.chainstack.com/docs/chainstack-web3-development-best-practices Best practices for building secure and efficient decentralized applications, covering error handling, load balancing, WebSocket connections, and more. They cover a range of essential topics, and by following these best practices, developers can create DApps that not only meet their functional requirements but also provide a reliable and secure experience for end-users, contributing to a more robust decentralized ecosystem. ## Guides list > [card] [HTTP batch request VS multicall contract](/docs/http-batch-request-vs-multicall-contract) > [card] [How to store your Web3 DApp secrets: Guide to environment variables](/docs/how-to-store-your-web3-dapp-secrets-guide-to-environment-variables) > [card] [Introduction to smart contract auditing using Foundry](/docs/introduction-to-smart-contract-manual-auditing-with-foundry-and-slither) > [card] [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project) > [card] [Get the most out of Chainstack platform API](/docs/guide-get-the-most-out-of-the-chainstack-platform-api) > [card] [Understanding eth\\\_getLogs limitations](/docs/understanding-eth-getlogs-limitations) > [card] [Monitoring transaction propagation from node to mempool in EVM networks with Python](/docs/monitoring-transaction-propagation-from-node-to-mempool-in-evm-networks-with-python) > [card] [Handle real-time data using WebSocket with JavaScript and Python](/docs/handle-real-time-data-using-websockets-with-javascript-and-python) > [card] [Make your DApp more reliable with Chainstack](/docs/make-your-dapp-more-reliable-with-chainstack) > [card] [Best practices for error handling in API requests](/docs/best-practices-for-error-handling-in-api-requests) > [card] [Mastering multithreading in Python for Web3 requests: A comprehensive guide](/docs/mastering-multithreading-in-python-for-web3-requests-a-comprehensive-guide) > [card] [Authentication methods available on Chainstack](/docs/authentication-methods-for-different-scenarios) > [card] [Scaling your DApp with Kubernetes: A comprehensive guide](/docs/tutorial-on-how-to-make-your-dapp-reliable-and-scalable-with-kubernetes) > [card] [Navigating the Web3 landscape: How to choose the right blockchain network for your DApp](/docs/navigating-the-web3-landscape-how-to-choose-the-right-blockchain-network-for-your-dapp) > [card] [Ethereum: BUIDLing a redundant event listener with ethers and web3.js](/docs/ethereum-redundant-event-llstener-ethers-web3js) > [card] [Ethereum: How to set up a redundant event listener with Python](/docs/ethereum-redundant-event-listener-python-version) # Checking node latency Source: https://docs.chainstack.com/docs/checking-node-latency Measure latency from your machine to a Chainstack node. Use Chainstack Compare for benchmarking, or run nmap / time curl / a Python latency logger for ad-hoc checks. **TLDR:** * For benchmarking node performance across regions and providers, use the [Chainstack Compare](https://compare.chainstack.com/). * For ad-hoc latency checks, `nmap -p 443 ` measures the TCP-connect time, and `time curl ...` measures full JSON-RPC round-trip. * `ping` and `traceroute` won't work — ICMP is disabled on Chainstack nodes. * For sustained measurement and logging, use the Python recipe below. ## Use Chainstack Compare [Chainstack Compare](https://compare.chainstack.com/) benchmarks RPC nodes across providers, protocols, and regions on metrics like latency, requests-per-second, and data-fetching throughput. Start there before reaching for ad-hoc tools — it covers what you usually want to know. ## Why `ping` and `traceroute` don't work ICMP is disabled by default on the instances running Chainstack nodes for security reasons. Running `ping`, `traceroute`, or `tracert` against your node hostname will time out or show lost packets — that's expected behavior, not an outage. Use the TCP/HTTP methods below instead. ## `nmap` — TCP-connect latency [`nmap`](https://nmap.org/) measures the latency of a TCP handshake against the node, which is a good proxy for raw network round-trip time. Chainstack endpoints serve HTTPS on port `443`: ```bash Bash theme={"system"} nmap -p 443 ethereum-mainnet.core.chainstack.com ``` Sample output: ``` PORT STATE SERVICE 443/tcp open https Nmap done: 1 IP address (1 host up) scanned in 0.18 seconds ``` For a more precise number, add `--reason --packet-trace` or use [`tcptraceroute`](https://github.com/mct/tcptraceroute). ## `time curl` — full JSON-RPC round-trip This measures the full HTTPS round-trip including TLS handshake plus the node's processing time for a real RPC call: ```bash Bash theme={"system"} time curl -sS 'https://ethereum-mainnet.core.chainstack.com/AUTH_KEY' \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \ -o /dev/null ``` Use a fast method like `eth_blockNumber` or `eth_chainId` to isolate transport latency from node-side processing. For finer breakdown, use `curl`'s built-in `-w` timing variables: ```bash Bash theme={"system"} curl -sS 'https://ethereum-mainnet.core.chainstack.com/AUTH_KEY' \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \ -o /dev/null \ -w 'dns: %{time_namelookup}s connect: %{time_connect}s tls: %{time_appconnect}s ttfb: %{time_starttransfer}s total: %{time_total}s\n' ``` ## Python — sustained measurement with CSV logging For a longer recording session — for example, watching latency over an hour to characterize jitter — use this script: ```python Python theme={"system"} import csv import time from datetime import datetime, timezone from pathlib import Path import requests NODE_URL = "https://ethereum-mainnet.core.chainstack.com/AUTH_KEY" METHOD = "eth_blockNumber" CSV_PATH = Path("latency.csv") INTERVAL = 5 # seconds payload = {"jsonrpc": "2.0", "method": METHOD, "params": [], "id": 1} if not CSV_PATH.exists(): with CSV_PATH.open("w", newline="") as f: csv.writer(f).writerow(["timestamp_utc", "method", "latency_ms", "status"]) while True: t0 = time.perf_counter() try: r = requests.post(NODE_URL, json=payload, timeout=10) status = r.status_code except requests.RequestException as e: status = f"error: {e.__class__.__name__}" ms = (time.perf_counter() - t0) * 1000 with CSV_PATH.open("a", newline="") as f: csv.writer(f).writerow([ datetime.now(timezone.utc).isoformat(timespec="milliseconds"), METHOD, f"{ms:.2f}", status, ]) time.sleep(INTERVAL) ``` The resulting `latency.csv` is ready for `pandas`, DuckDB, or a quick plot: ```bash Bash theme={"system"} duckdb -c "SELECT quantile(latency_ms, [0.5, 0.9, 0.99]) FROM read_csv_auto('latency.csv')" ``` ## See also * [Chainstack Compare](https://compare.chainstack.com/) * [Nodes, clouds, regions, and locations](/docs/nodes-clouds-regions-and-locations) * [Best practices for error handling in API requests](/docs/best-practices-for-error-handling-in-api-requests) # Common errors with @solana/web3.js Source: https://docs.chainstack.com/docs/common-errors-with-solana-web3js Fix the @solana/web3.js v1 WSS-derivation bug that causes 404 or 503 on subscriptions to Chainstack Solana endpoints. Pass wsEndpoint explicitly. **TLDR:** * `@solana/web3.js` v1 derives the WSS URL from the HTTP URL via its internal `makeWebsocketUrl` helper. The derived URL does not match a Chainstack WSS endpoint, and any subscription call fails with `Unexpected server response: 404` or `503`. * Fix v1 by passing `wsEndpoint` explicitly to `Connection`. * v1 is now [legacy](https://solana.com/docs/clients/official/javascript). [`@solana/kit`](/docs/solana-tooling) is the recommended SDK — it has no derivation bug because it takes RPC and subscription transports as separate inputs. ## The WSS-derivation bug When you call `new Connection(httpEndpoint)` without a `wsEndpoint`, v1 routes the URL through `makeWebsocketUrl` — a helper that rewrites the scheme (`https://` → `wss://`) and reshapes the host. The result does not match a Chainstack node's WSS endpoint, so the first subscription call fails: ``` ws error: Unexpected server response: 404 ``` You can also see `503` if the rewritten host resolves but the request hits a different upstream that returns 503 for the unknown path. This bites only the v1 line, which is now on the [maintenance branch](https://github.com/solana-foundation/solana-web3.js/tree/maintenance/v1.x) — security fixes only, no new features. The same project, renamed and restructured, ships today as [`@solana/kit`](/docs/solana-tooling). ## Fix: pass `wsEndpoint` explicitly Find the HTTPS and WSS endpoints in your node access details ([Manage your node](/docs/manage-your-node#view-node-access-and-credentials)) and pass both to `Connection`: ```javascript Node.js theme={"system"} const { Connection } = require("@solana/web3.js"); const rpcEndpoint = "https://solana-mainnet.core.chainstack.com/AUTH_KEY"; const wsEndpoint = "wss://solana-mainnet.core.chainstack.com/AUTH_KEY"; const connection = new Connection(rpcEndpoint, { wsEndpoint }); ``` > [!TIP] > > The HTTPS and WSS endpoints share the same host on Chainstack — there is no `ws-` subdomain. Only the scheme changes. ## Better: migrate to `@solana/kit` Per the [official Solana docs](https://solana.com/docs/clients/official/javascript), `@solana/kit` is now the recommended TypeScript SDK and `@solana/web3.js` is the legacy line. Kit takes RPC and subscription transports as separate inputs, so the derivation step that breaks v1 is gone: ```typescript TypeScript theme={"system"} import { createSolanaRpc, createSolanaRpcSubscriptions, } from "@solana/kit"; const rpc = createSolanaRpc("https://solana-mainnet.core.chainstack.com/AUTH_KEY"); const rpcSubscriptions = createSolanaRpcSubscriptions("wss://solana-mainnet.core.chainstack.com/AUTH_KEY"); ``` If a full rewrite is not on the table, the [`@solana/web3-compat`](https://solana.com/docs/frontend/web3-compat) bridge lets you swap your `@solana/web3.js` import for `@solana/web3-compat` and migrate file by file onto Kit primitives. It is a Phase 0 release — supported surface is currently `Connection` methods, `Keypair`, `Transaction`/`VersionedTransaction`, and `sendAndConfirmTransaction`; anything else falls back to legacy v1. Keep the v1 dependency in your `package.json` until your usage fits inside the supported surface. ## See also * [Solana tooling](/docs/solana-tooling) — modern Solana stack (Kit, Anchor 1.0, Codama, LiteSVM, Surfpool) * [Solana blockSubscribe 1009 error on WebSocket](/docs/solana-blocksubscribe-1009-error-on-websocket) — fix `1009` (`MESSAGE_TOO_BIG`) on large block pushes * [Handle real-time data using WebSockets with JavaScript and Python](/docs/handle-real-time-data-using-websockets-with-javascript-and-python) # Common SSL issues on Python and how to fix it Source: https://docs.chainstack.com/docs/common-ssl-issues-on-python-and-how-to-fix-it Fix Python CERTIFICATE_VERIFY_FAILED errors when connecting to Chainstack nodes. Update certifi, install macOS certificates, or use a certifi-aware SSL context. **TLDR:** * Python's `CERTIFICATE_VERIFY_FAILED` errors happen when your system can't chain the server's certificate back to a trusted root. * The safe fix is to update your system's CA certificates — install [`certifi`](https://pypi.org/project/certifi/), or run `Install Certificates.command` on macOS. * Disabling SSL verification works as a development-only workaround but should never ship to production. ## The error When a Python HTTP client connects to your Chainstack endpoint over HTTPS, it validates the server's TLS certificate by chaining it back to a trusted root certificate authority. If your system's CA bundle is outdated or missing, you'll see errors like: ``` [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate ``` ## Recommended fix: refresh your CA bundle ### Update `certifi` (all OSes) The [`certifi`](https://pypi.org/project/certifi/) package ships Mozilla's CA bundle for Python. Updating it covers most cases: ```bash Bash theme={"system"} pip install --upgrade certifi ``` If you use `requests`, `urllib`, `aiohttp`, or `httpx`, they pick up the updated bundle automatically the next time they construct a default SSL context. ### Install Certificates (macOS only) Python installers on macOS don't always install the system certificates. Run the bundled installer: 1. Open Spotlight (`Cmd + Space`). 2. Type `Install Certificates.command` and run it. This installs `certifi`'s CA bundle into the Python you launched. ## Explicit certifi-aware context If updating `certifi` alone doesn't take effect, build the SSL context explicitly: ```python Python theme={"system"} import ssl import certifi from urllib.request import urlopen url = "https://ethereum-mainnet.core.chainstack.com/AUTH_KEY" context = ssl.create_default_context(cafile=certifi.where()) response = urlopen(url, context=context) ``` ## Development-only workarounds > [!WARNING] > > These workarounds disable certificate validation. Use only for local debugging — never in production. Skipping verification makes your connection vulnerable to man-in-the-middle attacks. ### Unverified `ssl` context ```python Python theme={"system"} import ssl import urllib.request context = ssl._create_unverified_context() urllib.request.urlopen(url, context=context) ``` ### Override the default HTTPS context ```python Python theme={"system"} import ssl ssl._create_default_https_context = ssl._create_unverified_context ``` ### `requests` with `verify=False` ```python Python theme={"system"} import requests requests.get(url, verify=False) ``` ## See also * [Best practices for error handling in API requests](/docs/best-practices-for-error-handling-in-api-requests) * [Stack Overflow: CERTIFICATE\_VERIFY\_FAILED — unable to get local issuer certificate](https://stackoverflow.com/questions/52805115/certificate-verify-failed-unable-to-get-local-issuer-certificate) # Cronos methods Source: https://docs.chainstack.com/docs/cronos-methods Complete reference of supported Cronos JSON-RPC methods on Chainstack, including method availability for eth, net, and web3 namespace calls. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Cronos API call examples](/reference/getting-started-cronos). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Cronos tooling Source: https://docs.chainstack.com/docs/cronos-tooling Complete Cronos tooling guide for Chainstack nodes. Use MetaMask, Hardhat, ethers.js, Remix IDE, Foundry, and more to build dApps on Cronos blockchain. Get started with a [reliable Cronos RPC endpoint](https://chainstack.com/build-better-with-cronos/) to use the tools below. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Cronos nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Cronos nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Cronos nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Php theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Cronos nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint * USERNAME — your node access username * PASSWORD — your node access password See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Cronos nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Cronos network ID: * Mainnet: `25` * Testnet: `338` See [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — Cronos network ID: * Mainnet: `25` * Testnet: `338` ## Brownie 1. Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). 2. Use the `brownie networks add` command with the node endpoint: ```shell Shell theme={"system"} brownie networks add Cronos ID name="NETWORK_NAME" host= YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `chainstack-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password * NETWORK\_ID — Cronos network ID: * Mainnet: `25` * Testnet: `338` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use forge to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use cast to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Cronos: Dutch auction smart contract with Hardhat Source: https://docs.chainstack.com/docs/cronos-tutorial-dutch-auction-smart-contracts-on-cronos-with-hardhat Deploy a Dutch auction NFT contract on Cronos Mainnet using Hardhat where the price decreases over time until a buyer mints at an acceptable price. **TLDR:** * This tutorial shows how to create and deploy a Dutch auction NFT contract on Cronos Mainnet using Hardhat. * The price starts high and lowers at fixed intervals; buyers can mint an NFT once the price is acceptable. * You’ll set up Hardhat, code and compile the contract, then verify and interact with it using the Cronos Explorer. * Because this deploys to Cronos Mainnet, deployment and minting spend real CRO — fund your wallet with a small amount to start. ## Main article A Dutch auction is a type of an auction in which the initial price of an NFT starts at its ceiling and lowers by a small amount at set intervals. Buyers make bids at reduced prices until the end of an auction. A Dutch auction continues until either all assets are sold out or the auction time ends. The objective of this tutorial is to familiarize you with the Cronos network, Hardhat, and Dutch auction smart contracts. In the end of the tutorial, you will be able to create a simple Dutch auction smart contract to help you sell your NFTs to the highest bidder. Specifically, in this tutorial, you will: * Create a Dutch auction smart contract. * Deploy and verify the contract on Cronos Mainnet through a node deployed with Chainstack. * Interact with the deployed contract. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Cronos RPC endpoint](https://chainstack.com/build-better-with-cronos/). * [Hardhat](https://hardhat.org/docs) to compile and deploy the contract. * [MetaMask](https://docs.metamask.io/guide/) to interact with the contract through your Chainstack node. ## Overview To get from zero to deploying your own Dutch auction smart contract on Cronos Mainnet, do the following: 1. With Chainstack, create a public chain project (A public chain project- a project to join public networks). 2. With Chainstack, join Cronos Mainnet. 3. With Chainstack, access your Cronos node endpoint. 4. With Hardhat, set up your development environment. 5. With Hardhat, create and compile your Dutch auction contract. 6. With MetaMask, fund your wallet with CRO to cover deployment and minting gas. 7. With Hardhat, deploy your Dutch auction contract. 8. With the [Cronos Explorer](https://explorer.cronos.com/), interact with your Dutch auction contract. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join Cronos Mainnet See [Join a public network](/docs/manage-your-networks). ### Get your Cronos node endpoint See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Install Hardhat See [Hardhat documentation](https://hardhat.org/hardhat-runner/docs/getting-started#installation). ### Initialize a Hardhat project In your project directory, run `npx hardhat`. Select **Create a JavaScript project** and agree to install the sample project's dependencies. This will create a sample project directory with a smart contract draft. ### Install additional dependencies To complete the project, we need to install several additional dependencies. The [OpenZeppelin Contract library](https://docs.openzeppelin.com/) allows you to inherit smart contracts. To install it, run: ```bash Shell theme={"system"} npm i @openzeppelin/contracts ``` The [dotenv library](https://github.com/motdotla/dotenv) allows you to export and keep sensitive data securely. To install it, run: ```bash Shell theme={"system"} npm i dotenv ``` Contract verification uses [`@nomicfoundation/hardhat-verify`](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify), which is already bundled in the `@nomicfoundation/hardhat-toolbox` the sample project installed — no separate verification plugin is needed. You point it at the Cronos Explorer in the Hardhat config below. You need to create an environment file to store your sensitive data with the project. To create it, in your project directory, run: ```bash Shell theme={"system"} touch .env ``` ### Create and compile the contract 1. Navigate to your previously created project directory and go to the `contracts` directory. In the directory, create your Dutch auction smart contract: `DutchAuction.sol`. ```solidity solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract MyToken is ERC721, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; uint256 public immutable startPrice = 10 ether; uint256 public immutable startAt; uint256 public immutable endsAt; uint256 public immutable endPrice = 5 ether; uint256 public immutable discountRate = 0.01 ether; uint256 public duration = 500 minutes; uint256 public immutable MAX_SUPPLY = 100; mapping (address => bool) public onlyOne; constructor() ERC721("CronosNFT", "CroNFT") { startAt = block.timestamp; endsAt = block.timestamp + duration; } function price() public view returns (uint256) { if (block.timestamp > endsAt ) { return endPrice; } uint256 minutesElapsed = (block.timestamp - startAt) / 60; return startPrice - (minutesElapsed * discountRate); } function safeMint(address to) public payable { require(msg.value >= price(), "Not enough ether sent"); require(!onlyOne[msg.sender], "Sorry, Address already has 1 NFT!"); uint256 tokenId = _tokenIdCounter.current(); require(tokenId < MAX_SUPPLY, "No more items left."); _safeMint(to, tokenId + 1); _tokenIdCounter.increment(); onlyOne[msg.sender]=true; } function withdraw() public onlyOwner { payable(owner()).transfer(address(this).balance); } } ``` The contract implementation is the following: * The price of an NFT starts at 10 ether and decreases to 5 ether over 500 minutes. A maximum of 100 NFTs can be minted. * The contract uses the `onlyOne` mapping to ensure that each address can only own one NFT from your collection. * The contract sets the values of the start and end price, which cannot be changed later on. * The function `price()` uses `block.timestamp` to calculate the price of an NFT each time the `safeMint` function is called. * If all the required conditions are satisfied, the wallet address gets an NFT minted. 2. To compile the contract, in your project directory, run: ```bash Shell theme={"system"} npx hardhat compile ``` ### Fund your account To deploy your smart contract on Cronos Mainnet, the deploying wallet needs CRO to cover gas. Fund it with a small amount of CRO from an exchange or an existing wallet. ### Set up the environment and configuration files 1. In your project directory, navigate to a previously created environment file and edit it to add the following data: * `RPC_URL` — your Cronos node HTTPS endpoint deployed with Chainstack. See also [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). * `PRIVATE_KEY` — the private key of your MetaMask wallet, funded with enough CRO to cover deployment gas. * `API_KEY` — a Cronos Explorer API key used to verify the deployed contract. Create one in the [Cronos Explorer](https://explorer.cronos.com/) (see [Block explorer and API keys](https://docs.cronos.com/block-explorers/block-explorer-and-api-keys)). Example of the environment file data: ```env env theme={"system"} RPC_URL=YOUR_CHAINSTACK_ENDPOINT PRIVATE_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXu7 API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXG5 ``` 2. In your project directory, navigate to the Hardhat configuration file and replace the current data with the following data: ```js JavaScript theme={"system"} require("@nomicfoundation/hardhat-toolbox"); require('dotenv').config(); module.exports = { solidity: "0.8.10", networks: { cronos: { url: `${process.env.RPC_URL}`, chainId: 25, accounts: [process.env.PRIVATE_KEY] }, }, etherscan: { apiKey: { cronos: `${process.env.API_KEY}` }, customChains: [ { network: "cronos", chainId: 25, urls: { apiURL: `https://explorer-api.cronos.org/mainnet/api/v1/hardhat/contract?apikey=${process.env.API_KEY}`, browserURL: "https://explorer.cronos.com" } } ] } }; ``` where * `dotenv` — library to import your sensitive data from the environment file securely * `cronos` — the Cronos Mainnet network to deploy the contract to * `url` — your Cronos node HTTPS endpoint imported from the environment file * `accounts` — your MetaMask wallet private key imported from the environment file * `etherscan` — Cronos Explorer verification settings: your API key plus the `customChains` entry that points `hardhat-verify` at the Cronos Explorer ### Deploy the Dutch auction contract 1. In your project directory, navigate to the scripts directory and create the `DeployDutch.js` file. 2. Edit the `DeployDutch.js` file to add the basic deployment script of Hardhat: ```js JavaScript theme={"system"} const hre = require("hardhat"); async function main() { const CronosToken = await hre.ethers.getContractFactory("MyToken"); console.log("Deploying your contract, please Wait....."); const cronosToken = await CronosToken.deploy(); await cronosToken.deployed(); console.log("CronosToken deployed to:", cronosToken.address); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); }); ``` 3. In your project directory, run the following script: ```bash Shell theme={"system"} npx hardhat run scripts/DeployDutch.js --network cronos ``` The contract will deploy and the terminal will return the contract address. Use this address to verify and interact with your contract. ### Verify your contract on Cronos Mainnet Once your contract is deployed, verify it on Cronos Mainnet. In your terminal, run: ```bash Shell theme={"system"} npx hardhat verify --network cronos CONTRACT_ADDRESS ``` where CONTRACT\_ADDRESS your contract address returned at the previous step ### Interact with the contract Now that your contract is verified, the [Cronos Explorer](https://explorer.cronos.com/) is effectively a front-end instance for your contract. ## Conclusion This tutorial guided you through the basics of using Hardhat to deploy a Dutch auction smart contract to Cronos Mainnet, and verify it against the Cronos Explorer with `hardhat-verify`. Because this deploys to Cronos Mainnet, every deployment and mint spends real CRO — keep amounts small while you experiment. ### About the author > [card] Priyank Gupta > > ![Priyank Gupta](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/56264430/224220454-0df06476-4ea8-4f40-8255-88f1cb4ab89d.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=014a7d3f64f7716d099c62d1868b8dd6) > > Developer Advocate @ Chainstack > \ > BUIDLs on Ethereum, zkEVMs, and The Graph protocol > \ > Part-time Rust aficionado > > [https://github.com/Genesis3800](https://github.com/Genesis3800) > > [https://twitter.com/PriyankGupta03](https://twitter.com/PriyankGupta03) > > [https://www.linkedin.com/in/priyank-gupta-0308/](https://www.linkedin.com/in/priyank-gupta-0308/) # cryo with Chainstack: Blockchain data mastery in Python Source: https://docs.chainstack.com/docs/cryo-with-chainstack-and-python Use cryo with a Chainstack Global Node to extract, filter, and analyze blockchain data in Parquet format using Python and the Polars library. **TLDR** * Integrate **cryo** (a Rust-based CLI for blockchain data extraction) with Python via **cryo\_python** for streamlined data collection and analysis. * **cryo.collect()** fetches data into Python-friendly objects (Pandas, Polars, etc.) for real-time analysis, while **cryo.freeze()** saves data to files in JSON/Parquet/CSV. * Combine Chainstack's high-performance Global Nodes with Python's libraries (pandas, matplotlib, etc.) to query, process, and visualize blockchain data with speed and precision. ## Main article In blockchain data exploration, we previously introduced you to `cryo`, Paradigm's powerful command-line interface tool. As you might recall, this tool is a beacon for developers, researchers, and blockchain enthusiasts, optimizing the process of extracting data from various blockchain networks. Our initial journey through `cryo` revealed its data formatting efficiency and seamless integration with Chainstack [Global Nodes](/docs/global-elastic-node). > [!NOTE] > > Learn how to use `cryo` and how it works with **[cryo: Your gateway to blockchain data](/docs/cryo-your-gateway-to-blockchain-data#basic-usage-of-cryo)**. Now, we embark on a sequel, bridging `cryo` with the world of Python. This guide will show you how to use the Python wrapper made for the `cryo` CLI by covering setup, basic usage, and data extraction and manipulation using common Python libraries. ## Python and cryo for blockchain data manipulation Python is known for its simplicity and data manipulation and analysis capability. The Python wrapper allows you to couple `cryo`'s Rust-based efficiency for data extraction with Python's data manipulation capabilities. This integration enhances the analytical power at your fingertips, allowing you to leverage Python's rich library ecosystem for in-depth data analysis, visualization, and machine learning. ## Prerequisites and setup This section will lay the groundwork for integrating the `cryo` tool with Python. This process involves ensuring that your system has the necessary tools and libraries and installing the Python wrapper for `cryo`. ### Prerequisites Before diving into the installation process, ensure your environment is primed for the task. The following prerequisites are essential: * **Chainstack Global Node RPC**: Get a high-performance Chainstack [Global Node](/docs/global-elastic-node) RPC before starting. Follow these steps to deploy an Ethereum node: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). To follow this guide, deploy a **Standard** Ethereum node, which will default to a Global Node. Once you deploy the node, you'll have access to an RPC endpoint, which will look like this: ``` https://ethereum-mainnet.core.chainstack.com/YOUR_CREDENTIALS ``` Create a `.env` file in your root directory and place the endpoint in it. ```python Python theme={"system"} ETH_RPC="https://ethereum-mainnet.core.chainstack.com/YOUR_CREDENTIALS" ``` * **Rust**: Rust must be installed in your system for `cryo` to work, the Python integration is a lightweight wrapper for the `cryo` CLI, so you'll still need to meet the app's requirements. > [!NOTE] > > Install Rust following the [rustup](https://rustup.rs/) instructions. * **Python Environment**: Ensure that you have Python installed on your system and create a new virtual environment in your project's directory; you can run the following: ```bash Bash theme={"system"} python3 -m venv cryo-and-python ``` Then activate the virtual environment with: ```bash Bash theme={"system"} source cryo-and-python/bin/activate ``` * **Required Libraries**: `cryo_python` depends on several libraries, make sure to install the following libraries, ```bash Bash theme={"system"} pip install maturin pandas polars pyarrow python-dotenv web3 matplotlib ``` Note that the `python-dotenv web3 matplotlib` libraries are not strictly required to run `cryo_python`, but we'll use them along the guide. ### Installation and Setup With the prerequisites in place, let's move on to the installation steps: 1. **Clone the cryo Repository**: Use git to clone the `cryo` repository from GitHub. If you don't have git installed, you can download it from [git](https://git-scm.com/downloads). ```bash Bash theme={"system"} git clone https://github.com/paradigmxyz/cryo ``` 2. **Navigate to the Python Directory**: ```bash Bash theme={"system"} cd cryo/crates/python ``` 3. **Build** `cryo_python`: * Run the `maturin` build command: ```bash Bash theme={"system"} maturin build --release ``` * This command will compile the Rust code and create a wheel file (.whl) for the Python package. 4. **Install the Python Wrapper**: * Find the `.whl` file generated by maturin. It will be located in the `target/wheels` directory. * Install the wheel file using pip: ```bash Bash theme={"system"} pip install --force-reinstall .whl ``` * Replace `` with the actual path to the `.whl` file generated, it will look like this: ```bash Bash theme={"system"} /YOUR_PATH/cryo/target/wheels/cryo_python-0.3.0-cp310-cp310-macosx_11_0_arm64.whl ``` Your current draft provides a solid foundation. To enhance it, we can add more context and details based on the source files, particularly focusing on the functionality and technical nuances of `cryo.collect()` and `cryo.freeze()`. Here's an improved version: ## Basic Usage of `cryo_python` `cryo_python` serves as a lightweight wrapper for the `cryo` CLI offers a seamless Python interface to the powerful CLI commands. With `cryo_python`users can access two principal functions that mirror their CLI counterparts: * `cryo.collect()` extracts blockchain data and returns it as a Python-friendly data frame, enabling direct use within scripts for real-time analysis and manipulation. * `cryo.freeze()` fetches data and saves it to a file, facilitating subsequent use or long-term storage. > [!NOTE] > > Explore the source files for [cryo.collect()](https://github.com/paradigmxyz/cryo/blob/main/crates/python/python/cryo/_collect.py) and [cryo.freeze()](https://github.com/paradigmxyz/cryo/blob/main/crates/python/python/cryo/_freeze.py) in the GitHub repository. ### `cryo.collect()` Main Aspects 1. **Asynchronous Support**: `cryo.collect()` includes both `async_collect` and `collect` methods, designed to operate asynchronously. This feature is vital for efficiently handling large datasets or high-throughput tasks, ensuring optimal resource utilization and performance. 2. **Multiple Output Formats**: `cryo.collect()` allows you to organize data in various Python-friendly formats for diverse scenarios: * **Polars DataFrame**: Ideal for high-performance data manipulation, leveraging its fast, efficient data handling capabilities. * **Pandas DataFrame**: Provides broad compatibility with Python's extensive data analysis ecosystem. * **List of Dictionaries**: Facilitates easy handling of JSON-like data structures, simplifying serialization. * **Dictionary of Lists**: Offers an alternative structured data format suitable for specific data processing requirements. ### `cryo.freeze()` Main Aspects 1. **Data Type Flexibility**: `cryo.freeze()` can handle single and multiple data types, showcasing its versatility in accommodating various data collection needs. 2. **Argument Parsing**: Echoing `cryo.collect()`, `cryo.freeze()` also parses additional keyword arguments (`*kwargs`), enhancing the customization possibilities in data collection and storage. ### Usage examples Having grasped the basics of **`cryo_python`**, let's get into practical examples to demonstrate its usage. Throughout this guide, we'll consistently retrieve the RPC endpoint from a **`.env`** file. > [!NOTE] > > Ensure you have your RPC endpoint details in a `.env` file for these examples. ### `cryo.collect` basic example Start by creating a file named **`main.py`** and paste the following code: ```python Python theme={"system"} import os import cryo from dotenv import load_dotenv # Load environment variables from .env file load_dotenv() # Retrieve the Ethereum RPC URL from environment variables eth_rpc = os.getenv("ETH_RPC") # Collect blockchain data using the cryo library and return it as a pandas DataFrame # Specifying blocks range and output format data = cryo.collect( "blocks", blocks=["18734050:18735050"], rpc=eth_rpc, output_format="pandas", hex=True ) # Displaying the column names of the DataFrame print("Columns in the DataFrame:") for column in data.columns: print(column) # Print the entire DataFrame print(data) ``` Here's an explanation of how it works and what it does: 1. **Environment Setup**: * The code starts by importing the necessary modules: `os` for environment variable management, `cryo` for accessing blockchain data, and `load_dotenv` from the `dotenv` package to load environment variables from a `.env` file. * It then loads the environment variables using `load_dotenv()`, which reads the `.env` file and sets the variables. 2. **Accessing Ethereum RPC Endpoint**: * The `ETH_RPC` variable, which contains the URL to an Ethereum RPC endpoint, is fetched from the environment variables using `os.getenv("ETH_RPC")`. 3. **Data Collection with `cryo.collect`**: * The `cryo.collect` function has specific parameters to fetch data from the Ethereum blockchain. * `datatype`: Set to `"blocks"`, indicating that the function should fetch data about blockchain blocks. * `blocks`: Specifies the range of blocks to fetch data for (in this case, from block `18734050` to `18735050`). * `rpc`: The Ethereum RPC endpoint URL, passed as `eth_rpc`. * `output_format`: Set to `"pandas"`, indicating that the data should be returned as a Pandas DataFrame. * `hex`: The boolean parameter set to `True` will return the data already converted to hexadecimal. 4. **Output**: * The fetched data is stored in the variable `data`, a Pandas DataFrame. * The script then prints the column names of the DataFrame to provide an overview of the data structure. * Finally, it prints the DataFrame `data`, showing the fetched blockchain data. The result of this script is a detailed listing of data for the specified range of Ethereum blocks. The DataFrame columns represent each block's attributes, such as `block_hash`, `author`, `block_number`, `gas_used`, `extra_data`, `timestamp`, `base_fee_per_gas`, and `chain_id`. Here is an example of the output in the console: ```shell Shell theme={"system"} Columns in the DataFrame: block_hash author block_number gas_used extra_data timestamp base_fee_per_gas chain_id block_hash author ... base_fee_per_gas chain_id 0 0xdf6d5d7526eb50e68278998b2cc7a519a4c3daddb14a... 0xcdbf58a9a9b54a2c43800c50c7192946de858321 ... 37583327088 1 1 0x2818389bb471ebe60a74fb1865574c0ac50f40daf575... 0xdafea492d9c6733ae3d56b7ed1adb60692c98bc5 ... 38781853745 1 2 0xd65229e3d67c28f71b978ae789df6ee58c27420f8a35... 0x1f9090aae28b8a3dceadf281b0f12828e676c326 ... 38448400269 1 3 0x218f26e524d889d20604ad01b91feec5c2285dc3e747... 0x4675c7e5baafbffbca748158becba61ef3b0a263 ... 38316617096 1 4 0xace296b1c263fee4f35d831086c93aa820577dcc1bea... 0x388c818ca8b9251b393131c08a736a67ccb19297 ... 37638298982 1 .. ... ... ... ... ... 995 0xc59d9ad3444b0352c36f3ec7e3a3561bbc90d9118232... 0x690b9a9e9aa1c9db991c7721a92d351db4fac990 ... 71896254942 1 996 0xd611033a7769913ba0e8abdc8ae0ab0fee224435c512... 0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97 ... 70283122265 1 997 0x2a4448fe72e37c169868e37ea4fce71789c31ecc9108... 0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5 ... 73132855124 1 998 0x6ce066a304e334c6a98154b51f2a1b24edf749467424... 0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97 ... 74400095143 1 999 0xbc293dc0e8d0a61f24256433f7faaf2a8e754a5557d9... 0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97 ... 73090669589 1 [1000 rows x 8 columns] ``` Running this Python script is the equivalent of running this command from the `cryo` CLI directly: ```bash Bash theme={"system"} cryo blocks --blocks 18734050:18735050 --rpc YOU_CHAINSTACK_NODE ``` > [!NOTE] > > Please note that Chainstack endpoints on the **Developer** plan are limited to 30 RPS, so you might need to add rate limiting to your code; starting from the [**Growth** plan](https://chainstack.com/pricing/), there is no rate limit. To manage rate limits, **`cryo.collect`** can be adjusted using the `requests_per_second` parameter: ```python Python theme={"system"} data = cryo.collect( "blocks", blocks=["18734050:18735050"], rpc=eth_rpc, output_format="pandas", hex=True, requests_per_second=25 ) ``` ### `cryo.freeze` basic example The principle of **`cryo.freeze`** is quite similar to **`cryo.collect`**. In a new file, paste this code: ```python Python theme={"system"} import os from dotenv import load_dotenv import cryo # Load environment variables from the .env file load_dotenv() # Retrieve the Ethereum RPC URL from environment variables eth_rpc = os.getenv("ETH_RPC") # Fetch and save blocks data in JSON data = cryo.freeze( "blocks", blocks=["18734050:18735050"], rpc=eth_rpc, output_dir="blocks_data/", file_format="json", hex=True, requests_per_second=500 ) ``` This script uses **`cryo.freeze`** to fetch and save the same block data as a JSON file in the specified directory. The logic and syntax closely follow the **`cryo`** CLI. The result is a JSON file containing data for the blocks in the `root/blocks_data/` directory. Since both **`cryo.freeze`** and **`cryo.collect`** are just wrappers around the CLI; you can use the same commands. Let's explore a few more examples. ### Fetching ERC-20 balances with `cryo` This section will guide you in using `cryo_python` to retrieve ERC-20 token balances from specified addresses and contracts. We'll get the balance of the [APECoin](https://etherscan.io/address/0x4d224452801ACEd8B2F0aebE155379bb5D594381) token in the [Binance](https://etherscan.io/token/0x4d224452801ACEd8B2F0aebE155379bb5D594381?a=0xf977814e90da44bfa03b6295a0616a897441acec) address in a range of 10,000 blocks. Start by creating a new Python file and paste the following code: ```python Python theme={"system"} import os from dotenv import load_dotenv import cryo # Load environment variables load_dotenv() # Access Ethereum RPC URL from environment variables eth_rpc = os.getenv("ETH_RPC") # Fetch ERC-20 token balances for a specified address within a block range data = cryo.freeze( "erc20_balances", blocks=["18.68M:18.69M"], contract=['0x4d224452801ACEd8B2F0aebE155379bb5D594381'], address=['0xF977814e90dA44bFA03b6295A0616a897441aceC'], rpc=eth_rpc, output_dir="blocks_data/", file_format="json", hex=True, requests_per_second=900 ) ``` Executing this script will generate a JSON file containing the ERC-20 balance data structured as follows: ```bash Bash theme={"system"} Schema for ERC-20 Balances ───────────────────────── - block_number: uint32 - erc20: hex - address: hex - balance_binary: binary - balance_string: string - balance_f64: float64 - chain_id: uint64 ``` This structure, `erc20_balances`efficiently organizes ERC-20 balances by block, offering a clear and accessible format for data analysis. > [!NOTE] > > Check the [`cryo` documentation](https://github.com/paradigmxyz/cryo?tab=readme-ov-file#cryo-datasets) to find what other datasets you can fetch. ## Fetch and manipulate blockchain data Having explored the basic functionality of `cryo_python`, let's now get into a more advanced application by integrating it with essential Python libraries for data manipulation and visualization. ### Find the top 10 block authors In this example, we'll fetch Ethereum blockchain data and visualize the top block authors using `cryo_python`, `pandas`, and `matplotlib`. In a Python file, paste the following: ```python Python theme={"system"} import os import time import pandas as pd import matplotlib.pyplot as plt from dotenv import load_dotenv from web3 import Web3 import cryo # Constants ETH_RPC_VAR = "ETH_RPC" LOOKBACK_BLOCKS = 5000 TOP_AUTHORS_COUNT = 10 # Load environment variables load_dotenv() # Function to get the block range def get_block_range(web3, lookback_blocks): latest_block = web3.eth.block_number start_block = max(0, latest_block - lookback_blocks) # Avoid negative block numbers return f"{start_block}:{latest_block}" # Function to fetch data from cryo def fetch_block_data(block_range, eth_rpc): try: # Start timer for fetching blocks fetch_start_time = time.time() # Fetch the block data data = cryo.collect( "blocks", blocks=[block_range], rpc=eth_rpc, output_format="pandas", hex=True ) # Calculate and print the time taken to fetch the blocks fetch_time = time.time() - fetch_start_time print(f"Time taken to fetch blocks: {fetch_time:.2f} seconds") return data except Exception as e: print(f"An error occurred while fetching block data: {e}") return pd.DataFrame() # Return an empty DataFrame on error # Function to plot the top authors def plot_top_authors(data, num_entries): top_authors = data['author'].value_counts().head(TOP_AUTHORS_COUNT) plt.figure(figsize=(12, 6)) top_authors.plot(kind='bar') plt.xticks(rotation=45, ha='right') plt.title(f'Top {TOP_AUTHORS_COUNT} Authors by number of blocks mined from past {num_entries} blocks', fontsize=14) plt.xlabel('Author', fontsize=14) plt.ylabel('Number of Blocks', fontsize=14) plt.tight_layout() plt.show() # Main execution def main(): eth_rpc = os.getenv(ETH_RPC_VAR) if not eth_rpc: raise ValueError(f"Environment variable {ETH_RPC_VAR} not found") w3 = Web3(Web3.HTTPProvider(eth_rpc)) if not w3.is_connected(): print("Failed to connect to Ethereum node.") return block_range = get_block_range(w3, LOOKBACK_BLOCKS) print(f"Fetching blocks from {block_range} range.") data = fetch_block_data(block_range, eth_rpc) if not data.empty: num_entries = len(data) print(f"Number of blocks fetched: {num_entries}") plot_top_authors(data, num_entries) if __name__ == "__main__": main() ``` Here's a step-by-step breakdown of what this script does: 1. **Setting Up the Environment**: We start by importing necessary libraries like `os`, `time`, `pandas`, `matplotlib.pyplot`, and `Web3`, along with `cryo`. Then, we define constants for the RPC URL, the number of blocks to look back on, and the number of top authors to display. 2. **Fetching Blockchain Data**: We define a function to determine the range of blocks to fetch based on the current block number. Another function uses `cryo.collect` to get data on these blocks and returns it as a pandas DataFrame. We track the time taken for this operation, offering insights into the performance of our data retrieval process. 3. **Data Visualization**: With the blockchain data in hand, we analyze the top block authors using a function that counts the occurrences of each author in the data. We then use `matplotlib` to create a bar chart, showcasing the top authors based on the number of blocks mined. 4. **Executing the Script**: In the `main` function, we initialize a Web3 instance, connect to the Ethereum node, fetch the block data, and, if successful, visualize the top authors. We handle potential errors, such as missing environment variables or connection issues, to ensure robustness. 5. **Running the Code**: This script is designed as a standalone program. When executed, it will display a bar chart illustrating the most active Ethereum block authors over a specified block range. This example demonstrates how to effectively combine `cryo` with other Python tools to fetch, process, and visualize Ethereum blockchain data, providing valuable insights into blockchain activity. Here is an example of the console output and chart. The console will output something like the following: ```bash Bash theme={"system"} Fetching blocks from 18874064:18879064 range. Time taken to fetch blocks: 30.83 seconds Number of blocks fetched: 5000 Top Authors by Number of Blocks Mined: 0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5: 1417 blocks 0x1f9090aae28b8a3dceadf281b0f12828e676c326: 1385 blocks 0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97: 443 blocks 0x388c818ca8b9251b393131c08a736a67ccb19297: 295 blocks 0xb9342d6a9789cc6479e48cfef67590c1bd05744e: 213 blocks 0x88c6c46ebf353a52bdbab708c23d0c81daa8134a: 183 blocks 0xdafea492d9c6733ae3d56b7ed1adb60692c98bc5: 175 blocks 0x0aa8ebb6ad5a8e499e550ae2c461197624c6e667: 89 blocks 0x4675c7e5baafbffbca748158becba61ef3b0a263: 55 blocks 0x690b9a9e9aa1c9db991c7721a92d351db4fac990: 52 blocks ``` And the chart will look like this: ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/fb237c9-authors.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=9dbf32c2c11775aa8b35bd3e6e05c8a9) ### Visualise ERC-20 balance changes over time The next example we'll work on will use the same `erc20_balances` dataset used in one of the previous examples. This time, we'll fetch and visualize how much [WETH](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) is in the[`WETH-USDT` pool](https://etherscan.io/address/0x0d4a11d5EEaaC28EC3F61d100daF4d40471f185) from Uniswap V2. In a new file, paste the following code: ```python Python theme={"system"} import os import time from dotenv import load_dotenv import cryo import pandas as pd import matplotlib.pyplot as plt import matplotlib.ticker as ticker from web3 import Web3 # Constants ETH_RPC_VAR = "ETH_RPC" LOOKBACK_BLOCKS = 7200 # Approx a day in the past CONTRACT_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' # WETH WALLET_ADDRESS = '0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852' # WETH-USDT pool Uniswap V2 # Initialize environment variables and Web3 load_dotenv() eth_rpc = os.getenv(ETH_RPC_VAR) w3 = Web3(Web3.HTTPProvider(eth_rpc)) def check_eth_rpc_connection(eth_rpc): """Check connection to Ethereum RPC.""" if not eth_rpc: raise ValueError(f"Environment variable {ETH_RPC_VAR} not found") if not w3.is_connected(): raise ConnectionError("Failed to connect to Ethereum node.") def get_block_range(lookback_blocks): """Determine the range of blocks to fetch.""" latest_block = w3.eth.block_number start_block = max(0, latest_block - lookback_blocks) return f"{start_block}:{latest_block}" def fetch_erc20_balances(block_range): """Fetch ERC-20 token balances within a given block range.""" return cryo.collect( "erc20_balances", blocks=[block_range], contract=[CONTRACT_ADDRESS], address=[WALLET_ADDRESS], rpc=eth_rpc, output_format="pandas", hex=True, requests_per_second=900 # Adapt the RPS to your endpoint ) def convert_balance_to_ether(balance_str): """Convert balance from Wei to Ether, handling None values.""" return None if balance_str is None else Web3.from_wei(int(balance_str), 'ether') def plot_balance_change_over_time(data): """Plot the balance change over time on a chart.""" plt.figure(figsize=(12, 6)) plt.plot(data['block_number'], data['balance_ether'], marker='o') # Set axis labels and chart title with contract and address plt.xlabel("Block Number") plt.ylabel("Balance (Ether)") plt.title(f"ERC-20 Token Balance Change for {CONTRACT_ADDRESS}\nWallet {WALLET_ADDRESS}") # Manually set the x-axis ticks based on the block range block_numbers = data['block_number'] tick_spacing = (block_numbers.max() - block_numbers.min()) // 10 # for example, 10 evenly spaced ticks ticks = range(int(block_numbers.min()), int(block_numbers.max()), int(tick_spacing)) plt.xticks(ticks, [f"{tick:,.0f}" for tick in ticks]) # Format the y-axis to show balances rounded to 4 decimal places plt.gca().yaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.4f}')) # Add grid, tighten layout, and display the plot plt.grid(True) plt.tight_layout() plt.show() def main(): """Main function to fetch and plot ERC-20 token balance changes.""" check_eth_rpc_connection(eth_rpc) block_range = get_block_range(LOOKBACK_BLOCKS) # Start timing the data fetch start_time = time.time() # Fetch the data data = fetch_erc20_balances(block_range) # End timing the data fetch end_time = time.time() elapsed_time = end_time - start_time print(f"Data fetched in {elapsed_time:.2f} seconds.") if data.empty: print("No data available for plotting.") return # Prepare data for plotting data = data[['block_number', 'erc20', 'address', 'balance_string']] data['balance_ether'] = data['balance_string'].apply(convert_balance_to_ether) data = data[data['balance_ether'].notnull()] # Filter out rows with None values # Print data summary to the console print("\nData summary:") print(f"Block rage: {block_range}") print(f"Start balance in Ether: {data.iloc[0]['balance_ether']}") print(f"End balance in Ether: {data.iloc[-1]['balance_ether']}") # Plot the balance changes over time plot_balance_change_over_time(data) if __name__ == "__main__": main() ``` Here's a step-by-step explanation of what's going on: 1. **Fetch Block Range**: * It calculates the range of blocks to query by finding the latest block number and subtracting the lookback period to determine the start block; in the example, we analyze about a day's worth of blocks. 2. **Fetch ERC-20 Balances**: * The script fetches ERC-20 token balance data from the specified contract, wallet address, and block range. The `cryo.collect` function is called, and the data is returned in a pandas DataFrame format. 3. **Data Conversion and Cleaning**: * A conversion function transforms balance values from Wei (the smallest unit of Ether) to Ether for readability. It handles any `None` values to avoid errors during conversion. 4. **Summarizing Data**: * The script prints out a summary of the data to the console, including the block range and the start and end balances in Ether, providing a quick overview of the dataset. 5. **Data Visualization**: * It then plots the balance changes over time using `matplotlib`. The x-axis represents block numbers, and the y-axis represents the balance in Ether. * The axis tick labels are formatted for better readability, and the chart is titled with the contract and wallet address for reference. > [!NOTE] > > Remember to adapt the request per second. Here is an example of the result: ```python Python theme={"system"} Data fetched in 16.51 seconds. Data summary: Block rage: 18872292:18879492 Start balance in Ether: 27034.167858289615425314 End balance in Ether: 27039.186876795597977365 ``` Graph for the balance change over a day: ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/087e971-balances.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=6b95a0b74b1dc072c294463e3880d05c) As you can see, we can use `cryo` to fetch data and manipulate it with Python, a very powerful combo. ## Conclusion The integration of `cryo` with Python is a significant advancement for blockchain data analysis. It combines `cryo`'s efficient data extraction capabilities with Python's powerful data processing and visualization tools. This synergy, coupled with high-performance Chainstack Global Nodes, enables users to easily extract, analyze, and visualize blockchain data, making it an invaluable resource for developers, researchers, and enthusiasts in the blockchain community. The practical examples demonstrate this integration's real-world utility, highlighting its potential to yield insightful and actionable information from complex blockchain datasets. In essence, `cryo` and Python offer an effective and accessible platform for in-depth blockchain data exploration. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # cryo: Your gateway to blockchain data Source: https://docs.chainstack.com/docs/cryo-your-gateway-to-blockchain-data Extract blockchain data into JSON or Parquet using cryo, a Rust CLI tool for blocks, transactions, and logs paired with Chainstack RPC endpoints. **TLDR** * **cryo** is a Rust-based command-line tool for extracting blockchain data (blocks, transactions, logs) into formats like JSON or Parquet. * You can flexibly specify block ranges, columns, or events to filter exactly the data you want. * Pairing cryo with a high-performance Chainstack Global Node unlocks near-unlimited RPS for lightning-fast data retrieval and analyses. * Prebuilt event scrapers for ERC-20/721 Transfers or custom logs provide a seamless research and development experience. ## Main article Cryo, by [Paradigm](https://www.paradigm.xyz/), is a tool that is as cool as its name suggests. If you're venturing into blockchain data, whether you're a researcher, developer, or just a curious explorer, `cryo` is about to become your new best friend. This guide is designed to walk you through what `cryo` is, how it works, and how to harness its power to fetch blockchain data quickly. At its core, `cryo` is a command-line interface (CLI) tool, but don't let the simplicity of its interface fool you. This tool packs a powerful punch, making an easy and flexible way to extract blockchain data into various user-friendly formats. Whether you need your data in Parquet, CSV, JSON, or piped directly into a Python data frame, `cryo` has got you covered. So, whether you're planning to build complex applications, conduct in-depth research, or satisfy your curiosity about blockchain operations, `cryo` will make the process simple and fast. Today, let's learn how to use `cryo` coupled with a high-performance Chainstack [Global Node](/docs/global-elastic-node). > [!NOTE] > > Here you can find the [cryo repository](https://github.com/paradigmxyz/cryo). ## Understanding how `cryo` works Understanding the magic under the hood is always important to utilize a full tool's potential. This section will give you a glimpse into the inner workings of `cryo`, explaining its data extraction process, how it handles data schemas and formatting, and the range of blockchain networks it supports. ### Data extraction process `cryo`'s primary tool is the JSON-RPC protocol, a widely used standard that allows for communication with a blockchain node. When you run a `cryo` command, it sends out JSON-RPC requests to a blockchain node. These requests ask for specific pieces of data like blocks, transactions, or logs. The node responds with raw data, which `cryo` then meticulously processes. It's not just about fetching data; `cryo` transforms this data into structured and readable formats like CSV, JSON, or Parquet files. This transformation makes it incredibly straightforward to use this data in various applications or analyses. ### Efficiency oowered by Rust The speed and efficiency of `cryo` are standout features, largely attributed to its development in Rust. Renowned for its performance and memory safety, Rust enables `cryo` to handle blockchain data with exceptional speed and efficiency. This results in rapid data processing, even when dealing with the large datasets typical in blockchain networks. Rust's prowess in concurrency further amplifies `cryo's` ability to manage multiple data extraction tasks simultaneously, ensuring swift and smooth operation. In short, `cryo` leverages Rust's strengths to offer a fast, reliable, and efficient data extraction experience. ### Supported chains The blockchain world is vast and diverse, and `cryo` is built to navigate this diversity. It's compatible with various blockchain networks, making it a versatile tool for users interested in different ecosystems. Primarily, `cryo` is compatible with Ethereum and supports EVM-based networks. This wide range of compatibility is possible because `cryo` utilizes [ethers.rs](http://ethers.rs/) for JSON-RPC requests, allowing it to interact with any chain compatible with ethers-rs. This versatility makes `cryo` a valuable asset, whether you're getting into the bustling world of Ethereum or exploring the unique landscapes of its various Layer 2 solutions and sidechains. ## Installation and setup Getting `cryo` up and running involves a few straightforward steps. This section will guide you through the prerequisites, the installation process, and setting up essential environment variables. We'll also set up a Chainstack global endpoint. ### Prerequisites Before installing `cryo`ensure that your system meets the following requirements: 1. **Rust**: `cryo` is built in Rust, so you must install Rust on your machine. If you haven't installed Rust, you can do so via [rustup](https://www.rust-lang.org/tools/install), the recommended way to install the Rust programming language. ### Installation steps `cryo` can be installed either directly from the source or via `crates.io`. Here's how you can do it: **Method 1: Install from source** 1. Clone the `cryo` repository: ``` git clone https://github.com/paradigmxyz/cryo.git ``` 2. Navigate to the `cryo` directory: ``` cd cryo ``` 3. Install `cryo` using Cargo: ``` cargo install --path ./crates/cli ``` **Method 2: Install from [crates.io](http://crates.io/)** 1. Run the install command: ``` cargo install cryo_cli ``` > [!NOTE] > > Installing from source has been the most reliable method so far. ## Basic usage of `cryo` Diving into `cryo` begins with understanding its basic and help commands and the variety of data types it can extract. This section will cover the foundational aspects of using `cryo`, including the essential commands and options that make it a versatile tool for blockchain data extraction. ### Basic commands `cryo` offers several commands to help you navigate its functionalities: 1. **`cryo help`**: This is your go-to command for any assistance. It provides an overview of all available commands and options in `cryo`. Whenever in doubt, just type `cryo help` in your terminal. 2. **`cryo help syntax`**: Blockchain data queries can sometimes get complex. The `cryo help syntax` command is designed to help you understand how to effectively specify block ranges, transaction hashes, and other query parameters. 3. **`cryo help datasets`**: This command displays all the available datasets that `cryo` can extract. Datasets include `blocks`, `transactions`, `logs`, and many others, each serving a specific type of data extraction. 4. **`cryo help DATASET(S)`**: Use this command for detailed information about a specific dataset. It helps you understand the nuances of each dataset, what data it includes, and how it can be used. For instance, if you want to know more about the `logs` dataset, you should use `cryo help logs`. ### Data types and options `cryo` can extract various types of blockchain data, each with its own set of applicable options: * **Logs**: Extracts event logs from the blockchain. Useful for tracking events emitted by smart contracts. * **Blocks**: Retrieves block data. This is essential for analyses that require details like block data, block time, and transactions within a block. * **Transactions**: Fetches transaction data, crucial for examining transaction flows, gas prices, and contract interactions. To further refine your data extraction, `cryo` provides a range of options: * **`-include-columns`**: Specify which columns to include in your output. For instance, if you're only interested in certain aspects of a transaction, like gas prices and transaction hashes, this option allows you to focus on just those columns. * **`-exclude-columns`**: Conversely, if there are columns you want to omit from your output, this option lets you exclude them, streamlining your dataset. * **`-blocks`**: A crucial option for specifying the range of blocks you are interested in. You can define a single block, a range, or multiple ranges. * **`-contract`**: This option lets you specify a particular contract address when dealing with log-related data. `cryo` also includes various other options for output format (`--csv`, `--json`), sorting (`--sort`), and filtering based on transaction parameters. Combining these data types and options gives you a powerful toolkit to customize your data extraction precisely to your needs. ## Using `cryo` with a custom RPC To use `cryo` you'll need a custom RPC (Remote Procedure Call) endpoint. This section will explain what a custom RPC endpoint is and how to use it with `cryo`. ### Chainstack RPC nodes An RPC endpoint in the context of blockchain is a server interface that allows you to interact with the blockchain network. It's like a gateway through which you send requests (like fetching data) and receive responses. So, how do you choose an RPC? Cryo is a high-performing tool that can send many requests per second, and the Chainstack Global Node is ideal for this tool. > [!NOTE] > > ### Chainstack Global Nodes > > Chainstack Global Nodes are geo-load-balanced nodes enabling intelligent routing of requests to the nearest server, reducing latency and delivering maximum performance. > > By proactively monitoring node status, Global Nodes adapt to network conditions in real time providing instant failover to another node during network interruptions on a global scale. ### Get a Global Node Follow these steps to deploy an Ethereum node: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). To follow this guide, deploy a global Ethereum node. Once you deploy the node, you'll have access to an RPC endpoint, which will look like this: ``` https://ethereum-mainnet.core.chainstack.com/YOUR_CREDENTIALS ``` Now, we are ready to start fetching some blockchain data. ### Use a custom RPC with `cryo` To use a custom RPC endpoint with `cryo`, you can use the `--rpc` flag followed by the URL of your RPC endpoint or add it as an environment variable. Here's how to do it: * In your `cryo` command, add the `-rpc` flag followed by your custom RPC URL. For example: ``` cryo --rpc ``` * If you want to use it as an environment variable, export it as a variable named `ETH_RPC_URL` by running this in the console: ``` export ETH_RPC_URL=https://ethereum-mainnet.core.chainstack.com/YOUR_CREDENTIALS ``` > [!NOTE] > > If you add the endpoints as an environment variable, you do not need to add the `--rpc` flag when running a command. ## Fetch some data At this point, you are ready to get your hands dirty with blockchain data; let's explore a few kinds of datasets you can get from `cryo` and you can fine-tune the requests. ### Extract basic block information Block information is a fundamental kind of data needed to analyze the blockchain's state at specific times, which is essential for historical analysis, auditing, and verifying transaction integrity. It is also somewhat resource-intensive, especially with a wide block range. The basic block data command syntax is: ``` cryo blocks --blocks START_BLOCK:END_BLOCK ``` Let's explore some `cryo` commands to work with blocks. ### Dry command The `--dry` flag in `cryo` is useful for previewing the structure and content of the data you plan to extract without actually executing the data extraction. Using the `--dry` command with cryo provides a snapshot of the parameters, source details, output configuration, and the data schema for the requested dataset. This feature is highly beneficial for confirming the data fields and format before running a full data extraction process. It helps in understanding the range of data (like block numbers and types of data points), source information (like network and RPC URL), and how the data will be output (such as the file format and chunk size). This preemptive insight allows users to adjust their query parameters and output settings as needed, ensuring they get the data they need in the desired format. This is especially valuable for large-scale data operations where efficiency and precision are critical. Run it with: ``` cryo blocks --blocks 18734075:18735075 --dry ``` The output will be similar to the following: ``` cryo parameters ─────────────── - version: 0.2.0-183-gebfc97b - data: - datatypes: blocks - blocks: n=1,000 min=18,734,075 max=18,735,074 align=no reorg_buffer=0 - source: - network: ethereum - rpc url: - max requests per second: unlimited - max concurrent requests: unlimited - max concurrent chunks: 4 - output: - chunk size: 1,000 - chunks to collect: 1 / 1 - output format: parquet - output dir: /PATH - report file: $OUTPUT_DIR/.cryo/reports/2023-12-07_12-12-44.189964.json schema for blocks ───────────────── - block_number: uint32 - block_hash: binary - timestamp: uint32 - author: binary - gas_used: uint64 - extra_data: binary - base_fee_per_gas: uint64 - chain_id: uint64 sorting blocks by: block_number other available columns: parent_hash, state_root, transactions_root, receipts_root, logs_bloom, total_difficulty, size [dry run, exiting] ``` Let's briefly analyze the response and see what customizations we can make. 1. **Version and Data Types**: It lists the version of cryo being used and the data types selected for extraction (e.g., blocks). This is useful for ensuring you're working with the correct version and data set. 1. **Data Details**: It specifies the range of data to be extracted (e.g., block numbers and their range), which helps verify that you're targeting the correct segment of the blockchain. 2. **Source Information**: Shows the network (e.g., Ethereum), RPC URL, and rate limits. This confirms the blockchain source and helps manage resource usage. 1. This can be particularly important when dealing with rate limits and ensuring network compatibility. For example, free Chainstack endpoints are rate-limited at 30 requests per second. 3. **Output Configuration**: Details about how the data will be output, including format (e.g., Parquet), directory path, chunk size, and the number of chunks. This is crucial for understanding how the data will be organized and stored, allowing for proper data storage and management planning. 4. **Data Schema**: Lists the columns included in the output (e.g., block\_number, block\_hash, timestamp). This is essential for understanding the structure of the extracted data, enabling users to anticipate the kind of information they will receive and how it can be utilized in their analysis or application. 5. **Sorting and Additional Columns**: Information on how the data will be sorted and other available columns that were not included in the extraction but are available for use. This can be useful for refining future data extraction queries. ### Customize a fetch command From the dry run above, we can see a few details: * The command will fetch 1000 blocks, which is the range we want. * There is no rate limit, and concurrent requests are unlimited. * The output will be a [Parquet file](https://www.databricks.com/glossary/what-is-parquet) with 1000 entries. * `schema for blocks` displays what kind of data will be extracted. We can use a few extra flags to customize this: ``` cryo blocks \ --blocks 18734075:18735075 \ --json \ --requests-per-second 30 \ --columns block_number block_hash timestamp chain_id \ --dry ``` This command will limit the RPS to 30, return a JSON file, and only keep the block number, block hash, timestamp, and chain ID. Running it will return the following: ``` cryo parameters ─────────────── - version: 0.2.0-183-gebfc97b - data: - datatypes: blocks - blocks: n=1,000 min=18,734,075 max=18,735,074 align=no reorg_buffer=0 - source: - network: ethereum - rpc url: - max requests per second: 30 - max concurrent requests: 30 - max concurrent chunks: 4 - output: - chunk size: 1,000 - chunks to collect: 1 / 1 - output format: json - output dir: /PATH - report file: $OUTPUT_DIR/.cryo/reports/2023-12-07_13-01-04.300002.json schema for blocks ───────────────── - block_number: uint32 - block_hash: hex - timestamp: uint32 - chain_id: uint64 sorting blocks by: block_number other available columns: parent_hash, author, state_root, transactions_root, receipts_root, gas_used, extra_data, logs_bloom, total_difficulty, size, base_fee_per_gas [dry run, exiting] ``` Once you verify that's the data you need, run the same command without the `--dry` flag to actually send the requests, you'll get an output similar to this: ``` collecting data ─────────────── started at 2023-12-07 13:03:52.669 done at 2023-12-07 13:04:26.789 collection summary ────────────────── - total duration: 34.120 seconds - total chunks: 1 - chunks errored: 0 / 1 (0.0%) - chunks skipped: 0 / 1 (0.0%) - chunks collected: 1 / 1 (100.0%) - blocks collected: 1,000 - blocks per second: 29.3 - blocks per minute: 1,758.5 - blocks per hour: 105,509.5 - blocks per day: 2,532,227.9 ``` As you can see, it gives us the details of the operation, and you'll find a JSON file in your output path with the following structure: ```Json JSON theme={"system"} [ { "block_hash": "0x0294b4c63bcc7d721ecafe323a38e391efc13698117851f2eef969bbc2267874", "block_number": 18734075, "timestamp": 1701948011, "chain_id": 1 }, { "block_hash": "0xcf9d0f402eaaa4e6082145e3ee932a1749f6d6d75a3885e7c59098d259b1b544", "block_number": 18734076, "timestamp": 1701948023, "chain_id": 1 }, { "block_hash": "0xae2b4c8e6c5e0e8c0e962d935f77d28350fed20d8fb63ec811d6d70aab6e2224", "block_number": 18734077, "timestamp": 1701948035, "chain_id": 1 }, ] ``` Because of the rate limit, it took 34 seconds to complete the request; the good news is that Chainstack does not have any rate limit starting from a [paid plan](https://chainstack.com/pricing/), which is recommended if you need really high-performance nodes. The following is the result of the same request using a premium Chainstack [Global Node](/docs/global-elastic-node) with unlimited RPS: ``` - source: - network: ethereum - rpc url: https://ethereum-mainnet.core.chainstack.com/ - max requests per second: unlimited - max concurrent requests: unlimited - max concurrent chunks: 4 collecting data ─────────────── started at 2023-12-07 13:09:24.810 done at 2023-12-07 13:09:28.437 collection summary ────────────────── - total duration: 3.627 seconds - total chunks: 1 - chunks errored: 0 / 1 (0.0%) - chunks skipped: 0 / 1 (0.0%) - chunks collected: 1 / 1 (100.0%) - blocks collected: 1,000 - blocks per second: 275.7 - blocks per minute: 16,540.6 - blocks per hour: 992,437.6 - blocks per day: 23,818,503.0 ``` As you can see, this is an enormous improvement, almost 90% faster, and this is the power of this Rust-based tool. This is the Gist of this powerful tool, and you can follow the same principle for the other datasets, but we'll explore a couple more. ### Extracting event logs with `cryo` Event logs on the blockchain offer invaluable insights into contract interactions and transactions and are one of the most sought-after data types. `cryo` simplifies the extraction of these logs, offering two primary approaches: 1. Utilizing pre-configured event scrapers for standard events. 2. Fetching custom event logs tailored to specific requirements. ### Using pre-configured event scrapers `cryo` comes with built-in capabilities to extract standard event logs like ERC-20 and ERC-721 `Transfer` events. This functionality is accessible without needing intricate parameters, significantly streamlining the process. For example, extracting ERC-20 `Transfer` events can be achieved with a straightforward command: ``` cryo erc20_transfers --blocks latest --json ``` > [!NOTE] > > This command will retrieve the`Transfer` events from the latest block in JSON format. You can specify additional parameters for more targeted data retrieval, such as a specific token address or a range of blocks. For instance, to extract `Transfer` events of the APE token over a range of 500 blocks, the command would be: ``` cryo erc20_transfers \ --address 0x4d224452801ACEd8B2F0aebE155379bb5D594381 \ --blocks 18735627:18736127 \ --json ``` The output is structured as a JSON file like the following: ```Json JSON theme={"system"} [ { "block_number": 18735629, "transaction_index": 127, "log_index": 271, "transaction_hash": "0x652dd336bdcac90f521ebce7f788ac4179db5d736d246d9b4fa6c29ecd911731", "erc20": "0x4d224452801aced8b2f0aebe155379bb5d594381", "from_address": "0x5f65f7b609678448494de4c87521cdf6cef1e932", "to_address": "0xc469b4efd8566f8774437795ece23851a325f661", "value_binary": "0x000000000000000000000000000000000000000000000012574733bb4f6eb2dd", "value_string": "338330445611002671837", "value_f64": 3.383304456110027e20, "chain_id": 1 }, { "block_number": 18735631, "transaction_index": 157, "log_index": 258, "transaction_hash": "0xa554db46136e23b11cd859ac4d6879e800f57979e847a3d9753e41b8400c5954", "erc20": "0x4d224452801aced8b2f0aebe155379bb5d594381", "from_address": "0x21a31ee1afc51d94c2efccaa2092ad1028285549", "to_address": "0x648e9390d7dbf9ee00e606b0a62e77c58f767a40", "value_binary": "0x0000000000000000000000000000000000000000000003c1fbde01786f130000", "value_string": "17745470000000000000000", "value_f64": 1.774547e22, "chain_id": 1 }, ] ``` You can do the same for ERC-721 `Transfers`. ### Extracting custom event logs `cryo` also allows users to extract custom event logs. This feature is useful for analyzing non-standard events or those specific to a particular smart contract. For example, to fetch custom events, you can use the `logs` dataset command with specific topics or event signatures. To extract the same `Transfer` events as above using a custom approach, the command would be: ``` cryo logs \ --blocks 18735627:18736127 \ --contract 0x4d224452801ACEd8B2F0aebE155379bb5D594381 \ --topic0 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef \ --json ``` Alternatively, you can directly use the event signature: ``` cryo logs \ --blocks 18735627:18736127 \ --contract 0x4d224452801ACEd8B2F0aebE155379bb5D594381 \ --event-signature "Transfer(address indexed from, address indexed to, uint256 value)" \ --json ``` Consider extracting data about new token pairs created on [SushiSwap V2](https://etherscan.io/address/0xc0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac#code) for a more complex example. The following command accomplishes this for a specific block: ``` cryo logs \ --blocks 18687074 \ --contract 0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac \ --event-signature "PairCreated(address indexed token0, address indexed token1, address pair, uint)" \ --json ``` > [!NOTE] > > The extracted transactions are sorted by block number and log index by default. For more examples and custom event extraction scenarios, refer to the [`cryo` documentation](https://github.com/paradigmxyz/cryo/blob/main/examples/uniswap.sh). ### Beyond data collection The versatility of `cryo` extends beyond event logs, encompassing various data types inherent to blockchain technology. Each dataset can be customized and extracted based on user-specific requirements, enabling various analysis and research possibilities. Post extraction, the data can be seamlessly integrated into different frameworks or tools for further processing and analysis, offering a comprehensive solution for blockchain data retrieval. ## Conclusion In this guide, we've journeyed through the remarkable capabilities of `cryo`, a tool that stands out in blockchain data extraction. Cryo offers a seamless and efficient way to access blockchain data, from its intuitive command-line interface to its powerful Rust-based engine. Whether you're getting into complex application development, embarking on in-depth research, or simply exploring the blockchain landscape, `cryo` proves to be an indispensable ally. Its ability to interact with various blockchain networks and its flexibility in data formatting and extraction ensures that your blockchain data needs are met with precision and ease. Integrating `cryo` with high-performance RPC endpoints like Chainstack Global Nodes further elevates its efficiency, providing lightning-fast data retrieval and enhanced reliability. This synergy enables you to harness the full potential of blockchain data, unlocking insights and opportunities that were previously challenging to access. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Debug & Trace APIs Source: https://docs.chainstack.com/docs/debug-and-trace-apis Use debug and trace APIs on Chainstack to monitor, inspect, and replay EVM transactions for smart contract debugging and performance analysis. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). ## Overview They provide detailed information about transactions, blocks, and smart contracts execution making it easier to identify and resolve issues within decentralized applications. By using debug and trace APIs, developers can build more reliable and secure DApps with improved user experience and thus contribute to the overall health of a network. > [!NOTE] > > ### Historical states limitations > > To debug and trace transactions, you need to have historical states on the node. Full nodes of different protocols can [store different amount of historical states](/docs/protocols-modes-and-types#modes). At the same time, archive nodes keep historical states for the *entire chain*. > [!NOTE] > > The debug and trace APIs do not include the mempool. The `txpool_*` methods are a separate namespace with their own requirements — see [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ## Protocols with debug and trace APIs ### Ethereum To enable debug and trace APIs on your Ethereum node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). #### Dedicated Nodes To enable debug and trace APIs on your dedicated Ethereum node, you must have a [paid plan](https://chainstack.com/pricing/). Dedicated Nodes can be deployed either as the Geth or Erigon client implementation for the full and archive modes respectively, however, they slightly differ from each other: * With a node running on Geth, only `debug_*`namespace is exposed. * With a node running on Erigon, both `debug_*` and `trace_*`namespaces are exposed. Learn how to [deploy your dedicated Ethereum node run on different clients](/reference/enable-debug-trace-apis-for-your-ethereum-node) with debug and trace APIs enabled. For the full list of the available debug and trace API methods, see: > [card] [Geth: debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug) > [card] [Erigon: RPC implementation status](https://github.com/ledgerwatch/erigon/blob/stable/cmd/rpcdaemon/README.md#rpc-implementation-status) ### Polygon To enable debug and trace APIs on your Polygon node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). #### Dedicated Nodes To enable debug and trace APIs on your dedicated Polygon node, you must have a [paid plan](https://chainstack.com/pricing/). Dedicated Polygon nodes run the Bor client, which exposes the `debug_*` namespace. Bor does not implement the Erigon-only Parity `trace_*` namespace — see [Polygon methods](/docs/polygon-methods) for the `trace_*` → `debug_*` migration. ### BNB Smart Chain To enable debug and trace APIs on your BNB Smart Chain node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). #### Dedicated Nodes To enable debug and trace APIs on your dedicated BNB Smart Chain node, you must have a [paid plan](https://chainstack.com/pricing/). Dedicated full nodes run BSC Geth and archive nodes run Reth, and the two expose different namespaces: * With a full node running BSC Geth, only the `debug_*` namespace is exposed. * With an archive node running Reth, both the `debug_*` and Parity `trace_*` namespaces are exposed. For the full list of the available debug and trace API methods, see: > [card] [Geth: debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug) > [card] [Reth: trace namespace](https://reth.rs/jsonrpc/trace) ### Base To enable debug and trace APIs on your Base node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). ### Avalanche To enable debug and trace APIs on your Avalanche node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node in the [archive mode](/docs/protocols-modes-and-types#modes). Your node will run the AvalancheGo client, which is the Go language implementation of an Avalanche node. Avalanche offers an API interface identical to [Geth's API](https://geth.ethereum.org/docs/rpc/server), but with a limited set of services that include `debug_trace*`. For a full list of the Geth debug API methods, see the [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug) section of the Geth documentation. You can deploy a dedicated Avalanche node starting from a [paid plan](https://chainstack.com/pricing/). ### Arbitrum To enable debug and trace APIs on your Arbitrum node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). #### Dedicated Nodes To enable debug and trace APIs on your dedicated Arbitrum node, you must have a [paid plan](https://chainstack.com/pricing/). #### Available methods Both Global Nodes and dedicated Arbitrum nodes with debug and trace APIs enabled will expose the `debug_*` and `arbtrace_*` methods. Arbitrum uses the Nitro client (a Geth fork) and does not support the `trace_*` namespace. Instead, use the `arbtrace_*` methods for pre-Nitro blocks and `debug_*` methods for post-Nitro blocks. Blocks older than 22,207,815 were added to the chain before the [Nitro migration](https://developer.arbitrum.io/migration/dapp_migration) and cannot be queried with Geth `debug_*` methods. Starting from block 22,207,815, Arbitrum migrated to Nitro which made Geth `debug_*` methods available for newer blocks. Use the following `arbtrace_*` methods for calling on blocks prior to 22,207,815: * arbtrace\_block * arbtrace\_call * arbtrace\_callMany * arbtrace\_filter * arbtrace\_get * arbtrace\_replayBlockTransactions * arbtrace\_replayTransaction * arbtrace\_transaction The following `debug_*` methods are available for blocks from 22,207,815 onward: * debug\_traceBlockByHash * debug\_traceBlockByNumber * debug\_traceTransaction * debug\_traceCall For the full list of the available `debug_*` namespace methods, see [Geth documentation](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### zkSync Era To enable debug and trace APIs on your zkSync Era node, you must deploy an node in the archive mode. The node will expose the `debug_*` API methods. A debug and trace node for zkSync Era is available starting from a [paid plan](https://chainstack.com/pricing/). ### Optimism To enable debug and trace APIs on your Optimism node, deploy a [Global Node](/docs/global-elastic-node) with the debug & trace APIs enabled. For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Scroll To enable debug and trace APIs on your Scroll node, deploy a [Global Node](/docs/global-elastic-node) with the debug & trace APIs enabled. ### Ronin To enable debug and trace APIs on your Ronin node, deploy a [Global Node](/docs/global-elastic-node) with the debug & trace APIs enabled. ### Gnosis Chain To enable debug and trace APIs on your [Gnosis Chain](/docs/gnosis-chain) node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). The node will expose the `debug_*` and `trace_*` API methods on both Gnosis Chain Mainnet and Chiado Testnet. A Gnosis Chain Mainnet Trader Node exposes the `debug_*` and `trace_*` methods in the archive mode, which runs Erigon. The full-mode Trader Node runs Nethermind without the debug and trace APIs. #### Dedicated Nodes To enable debug and trace APIs on your dedicated Gnosis Chain node, you must have a [paid plan](https://chainstack.com/pricing/). Dedicated Gnosis Chain Mainnet nodes run Nethermind or Reth in both the full and archive modes, and Dedicated Chiado Testnet nodes run Nethermind in the full mode. Both clients expose the `debug_*` and `trace_*` namespaces. For the full list of the available methods, see the [Nethermind JSON-RPC documentation](https://docs.nethermind.io/nethermind/ethereum-client/json-rpc), the [Erigon documentation](https://docs.erigon.tech/), and the Reth documentation for the [debug namespace](https://reth.rs/jsonrpc/debug) and the [trace namespace](https://reth.rs/jsonrpc/trace). ### Fantom To enable debug and trace APIs on your Fantom node, you must deploy a Dedicated Node in the full or archive mode. The node will expose the `debug_*` API methods. A dedicated debug and trace node for Fantom is available starting from a [paid plan](https://chainstack.com/pricing/). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Hyperliquid To enable debug and trace APIs on your Hyperliquid HyperEVM node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). Debug and trace APIs are available on both mainnet and testnet. The HyperEVM supports standard Ethereum debug and trace APIs, allowing you to debug smart contracts and trace transactions on Hyperliquid's EVM-compatible layer. For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Sonic To enable debug and trace APIs on your Sonic node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Unichain To enable debug and trace APIs on your Unichain node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Berachain To enable debug and trace APIs on your Berachain node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Robinhood Chain To enable debug and trace APIs on your Robinhood Chain node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Elysium To use debug and trace APIs on your Elysium node, you must have a [paid plan](https://chainstack.com/pricing/). The node will expose the `debug_*` API methods. Elysium runs the Nitro client and does not serve the `trace_*` or `arbtrace_*` namespaces — both return `-32601`. Use `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceCall` instead. The default native tracers and the extended tracers `flatCallTracer`, `muxTracer`, and `erc7562Tracer` are available. For Parity-style flat traces, use `flatCallTracer`. See [Available native tracers](/docs/limits#available-native-tracers) and [Elysium methods](/docs/elysium-methods) for the full per-method availability. ### Arc To enable debug and trace APIs on your Arc node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). Arc runs the Reth client, which exposes both the `debug_*` and `trace_*` namespaces. For the full list of the available methods, see the Reth documentation for the [debug namespace](https://reth.rs/jsonrpc/debug) and the [trace namespace](https://reth.rs/jsonrpc/trace). ### Linea To enable debug and trace APIs on your Linea node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Mantle To enable debug and trace APIs on your Mantle node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Zora To enable debug and trace APIs on your Zora node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Celo To enable debug and trace APIs on your Celo node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### Kaia To enable debug and trace APIs on your Kaia node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### opBNB To enable debug and trace APIs on your opBNB node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). For the full list of the available debug and trace API methods, see [Debug namespace](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug). ### World Chain To enable debug and trace APIs on your World Chain node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). World Chain runs `world-chain`, an [OP Stack](https://docs.optimism.io/) execution client built on [Reth](https://github.com/paradigmxyz/reth), so it exposes both the `debug_*` and the `trace_*` namespaces. For the full list of the available methods, see the Reth documentation for the [debug namespace](https://reth.rs/jsonrpc/debug) and the [trace namespace](https://reth.rs/jsonrpc/trace), and [World Chain methods](/docs/worldchain-methods) for per-method availability on Chainstack. > [!NOTE] > > `rpc_modules` is not available on World Chain — it returns `-32601 Method not found`. Do not use it to decide which namespaces you have. ### Ink To use debug and trace APIs on your Ink node, you must have a [paid plan](https://chainstack.com/pricing/). Ink runs op-reth, the [OP Stack](https://docs.optimism.io/) execution client built on [Reth](https://github.com/paradigmxyz/reth), so it exposes both the `debug_*` and the `trace_*` namespaces. The default native tracers are available; `flatCallTracer`, `muxTracer`, and `erc7562Tracer` are not — see [Available native tracers](/docs/limits#available-native-tracers). For the full list of the available methods, see the Reth documentation for the [debug namespace](https://reth.rs/jsonrpc/debug) and the [trace namespace](https://reth.rs/jsonrpc/trace). ### Stable To enable debug and trace APIs on your Stable node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). The node will expose the `debug_*` API methods. > [!WARNING] > > Stable does not serve the `trace_*` namespace. `trace_block`, `trace_transaction`, `trace_call`, `trace_callMany`, `trace_filter`, `trace_get`, `trace_rawTransaction`, `trace_replayBlockTransactions`, and `trace_replayTransaction` all return `-32601`. Use the `debug_*` equivalents instead — `debug_traceBlockByNumber` in place of `trace_block`, and `debug_traceTransaction` in place of `trace_transaction`, both with the `callTracer`. See [Stable methods](/docs/stable-methods) for the full per-method availability. The available tracing methods are `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceCall`. The Geth-era inspection methods — `debug_storageRangeAt`, `debug_accountRange`, `debug_dumpBlock`, and the rest — are not implemented; use `eth_getStorageAt` in place of `debug_storageRangeAt`. ### Plasma To enable debug and trace APIs on your Plasma node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). Plasma runs the [Reth](https://github.com/paradigmxyz/reth) client, which exposes both the `debug_*` and the `trace_*` namespaces. For the full list of the available methods, see the Reth documentation for the [debug namespace](https://reth.rs/jsonrpc/debug) and the [trace namespace](https://reth.rs/jsonrpc/trace). ### Tempo To enable debug and trace APIs on your Tempo node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). Tempo runs a Reth-based client and exposes both the `debug_*` and the `trace_*` namespaces. For the full list of the available methods, see the Reth documentation for the [debug namespace](https://reth.rs/jsonrpc/debug) and the [trace namespace](https://reth.rs/jsonrpc/trace). ### MegaETH To enable debug and trace APIs on your MegaETH node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). MegaETH runs `mega-reth`, a [Reth](https://github.com/paradigmxyz/reth) derivative, and exposes both the `debug_*` and the `trace_*` namespaces. For the full list of the available methods, see the Reth documentation for the [debug namespace](https://reth.rs/jsonrpc/debug) and the [trace namespace](https://reth.rs/jsonrpc/trace). ### Monad To enable debug and trace APIs on your Monad node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). The node will expose the `debug_*` API methods. Monad does not serve the `trace_*` namespace — `trace_block`, `trace_transaction`, `trace_call`, and `trace_filter` all return `-32601`. Use `debug_traceBlockByNumber` and `debug_traceTransaction` instead. > [!NOTE] > > Monad accepts a narrower set of tracers than the other EVM chains: `callTracer` and `prestateTracer` work, while `4byteTracer` and `noopTracer` are rejected with `-32602`. See [Available native tracers](/docs/limits#available-native-tracers). ### Cronos To enable debug and trace APIs on your Cronos node, you must have a [paid plan](https://chainstack.com/pricing/) and deploy the node as a [Global Node](/docs/global-elastic-node). The node will expose the `debug_*` API methods. Cronos does not serve the `trace_*` namespace. The available tracing methods are `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceCall`; the Geth-era inspection methods such as `debug_storageRangeAt` are not implemented. Note that Cronos Mainnet archive nodes rate-limit `debug_traceBlockByNumber` to 10 RPS on all plans — see [Cronos-specific limits](/docs/limits#cronos-specific). ### X Layer To use debug and trace APIs on your X Layer node, you must have a [paid plan](https://chainstack.com/pricing/). X Layer runs a Reth-based [OP Stack](https://docs.optimism.io/) execution client, so it exposes both the `debug_*` and the `trace_*` namespaces. `callTracer` and `prestateTracer` are available; `flatCallTracer`, `muxTracer`, and `erc7562Tracer` are not — see [Available native tracers](/docs/limits#available-native-tracers). For the full list of the available methods, see the Reth documentation for the [debug namespace](https://reth.rs/jsonrpc/debug) and the [trace namespace](https://reth.rs/jsonrpc/trace). ## Usage examples You can debug and trace transactions by replaying them in the Ethereum Virtual Machine to get the execution details in the exact same way as they happened on the chain. ### debug\_traceBlockByNumber Trace all transactions included in a block with `debug_traceBlockByNumber` in [block 14,976,695](https://etherscan.io/txsInternal?block=14976695): ```bash cURL theme={"system"} curl 'YOUR_CHAINSTACK_ENDPOINT' \ -H 'Content-Type: application/json' \ -d '{"id": 1, "method": "debug_traceBlockByNumber", "params": ["0xE486B7", {"tracer": "callTracer"}]}' ``` where * `0xE486B7` — the number of the block to get the traces of included transactions. For Geth, you need to provide the block number in hex. * YOUR\_CHAINSTACK\_ENDPOINT — your Chainstack node HTTPS endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). See a reverted transaction in the output: ```Json JSON theme={"system"} "calls": [ { "type": "DELEGATECALL", "from": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", "gas": "0x38ed0", "gasUsed": "0x31147", "input": "0x472b43f30000000000000000000000000000000000000000000000000429d069189e00000000000000000000000000000000000000000000000000160d962fcdfd0bb02400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000b5ec97d9a8a9941a28a88084a1f670c62bd8bf40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000b00b2e950d7ef8bdc49377c49676d1550deab982", "error": "execution reverted" ``` ### trace\_block Trace all transactions included in a block with `trace_block` in [block 14,976,695](https://etherscan.io/txsInternal?block=14976695): ```bash cURL theme={"system"} curl 'YOUR_CHAINSTACK_ENDPOINT' \ -H 'Content-Type: application/json' \ -d '{"id": 1, "method": "trace_block", "params": ["14976695"]}' ``` where * `14976695` — the number of the block to get the traces of included transactions * YOUR\_CHAINSTACK\_ENDPOINT — your Chainstack node HTTPS endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). See a reverted transaction in the output: ```Json JSON theme={"system"} "action": { "from": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", "callType": "delegatecall", "gas": "0x38ed0", "input": "0x472b43f30000000000000000000000000000000000000000000000000429d069189e00000000000000000000000000000000000000000000000000160d962fcdfd0bb02400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000b5ec97d9a8a9941a28a88084a1f670c62bd8bf40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000b00b2e950d7ef8bdc49377c49676d1550deab982", "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", "value": "0x429d069189e0000" }, "blockHash": "0x03a83bf066e81498804f26caf5d49e47820f6a0e92fd1c9cb7dc3b87bf46cf0f", "blockNumber": 14976695, "error": "Reverted" ``` > [!NOTE] > > ### Advanced options on paid plans > > Dedicated Nodes, archive nodes, debug & trace APIs are available starting from a [paid plan](https://chainstack.com/pricing/). # Dedicated Node Source: https://docs.chainstack.com/docs/dedicated-node Learn about Chainstack Dedicated Nodes with exclusive compute resources, high customization, debug and trace APIs, and ADNL protocol support for TON blockchain. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). ## What is a Dedicated Node? A Dedicated Node is a node that is exclusively deployed for and belongs to a particular customer, and its compute resources are not shared with anybody else. Such nodes are highly customizable and can offer more additional services than other node types. This means that each Dedicated Node can be configured to perfectly match the case and needs of a particular customer. For the full list of chains available as Dedicated Nodes, see [Networks](/docs/protocols-networks). > [!NOTE] > > ### Dedicated Node plans > > **Subscription level** — a Pro, Business, or Enterprise subscription is required. > > See [Pricing](https://chainstack.com/pricing/). > [!TIP] > > ### Debug and trace API > > Regardless of mode, all Dedicated Nodes will have debug and trace API enabled for protocols which support this feature. See the [full list of protocols with debug and trace API](/docs/debug-and-trace-apis). > [!NOTE] > > ### Pool inspection is separate from debug and trace > > A Dedicated Node can serve the `txpool_*` namespace in the full mode and the archive mode — ask for it when you deploy. Enabling debug and trace does not enable it, and Global Nodes and Trader Nodes do not serve `txpool_content`, `txpool_contentFrom`, or `txpool_inspect`. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). > [!NOTE] > > ### TON Dedicated Nodes: ADNL protocol support > > TON Dedicated Nodes support the [ADNL protocol](https://docs.ton.org/v3/documentation/network/protocols/adnl/overview) in addition to the standard HTTP API. ADNL (Abstract Datagram Network Layer) provides secure, low-level network communication for advanced TON blockchain interactions and specialized use cases. ## Billing model across node types Dedicated Nodes are billed differently from the per-request node types — knowing which model fits your workload is the main reason to pick one over the other. | | [Global Node](/docs/global-elastic-node) | [Trader Node](/docs/trader-node) | Dedicated Node | [Unlimited Node](/docs/unlimited-node) add-on | | - | - | - | - | - | | Resources | Load-balanced, region-optimized, shared | Regional, optionally with dedicated gateway | Exclusive to you | Applies on top of any node | | You pay for | JSON-RPC requests ([RUs](/docs/request-units)) | JSON-RPC requests ([RUs](/docs/request-units)) | Compute and storage of the node, monthly | Flat monthly fee tied to an RPS tier (25–1000 RPS) | | Request count | Metered per request | Metered per request | Unlimited, not metered | Unlimited within the tier's RPS cap | | Best for | The default choice — globally distributed, scales to any traffic level | Latency-critical regional workloads, Warp transactions | Sustained heavy load, customization, specialized configs | Predictable monthly bill for steady high traffic | If you run sustained high-throughput workloads (heavy `eth_subscribe`, deep `eth_getLogs` sweeps, debug/trace pipelines), a Dedicated Node — or any node with the Unlimited add-on — typically lands cheaper than per-request billing at the same throughput. ## Deploy a Dedicated Node You can deploy a Dedicated Node directly from the Chainstack console. 1. Log in to the [Chainstack console](https://console.chainstack.com/). 2. Open the project where you want the node, or create a new one. 3. Click **Get Started** (new project) or **Join Network** (existing project). 4. Choose the protocol and network, then click **Next**. 5. Select **Dedicated** as the node type. 6. Pick the node mode: **Full** or **Archive**. 7. Choose the cloud provider. 8. Give the node a name and click **Next**. 9. Review the configuration and monthly cost. 10. Click **Deploy**. You'll receive an email notification when the node is provisioned and ready. For specialized configurations or anything not exposed in the UI, use the [contact us form](https://chainstack.com/contact/). # Dedicated Node monitoring Source: https://docs.chainstack.com/docs/dedicated-node-monitoring Monitor your Chainstack Dedicated Node. Check CPU, memory, storage, and chain head on Chainstack, or scrape a Prometheus-compatible metrics endpoint into your own Grafana dashboards, alerts, and SLOs. Chainstack Dedicated Nodes report node-level compute, storage, and chain-head metrics. You can read the current values on Chainstack, and you can scrape the same metrics from a Prometheus-compatible endpoint into your own observability stack to build dashboards, alerts, and service-level objectives (SLOs) alongside your application metrics. > [!NOTE] > > **Availability** > > Monitoring is available on **Dedicated Nodes** only. A Pro, Business, or Enterprise subscription is required—see [Chainstack pricing and subscription plans](https://chainstack.com/pricing/). ## Node performance on Chainstack For a quick read on how your node is doing, Chainstack shows the current metric values as a set of tiles on the node itself. On Chainstack, open your project, select your Dedicated Node, and find **Node performance** on the **Overview** tab. Each tile appears only when the metric it reads is available for your node: * **CPU** — usage as a percentage of the node's CPU limit, with the absolute usage and limit in cores below it * **RAM** — memory usage as a percentage of the node's memory limit, with the absolute usage and limit below it * **Storage** — provisioned disk size, the measure your Dedicated Node is billed on * **Block number** — latest block height on the node, with the age of that block below it The tiles refresh every 30 seconds, and a value can lag real time by up to a minute. **Block number** is served for supported protocols only—see [Available metrics](#available-metrics). > [!NOTE] > > The tiles show current values, not history. To keep a time series, alert on it, or combine it with your own application metrics, scrape the [metrics endpoint](#metrics-endpoint) into your own stack. ## Metrics endpoint Every Dedicated Node serves a Prometheus-compatible metrics endpoint you can scrape from Prometheus, Grafana Agent, VictoriaMetrics, or any Prometheus-compatible scraper. It returns node-level metrics in the standard Prometheus exposition format, and it is authenticated and isolated per node so you only ever see your own data. ### Endpoint URL The metrics endpoint lives on your node's own hostname, at the `/metrics` path, and uses the **same access token as your node's RPC endpoint**: ```text theme={"system"} https://nd-123-456-789.p2pify.com/YOUR_AUTH_TOKEN/metrics ``` There is no separate credential to manage. Anywhere you already use your Dedicated Node's HTTPS endpoint, append `/metrics` to reach the metrics route. ### Find your endpoint **1. Open your node** Log in to the [Chainstack console](https://console.chainstack.com/), open your project, and select your Dedicated Node. **2. Copy the metrics endpoint** On the node detail page, find the metrics endpoint URL in the node's access and credentials details. It combines your node's host with your access token. You can also retrieve the URL programmatically from the [Chainstack platform API](/reference/platform-api-getting-started) node retrieve response. ### Authentication and isolation Authentication uses the access token in the endpoint path—the same token as your RPC endpoint: * A missing or invalid token is rejected with `401`. * A token issued for one node cannot be used to scrape another node. * Each response is scoped on the server to your node only. Query-string and request-body parameters supplied by the client are stripped, so you cannot widen the scope to any other node, and no other organization can reach your data. Only `GET` and `HEAD` requests are accepted on the metrics endpoint. Other methods return `405`. ### Available metrics The endpoint exposes the following node-level series, all gauges. Each carries a `node_id` label identifying your node. **Compute and storage** | Metric | Description | | - | - | | `chainstack_node_cpu_usage_cores` | Current CPU usage, in cores. | | `chainstack_node_cpu_limits_cores` | CPU limit allocated to the node, in cores. | | `chainstack_node_cpu_utilization_ratio` | CPU usage as a fraction of the CPU limit, from 0 to 1. | | `chainstack_node_memory_usage_bytes` | Current memory (RAM) usage, in bytes. | | `chainstack_node_memory_limits_bytes` | Memory limit allocated to the node, in bytes. | | `chainstack_node_memory_utilization_ratio` | Memory usage as a fraction of the memory limit, from 0 to 1. | | `chainstack_node_storage_requests_bytes` | Provisioned disk size, in bytes—the measure your Dedicated Node is billed on. | **Chain head** | Metric | Description | | - | - | | `chainstack_node_block_number` | Latest block height observed on the node. | | `chainstack_node_block_timestamp` | Unix timestamp, in seconds, of the latest block. | | `chainstack_node_block_age_seconds` | Seconds between the evaluation time and the timestamp of the latest block the node has ingested. | > [!NOTE] > > Chain head metrics (`chainstack_node_block_*`) are available for supported protocols only. If your node's protocol is not yet covered, these series are absent from the response while the compute and storage series are still served. `chainstack_node_block_age_seconds` is a local measure—wall-clock time minus the timestamp of the newest block this node has ingested—not a comparison against the network's chain head. Read it against a per-protocol threshold rather than as an absolute: a fully synced node still reports a nonzero value close to the chain's normal block interval, which is a few seconds on Ethereum-family chains and several minutes on Bitcoin-family chains. The value also grows when the chain itself stops producing blocks, not only when a node lags. It is recomputed periodically on the server, so a scraped reading can be a few tens of seconds old. Example response: ```text theme={"system"} # HELP chainstack_node_cpu_usage_cores Current CPU usage in cores # TYPE chainstack_node_cpu_usage_cores gauge chainstack_node_cpu_usage_cores{node_id="nd-123-456-789"} 15.74 chainstack_node_cpu_limits_cores{node_id="nd-123-456-789"} 32 chainstack_node_cpu_utilization_ratio{node_id="nd-123-456-789"} 0.4919 chainstack_node_memory_usage_bytes{node_id="nd-123-456-789"} 916643250176 chainstack_node_memory_limits_bytes{node_id="nd-123-456-789"} 1717986918400 chainstack_node_memory_utilization_ratio{node_id="nd-123-456-789"} 0.5335 chainstack_node_storage_requests_bytes{node_id="nd-123-456-789"} 5867999068160 chainstack_node_block_number{node_id="nd-123-456-789"} 21453897 chainstack_node_block_timestamp{node_id="nd-123-456-789"} 1737724800 chainstack_node_block_age_seconds{node_id="nd-123-456-789"} 6 ``` ### Quick check Confirm the endpoint responds with a single request: ```bash theme={"system"} curl 'https://nd-123-456-789.p2pify.com/YOUR_AUTH_TOKEN/metrics' ``` ### Scrape with Prometheus Add your node as a scrape target. The access token goes in `metrics_path`, and the node host goes in `targets`: ```yaml theme={"system"} scrape_configs: - job_name: chainstack-dedicated-node scheme: https metrics_path: /YOUR_AUTH_TOKEN/metrics scrape_interval: 30s static_configs: - targets: - nd-123-456-789.p2pify.com ``` > [!TIP] > > A scrape interval of 15–60 seconds is recommended. This gives you responsive dashboards while staying well within the endpoint's rate limits. ### Visualize in Grafana With the node scraped into Prometheus, add the metrics as a data source in Grafana and build panels from the series. A starter set of panels: **CPU utilization (%)** ```text theme={"system"} chainstack_node_cpu_utilization_ratio{node_id="nd-123-456-789"} * 100 ``` **Memory utilization (%)** ```text theme={"system"} chainstack_node_memory_utilization_ratio{node_id="nd-123-456-789"} * 100 ``` **Provisioned storage (GiB)** ```text theme={"system"} chainstack_node_storage_requests_bytes{node_id="nd-123-456-789"} / 1024 / 1024 / 1024 ``` **Latest block** ```text theme={"system"} chainstack_node_block_number{node_id="nd-123-456-789"} ``` **Block age (seconds)** ```text theme={"system"} chainstack_node_block_age_seconds{node_id="nd-123-456-789"} ``` From here you can add alerts—for example, on sustained high CPU or memory utilization, or on block age exceeding a threshold suited to your node's protocol—and SLOs that combine node health with your own application metrics. ### Rate limits The metrics endpoint is rate-limited to protect the node. A scrape interval of 15–60 seconds passes comfortably; excessive scrape rates are throttled with a `429` response and a `Retry-After` header indicating how many seconds to wait before retrying. Rate limiting on the metrics endpoint is independent of your RPC traffic—hitting the metrics limit does not throttle JSON-RPC, WebSocket, or other requests to the node. For more, see [Chainstack limits and rate limiting](/docs/limits). # Deep dive into Merkle proofs and eth_getProof Source: https://docs.chainstack.com/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method Understand Merkle proofs and use eth_getProof to verify Ethereum account state and storage slots without trusting a full node for data authenticity. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR** * Introduces the eth\_getProof method, which returns a Merkle proof of an account’s state and storage at a specific block. * Explains Ethereum’s Merkle trie structure and how merkle proofs allow verifying account/storage data without downloading the entire state. * Demonstrates retrieving a proof for an ERC-20 contract (e.g. USDT) and shows a sample Python script to verify that proof off-chain. * Reminds that archive nodes are required and that eth\_getProof is supported on both Geth (no block limit) and Erigon (100,000-block limit). ## Main article [`eth_getProof`](/reference/getproof) is an Ethereum JSON-RPC method. It returns the Merkle proof of a specific account and its storage at a given block. If you're not familiar with these terms, don't worry. Most people don't understand them. By the end of this article, you should have a better understanding of what `eth_getProof` is and how to use it. > [!NOTE] > > eth\_getProof requires an archive node. ## The Merkle trie `eth_getProof` was proposed in [EIP-1186](https://eips.ethereum.org/EIPS/eip-1186). The proposal summary states the following: > In order to allow verification of accounts outside the client, we need an additional function delivering us the required proof. These proofs are important to secure Layer2-Technologies. But how does the proof work? In the Chainstack blog **[Deep dive into eth\_call](https://chainstack.com/deep-dive-into-eth_call/)**, we discuss how Ethereum uses Merkle tries for data storage. There are four types of tries used by Ethereum: * State trie * Storage trie * Receipt trie * Transaction trie The state trie of Ethereum is like a real tree in some ways. All the account information is stored at the end of the branches, which are represented by the leaves. ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/8c9ae6a-merkle-trie.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=cfa1bb66133d7a27ea4b556b9842a794) Merkle trie in a nutshell Each block has a unique hash value that references the storage root. In a full mode, which has access to the most recent 128 blocks, there are 128 isolated state tries (in reality, it is slightly more complicated than this). When the EVM needs to access the account state, it uses the block number to find the proper root hash, then uses the root hash to determine which trie to query, and finally traverses down the branches to identify the correct leaf containing the account information. ## Merkle proof Now, here's the question: we have account information (`storageRoot`, `codeHash`, balance, and `nonce`); how do we verify this information’s authenticity and correctness? The answer is by using the storage root hash. For Ethereum, the final root hash is derived by hashing every layer of the trie. ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/d0e03f2-binary-merkle.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=3c7f22222103f7d7827cb6a26db9c097) A simplified version of binary Merkle trie. Source [Merkle proofs for offline data integrity](https://ethereum.org/en/developers/tutorials/merkle-proofs-for-offline-data-integrity/) ```javascript stateRootHash theme={"system"} //In a very simplified fashion stateRootHash = hash( hash( hash(hash(A)+hash(B))+hash(hash(C)+hash(D)) )+ hash( hash(hash(E)+hash(F))+hash(hash(G)+hash(H)) ) ) ``` The final root hash is computed by hashing all the sub-nodes in the state trie. If any of the account information differs from the original, the resulting root hash will also be different. Therefore, any account information can be verified using an authenticated root hash. For example, consider the following Ethereum account: `0xdac17f958d2ee523a2206206994597c13d831ec7`. At block `16947990`, its account information is as follows: ```javascript Account states theme={"system"} 0xdac17f958d2ee523a2206206994597c13d831ec7{ balance : '1', codeHash : '0xb44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55', nonce : '1', storageHash : '0x171b13e236ba0fd523b341866fdd3db37aeb8eb9bb578e819cbd983971309e3c' } ``` The state root hash at block `16947990` is: `0x98612efbedabf19646d53f903810e156143d3174a4e985b00cbf01dc257431d1` If any of the account information does not match the original value, the final root hash will not match `0x98612efbedabf19646d53f903810e156143d3174a4e985b00cbf01dc257431d1`. Therefore, we can prove that this information is incorrect. Here comes another question: do we need every account's information to produce a proof? The answer is obviously no. With millions of accounts on the blockchain, it is not feasible to retrieve all these data just to verify one single account, especially for off-chain verification. In fact, to verify account C, we don’t really need the information from leaf nodes A, B, D, E, F, G, H; we just need the hash of the intermediate branch nodes. ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/96b9e28-merkle-proof.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=467ec889b92f50721f84a99eee4f13ca) A simplified version of Merkle proof. Source: . [Merkle proofs for offline data integrity](https://ethereum.org/en/developers/tutorials/merkle-proofs-for-offline-data-integrity/) If the value of `hashEFGH`, `hashAB`, and `hashD` are known: ```javascript Nodes hashes theme={"system"} const hashEFGH = hash(hash(hash(E)+hash(F))+hash(hash(G)+hash(H))) const hashAB = hash(hash(A)+hash(B)) const hashD = hash(D) ``` The original equation becomes: ```javascript stateRootHash theme={"system"} //In a very simplified fashion stateRootHash = hash(hash(hashAB + hash(hash(C)+hashD)+ hashEFGH) ``` This is much easier, isn't it? Essentially, `eth_getProof` returns the values of `hashEFGH`, `hashAB`, and `hashD`. It is also known as Merkle proof. Merkle proof doesn't only work for account information but also for storage information. Storage refers to the data that lives within an account, such as the owner and balance mapping from an ERC-20 token smart contract. ## eth\_getProof `eth_getProof` returns the Merkle proof for a specific account and its associated storage values. Merkle proofs are used to verify the inclusion of a certain piece of data within a Merkle trie. ### Parameters * `DATA` — Ethereum address of the account for which the proof is requested * `ARRAY` — the array of 32-byte storage keys that need to be proven and included. See [eth\_getStorageAt](/reference/ethereum-getstorageat) in the API reference. * `QUANTITY|TAG` — the integer block number identifying the block for which the proof is requested or a string tag such as `"latest"` or `"earliest"`. ### Response `Object` — an account object with: * `balance`: `QUANTITY` — the balance of the account. See [eth\_getBalance](/reference/ethereum-getbalance) in the API reference. * `codeHash`: `DATA`— the code hash of the account. For a simple account without code, it will return `"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"`. * `nonce`: `QUANTITY` — the nonce of the account. See [eth\_getTransactionCount](/reference/ethereum-gettransactioncount). * `storageHash`: `DATA` — SHA3 of the `StorageRoot`. All storage will deliver a Merkle proof starting with this root hash. This hash is used as the starting point to retrieve a Merkle proof for all storage entries associated with the account. * `accountProof`: `ARRAY` — the array of RLP-serialized Merkle trie nodes, starting with the stateRoot-Node, following the path of the SHA3 (address) as key, and can be used to prove the existence and validity of a piece of data in the trie. * `storageProof`: `ARRAY` — the requested array of storage entries, where each entry is represented as an object that includes the following properties: * `key`: `QUANTITY` — the requested storage key * `value`: `QUANTITY` — the storage value * `proof`: `ARRAY` — the array of RLP-serialized Merkle trie nodes, starting with the `storageHash-Node`, following the path of the SHA3 (key) as the path. Source: [EIP-1186 specification](https://eips.ethereum.org/EIPS/eip-1186) See the following code samples: ```bash cURL theme={"system"} curl "YOUR_CHAINSTACK_ENDPOINT" \ -X POST \ -H "Content-Type: application/json" \ -d '{"id": 1,"jsonrpc": "2.0","method": "eth_getProof","params": ["0xdac17f958d2ee523a2206206994597c13d831ec7",["0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c"],"latest"]}' ``` ```js web3.js theme={"system"} const Web3 = require('web3'); const web3 = new Web3(new Web3.providers.HttpProvider('YOUR_CHAINSTACK_ENDPOINT')); async function main() { // Gets the Merkle proof of USDT's smart contract var proof = await web3.eth.getProof( "0xdac17f958d2ee523a2206206994597c13d831ec7", [ //Storage slot hash, value = keccak256(storage address + slot index) "0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c" ], "latest" ) var block = await web3.eth.getBlock("latest") //state root is very important for Merkle verification var stateRoot = block.stateRoot console.log("state root:" + stateRoot) console.log(proof) } main(); ``` ```py web3.py theme={"system"} from web3 import Web3 httpUrl = "YOUR_CHAINSTACK_ENDPOINT" w3 = Web3(Web3.HTTPProvider(httpUrl)) proof = w3.eth.get_proof('0xdAC17F958D2ee523a2206206994597C13D831ec7', ["0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c"], "latest") print(proof) ``` Sample result: ```shell Shell theme={"system"} { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', accountProof: [ '0xf90211a06a718c2c9da77c253b12d7b2569657901e37bb691718f5dda1b86157ab1dd5eda0e7f19ed5e21bccc8d3260236b24f80ad88b3634f5d005f37b838881f0e12f1bda0abb301291704e4d92686c0f5f8ebb1734185321559b8d717ffdca95c99591976a0d0c2026bfab65c3b95276bfa82af9dec860b485f8857f293c148d63a2182128fa0c98044ec9a1273a218bed58b478277dd39173ad7b8edb95c200423a6bc8fc25fa056e5a55d9ddccdbf49362857200bbb1f042d61187c9f5f9ddcff5d2f1fc984a2a02a5b7200af424114f99a4b5f0a21c19aac82209e431ed80bfde177adb1004bdfa0026e4374f0518ff44a80fa374838ecb86cc64ac93bb710fea6dff4198f947b27a03fea341d87984673ad523177ed52f278bf4d8f97e6531c8ece932aeede4802f4a0bfe2f4a7fcb78f7e9f080dea7b6977fb1d88c441696e4456dad92b9d34ff0f43a02a3eb5c0edb14626c9c629601027bd60178bb2b688a67cea4d179fc432436615a0747355b8e02f3b884b4ffe5cea1619e32515fea064cca98208591af8c744e894a0874253737bae37f020ad3bb7e3292c7c4a63cdc158af6b33aaa4deaef016dccba03d8192bc1fc6aa1548912e763a0b5013a94399cefad7b47cf388873b2b794068a09b67f9737c6028d796bfd1c5da57a6f45824dc891f848ea0e1f8019d1fb5fba8a0aa871f9de8da85960fcd8a22cdf21c27f11e3966c14a6737ffd414b98dda00b280', '0xf90211a0d360be1e1da1a0c32bc4c105833bd531e59d110684007b7c50fb2709002973eca0cf6dd1e350a7031b4e2ab49c899fd8bd47551c8565d8fd8d1d7796c83820c3b1a0eb0a88c29bb33989a589156f7bf07d9efc74034dd9d3f5b73385c3b45c3249bea02783c25f97a6ddb8dc07adf4b176991836d39184b1f678adeda832fff15e3664a00a4e288060045e587774d8a64993a7add73068b16863145e1e8eeb4602e18e19a0340851f4046ad1298962d6e47d05c66329549c839c158748aaad7ae00b943aefa085b127bc2a3bd17604283de21b2b3c9aa8f1d4b7b85c94d8105a46fe32c77688a00f531d62b3c5435324c01009c284fe31277e8d38302b75ea01be89f09e205969a00011c8351c0e3d639ac54b9d3a59de630b16a67de8270d7d6064d0a67e93f9cca048780d32b7f2db88650b51c46f46fd0a68795edee1fd5ecee6eb3595741d9669a0c91afd74eaf8e08a997061a62b354e2516fdc494e8e26cc50ceeb8f4a175608ba0e2c07f1b48fab80eecb340f5882e8c7b32ee416e4045c61f1df646a133487303a01a1eff78435a7a29a29463bdc3486ae81364b00bea82ba0fdf67a110770f2261a04f2eb440ba71c72da5fd7f0e439018d6671dc809f747213a1ea755848124e994a074ff9f37fce99daa3ed01dd763076450022996fc729be2cc43c61ec5182c2366a0b80b36b7b621112592f52390b89748d422e9b1517c4b0203b8176a53f89d4a6680', '0xf90211a0b25f283bd01a8c8b2418049f9585bc37ff2c1e2e12eab4b7f64ae1f26647389aa02ad96c150d7c3c9c194d30315456852cf6a0a940e0191ae5d04007454823d4e9a0b220cf7a855e2dbcc0b973134e2e119b982d7d40dbb1b27d99816c41f40e829aa049224431da84cbf1b7ae813abcc9ef4c1dfc1760f6ddc5d57f7354bf3cbf6cc4a015191f879ac115b362f0257fd3eedb789537e836574a5b1abf1c9982ebe3bdfea07913c1b6e7282569d2d421e9fa2257f5d1698e93303bc49b941704287d7aaefea0a526576981ce6fd9f2bd48dd2ca6d5272f2fbdc85f0ee35a295f6ccd97ae8765a0313fad407f0c737c29024c02a890c4ecc12d7771c05ab7b435e5087a7cdef4d9a0d2044603cba9d4afdaf6fd2470e729ef3a65242de71276f20d59accfa6b53a7ca0457caacb9370c09b15f7d904adefd2308be94e23669ba5f43241ffff5f438a0aa09fb2dd45a383a0cc088a72b14117e1e9b7d6889218f3ac7631e8de644c5cb76da0c675dcd4d3fb692b514851c6106e2b09e6f5661d56a0a32ae02e2efc1515c235a074949a59ff1bdba87548510d6e404ec4532f4456dfdec8e753d92fda11a3088ba0a328c6ab1ab8f70db4d23e95bb163c13ba0c508f063a5b1393a4efd7ff375f05a0c722fe3ce796998269373cbb2fc229b2bdf2c43c6c2df003309422e043ce6c03a024e69343286eec44fa4744f6907209116e5383cff3fa98fe81ba06e7e8d4366680', '0xf90211a00e99ba2198124b8241ea304551fe973215829e2fbc0438d67922707a2a847432a0bb9ce24fd527879c5fe6dbbec1ef5a05ed9d1ca88e921d140bafbec1112f6a6aa099787fd6c7a1989229c4291ef5267335e66152ce417daea46e66d19cb6f81d1ca0e430ff4b8d5621baa5978673344e78b4d8b4df51431b6e63785267c98a24ce18a0bb3e91a825fe3d42ed270a93e9ad1aabd566c40cb28e622f7f1d7ee967c8afd6a0aa364b0056870c6507bc3262a5f851ecb13684088bdb13996d3cb2db401ce3ffa0a3732eba4c7a6e062665ab5be08acb986c3db87556fb138548cc900ff1e56995a026b088e90c9738b8ce16e853107a937a50d52726a24f9f6ce60f587762eb45a2a006c9d5bc3c064b5c1fb565bff91cace9161c64ae653a329610c1dcf34d434429a06c16df2edc70656d322d0c2403bad7d45bc790ffc3e7adeef856d98ea6afc91ba0ae05ed5d6c34b5da29c2e94d7880aeba0906f95f4ec10b132a1d4766a0701c98a01470a86aa350d1ada0c082eac75de828a851f9c8c7c4aa49b1556fe3a5574966a0334eef025100a6da1033710dd98e0475f29d3d7e397caf618ca71c336c5f4f49a0ef0b3abbebcff34d6a8a8f5cdbfbd154ab3452b58dcb09de58ec983644963675a041857e865ec38e200a13bc1a3cb71c7d69aeef7ffdee8be515c9a5b691ce091fa059edd0eb3bbec36bbf38a19802d4646c00ba821ab55fdeea12e15bab62c4e1e580', '0xf90211a0af0c7fa65ffcb84c31e68c1cf00e1a20bf8bb497c39883e19b66a99975b03431a0c492cab3623eb7926069794c3c718733e16c5fd0d4a13fb7c752ee9809aac7ada05003cea7132aa70d6f36731d60640a90bcd8f4fd493e4540d5ab1b4943679c0ca0fd700683405b1d2306b586dd3b5b2f92f1692fae20d17cd8b8e59d09b9c6670da01db8683910e46e56e8afeb9fe2b7c35382e5a0914d7b0dd8f0e8cb9981ba7435a0fa7f75d73aa73c35824387bec81388315caa4aee3f4f5562f971beb256c62d49a0ee478e420d83f413e8568dacfd5d83f83a5dd7c45f494b504828e5dc962f0e3ea094b95444a917ac94a675681f6bf851172ad0969801a783a63a71edafed45e7a7a0a0c46586e109abe80fe50361dd582e3f143cb416828239faa43bb2b890869501a0ae051d5d43634c68bf9c97823256cc68580f194dfdbd0c301140c7ca5853430ca0660b9365bb77ec9cdc6eb95516c162dca20727c6f828dbbeb1ae110dde4d3134a09feb1b75e84ff6722e4d837bfb6d207b6ee3b21b86844a01140ce293813b49a1a0ed58a70b04efa3bdc0babe2abfa20824a75d61d52291bfdb5cf08597800764d6a020a2d5d3a83f9e35ad9fd1c448626d90af0eb3efefaa4f2f93207b4096ef5507a0fc8efc4484dcf0a54f0574de9aaade0dcff6ec3599edb9f82efb26b6566dcaeaa032f7e79856db3fd984f72bb2c93d4dab328198d355a61c975fab1f08bdb2046580', '0xf90211a0c87222cccea2bf32759fcee9dbaacbe3ea4165dd6184af6773651c5e00e34a8ba0be90e6e5d1a67ab5587779c60ac136d6a96db62b84c04998a5f03a367346abd6a05344aa1c9ca2e3e56bf98fd718ec43728578d148e1967fbaf8bf17a2a073a0bda011a2f9312c3308640a0d6ceeae218747290f23806067456da1d444c65abae437a0b3097a108bfce79af6699da4ae3003cd4929f0b4576aad655c31cb725bde84c7a0c133d3c637e174f36a73c22b1039eb003da6374bc0929321241badb3efa3c4a9a0f13059f2301ad9862ce02e3f7f3f2c9ab78eb30583764d73654f7f1f8b1e86fda06544e3915748b18204e09df75ff20d2fa6bd8121e2e669699012d54590383d6fa070e3a8e093691581d58fadb560b510262a758037632cd8670d3a36df828976b7a062a88a2900544dc76a32255a6b2b2a2eef8fa68279700c00adc7508286702552a0a474aeebd5603dfce46a6ecd1ecd519068dc034a544fde03ac42d4018e60a334a0b7d528fc41c8fdc8ea18c6e7d0099270c777ec1403cf879d1f5134bdc12a6c6ca04440f1242e42c5bfa7c536591ab89c8e84bea417435871c32eef1e25295b20daa06a5dcfe3cc84cff9d3e3c3ae868cfba8f0dd111a90c3f85869dab5b893f96643a026b2fb9dd7d08b0ed2f1c44fbf875011412a384f86f751c92e1013248d4aa371a0c75597b2b789fc4e939b71937390ce9d7d53159431328ac52180eef08ef200f280', '0xf90191a0f0c5b800b542001597f2b7a8e106ac0e2849d2cc1df1727ac35c4ea3965f1c9180a08537f2e248702a6ae2a57e9110a5740f5772c876389739ac90debd6a0692713ea00b3a26a05b5494fb3ff6f0b3897688a5581066b20b07ebab9252d169d928717fa0a9a54d84976d134d6dba06a65064c7f3a964a75947d452db6f6bb4b6c47b43aaa01e2a1ed3d1572b872bbf09ee44d2ed737da31f01de3c0f4b4e1f046740066461a076f251d160b9a02eb0b5c1d83b61c9cdd4f37361705e79a45529bf49801fb824a0774a01a624cb14a50d17f2fe4b7ae6af8a67bbb029177ccc3dd729a734484d3ea05921b8a19aebe4fff5a36071e311778f9b93459183fdf7f6d870b401fa25dcbba0c8d71dd13d2806e2865a5c2cfa447f626471bf0b66182a8fd07230434e1cad2680a0e9864fdfaf3693b2602f56cd938ccd494b8634b1f91800ef02203a3609ca4c21a0c69d174ad6b6e58b0bd05914352839ec60915cd066dd2bee2a48016139687f21a0513dd5514fd6bad56871711441d38de2821cc6913cb192416b0385f025650731808080', '0xf8669d3802a763f7db875346d03fbf86f137de55814b191c069e721f47474733b846f8440101a0146f8675fabbf90b214375a6839a8ddfb33f4c556a26ade8a48c4a82d7055100a0b44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55' ], balance: '1', codeHash: '0xb44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55', nonce: '1', storageHash: '0x146f8675fabbf90b214375a6839a8ddfb33f4c556a26ade8a48c4a82d7055100', storageProof: [ { key: '0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c', value: '0x3499e1d1', proof: [Array] } ] } ``` ## Verifying the proof [This script](https://colab.research.google.com/drive/1ksKdHyUquq1Ea4RoDhypoLNg5ipxdE7H?usp=sharing) on Google Colaboratory is a working Python sample from the [web3.py’s official documentation](https://web3py.readthedocs.io/en/stable/web3.eth.html#web3.eth.Eth.get_proof). To run the script, follow the steps below. First, you need to uncomment and run the following `pip install` commands to install py-trie and [web3.py](https://github.com/ethereum/web3.py). ```bash Shell theme={"system"} # pip install trie # https://github.com/ethereum/py-trie # pip install web3 # https://web3py.readthedocs.io/ ``` Then you can go to **Runtime > Run all** or press the button beside each cell. Most importantly, don’t forget to fill in your Chainstack endpoint in the following line: ```bash Shell theme={"system"} httpUrl = "YOUR_CHAINSTACK_ENDPOINT" ``` You can sign up and get an Ethereum RPC endpoint for free: > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) Next, you can run the code to see it in action. Scroll all the way down to the last section of the script. This is where the code is executed. ![getProof code](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/197bc5d-getProof-code.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=f8b0410bb3b683377fb1a6bfcd3b5cbe) To begin, we retrieve the latest block information by calling `w3.eth.get_block("latest")`. Next, we obtain the Merkle proof of the account `0xdAC17F958D2ee523a2206206994597C13D831ec7` and one of its storage values by calling `w3.eth.get_proof('0xdAC17F958D2ee523a2206206994597C13D831ec7', ["0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c"], "latest")`. Then, we verify the proof by calling `verify_eth_get_proof(proof, block.stateRoot)`. If the proof is valid, `verify_eth_get_proof` returns `true` to the `isValidProof` variable. Otherwise, it returns a "Failed to verify account proof" error. ![Proof response](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/7122d41-proof.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=f0dfa0054c717805cd376713b81593b0) ## Q\&A > [accordion] Why eth\_getProof? > > This method allows for efficient verification of the network's state without having to download the entire state trie for a block. This can be particularly useful for building lightweight clients or auditing smart contract state changes. > [accordion] Why Merkle trie? > > There are many reasons why Ethereum uses a Merkle trie for data storage, but one of the most important is its verifiability. As a famous blockchain quote goes, "Don’t trust, verify.” You can learn more about the Merkle trie in the [Ethereum documentation article](https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/). > [accordion] How big is a Merkle trie? > > The implementation determines this. For Ethereum, the trie is hexary, which means that there are 16 entries for every node. > [accordion] Does Ethereum keep a separate storage trie for each state? > > Yes, Ethereum keeps a separate storage trie for each state. > > The storage trie is a part of the state trie that stores the contract state. Each contract on the blockchain has its own storage trie, which is a key-value store that maps 256-bit keys to 256-bit values. This allows smart contracts to persist data across transactions and to maintain their own state. > > When a contract is executed, and its state is updated, the new state is stored in a new storage trie, which is then added to the state trie. This creates a new state root, which represents the updated state of the blockchain. > [accordion] What is RLP? > > RLP (recursive length prefix) serialization is a technique widely used by Ethereum clients for serialization purposes. In a Merkle trie, RLP is used to convert key-value pairs (the node entries) into a long string, which is then used for hashing. You can read more in [Recursive-length prefix (RLP) serialization](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/). > [accordion] How far back can eth\_getProof go on Chainstack? > > On Ethereum Mainnet Global Nodes, `eth_getProof` returns proofs for the latest 10,000 blocks. An older block returns `-32602` `distance to target block exceeds maximum proof window`. The proof history differs by network — see [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). ## Conclusion This concludes the article. Congratulations! You now know how to use the eth\_getProof method to retrieve a Merkle proof and verify a user's ERC-20 token balance. When using `eth_getProof`, remember to provide the correct block number, account address, and key path to retrieve the desired proof. > [!NOTE] > > ### See also > > > [card] [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) > > > [card] [Uncovering the power of eth\\\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) > > > [card] [Expanding your blockchain horizons: The eth\\\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) > > > [card] [Geth vs Erigon: Deep dive into RPC methods on Ethereum clients](/docs/geth-vs-erigon-deep-dive-into-rpc-methods-on-ethereum-clients) > > > [card] [Ethereum logs tutorial series: Logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters) ### About the author > [card] Wuzhong Zhu > > ![Wuzhong Zhu](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1548860905064017921/xCKUgveS_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=075e8dc4db13d9384c63bf4904eb520d) > > Developer Advocate @ Chainstack > \ > BUIDLs on Ethereum, zkEVMs, and The Graph protocol > > [https://twitter.com/wuzhongzhu](https://twitter.com/wuzhongzhu) > > [https://www.linkedin.com/in/wuzhong-zhu-44563589/](https://www.linkedin.com/in/wuzhong-zhu-44563589/) > > [https://github.com/wuzhong-zhu](https://github.com/wuzhong-zhu) # Ethereum: Battleship game with zero-knowledge proofs Source: https://docs.chainstack.com/docs/develop-a-battleship-game-using-zero-knowledge-concepts-on-ethereum Build a Battleship game on Ethereum using zero-knowledge proofs with Circom circuits and Solidity verifiers deployed through a Chainstack RPC node. **TLDR** * Showcases how to build a Battleship game on Ethereum using a lightweight cryptographic technique for hidden coordinates. * Stores hashed ship positions as ECDSA signatures so only the owner can prove hits or misses. * Demonstrates verifying these signatures on-chain to ensure game fairness and incomplete information. * Provides a complete Solidity contract and tests with Hardhat for a two-player Battleship match. ## Main article Zero-knowledge proof is a method by which one party (the prover) can prove to another party (the verifier) that they know a value, without conveying any information apart from the fact that they know the value. This concept is commonly used in zero-knowledge chains that are becoming more common in the blockchain ecosystem lately. This article will guide you through how you can develop a blockchain game using this principle. > [!NOTE] > > Learn more about zero-knowledge based blockchains by reading [zkEVM and zkRollups Explained](https://chainstack.com/zkevm-and-zkrollups-explained/). ## What is battleship Battleship, a guessing game played between players on separate grids representing their fleets of ships. Each player has their own grid and they place their ships on the grid in secret. The objective of the game is to sink the opponent's fleet of ships by correctly guessing the locations of their ships on the grid. Players take turns calling out coordinates, any `(x,y)`, on the opponent's grid, in an attempt to find the location of the opponent's ships. If a player guesses a coordinate where a ship is located, the opponent must respond with "hit". If a player guesses a coordinate where there is no ship, the opponent must respond with "miss". The player who sinks all of their opponent's ships first wins the game. ## What we'll be doing? In this tutorial, we'll be building our very own game of battleship on Ethereum. There is a slight hitch however. Battleship is a [game of incomplete information](http://gametheory101.com/courses/game-theory-101/introduction-to-incomplete-information/), and that doesn't sit nicely with the public, permissionless nature of Ethereum (and other public blockchains like Polygon). While we can declare state variables as private, [anyone could still access their values](https://ethereum.stackexchange.com/questions/44893/how-do-i-see-the-value-of-a-string-stored-in-a-private-variable). How do we store private data on a public blockchain? It's a catch-22, or is it? ## Our approach There are multiple approaches to privacy on the blockchain thus far. One particularly promising one has been [ZKPs](https://medium.com/@ashwin.yar/zkps-the-ultimate-shield-for-your-privacy-in-web3-a-beginners-introduction-to-the-future-of-e60918da01b4) (zkSNARKs, zkSTARKs etc.). However in this tutorial, we'll be going lightweight. We need to a create a unique identifier for each players ship's coordinate that would be impossible to guess/reverse by anybody else, but which can be verified in the future, to reveal the initial data (ship's coordinate). We need a one-way function, and public-private signatures are a perfect match for this use case. Public-private signatures are also known as digital or cryptographic signatures. It is the same technology behind authorizing transactions on the blockchain. We sign an arbitary byte of data (a ship coordinate in this case) with our private key, and this generates a signature. We can then retrieve the corresponding public key/address of a signature and confirm if it matches the expected value. Signing a message with a private key is deterministic, meaning signing the same data with the same private key will always produce the same signature. Here's a brief overview of how it all fits together: * *Player1* signs their ship coordinates and we store those signatures in our smart contract. * *Player2* declares which coordinates they've shot at. * *Player1* signs all the "shot" coordinates, and we check if such a signature exists in our smart contract. No? That was a miss. Yes? A ship has been hit! Couple of things to consider: * **Could *Player1* sign the wrong coordinates and provide us inaccurate data?**. Possibly, that's why we verify the signature in our smart contract to make sure they signed the right data. * **Since we treat all shots as the same regardless of who shot it, could *Player1* sink his/her own ships?**. In this implementation, yes. We could make it otherwise, but I think a bomb is a bomb, regardless of where it blows. You decide if that's a bug or feature. ## Diving in This is the source code of a smart contract in the Solidity programming language for our battleship game. For now, I restricted it to a two-players game, but with a little bit of work, it could support much more. Each player gets 10 coordinates (blocks) to form their ships. I think it would be best to envision the game as multiple states. ### State 1: Game start * The smart contract gets deployed with a whitelist of player addresses allowed to play in that game. It makes it easier to track when all players have joined and the game has started. * Players pick their ships coordinates, sign them, and send the signatures to the smart contract. Once they have done that, they have joined the game. * When all players have joined the game, we're off to the races. Players can not change their signatures (ship positions), nor can they take shots at this stage. ### State 2: Game in progress. Turn in progress * Turn 1, or turn 10, it doesn't matter much. Each player can pick a single coordinate where he'd be shooting at. We store this coordinate in our smart contract. * We make sure everyone has taken a shot before marking the turn as over. ### State 3: Game in progress. Turn over * Players retrieve all shots for that turn, sign them, and submit the signatures back to the smart contract. * For each signature, we verify that the player signed the right data. If the signature exists in our list of known ships, it's a hit! ### State 4: Game over * For a player to win, every other player must have lost all their ships. It's a draw when nobody has any ships left. Time to get into the code. > [!NOTE] > > ### If you'd like to follow along: > > Find the repository on the Chainstacklabs GitHub and find the instructions: [Game of Battleship Solidity and Hardhat](https://github.com/chainstacklabs/zk-battleship-game-hardhat) > > Heads up! It's a TypeScript project, but if you're not familiar with TypeScript, basic JavaScript understanding should be more than enough to follow along. ```shell Shell theme={"system"} git clone https://github.com/chainstacklabs/developer-hub-content ``` Additionally, install [Hardhat](https://hardhat.org/tutorial/creating-a-new-hardhat-project) and the dependencies. ```shell Shell theme={"system"} npm install --save-dev hardhat ``` ```shell Shell theme={"system"} npm install ``` And you will also need a Chainstack node to deploy the contract on Sepolia, which is the recommended chain to test DApps, or Sepolia. Deploy a node with Chainstack: > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) ## Verifying digital signatures Since all signatures created on Ethereum make use of the ECDSA curve, there have been suggestions to have a [native compiled function for verifying signatures](https://eips.ethereum.org/EIPS/eip-665). However, that's not yet available, and we have to roll our own solution. We need a smart contract to derive the corresponding address of the public key used to create the digital signature. We'll be using the SigVerifier contract from the official Solidity documentation with some modifications. ```sol sol theme={"system"} // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; contract SigVerifier { function RecoverSigner( bytes32 _hashedMessage, uint8 _v, bytes32 _r, bytes32 _s ) public pure returns (address) { bytes memory prefix = "\x19Ethereum Signed Message:\n32"; bytes32 prefixedHashMessage = keccak256( abi.encodePacked(prefix, _hashedMessage) ); address signer = ecrecover(prefixedHashMessage, _v, _r, _s); return signer; } function SplitSignature( bytes memory sig ) public pure returns (uint8 v, bytes32 r, bytes32 s) { require(sig.length == 65, "Invalid Signature"); assembly { // first 32 bytes, after the length prefix. r := mload(add(sig, 32)) // second 32 bytes. s := mload(add(sig, 64)) // final byte (first byte of the next 32 bytes). v := byte(0, mload(add(sig, 96))) } return (v, r, s); } } ``` Digital signatures in Ethereum are based on the ECDSA curve. Each signature contains three parameters, `r`, `s`, and `v`. We can derive these parameters from a signature by splitting it into the requisite number of bytes. Retrieving the public address from these parameters is as simple as invoking Solidity's `ecrecover` function. This is a relatively costly process and consumes quite a bit of gas compared to normal transactions. To optimize the gas cost, we made use of assembly in our smart contract to split the signature. We could also have split the signature offchain with JavaScript or another programming language. ## The smart contract ```sol sol theme={"system"} // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.10; import "contracts/Verify.sol"; contract BattleShipGame is SigVerifier { // Admin address payable public owner; // Max Number of players participating in the game uint public constant NO_PLAYERS = 2; // Number of ship pieces a player can have, to build their ships uint public constant NO_SHIP_PIECES = 10; // All the players participating in the game mapping(address => bool) public players; address[] public playersAddress; // We use an array because it's easier to iterate than a mapping // Player ships mapping(address => mapping(bytes => bool)) ships; // Player ships that have been destroyed mapping(address => Coordinate[]) destroyedShips; // Players who have lost all their ships mapping(address => bool) public destroyedPlayers; uint public numberOfDestroyedPlayers; mapping(address => Coordinate) public playerShots; mapping(address => bool) public playerHasPlayed; mapping(address => bool) public playerHasPlacedShips; mapping(address => bool) public playerHasReportedHits; bool public isGameOver; struct Coordinate { uint8 x; uint8 y; } struct ShipShotProof { bytes signature; // The address of the player that shot // at this coordinate address shotBy; } event ShotReport( Coordinate coord, address target, address shotBy, bool isHit ); event PlayerJoinedGame(address player, uint playerIndex); // Emitted when a player loses all his ships event PlayerLost(address player); constructor(address[] memory _playersAddress) payable { require( _playersAddress.length == NO_PLAYERS, "_playersAddress does not match the number of expected players" ); for (uint i = 0; i < _playersAddress.length; i++) { address playerAddress = _playersAddress[i]; players[playerAddress] = true; playersAddress.push(playerAddress); emit PlayerJoinedGame(playerAddress, i); } owner = payable(msg.sender); } function joinGame(bytes[] memory _playerShips) public { require(!isGameOver, "Game is over"); require(players[msg.sender], "Address is not a part of this game"); require( _playerShips.length == NO_SHIP_PIECES, "Number of ship pieces does not match the expected value" ); require( !playerHasPlacedShips[msg.sender], "Player has already placed ships" ); for (uint i = 0; i < _playerShips.length; i++) { bytes memory shipHash = _playerShips[i]; require( !ships[msg.sender][shipHash], "User has already placed a ship on this tile." ); ships[msg.sender][shipHash] = true; } playerHasPlacedShips[msg.sender] = true; } function takeAShot(Coordinate memory _coord) public { require(isGameStarted(), "Game hasn't started"); require(!isGameOver, "Game is over"); require(players[msg.sender], "msg.sender is not a player in this game"); require( !playerHasPlayed[msg.sender], "Player has made a move for this turn" ); playerShots[msg.sender] = _coord; playerHasPlayed[msg.sender] = true; } function reportHits(ShipShotProof[] memory _shotSignatures) public { require(isGameStarted(), "Game hasn't started"); require(!isGameOver, "Game is over"); require(isTurnOver(), "All players have not played for this turn."); require( _shotSignatures.length <= NO_PLAYERS, "No way you can validate more shots than there are players!" ); for (uint i = 0; i < _shotSignatures.length; i++) { ShipShotProof memory shotProof = _shotSignatures[i]; (bool _isHit, Coordinate memory coord) = isHit(shotProof); if (_isHit) { destroyPlayerShip(msg.sender, coord); } emit ShotReport({ coord: coord, target: msg.sender, shotBy: shotProof.shotBy, isHit: _isHit }); } playerHasReportedHits[msg.sender] = true; } function isHit( ShipShotProof memory _hitProof ) internal view returns (bool, Coordinate memory) { Coordinate memory _playerShot = playerShots[_hitProof.shotBy]; bytes32 _calculatedHash = keccak256( abi.encodePacked(_playerShot.x, _playerShot.y) ); (uint8 v, bytes32 r, bytes32 s) = SplitSignature(_hitProof.signature); address signer = RecoverSigner(_calculatedHash, v, r, s); require( signer == msg.sender, "msg.sender and derived message signer do not match" ); // A ship piece at this coordinate exists return (ships[msg.sender][_hitProof.signature] == true, _playerShot); } // function destroyPlayerShip( address _player, Coordinate memory _coord ) internal { destroyedShips[_player].push(_coord); // All of a player's ships have been destroyed if (destroyedShips[_player].length == NO_SHIP_PIECES) { destroyedPlayers[_player] = true; numberOfDestroyedPlayers++; emit PlayerLost(_player); } } // Check if all players have played for this turn function isTurnOver() public view returns (bool) { for (uint i = 0; i < playersAddress.length; i++) { address _playerAddress = playersAddress[i]; if (!playerHasPlayed[_playerAddress]) { return false; } } return true; } function hasReportedShots() public view returns (bool) { for (uint i = 0; i < playersAddress.length; i++) { address _playerAddress = playersAddress[i]; if (!playerHasReportedHits[_playerAddress]) { return false; } } return true; } // End the current turn and reset all variables function endTurn() public returns (bool) { require( isTurnOver(), "The turn is not yet over, some players are yet to shoot" ); require(hasReportedShots(), "Some players are yet to report hits"); // Do we have a winner? // Only one player is left standing // It's also possible that everybody destroyed everybody. (Edge case) if (numberOfDestroyedPlayers >= (NO_PLAYERS - 1)) { isGameOver = true; } for (uint i = 0; i < playersAddress.length; i++) { address _playerAddress = playersAddress[i]; playerHasPlayed[_playerAddress] = false; playerHasReportedHits[_playerAddress] = false; playerShots[_playerAddress] = Coordinate({x: 0, y: 0}); } return true; } function isGameStarted() public view returns (bool) { for (uint i = 0; i < playersAddress.length; i++) { address _playerAddress = playersAddress[i]; if (!playerHasPlacedShips[_playerAddress]) { return false; } } return true; } function getWinner() public view returns (address winner) { require(isGameOver, "The game isn't over yet"); for (uint i = 0; i < playersAddress.length; i++) { address _playerAddress = playersAddress[i]; if (!destroyedPlayers[_playerAddress]) { return _playerAddress; } } // No winner return address(0); } } ``` Here is a summary of what the code does: * The game has a fixed number of players (two), each of whom can place 10 ship pieces on any coordinate. * Players take turns shooting at their opponent's grid by specifying the coordinates of a square. If a ship piece is on that square, it is destroyed. * If a player loses all of their ships, the game is over and the other player wins. * The contract uses the `SigVerifier` contract from the `Verify.sol` file, which provides functions for verifying digital signatures. * The contract emits events whenever a player takes a shot or loses all of their ships. The contract has several mappings to keep track of the state of the game. > [!NOTE] > > If you're not familiar with EVM programming, mappings might seem an odd choice to represent our data model. However, to keep gas costs down and optimize UX, we want to avoid looping through and modifying large arrays in our smart contract. Here are some of the key mappings: * `players` — a mapping of addresses to booleans indicating whether each address is a player in the game. * `ships` — a mapping of each player's address to a mapping of ship hashes to booleans. The `ships` mapping keeps track of which ship pieces each player has placed on the grid. * `destroyedShips` — a mapping of each player's address to an array of `Coordinate` structs. The `destroyedShips` mapping keeps track of which of a player's ships have been destroyed. * `playerShots` — a mapping of each player's address to a `Coordinate` struct. The `playerShots` mapping keeps track of the coordinates of the square each player has shot at during their turn. * `playerHasPlayed` — a mapping of each player's address to a boolean indicating whether that player has taken a shot during the current turn. * `playerHasPlacedShips` — a mapping of each player's address to a boolean indicating whether that player has placed all their ships on the grid. * `playerHasReportedHits` — a mapping of each player's address to a boolean indicating whether that player has reported all the hits from their opponent's shots during the current turn. The contract has several functions: * `joinGame` — allows a player to join the game and place their ships on the grid. * `takeAShot` — allows a player to take a shot at their opponent's grid. * `reportHits` — allows a player to report the hits from their opponent's shots. * `isHit` — whether a shot hits a ship and returns a boolean and the coordinate of the shot. * `destroyPlayerShip`: an internal function that adds a destroyed ship coordinate to the `destroyedShips` mapping and checks whether the player has lost all their ships. If so, the game is over. The contract also has several state variables: * `owner` — the address of the contract owner. * `NO_PLAYERS` — a constant that specifies the number of players in the game. * `NO_SHIP_PIECES` — a constant that specifies the number of ship pieces each player can place on the grid. * `playersAddress` — an array of addresses representing the players in the game. * `numberOfDestroyedPlayers` — a counter of the number of players who have lost all their ships. * `isGameOver` — a boolean indicating whether the game is over. > [!NOTE] > > Going through the [unit tests for the battleship contract](https://github.com/chainstacklabs/zk-battleship-game-hardhat/blob/main/test/Battleship.ts) will provide a lot of insight into its expected behavior, and how end users would interact with it. ## Interacting with the smart contract To be able to play a game with the smart contract, we need to create valid signatures that can be verified with Ethereum's [`ecrecover`](https://soliditydeveloper.com/ecrecover) method. It can be a little tricky to get signatures right with ethers.js, but here's one way: ```javascript Javascript theme={"system"} import { ethers, Signer } from "hardhat"; export type ShipShotProof = { signature: string; // The address of the player that shot // at this coordinate shotBy: string; }; export type Coordinate = { x: number; y: number; }; export type Ship = Array; export async function signShipCoordinates(ships: Array, signer) { let signedShips = []; for (const ship of ships) { let signedShip = []; for (const coord of ship) { let { flatSig } = await signCoordinate(coord, signer); signedShip.push(flatSig); } signedShips.push(signedShip); } return signedShips; } export async function signCoordinate(coord: Coordinate, signer: Signer) { let hashedCoord = ethers.utils.solidityKeccak256( ["uint8", "uint8"], [coord.x, coord.y] ); hashedCoord = ethers.utils.arrayify(hashedCoord); let flatSig = await signer.signMessage(hashedCoord); return { flatSig, hashedCoord }; } export async function generateShipShotProof( player: Signer, allPlayers: Array, battleshipGame: any ) { let shotReports = []; for (const playerAddress of allPlayers) { let playerShot = await battleshipGame.playerShots(playerAddress); let { flatSig } = await signCoordinate(playerShot, player); let report: ShipShotProof = { signature: flatSig, shotBy: playerAddress, }; shotReports.push(report); } return shotReports; } ``` In brief: * `signShipCoordinates(ships: Array, signer)` takes an array of ships and a `Signer` object (wallet) as arguments, and returns an array of signed ships. * `generateShipShotProof(player: Signer, allPlayers: Array, battleshipGame: any)` quickly generates a list of proofs for each reported shot coordinate. To prevent signing arbitrary messages from signing transactions, messages are prefixed with `"\x19Ethereum Signed Message:\n"` + `length of the message`. Taking a look at our SigVerifier contract, we hardcoded the length of the message to be 32. We do that because we always hash our messages and data, and the length of hashes is always 32 bytes. ### Stats for nerds Here are some screenshots of the test case running through ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/29cbde7-hardhat-test-1.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=ea0750a08a0cf32983860e7e8fb1891a) Hardhat test run 1 ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/2be1d23-hardhat-test-2.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1561ab24e824a048b1fc470960b102b3) Hardhat test run 2 ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/6e764ba-hardhat-test-3.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=7f3fb0c54a6f088fe182391c3a0054e4) Hardhat test run 3 ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/fa87b31-hardhat-test-4.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=c92cd151d79bf72d94cbee1b2c142afd) Hardhat test run 4 And gas reports: ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/99d4a27-gas-reports.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=7dc4e472af0c3cc142fa55420367fee5) Apparently `joinGame` is our most expensive function. ## Conclusion We're finally done! We've built a complete game of incomplete information on a public blockchain. While we've built a game for recreation, these concepts could easily be applied to other ideas and projects. For example, we could create an anonymous NFT marketplace, where the owners of NFTs remain private, but they can verify their identity and sign off on bids. ### Further reading > [card] [Ethereum docs: Transactions](https://ethereum.org/en/developers/docs/transactions/#whats-a-transaction) > [card] [Smart contract languages](https://ethereum.org/en/developers/docs/smart-contracts/languages/#vyper) > [card] [ERC-2098: Compact Signature Representation](https://eips.ethereum.org/EIPS/eip-2098) > [card] [ERC-1271: Standard Signature Validation Method for Contracts](https://eips.ethereum.org/EIPS/eip-1271) ## Improvements * We don't actually restrict players' ships to a board size. That doesn't seem quite practical. To do this, we'd have to somehow prove the coordinates are valid, without showing anyone. While beyond the scope of this article, it is a valid use case for [ZKPs](/docs/develop-a-battleship-game-using-zero-knowledge-concepts-on-ethereum#our-approach). I created a [circom circuit](https://gist.github.com/TobeTek/788aa89e5a483b5eeb1e7272ee1369f7) that does just that. * We could add support for more players. We'd need to add a check to prevent destroyed players from being able to play. * Using modifiers in the smart contract for tracking game state (`isTurnOver`, `isGameOver` etc.). I chose plain reverts for simplicity. * Our game doesn't yet have a UI. An interactive web UI would be a great addition! > [!NOTE] > > ### See also > > > [card] [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) > > > [card] [Uncovering the power of eth\\\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) > > > [card] [Expanding your blockchain horizons: The eth\\\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) > > > [card] [Deep dive into Merkle proofs and eth\\\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) > > > [card] [Geth vs Erigon: Deep dive into RPC methods on Ethereum clients](/docs/geth-vs-erigon-deep-dive-into-rpc-methods-on-ethereum-clients) > > > [card] [Ethereum logs tutorial series: Logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters) > > > [card] [Mastering custom JavaScript tracing for Ethereum virtual machine](/docs/mastering-custom-javascript-tracing-for-ethereum-virtual-machine) ### About the author > [card] TobeTek > > Experienced with Django, micro-services, and RESTful APIs > \ > Working on AI and ML projects > > [https://github.com/TobeTek](https://github.com/TobeTek) > > [https://www.linkedin.com/in/emmanuel-katchy](https://www.linkedin.com/in/emmanuel-katchy) # Developer Portal MCP server Source: https://docs.chainstack.com/docs/developer-portal-mcp-server Access Chainstack documentation, API references, and blockchain development guides through the Developer Portal MCP server for AI-assisted coding. > [!WARNING] > > **Deprecated.** Documentation search is now available through the unified [Chainstack MCP server](/docs/chainstack-mcp-server) at `https://mcp.chainstack.com/mcp`. The Developer Portal MCP server below will continue to work but is no longer the recommended integration. The **Developer Portal MCP server** is integrated with Chainstack's documentation platform powered by Mintlify. This MCP server provides AI models with comprehensive access to Chainstack's blockchain development resources, documentation, and guides. This is a streamable HTTP MCP server, and not an SSE connection. Streamable HTTP is the latest MCP standard overtaking SSE. ## Installation ### Cursor Just click the **Connect to Cursor** button at the top of any page to automatically install the MCP server in Cursor. ### VS Code Just click the **Connect to VS Code** button at the top of any page to automatically install the MCP server in VS Code. ### Claude Code To add the Chainstack MCP server to Claude Code, use the following command: ``` claude mcp add --transport http Chainstack-Developer-Portal https://docs.chainstack.com/mcp ``` The MCP server will be automatically configured and available in your Claude Code conversations. ### Windsurf To add the Chainstack MCP server to Windsurf: 1. Open Windsurf Settings by pressing `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux) 2. Type "Open Windsurf Settings" and select it 3. Navigate to the **Cascade** section 4. Click **"Add Custom Server +"** or edit the configuration file directly Add the following configuration to `~/.codeium/windsurf/mcp_config.json`: ```json theme={"system"} { "mcpServers": { "chainstack-developer-portal": { "serverUrl": "https://docs.chainstack.com/mcp" } } } ``` 5. Save the configuration and click **"Refresh Servers"** in the Cascade settings 6. Verify the integration by clicking the Hammer Icon in the Cascade toolbar — the Chainstack documentation search tool should appear ### Codex To add the Chainstack MCP server to OpenAI Codex, first set a dummy environment variable: ```bash theme={"system"} export PASTE_THIS_OR_ANY_DUMMY_TOKEN=paste_this_or_any_dummy_token ``` Then add the MCP server: ```bash theme={"system"} codex mcp add devportal --url https://docs.chainstack.com/mcp --bearer-token-env-var "PASTE_THIS_OR_ANY_DUMMY_TOKEN" ``` > [!NOTE] > > The Developer Portal MCP server is public and requires no authentication. The dummy token is a workaround for a [known Codex OAuth issue](https://github.com/openai/codex/issues/5588). ## Manual configuration For other AI tools that support streamable HTTP MCP, use the following URL: ``` https://docs.chainstack.com/mcp ``` This URL provides access to the complete Chainstack documentation and development resources through the Model Context Protocol. Again, do note that this is a streamable HTTP MCP connection, and not an SSE connection. Streamable HTTP is the latest MCP standard overtaking SSE. # Elysium methods Source: https://docs.chainstack.com/docs/elysium-methods Complete reference of Elysium JSON-RPC methods supported on Chainstack, with per-method availability for the eth, debug, net, web3, and arb namespaces on Elysium Testnet Global Nodes. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). Elysium is an Arbitrum Orbit chain from Kinetiq that settles to HyperEVM and uses HYPE for gas. Chainstack serves Elysium Testnet (chain ID `99801`) on [Nitro](https://github.com/OffchainLabs/nitro), the Arbitrum client. The availability below applies to Elysium Testnet on Global Nodes. ## Tracing Tracing is served through the `debug_*` namespace — `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceCall` — back to genesis. They accept these tracers: * Default native tracers — `callTracer`, `prestateTracer`, `4byteTracer`, and `noopTracer`. * Extended tracers — `flatCallTracer`, `muxTracer`, and `erc7562Tracer`. * `stylusTracer` — records the host I/O calls of Stylus (WebAssembly) contracts. * The struct/opcode logger — used when you pass no `tracer`. See [Available native tracers](/docs/limits#available-native-tracers). The Parity-style `arbtrace_*` and `trace_*` namespaces are not exposed and return `-32601`. For traces in the Parity-style flat format, use `flatCallTracer`. On Nitro, the top-level entry of a `flatCallTracer` trace, like the top-level `callTracer` frame, also carries `beforeEVMTransfers` and `afterEVMTransfers`, which list the fee payment, gas refund, and fee collection transfers. See [Debug and trace APIs](/docs/debug-and-trace-apis#elysium). For a walkthrough that reads these traces, see [Elysium: Trace internal calls and HYPE transfers](/docs/elysium-tutorial-trace-internal-calls). ## Pending transactions are accepted but never delivered Elysium has a single sequencer and no public mempool. `eth_newPendingTransactionFilter` returns a filter ID and `eth_subscribe("newPendingTransactions")` returns a subscription ID, but neither delivers a transaction, including the transactions you send through the same endpoint. `txpool_status` returns `-32601`. `txpool_content` and `txpool_inspect` return `-32601` too, because Global Nodes do not serve them on any EVM chain. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ## eth\_getProof window `eth_getProof` returns a proof only for blocks less than 127 behind the tip. A request for an older block returns `-32000 historical state is not available`. See [EVM `eth_getProof` history limits](/docs/limits#evm-eth_getproof-history-limits). ## Method availability | Method | Availability | Comment | | - | - | - | | arb\_getRawBlockMetadata | | returns `null` | | arbtrace\_block | | Parity-style trace not exposed; use `debug_*` tracers | | arbtrace\_call | | | | arbtrace\_callMany | | | | arbtrace\_filter | | | | arbtrace\_get | | | | arbtrace\_replayBlockTransactions | | | | arbtrace\_replayTransaction | | | | arbtrace\_transaction | | | | debug\_accountRange | | | | debug\_chaindbProperty | | not available on Global Nodes | | debug\_dumpBlock | | not available on Global Nodes | | debug\_getAccessibleState | | not available on Global Nodes | | debug\_getBadBlocks | | | | debug\_getModifiedAccountsByHash | | | | debug\_getModifiedAccountsByNumber | | | | debug\_getRawBlock | | | | debug\_getRawHeader | | | | debug\_getRawReceipts | | | | debug\_getRawTransaction | | | | debug\_intermediateRoots | | | | debug\_preimage | | | | debug\_printBlock | | | | debug\_storageRangeAt | | returns `-32603 method handler crashed`; use eth\_getStorageAt | | debug\_traceBadBlock | | returns `-32000 bad block not found` when the chain has no bad blocks | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | eth\_accounts | | | | eth\_blobBaseFee | | returns `null` | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_createAccessList | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | use eth\_getBalance / eth\_getCode / eth\_getTransactionCount | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getHeaderByHash | | | | eth\_getHeaderByNumber | | | | eth\_getLogs | | | | eth\_getProof | | blocks less than 127 behind the tip only — see [eth\_getProof window](#eth_getproof-window) | | eth\_getRawTransactionByBlockHashAndIndex | | | | eth\_getRawTransactionByBlockNumberAndIndex | | | | eth\_getRawTransactionByHash | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionBySenderAndNonce | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleByBlockHashAndIndex | | | | eth\_getUncleByBlockNumberAndIndex | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_hashrate | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | accepts the filter but never returns a transaction — see [Pending transactions](#pending-transactions-are-accepted-but-never-delivered) | | eth\_protocolVersion | | | | eth\_sendRawTransaction | | | | eth\_sendRawTransactionSync | | | | eth\_simulateV1 | | | | eth\_subscribe | | WSS. `newHeads` and `logs` deliver. `newPendingTransactions` subscribes but delivers nothing; `syncing` returns `-32601` | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | WSS | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | txpool\_content | | | | txpool\_inspect | | | | txpool\_status | | | | web3\_clientVersion | | | | web3\_sha3 | | | # Elysium tooling Source: https://docs.chainstack.com/docs/elysium-tooling Connect MetaMask, ethers.js, viem, web3.py, Foundry, and Hardhat to an Elysium Testnet node on Chainstack. Chain configuration, the HYPE gas token, contract verification on Blockscout, and the Elysium behaviors that affect tooling. Elysium is an Arbitrum Orbit chain from Kinetiq that settles to HyperEVM. It runs the standard EVM, so the Ethereum libraries and frameworks work against an Elysium node without modification. The differences are in the chain configuration: chain ID `99801`, HYPE as the gas token, and a Blockscout API for contract verification. ## Chain configuration Chainstack serves Elysium Testnet. Kinetiq has not published the Elysium Mainnet network details yet. | Property | Value | | - | - | | Network | Elysium Testnet | | Chain ID | `99801` | | Native gas currency | HYPE | | Decimals | `18` | | Parent chain | HyperEVM Testnet (chain ID `998`) | | RPC URL | your Chainstack [Elysium endpoint](/docs/manage-your-node#view-node-access-and-credentials) | | Multicall3 | `0xcA11bde05977b3631167028862bE2a173976CA11` | | Block explorer | [elysium.kinetiq.xyz/testnet-explorer](https://elysium.kinetiq.xyz/testnet-explorer) | | Contract verification API | `https://elysium.kinetiq.xyz/api` (Blockscout) | | Faucet | [elysium.kinetiq.xyz/testnet-faucet](https://elysium.kinetiq.xyz/testnet-faucet) | | Bridge from HyperEVM Testnet | [elysium.kinetiq.xyz/testnet-bridge](https://elysium.kinetiq.xyz/testnet-bridge) | Elysium's HYPE comes from HyperEVM through a 1:1 bridge, so you can fund an Elysium Testnet account from the faucet or by bridging testnet HYPE from HyperEVM Testnet. See [Elysium methods](/docs/elysium-methods) for per-method availability. ## MetaMask Add Elysium Testnet as a custom network. On Chainstack, get your [Elysium endpoint](/docs/manage-your-node#view-node-access-and-credentials), then in MetaMask select **Add a custom network** and fill in: * Network name — Elysium Testnet * Default RPC URL — your Chainstack Elysium endpoint * Chain ID — `99801` * Currency symbol — HYPE * Block explorer URL — `https://explorer-elysium-testnet.t.conduit.xyz` The block explorer URL is the explorer that Conduit, the chain's operator, hosts for Elysium Testnet. MetaMask builds transaction links as `/tx/`, and this explorer serves that path. The Kinetiq explorer at [elysium.kinetiq.xyz/testnet-explorer](https://elysium.kinetiq.xyz/testnet-explorer) shows the same chain but serves transactions at `/transaction/`. ## ethers.js Install [ethers.js](https://docs.ethers.org/): ```shell Shell theme={"system"} npm install ethers ``` ```javascript index.js theme={"system"} const { JsonRpcProvider, formatEther } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function main() { const network = await provider.getNetwork(); console.log("Chain ID:", network.chainId.toString()); const block = await provider.getBlockNumber(); console.log("Block:", block); const balance = await provider.getBalance("0xb9D83D298D46C4dd73618F19a2A40084Ce36476a"); console.log("Balance:", formatEther(balance), "HYPE"); } main(); ``` To receive new blocks as Elysium produces them, connect to the WSS endpoint and subscribe: ```javascript index.js theme={"system"} const { WebSocketProvider } = require("ethers"); const provider = new WebSocketProvider("CHAINSTACK_WSS_URL"); provider.on("block", (blockNumber) => { console.log("New block:", blockNumber); }); ``` ## viem Elysium is not in viem's bundled chain list, so define it with `defineChain`: > [!WARNING] > > The `elysiumTestnet` export in `viem/chains` is a different chain: Vulcan Forged's Elysium Testnet, chain ID `1338`, with LAVA as its currency. Importing it for Kinetiq's Elysium gives you the wrong currency symbol and explorer links, and `http()` without a URL falls back to the Vulcan Forged RPC. Define chain `99801` yourself. ```shell Shell theme={"system"} npm install viem ``` ```javascript index.mjs theme={"system"} import { createPublicClient, http, defineChain, formatEther } from "viem"; export const elysiumTestnet = defineChain({ id: 99801, name: "Elysium Testnet", testnet: true, nativeCurrency: { decimals: 18, name: "HYPE", symbol: "HYPE" }, rpcUrls: { default: { http: ["CHAINSTACK_NODE_URL"] } }, contracts: { multicall3: { address: "0xcA11bde05977b3631167028862bE2a173976CA11" }, }, }); const client = createPublicClient({ chain: elysiumTestnet, transport: http() }); console.log("Chain ID:", await client.getChainId()); console.log("Block:", await client.getBlockNumber()); const balance = await client.getBalance({ address: "0xb9D83D298D46C4dd73618F19a2A40084Ce36476a", }); console.log("Balance:", formatEther(balance), "HYPE"); ``` The `multicall3` entry lets `client.multicall` batch contract reads into a single `eth_call`. ## web3.py Install [web3.py](https://web3py.readthedocs.io/): ```shell Shell theme={"system"} pip install web3 ``` ```python main.py theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider("CHAINSTACK_NODE_URL")) print("Connected:", web3.is_connected()) print("Chain ID:", web3.eth.chain_id) print("Block:", web3.eth.block_number) address = Web3.to_checksum_address("0xb9d83d298d46c4dd73618f19a2a40084ce36476a") balance = web3.eth.get_balance(address) print("Balance:", web3.from_wei(balance, "ether"), "HYPE") ``` > [!NOTE] > > web3.py rejects lowercase addresses with `InvalidAddress`. Wrap any address you did not get from the library itself in `Web3.to_checksum_address()`, as in the `main.py` example. ## Foundry Install [Foundry](https://getfoundry.sh/) and pass your Chainstack endpoint with `--rpc-url`. Read chain data with `cast`: ```shell Shell theme={"system"} cast chain-id --rpc-url CHAINSTACK_NODE_URL cast block-number --rpc-url CHAINSTACK_NODE_URL cast balance 0xb9D83D298D46C4dd73618F19a2A40084Ce36476a --ether --rpc-url CHAINSTACK_NODE_URL ``` Deploy a contract with `forge create`. Without `--broadcast`, `forge create` only simulates the deployment. ```shell Shell theme={"system"} forge create src/Counter.sol:Counter \ --rpc-url CHAINSTACK_NODE_URL \ --private-key YOUR_PRIVATE_KEY \ --broadcast ``` Verify the deployed contract on the Elysium Testnet Blockscout. The API does not need a key. ```shell Shell theme={"system"} forge verify-contract DEPLOYED_CONTRACT_ADDRESS src/Counter.sol:Counter \ --chain 99801 \ --verifier blockscout \ --verifier-url https://elysium.kinetiq.xyz/api/ \ --watch ``` The explorer link that `forge verify-contract` prints leaves out the `/testnet-explorer` path. Open the verified contract at `https://elysium.kinetiq.xyz/testnet-explorer/address/DEPLOYED_CONTRACT_ADDRESS`. ## Hardhat In a [Hardhat 3](https://hardhat.org/) project that uses `@nomicfoundation/hardhat-toolbox-viem`, add Elysium Testnet to `hardhat.config.ts`. The `chainDescriptors` entry gives `hardhat verify` the address of the Elysium Testnet Blockscout API. ```typescript hardhat.config.ts theme={"system"} import { configVariable, defineConfig } from "hardhat/config"; import hardhatToolboxViem from "@nomicfoundation/hardhat-toolbox-viem"; export default defineConfig({ plugins: [hardhatToolboxViem], solidity: "0.8.28", networks: { elysiumTestnet: { type: "http", url: configVariable("ELYSIUM_RPC_URL"), accounts: [configVariable("ELYSIUM_PRIVATE_KEY")], }, }, chainDescriptors: { 99801: { name: "Elysium Testnet", blockExplorers: { blockscout: { name: "Elysium Testnet Explorer", url: "https://elysium.kinetiq.xyz/testnet-explorer", apiUrl: "https://elysium.kinetiq.xyz/api", }, }, }, }, }); ``` `configVariable` reads `ELYSIUM_RPC_URL` and `ELYSIUM_PRIVATE_KEY` from environment variables or the Hardhat keystore, which keeps the endpoint and the key out of the config file. Deploy with an Ignition module, for example `ignition/modules/Counter.ts`: ```typescript ignition/modules/Counter.ts theme={"system"} import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; export default buildModule("CounterModule", (m) => { const counter = m.contract("Counter"); return { counter }; }); ``` ```shell Shell theme={"system"} npx hardhat ignition deploy ignition/modules/Counter.ts --network elysiumTestnet npx hardhat verify blockscout --network elysiumTestnet DEPLOYED_CONTRACT_ADDRESS ``` ## Elysium behaviors that affect tooling **A HYPE transfer needs more than 21,000 gas.** Nitro adds the cost of posting the transaction to the parent chain to its gas, reported as `gasUsedForL1` in the receipt, so a plain transfer uses about 21,140 gas and `eth_estimateGas` returns more than 21,000. A transaction with a hard-coded gas limit of `21000` returns `-32000 intrinsic gas too low`. ethers.js, viem, web3.py, Foundry, and Hardhat all estimate gas when you don't set a limit. **HyperCore precompiles are not available.** Elysium does not have HyperEVM's HyperCore read precompiles (`0x0000000000000000000000000000000000000800` and up) or the CoreWriter contract (`0x3333333333333333333333333333333333333333`). A call to one of these addresses succeeds and returns empty data, as a call to any address without code does, so a contract ported from HyperEVM reverts when it decodes the result, not at the call. Kinetiq plans a HyperCore market-data precompile and an `ElysiumCoreWriter` predeploy for an ArbOS upgrade after the Mainnet launch. See [Building on Elysium](https://elysium.kinetiq.xyz/docs/building-on-elysium). **Pending transactions are never delivered.** Elysium has a single sequencer and no public mempool. `eth_subscribe("newPendingTransactions")` and `eth_newPendingTransactionFilter` return an ID but deliver nothing, so an ethers.js `provider.on("pending")` listener never fires. Watch new blocks or transaction receipts instead. See [Pending transactions are accepted but never delivered](/docs/elysium-methods#pending-transactions-are-accepted-but-never-delivered). **Block-count limits cover little time.** Elysium produces a block every 100–200 ms, and Kinetiq tunes the block time toward 100 ms, so limits defined in blocks span much less time than on Ethereum. A 10,000-block `eth_getLogs` request covers about 17 to 33 minutes of chain time, and `eth_getProof` serves state from less than 127 blocks behind the tip, about 13 to 25 seconds. Paginate log backfills accordingly. See [EVM range limits](/docs/limits#evm-range-limits) and [EVM `eth_getProof` history limits](/docs/limits#evm-eth_getproof-history-limits). # Elysium: Trace internal calls and HYPE transfers Source: https://docs.chainstack.com/docs/elysium-tutorial-trace-internal-calls Trace an Elysium transaction with debug_traceTransaction on a Chainstack node to see every internal call, the HYPE that contracts moved, the calls that reverted, and how Nitro split the fee. A transaction receipt on Elysium shows whether the transaction succeeded and which events it emitted. It doesn't list the calls that contracts made to each other, and it has no record of HYPE that a contract sends, because a HYPE transfer emits no event. `debug_traceTransaction` replays the transaction on your Elysium node and returns all of these calls as a tree. In this tutorial, you build a Node.js script that traces an Elysium Testnet transaction and prints its internal calls, the HYPE they moved, the calls that failed, and where the fee went. **TLDR:** * `debug_traceTransaction` with `callTracer` returns every internal call of an Elysium transaction, including the HYPE that contracts send to each other. * Elysium runs Nitro, so the top-level call also lists the balance changes outside EVM execution: the upfront gas payment, the refund, and the fees collected. * The script names each contract and function from the Elysium Testnet Blockscout and prints a readable call tree. * For a flat list of calls to store in a database, use `flatCallTracer`. ## Prerequisites * A Chainstack account with an Elysium Testnet node. The `debug_*` methods need a paid plan. See [Debug and trace APIs](/docs/debug-and-trace-apis#elysium). * Node.js 20 or later. ## Get an Elysium node 1. [Sign up with Chainstack](https://console.chainstack.com/). 2. [Deploy a node](/docs/manage-your-networks) on Elysium Testnet. 3. Copy the node's HTTPS endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## The example transaction This tutorial traces a token sale on Elysium Testnet, transaction [`0x6034ffc6c2209586e0787f18b0f11805c18dd432618d42c377d0b56433899508`](https://elysium.kinetiq.xyz/testnet-explorer/transaction/0x6034ffc6c2209586e0787f18b0f11805c18dd432618d42c377d0b56433899508). The sender called `sell` on the `SignalCurveHypeRouter` contract, attached no HYPE, and received HYPE for the tokens. Elysium's Global Nodes serve traces back to genesis, so you can trace this transaction or any other Elysium Testnet transaction. ## Step 1. Trace the transaction with cURL Call `debug_traceTransaction` with `callTracer`. The `onlyTopCall` option returns only the top-level call: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -X POST \ -H 'Content-Type: application/json' \ -d '{ "id": 1, "jsonrpc": "2.0", "method": "debug_traceTransaction", "params": [ "0x6034ffc6c2209586e0787f18b0f11805c18dd432618d42c377d0b56433899508", {"tracer": "callTracer", "tracerConfig": {"onlyTopCall": true}} ] }' ``` ```json JSON theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": { "beforeEVMTransfers": [ { "purpose": "feePayment", "from": "0x7c7C1dec39f30C90be787282a874BA09161b327c", "to": null, "value": "0x42e0ec072a00" } ], "afterEVMTransfers": [ { "purpose": "gasRefund", "from": null, "to": "0x7c7C1dec39f30C90be787282a874BA09161b327c", "value": "0x1f7425274d00" }, { "purpose": "feeCollection", "from": null, "to": "0x252431E84D5E22435A0c833C2220770c52F59633", "value": "0x236c913af400" }, { "purpose": "feeCollection", "from": null, "to": "0xa4B00000000000000000000000000000000000F6", "value": "0x35a4e900" } ], "from": "0x7c7c1dec39f30c90be787282a874ba09161b327c", "gas": "0xb386a", "gasUsed": "0x5f17d", "to": "0x75d98c98f92d1f5d65ca6a962f36476be34c64ae", "input": "0xff0efdf9000000000000000000000000a28c20bdd75645923bfffcb153802b2da12ded88000000000000000000000000000000000000000000056bdf765503c8c44268fa000000000000000000000000000000000000000000000000041919720ed09e230000000000000000000000007c7c1dec39f30c90be787282a874ba09161b327c000000000000000000000000000000000000000000000000000000006ac767410000000000000000000000000000000000000000000000000000000000000001", "output": "0x0000000000000000000000000000000000000000000000000423b244aa1db702", "value": "0x0", "type": "CALL" } } ``` In the top-level call: * `value` is `0x0` — the sender attached no HYPE. * `output` is the return value of `sell`, the HYPE the sender received: `0x423b244aa1db702` wei, or 0.298278008318179074 HYPE. * `beforeEVMTransfers` and `afterEVMTransfers` are Nitro fields that list the balance changes outside EVM execution. See [Where the fee went](#where-the-fee-went). For what the other fields mean, see [Reading EVM traces: callTracer and prestateTracer](/docs/reading-evm-traces-with-calltracer-and-prestatetracer). Without `onlyTopCall`, the same request returns all 87 calls of the transaction, nested. The script in the next steps turns them into a readable tree. ## Step 2. Set up the project ```shell Shell theme={"system"} mkdir elysium-trace && cd elysium-trace npm init -y npm install viem ``` ## Step 3. Write the script Create `trace-elysium-tx.mjs` and replace `YOUR_CHAINSTACK_ENDPOINT` with your Elysium node's HTTPS endpoint: ```javascript trace-elysium-tx.mjs theme={"system"} import { createPublicClient, defineChain, formatEther, http, parseAbi, toFunctionSelector, } from "viem"; const RPC_URL = "YOUR_CHAINSTACK_ENDPOINT"; const EXPLORER_API = "https://elysium.kinetiq.xyz/api/v2"; const SIGNATURE_API = "https://api.4byte.sourcify.dev/signature-database/v1/lookup"; // Nitro system addresses: the ArbOwnerPublic precompile and the pool that receives L1 posting fees const ARB_OWNER_PUBLIC = "0x000000000000000000000000000000000000006b"; const L1_PRICER_FUNDS_POOL = "0xa4b00000000000000000000000000000000000f6"; // Frame types that move HYPE. A DELEGATECALL frame repeats its parent's value without moving it. const VALUE_TRANSFER_TYPES = new Set(["CALL", "CREATE", "CREATE2", "SELFDESTRUCT"]); const elysiumTestnet = defineChain({ id: 99801, name: "Elysium Testnet", testnet: true, nativeCurrency: { decimals: 18, name: "HYPE", symbol: "HYPE" }, rpcUrls: { default: { http: [RPC_URL] } }, }); const client = createPublicClient({ chain: elysiumTestnet, transport: http() }); const txHash = process.argv[2]; if (!txHash) { console.error("Usage: node trace-elysium-tx.mjs "); process.exit(1); } async function getJson(url) { try { const response = await fetch(url); return response.ok ? await response.json() : null; } catch { return null; } } // Label and function names for an address, from the Elysium Blockscout. // A proxy also gets the functions of its implementation contracts. const contracts = new Map(); async function contractInfo(address) { const key = (address ?? "").toLowerCase(); if (!contracts.has(key)) { contracts.set(key, (async () => { const info = { label: key || "(no address)", functions: new Map() }; if (!key) return info; const details = await getJson(`${EXPLORER_API}/addresses/${key}`); if (!details?.is_contract) return info; const implementationName = details.implementations?.find((i) => i.name)?.name; info.label = details.token?.symbol ?? implementationName ?? details.name ?? key; const sources = [key, ...(details.implementations ?? []).map((i) => i.address_hash)]; for (const source of sources) { const contract = await getJson(`${EXPLORER_API}/smart-contracts/${source}`); for (const item of contract?.abi ?? []) { if (item.type === "function") info.functions.set(toFunctionSelector(item), item.name); } } return info; })()); } return contracts.get(key); } // Fallback for selectors that no verified ABI covers async function lookupSelectors(selectors) { const names = new Map(); if (selectors.size === 0) return names; const data = await getJson(`${SIGNATURE_API}?function=${[...selectors].join(",")}`); for (const [selector, matches] of Object.entries(data?.result?.function ?? {})) { const match = matches?.find((m) => m.hasVerifiedContract); if (match) names.set(selector, `${match.name.split("(")[0]}*`); } return names; } let trace; try { trace = await client.request({ method: "debug_traceTransaction", params: [txHash, { tracer: "callTracer" }], }); } catch (error) { console.error(`Cannot trace ${txHash}: ${error.details ?? error.message}`); process.exit(1); } const receipt = await client.request({ method: "eth_getTransactionReceipt", params: [txHash], }); // Flatten the call tree into rows, folding each proxy's DELEGATECALL into the call that reached the proxy const rows = []; function walk(frame, depth, parent) { const isProxyForward = frame.type === "DELEGATECALL" && parent && frame.from === parent.to && frame.input === parent.input; if (!isProxyForward) rows.push({ frame, depth, parent }); const childDepth = isProxyForward ? depth : depth + 1; for (const child of frame.calls ?? []) walk(child, childDepth, frame); } walk(trace, 0, null); function hasError(frame) { return Boolean(frame.error) || (frame.calls ?? []).some(hasError); } for (const { frame } of rows) await contractInfo(frame.to); const unknown = new Set(); const selectorOf = (frame) => (frame.input ?? "0x").slice(0, 10); for (const { frame } of rows) { const selector = selectorOf(frame); if (frame.type.startsWith("CREATE") || selector.length < 10) continue; if (!(await contractInfo(frame.to)).functions.has(selector)) unknown.add(selector); } const looked = await lookupSelectors(unknown); async function name(address) { const { label } = await contractInfo(address); return !address || label === address.toLowerCase() ? label : `${label} (${address})`; } async function describe(frame) { const info = await contractInfo(frame.to); const selector = selectorOf(frame); if (frame.type.startsWith("CREATE") || selector.length < 10) return name(frame.to); const fn = info.functions.get(selector) ?? looked.get(selector) ?? selector; return info.label === frame.to.toLowerCase() ? `${frame.to}.${fn}` : `${info.label}.${fn} (${frame.to})`; } console.log(`Transaction ${txHash}`); console.log(`From: ${receipt.from}`); console.log(`Status: ${receipt.status === "0x1" ? "success" : "failed"}`); console.log("\nCall tree (successful STATICCALLs hidden):"); for (const { frame, depth } of rows) { if (frame.type === "STATICCALL" && !hasError(frame)) continue; const value = BigInt(frame.value ?? "0x0"); let line = `${" ".repeat(depth)}${frame.type} ${await describe(frame)}`; if (value > 0n) line += ` value ${formatEther(value)} HYPE`; if (frame.error) line += ` -> ${frame.revertReason ?? frame.error}`; console.log(line); } console.log("\nInternal HYPE transfers:"); for (const { frame, depth } of rows) { const value = BigInt(frame.value ?? "0x0"); if (depth > 0 && VALUE_TRANSFER_TYPES.has(frame.type) && value > 0n) { console.log(` ${formatEther(value)} HYPE from ${await name(frame.from)} to ${await name(frame.to)}`); } } console.log("\nFailed calls:"); for (const { frame, parent } of rows) { if (!frame.error) continue; const caller = parent ? (await contractInfo(frame.from)).label : "sender"; console.log(` ${await describe(frame)} called by ${caller}: ${frame.revertReason ?? frame.error}`); } const block = BigInt(receipt.blockNumber); const [infraFeeAccount, networkFeeAccount] = await Promise.all( ["getInfraFeeAccount", "getNetworkFeeAccount"].map((functionName) => client.readContract({ address: ARB_OWNER_PUBLIC, abi: parseAbi([`function ${functionName}() view returns (address)`]), functionName, blockNumber: block, }) ) ); function feeLabel(transfer) { const to = transfer.to?.toLowerCase(); if (transfer.purpose === "feePayment") return "paid upfront for the gas limit"; if (transfer.purpose === "gasRefund") return "refund for unused gas"; if (to === L1_PRICER_FUNDS_POOL) return "L1 posting fee"; if (to === infraFeeAccount.toLowerCase() || to === networkFeeAccount.toLowerCase()) return "execution fee"; return transfer.purpose; } console.log("\nFee transfers outside EVM execution:"); for (const transfer of [...(trace.beforeEVMTransfers ?? []), ...(trace.afterEVMTransfers ?? [])]) { const party = transfer.from ?? transfer.to; console.log( ` ${transfer.purpose.padEnd(13)} ${formatEther(BigInt(transfer.value)).padEnd(22)} HYPE ${party} ${feeLabel(transfer)}` ); } ``` How the script reads the trace: * Contract names come from the Elysium Testnet Blockscout API at `https://elysium.kinetiq.xyz/api/v2` — the token symbol first, then the implementation name for a proxy, then the contract name. Function names come from the verified ABIs of the contract and of a proxy's implementations. * When no verified ABI covers a selector, the script looks it up in the [Sourcify signature database](https://4byte.sourcify.dev) and marks the name with `*`. A selector can match more than one signature, so a marked name is a likely match, not a verified one. * A proxy forwards a call to its implementation with a `DELEGATECALL` that carries the same input. The script folds that `DELEGATECALL` into the call that reached the proxy, so each proxied call takes one line. * The call tree hides a `STATICCALL` that succeeded, unless a call inside it failed. A `STATICCALL` reads state and can't change it or move HYPE. * HYPE moves only in `CALL`, `CREATE`, `CREATE2`, and `SELFDESTRUCT` frames. A `DELEGATECALL` frame repeats its parent's `value` without moving HYPE, so the script doesn't count it. * The script labels each fee transfer by its recipient. It reads the infrastructure and network fee accounts from the `ArbOwnerPublic` precompile at `0x000000000000000000000000000000000000006b`, and it recognizes the L1 pricer funds pool at `0xA4B00000000000000000000000000000000000F6`, which collects L1 posting fees. ## Step 4. Run the script ```shell Shell theme={"system"} node trace-elysium-tx.mjs 0x6034ffc6c2209586e0787f18b0f11805c18dd432618d42c377d0b56433899508 ``` ```text Output theme={"system"} Transaction 0x6034ffc6c2209586e0787f18b0f11805c18dd432618d42c377d0b56433899508 From: 0x7c7c1dec39f30c90be787282a874ba09161b327c Status: success Call tree (successful STATICCALLs hidden): CALL SignalCurveHypeRouter.sell (0x75d98c98f92d1f5d65ca6a962f36476be34c64ae) CALL ELI.transferFrom (0x5c1217c53cb0a6f848c9895c67becfb33e2fbf49) STATICCALL SignalProjectXAverageGuard.isPool (0xc8e6fab2c78b6e209ab355c248568e049dc30ad5) STATICCALL SignalCurveHypeRouter.0xc45a0155 (0x75d98c98f92d1f5d65ca6a962f36476be34c64ae) -> execution reverted CALL ELI.approve (0x5c1217c53cb0a6f848c9895c67becfb33e2fbf49) CALL SignalProjectXV2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens (0x645f45ac6b7701a53049e70c8eec608007917b36) CALL ELI.transferFrom (0x5c1217c53cb0a6f848c9895c67becfb33e2fbf49) STATICCALL SignalProjectXAverageGuard.isPool (0xc8e6fab2c78b6e209ab355c248568e049dc30ad5) STATICCALL SignalCurveHypeRouter.0xc45a0155 (0x75d98c98f92d1f5d65ca6a962f36476be34c64ae) -> execution reverted CALL UNI-V2.swap (0xa591ff46fbbd69fc82852455c6bb50451a0d492a) CALL WHYPE.transfer* (0xcd57f65c2b0e5881cfc2e609f7cd53b746e1f234) CALL ELI.approve (0x5c1217c53cb0a6f848c9895c67becfb33e2fbf49) CALL WHYPE.withdraw* (0xcd57f65c2b0e5881cfc2e609f7cd53b746e1f234) CALL SignalCurveHypeRouter (0x75d98c98f92d1f5d65ca6a962f36476be34c64ae) value 0.298278008318179074 HYPE CALL 0x7c7c1dec39f30c90be787282a874ba09161b327c value 0.298278008318179074 HYPE Internal HYPE transfers: 0.298278008318179074 HYPE from WHYPE (0xcd57f65c2b0e5881cfc2e609f7cd53b746e1f234) to SignalCurveHypeRouter (0x75d98c98f92d1f5d65ca6a962f36476be34c64ae) 0.298278008318179074 HYPE from SignalCurveHypeRouter (0x75d98c98f92d1f5d65ca6a962f36476be34c64ae) to 0x7c7c1dec39f30c90be787282a874ba09161b327c Failed calls: SignalCurveHypeRouter.0xc45a0155 (0x75d98c98f92d1f5d65ca6a962f36476be34c64ae) called by SignalProjectXAverageGuard: execution reverted SignalCurveHypeRouter.0xc45a0155 (0x75d98c98f92d1f5d65ca6a962f36476be34c64ae) called by SignalProjectXAverageGuard: execution reverted Fee transfers outside EVM execution: feePayment 0.0000735338 HYPE 0x7c7C1dec39f30C90be787282a874BA09161b327c paid upfront for the gas limit gasRefund 0.0000345837 HYPE 0x7c7C1dec39f30C90be787282a874BA09161b327c refund for unused gas feeCollection 0.0000389492 HYPE 0x252431E84D5E22435A0c833C2220770c52F59633 execution fee feeCollection 0.0000000009 HYPE 0xa4B00000000000000000000000000000000000F6 L1 posting fee ``` The script works on any Elysium Testnet transaction hash. ## Read the output ### Where the HYPE came from The call tree shows the sale step by step: 1. `sell` moves the sender's ELI tokens to the router with `transferFrom`. 2. The router swaps the ELI for WHYPE, the wrapped HYPE token, through `SignalProjectXV2Router` and a Uniswap V2-style pair. The pair sends the WHYPE to the router. 3. The router calls `withdraw` on WHYPE, and WHYPE sends 0.298278008318179074 HYPE to the router. 4. The router sends the same 0.298278008318179074 HYPE to the sender. The receipt of this transaction has 11 events, among them `Transfer` events for ELI and WHYPE and the router's own `CurveRouted` event, which reports the amount received. No event records HYPE moving from WHYPE to the router or from the router to the sender. Those two transfers are in the trace only, and the script lists them under `Internal HYPE transfers`. ### Calls that failed inside a successful transaction Both failed calls use the selector `0xc45a0155`, which is `factory()`. `SignalProjectXAverageGuard.isPool` checks whether an address is a liquidity pool by calling `factory()` on it with a low-level `staticcall`. The router has no `factory` function, so the call reverts, `isPool` returns `false`, and the token transfer continues. A failed call inside a successful transaction means that the calling contract handled the failure. When a whole transaction fails, start from the deepest call with an error, which is where the revert started. See [Reverts](/docs/reading-evm-traces-with-calltracer-and-prestatetracer#reverts). ### Where the fee went Nitro charges and distributes the transaction fee outside EVM execution and records each balance change in `beforeEVMTransfers` and `afterEVMTransfers` on the top-level call. The example transaction set a gas price of 0.12 gwei, but the receipt's `effectiveGasPrice` is the block's 0.1 gwei base fee, and every fee transfer uses the base fee: | Purpose | Account | HYPE | Calculation | | - | - | - | - | | `feePayment` | Sender | 0.0000735338 | Gas limit 735,338 × base fee, taken before execution | | `gasRefund` | Sender | 0.0000345837 | Unused gas 345,837 (735,338 − 389,501 used) × base fee | | `feeCollection` | `0x252431E84D5E22435A0c833C2220770c52F59633` | 0.0000389492 | Execution gas 389,492 (389,501 used − 9 `gasUsedForL1`) × base fee | | `feeCollection` | `0xA4B00000000000000000000000000000000000F6` | 0.0000000009 | `gasUsedForL1` 9 × base fee | The refund and the two fee collections add up to the upfront payment. The two `feeCollection` recipients: * `0x252431E84D5E22435A0c833C2220770c52F59633` — Elysium's infrastructure fee account, which is also its network fee account. `getInfraFeeAccount()` and `getNetworkFeeAccount()` on `ArbOwnerPublic` both return this address. The infrastructure fee account receives the execution gas at the minimum base fee. When the base fee exceeds the minimum, a second `feeCollection` entry pays the difference to the network fee account. In the example, the base fee equals the 0.1 gwei minimum, so the transaction has one execution fee entry. * `0xA4B00000000000000000000000000000000000F6` — Nitro's L1 pricer funds pool. It collects the L1 posting fee, the cost of posting the transaction to HyperEVM, which the receipt reports as `gasUsedForL1`. ## Store internal calls with flatCallTracer To store internal calls in a database, `flatCallTracer` returns the same calls as a flat array in the Parity trace format, one entry per call: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -X POST \ -H 'Content-Type: application/json' \ -d '{ "id": 1, "jsonrpc": "2.0", "method": "debug_traceTransaction", "params": [ "0x6034ffc6c2209586e0787f18b0f11805c18dd432618d42c377d0b56433899508", {"tracer": "flatCallTracer"} ] }' ``` The result has 87 entries. This entry is the transfer of 0.298278008318179074 HYPE from the router to the sender: ```json JSON theme={"system"} { "action": { "callType": "call", "from": "0x75d98c98f92d1f5d65ca6a962f36476be34c64ae", "gas": "0x3ceba", "input": "0x", "to": "0x7c7c1dec39f30c90be787282a874ba09161b327c", "value": "0x423b244aa1db702" }, "blockHash": "0xab38a555d6bb42acf24e3ccee3fa9bc767eb8ee9ef8e8fb0c41bb78f5c18342d", "blockNumber": 5169291, "result": { "gasUsed": "0x0", "output": "0x" }, "subtraces": 0, "traceAddress": [ 24 ], "transactionHash": "0x6034ffc6c2209586e0787f18b0f11805c18dd432618d42c377d0b56433899508", "transactionPosition": 3, "type": "call" } ``` * `traceAddress` — the call's position in the tree. `[24]` is the call at index 24, counting from 0, among the calls that the top-level call made. The top-level call has `traceAddress` `[]`. * `subtraces` — the number of calls that this call made. * `action.value` — the HYPE moved, in wei. On Elysium, the first entry, the top-level call, also carries `beforeEVMTransfers` and `afterEVMTransfers`. To trace a whole block, pass the same tracer to `debug_traceBlockByNumber` or `debug_traceBlockByHash`. ## See also * [Reading EVM traces: callTracer and prestateTracer](/docs/reading-evm-traces-with-calltracer-and-prestatetracer) — what each trace field means, and `debug_traceCall` for tracing a call before you send it. * [Elysium methods](/docs/elysium-methods) — the tracers and debug methods available on Elysium. * [Elysium tooling](/docs/elysium-tooling) — the chain configuration and libraries for Elysium Testnet. # ethers.js FallbackProvider for data reliability Source: https://docs.chainstack.com/docs/enhancing-blockchain-data-reliability-with-ethers-fallbackprovider Configure ethers.js FallbackProvider with multiple Chainstack endpoints for automatic failover and weighted provider selection in your blockchain dApp. **TLDR** * The FallbackProvider in ethers.js wraps multiple JSON-RPC providers to enhance data reliability and reduce single-node dependency. * It uses priorities, weights, and timeouts to determine consensus from multiple sources. * Configurable quorums ensure you only trust data that a majority or required number of providers agree on. * This method helps avoid inconsistent results from forks, latency issues, or out-of-sync nodes. ## Main article In the world of blockchain technology, where decentralization and transparency are paramount, ensuring the reliability and consistency of data is crucial. Sometimes, you might use different endpoints from different providers. However, they may be subject to network latency, temporary forks, or being out of sync with the rest of the network. Enter the `FallbackProvider`, a powerful tool provided by the `ethers.js` library. This utility is designed to enhance the reliability and accuracy of blockchain data by aggregating responses from multiple providers and forming a consensus. By leveraging redundancy and a consensus mechanism, the `FallbackProvider` mitigates the risks associated with relying on a single node, ensuring that the data you interact with is consistent and up-to-date. In this tutorial, we will dive into the inner workings of the `FallbackProvider`, exploring its configuration options, consensus mechanisms, and error-handling capabilities. ## The need for redundancy Blockchain networks are designed to be decentralized and distributed, with multiple nodes contributing to the maintenance and validation of the ledger. Relying solely on a single node to retrieve blockchain data can be risky, as it introduces potential points of failure and inconsistencies. One of the primary risks of depending on a single node is the possibility of temporary forks or network partitions. In such scenarios, different nodes may have divergent views of the blockchain's state, leading to inconsistent data being returned. Additionally, individual nodes may experience network latency, causing delays in propagating the latest blockchain data to other nodes. Nodes can occasionally become out of sync with the rest of the network, potentially providing outdated or incorrect information. To mitigate these risks and ensure the reliability and consistency of blockchain data, it is crucial to embrace redundancy by using multiple nodes. By querying multiple nodes and aggregating their responses, the chances of encountering inconsistent or inaccurate data are significantly reduced. Employing redundancy in blockchain data retrieval offers several benefits: 1. **Increased reliability**: With multiple nodes serving as data sources, the system becomes more resilient to individual node failures or temporary outages. If one node becomes unresponsive or returns erroneous data, the system can seamlessly fall back to other nodes, ensuring uninterrupted access to reliable blockchain data. 2. **Improved data accuracy**: By aggregating responses from multiple nodes, inconsistencies or temporary forks can be detected and resolved through a consensus mechanism. This mechanism ensures that the data retrieved is consistent with most nodes, reducing the likelihood of interacting with outdated or incorrect information. 3. **Load balancing**: Distributing queries across multiple nodes helps to balance the load and avoid overwhelming any single node with excessive requests. This load balancing can improve overall system performance and responsiveness. 4. **Fault tolerance**: Redundancy introduces fault tolerance into the system, as the failure or misconfiguration of a single node does not necessarily lead to complete system failure. The system can gracefully degrade and continue operating by leveraging the remaining functional nodes. > [!NOTE] > > Learn how to build a simple load balancer in JavaScript by reading [Make your DApp more reliable with Chainstack](/docs/make-your-dapp-more-reliable-with-chainstack). ## The ethers `FallbackProvider` The `ethers.js` library, a popular JavaScript library for interacting with Ethereum-based blockchains, provides the `FallbackProvider` tool. This utility is designed to enhance the reliability and consistency of blockchain data retrieval by leveraging redundancy and a consensus mechanism across multiple providers. At its core, the `FallbackProvider` acts as a wrapper around a set of individual providers, such as Ethereum JSON-RPC providers. When querying for blockchain data, the `FallbackProvider` sends requests to multiple providers simultaneously and aggregates their responses. It then applies a configurable consensus mechanism to determine the most reliable and consistent result. The `FallbackProvider` operates by distributing requests across multiple providers, each with its own priority, weight, and stall timeout settings. These settings allow the `FallbackProvider` to prioritize and weight the responses from different providers based on their expected reliability and responsiveness. When a request is made to the `FallbackProvider`, it sends the request to all configured providers concurrently. As responses start arriving, the `FallbackProvider` evaluates them against a pre-defined quorum value, which specifies the minimum number of providers that must agree on the same result for it to be considered a consensus. If the quorum is met, meaning that the required number of providers return the same result, the `FallbackProvider` considers this the consensus result and returns it to the caller. However, if the quorum is unmet, the `FallbackProvider` employs a fallback mechanism to handle potential inconsistencies or failures. The fallback mechanism prioritizes providers based on their assigned weights and stall timeouts. If a provider fails to respond within its configured stall timeout, the `Fallback provider` disregards its response and moves to the next highest-priority provider. This process continues until the quorum is met or all providers have been exhausted. By aggregating responses from multiple providers and applying a consensus mechanism, the `FallbackProvider` helps mitigate the risks of relying on a single node for blockchain data. It ensures that the data returned is consistent with most providers, reducing the likelihood of interacting with outdated, incorrect, or divergent information. ### Deploy a Chainstack node Before diving into the implementation, ensure you have a Chainstack account. Deploying a node on Chainstack is essential for accessing and interacting with blockchain networks. Deploy 3 or mix Chainstack and public nodes for a good demonstration. Choose different geographical regions for each to maximize network uptime and reduce latency, which is crucial for a robust and efficient blockchain application. > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) ## Project setup To set up the JavaScript project and integrate the `FallbackProvider` from the `ethers.js` library, we'll need to follow these steps: First, create a new directory for your project and initialize a new Node.js project by running `npm init` in your terminal. This will create a `package.json` file, which will manage your project's dependencies. > [!NOTE] > > Check out [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project) to learn how to manage Node projects. Next, install the required dependencies by running the following command: ``` npm install ethers dotenv ``` This will install the `ethers.js` library, which provides the `FallbackProvider` functionality, and the `dotenv` package, which allows us to load environment variables from a `.env` file. After the installation is complete, create a new file named `.env` in the root directory of your project. This file will store the URLs of the JSON-RPC providers you want to use with the `FallbackProvider`. Add the following lines to the `.env` file, replacing the placeholders with the actual provider URLs: ``` RPC_1="YOUR_NODE_URL" RPC_2="YOUR_NODE_URL" RPC_3="YOUR_NODE_URL" ``` You can add as many providers as you need, but we'll use three endpoints for this example. ## The full code Now that the project is setup create a new file named `index.js` and paste the following code: ```javascript Javascript theme={"system"} const { ethers } = require('ethers'); require("dotenv").config(); const url1 = process.env.RPC_1; const url2 = process.env.RPC_2; const url3 = process.env.RPC_3; const stallTimeout = 2000; // Example timeout const quorum = 2; // Quorum needed for consensus // Define JSON RPC Providers without the network object const provider1 = new ethers.JsonRpcProvider(url1); const provider2 = new ethers.JsonRpcProvider(url2); const provider3 = new ethers.JsonRpcProvider(url3); // Create a FallbackProvider instance with a specified quorum const fallbackProvider = new ethers.FallbackProvider([ { provider: provider1, priority: 2, // Will prioritize this provider weight: 3, // Assuming provider1 is the most reliable stallTimeout }, { provider: provider2, priority: 1, weight: 2, stallTimeout: 1500 // Adjusted based on expected responsiveness }, { provider: provider3, priority: 1, weight: 1, stallTimeout: 2500 // Adjusted for a provider that might be slower } ], quorum); async function getBlockNumber() { try { const blockNumber = await fallbackProvider.getBlockNumber(); console.log(`Latest block: ${blockNumber}`); } catch (error) { console.error("Error fetching block number. Error:", error.message); console.log("Attempting to restart the program..."); // Optionally, implement a retry mechanism or other logic here // For example, wait for a few seconds before retrying setTimeout(getBlockNumber, 3000); // Retry after 3 seconds } } // Call getBlockNumber every 3 seconds console.log('Fetching latest block from various providers...') setInterval(getBlockNumber, 3000); ``` ## Code breakdown The code is designed to interact with blockchain networks through Ethereum's JSON RPC API using multiple providers for enhanced reliability and performance. It uses the `ethers.js` library, a popular choice for interacting with the Ethereum blockchain and its ecosystems. Let's break down how this code works, focusing on its key components and functionalities: ### Setup and configuration ```jsx jsx theme={"system"} const { ethers } = require('ethers'); require("dotenv").config(); ``` This part imports the required dependencies. The `ethers` object is imported from the `ethers.js` library, which provides the functionality for interacting with Ethereum-based blockchains, including the `FallbackProvider`. The `dotenv` package is loaded, which allows us to load environment variables from the `.env` file. ### RPC URLs ```jsx jsx theme={"system"} const url1 = process.env.RPC_1; const url2 = process.env.RPC_2; const url3 = process.env.RPC_3; ``` Here, we retrieve the URLs of the JSON-RPC providers from the environment variables stored in the `.env` file. These URLs will be used to create instances of the `JsonRpcProvider`. ### Configuration constants ```jsx jsx theme={"system"} const stallTimeout = 2000; // Example timeout const quorum = 2; // Quorum needed for consensus ``` These lines define two constants: `stallTimeout` and `quorum`. `stallTimeout` is set to 2000 milliseconds (2 seconds), which determines the maximum time the `FallbackProvider` will wait for a response from a provider before considering it unresponsive. `quorum` is set to 2, specifying that at least two providers must return the same result to be considered a consensus. ### JSON RPC providers ```jsx jsx theme={"system"} // Define JSON RPC Providers without the network object const provider1 = new ethers.JsonRpcProvider(url1); const provider2 = new ethers.JsonRpcProvider(url2); const provider3 = new ethers.JsonRpcProvider(url3); ``` In this section, we create instances of the `ethers.JsonRpcProvider` using the URLs retrieved from the environment variables. These providers will be used as the underlying data sources for the `FallbackProvider`. ### Fallback provider ```jsx jsx theme={"system"} // Create a FallbackProvider instance with a specified quorum const fallbackProvider = new ethers.FallbackProvider([ { provider: provider1, priority: 2, weight: 3, // Assuming provider1 is the most reliable stallTimeout }, { provider: provider2, priority: 1, weight: 2, stallTimeout: 1500 // Adjusted based on expected responsiveness }, { provider: provider3, priority: 1, weight: 1, stallTimeout: 2500 // Adjusted for a provider that might be slower } ], quorum); ``` Here, we create an instance of the `ethers.FallbackProvider` by passing an array of provider configurations and the desired `quorum` value. Each provider configuration includes the following properties: * `provider`: The instance of the `JsonRpcProvider` to be used. * `Priority`: A numeric value representing the provider's priority. Higher values indicate higher priority. * `weight`: A numeric value representing the weight or reliability of the provider. Higher values indicate higher reliability. * `stallTimeout`: The maximum time (in milliseconds) to wait for a response from the provider before considering it unresponsive. In this example, `provider1` is given the highest priority (2) and weight (3), assuming it is the most reliable provider. `provider2` and `provider3` have lower priorities (1) and weights (2 and 1, respectively), with adjusted `stallTimeout` values based on their expected responsiveness. ### `getBlockNumber` function ```jsx jsx theme={"system"} async function getBlockNumber() { try { const blockNumber = await fallbackProvider.getBlockNumber(); console.log(`Latest block: ${blockNumber}`); } catch (error) { console.error("Error fetching block number. Error:", error.message); console.log("Attempting to restart the program..."); // Optionally, implement a retry mechanism or other logic here // For example, wait for a few seconds before retrying setTimeout(getBlockNumber, 3000); // Retry after 3 seconds } } ``` The `getBlockNumber` function is an asynchronous function that fetches the latest block number from the `fallbackProvider`. Inside the `try` block, it calls `fallbackProvider.getBlockNumber()` and awaits the result. The console logs the latest block number if the block number is fetched successfully. If an error occurs, it catches the error and logs the error message. It also logs a message indicating that it's attempting to restart the program and includes a comment suggesting that a retry mechanism or other logic could be implemented here. This example uses `setTimeout` to call `getBlockNumber` again after a 3-second delay. ### Periodic execution ```jsx jsx theme={"system"} // Call getBlockNumber every 3 seconds console.log('Fetching latest block from various providers...') setInterval(getBlockNumber, 3000); ``` Finally, this part logs a message to the console indicating it fetches the latest block from various providers. It then sets an interval using `setInterval` to call the `getBlockNumber` function every 3 seconds, continuously fetching and logging the latest block number. By combining the `FallbackProvider` with multiple JSON-RPC providers and configuring their priorities, weights, and stall timeouts, this code demonstrates how to enhance the reliability and consistency of blockchain data retrieval. The `FallbackProvider` will aggregate responses from the configured providers, apply the consensus mechanism based on the specified quorum, and handle failures or timeouts by falling back to other providers. ### Error handling and retries The error handling within `getBlockNumber` uses a `try-catch` block to catch any exceptions. Suppose an error occurs within the `FallbackProvider`, meaning there is a disagreement in the consensus or the providers with higher priority and weights fail. In that case, it logs the message and attempts to restart the function after a 3-second delay, demonstrating a simple retry mechanism. ## Understanding the `FallbackProvider` configuration The `FallbackProvider` instance is created by passing an array of provider configurations and the desired quorum value to the `ethers.FallbackProvider` constructor. This array allows you to specify multiple providers and configure their behavior within the `FallbackProvider`. Each provider configuration in the array is an object with the following properties: 1. `provider`: This is an instance of the `JsonRpcProvider` you want to include in the `FallbackProvider`. In this example, `provider1`, `provider2`, and `provider3` are instances created earlier using the provider URLs from the environment variables. 2. `priority`: This numeric value represents the provider's priority. Higher values indicate a higher priority. When the `FallbackProvider` needs to select a provider for a request, it will prioritize providers with higher priority values. In the example, `provider1` has the highest priority of 2, while `provider2` and `provider3` have a lower priority of 1. 3. `weight`: This numeric value represents the provider's weight or reliability. Higher values indicate a higher level of reliability. The `FallbackProvider` uses these weights when determining the consensus result. In the example, `provider1` has the highest weight of 3, indicating that it is considered the most reliable provider, while `provider2` weights 2, and `provider3` weights 1. 4. `stallTimeout`: This value specifies the maximum time (in milliseconds) that the `FallbackProvider` will wait for a response from the provider before considering it unresponsive or "stalled." If the provider doesn't respond within this time, the `FallbackProvider` will disregard its response and move on to the next provider. In the example, `provider1` uses the default `stallTimeout` value of 2000 milliseconds (2 seconds), `provider2` has a shorter `stallTimeout` of 1500 milliseconds (1.5 seconds), and `provider3` has a longer `stallTimeout` of 2500 milliseconds (2.5 seconds). By configuring these properties for each provider, you can fine-tune the behavior of the `FallbackProvider` based on your specific requirements and your providers' expected reliability and responsiveness. The `quorum` parameter passed to the `FallbackProvider` constructor specifies the minimum number of providers agreeing on the same result to be considered a consensus. In this example, the `quorum` is set to 2, meaning that at least two providers must return the same result for the `FallbackProvider` to consider it a valid consensus. Users can customize the configuration of the `FallbackProvider` by adjusting the properties of the provider objects in the array and the `quorum` value. For instance, if you have a provider that is known to be highly reliable, you can assign it a higher priority and weight. If you expect a provider to respond slower, you can increase its `stallTimeout` value accordingly. Additionally, you can adjust the `quorum` value based on the level of consensus you require for your application. ## Conclusion In this tutorial, we explored the `FallbackProvider` from the `ethers.js` library, a powerful tool designed to enhance the reliability and consistency of blockchain data retrieval. We learned the importance of redundancy when interacting with blockchain networks and how relying solely on a single node can introduce risks of inconsistent or inaccurate data due to factors like network latency, temporary forks, or out-of-synchrony nodes. The `FallbackProvider` addresses these challenges by leveraging multiple JSON-RPC providers and employing a consensus mechanism. By aggregating responses from multiple providers, prioritizing them based on their expected reliability, and applying a configurable quorum, the `FallbackProvider` ensures that the data retrieved is consistent with most providers, mitigating the risks associated with relying on a single source. We walked through the setup process, including installing dependencies, configuring environment variables, and creating instances of the `JsonRpcProvider` and `FallbackProvider`. We also explored the code implementation, breaking down each component and explaining the configuration options such as provider priority, weight, and stall timeout. By embracing redundancy and consensus mechanisms like the `FallbackProvider`developers can build more robust and fault-tolerant applications that interact with blockchain networks, ensuring reliable and accurate data retrieval, even in the face of potential inconsistencies or failures. # Solana: Enhancing SPL token transfers with retry logic Source: https://docs.chainstack.com/docs/enhancing-solana-spl-token-transfers-with-retry-logic Add retry logic to Solana SPL token transfers using transaction confirmation polling and exponential backoff for reliable token delivery on Chainstack. > **Part of the [How to land transactions on Solana](/docs/solana-how-to-land-transactions) guide.** **TLDR:** * This guide adds simple retry logic to the SPL token transfer process on Solana, building on the [previous code](/docs/transferring-spl-tokens-on-solana-typescript). * We wrap transaction submissions in a loop, retrying up to a set limit (`MAX_RETRY_FUNCTION`) with a short delay to handle transient failures (e.g., `TransactionExpiredBlockheightExceededError`). * We still include priority fees to bump transaction priority, and we can also configure more robust backoff or advanced retry patterns (exponential backoff, circuit breakers, etc.) if needed. * This approach significantly improves reliability, making the code more tolerant of temporary network congestion or node issues. ## Main article The [previous article](/docs/transferring-spl-tokens-on-solana-typescript) explored transferring SPL tokens on the Solana blockchain using TypeScript. While the provided code successfully demonstrated the token transfer process, it lacked retry logic—a crucial aspect for handling potential failures and retrying failed transactions in blockchain applications. Retry logic helps mitigate the impact of transient network issues or node overload by automatically retrying failed transactions multiple times. This approach increases the likelihood of successful execution, ensuring better reliability and improving the overall user experience. This guide will extend the existing codebase by adding a simple retry logic to the token transfer process. We will discuss why retries are needed, explore different error scenarios, and implement a straightforward retry mechanism. Incorporating retry logic will make our application more resilient to temporary network problems, laying the foundation for further improvements in robustness and performance. > [!NOTE] > > Read [Transferring SPL tokens on Solana: A step-by-step TypeScript tutorial](/docs/transferring-spl-tokens-on-solana-typescript) to find the full code base and learn how it works. ## Understanding the need for retry logic In blockchain applications, transactions can sometimes fail for various reasons, including network congestion, node overload, or other transient issues. One specific error that can occur when working with the Solana blockchain is the `TransactionExpiredBlockheightExceededError`. This error occurs when a transaction is repeatedly forwarded to subsequent block leaders without being included in any block until the associated blockhash, or recent blockhash, expires. The blockhash is a critical component of a transaction on Solana, acting as a reference to a recent block to ensure the transaction is processed promptly and to prevent double-spending. While it is currently impossible to completely eliminate this issue due to the non-deterministic nature of the current Solana mainnet scheduler, implementing retry logic can significantly mitigate the impact of such failures and improve the overall reliability of your blockchain application. Retrying failed transactions is crucial in blockchain applications for several reasons: 1. **Network resilience**: Blockchain networks can experience temporary disruptions, congestion, or node failures. Retrying transactions after a failure increases the chances of successful execution, ensuring that your application remains functional despite transient network issues. 2. **User experience**: In user-facing applications, failed transactions can lead to frustration and a poor user experience. By automatically retrying failed transactions, you can provide a seamless experience for your users, minimizing the need for manual intervention or retries. 3. **Data consistency**: In applications that involve critical data or financial transactions, failed transactions can result in data inconsistencies or financial losses. Retry logic helps ensure that transactions are eventually executed, maintaining data integrity and preventing potential losses. 4. **Fault tolerance**: Implementing retry logic is fundamental to building robust, fault-tolerant applications. By anticipating and handling failures gracefully, your application becomes more resilient and can recover from unexpected situations. By understanding the importance of retry logic and the potential issues that can arise in blockchain applications, you can take proactive steps to enhance the reliability and robustness of your Solana-based applications, providing a better user experience and ensuring the integrity of your data and transactions. ## Implementing the retry logic This section will focus on implementing a simple retry logic for the token transfer process. The approach involves wrapping the transaction send logic in a loop with a maximum number of retry attempts. We'll handle different error types, introduce a delay between retries, and log the retry attempts for better visibility. > [!NOTE] > > Find the original code base here: [Transferring SPL tokens on Solana: A step-by-step TypeScript tutorial](/docs/transferring-spl-tokens-on-solana-typescript#transferring-spl-tokens-code-walkthrough). ### Overview of the approach * Use a `for` loop to control the number of retry attempts * Set a maximum retry count using an environment variable (`MAX_RETRY_FUNCTION`) * Catch and handle errors within the loop * Implement a delay or backoff strategy between retries * Log or report retry attempts and errors Before starting, add two environment variables named `MAX_RETRY_FUNCTION` and `MAX_RETRY_WEB3JS` to your `.env` file and set the maximum number of retries. ``` MAX_RETRY_WEB3JS=10 # Max retries for the Web3.js instance MAX_RETRY_FUNCTION=5 # Max retries of the Retry function logic ``` The `MAX_RETRY_WEB3JS` variable controls the maximum number of retries performed by the Web3.js library when sending a transaction, while `MAX_RETRY_FUNCTION` controls the maximum number of retries for the custom retry logic implemented in our code. ## Code snippets and explanation Let's walk through the code snippet by snippet and explain what each part does in our exploration. This SPL transfer implementation includes priority fees, edit the micro lamports you want to add in this line: ```typescript TypeScript theme={"system"} const PRIORITY_RATE = 12345; // MICRO_LAMPORTS ``` ### Wrapping the transaction send logic in a retry loop ```typescript TypeScript theme={"system"} const retryCount = Number(process.env.MAX_RETRY_FUNCTION); // Default retry count set to 5 for (let attempt = 1; attempt <= retryCount; attempt++) { try { // Transaction send logic goes here ... return;// Exit the function on a successful transaction } catch (error) { // Handle errors and retry logic ... } } ``` In this snippet, we first retrieve the maximum retry count from the `MAX_RETRY_FUNCTION` environment variable. Then, we use a `for` loop to control the number of retry attempts. If the transaction is successful, we exit the function using the `return` statement. If an error occurs, we handle it in the `catch` block. ### Handling different error types ```typescript TypeScript theme={"system"} catch (error) { console.error(`Attempt ${attempt} failed with error: ${error}`); if (attempt === retryCount) { // Last attempt failed, throw the error throw new Error(`Transaction failed after ${retryCount} attempts.`); } // Additional error handling or logging can be added here ... } ``` We log the current retry attempt and the error message in the catch block. If it's the last attempt (`attempt === retryCount`), we throw the error, effectively terminating the retry loop. Depending on your specific requirements, you can add error handling or logging logic here. ### Implementing a delay or backoff strategy between retries ```typescript TypeScript theme={"system"} // Wait for 2 seconds before retrying await new Promise((resolve) => setTimeout(resolve, 2000)); ``` Introduce a delay or backoff strategy between retry attempts to avoid overwhelming the network or the Solana node with rapid retries. In this example, we use a simple 2-second delay (`setTimeout`) wrapped in a `Promise` to pause execution before the next retry. Based on your application's needs, you can adjust the delay duration or implement more advanced backoff strategies, such as exponential backoff. ### Logging or reporting retry attempts ```typescript TypeScript theme={"system"} console.log(`Attempt ${attempt}: Starting Token Transfer Process`); ... console.error(`Attempt ${attempt} failed with error: ${error}`); ``` To provide better visibility and debugging capabilities, we log the current retry attempt at the beginning of each iteration and log the error message with the attempt number in case of failure. By incorporating these code snippets and explanations, you can implement a simple retry logic for your token transfer process on the Solana blockchain. This retry logic will help improve your application's reliability and resilience by automatically retrying failed transactions up to a specified maximum number of attempts, with a delay between each retry to avoid overwhelming the network. ## Customizing the retry logic While the implemented retry logic provides a simple and effective mechanism for handling transient failures, several potential enhancements can further augment the reliability and performance of your Solana blockchain application. These enhancements warrant consideration and evaluation based on your application's requirements and constraints. ### Adaptive delay or backoff strategy The current implementation employs a fixed 2-second delay between retry attempts. While this approach is suitable for various scenarios, it may be advantageous to consider dynamically adjusting the delay based on network conditions, the number of retry attempts, or the specific error encountered. One widely adopted strategy is exponential backoff, in which the delay between retries increases exponentially with each failed attempt. This approach can reduce the load on the network during periods of high congestion and provide the network with time to recuperate. Alternatively, adaptive delays could be incorporated based on real-time network metrics, such as the current transaction confirmation time or the number of pending transactions in the mempool (for EVM chains). Monitoring these metrics can adjust the delay accordingly, balancing retry frequency and network load. ### Advanced retry patterns The current implementation uses a simple retry pattern, where all failed transactions are retried up to a maximum number of attempts. However, more advanced retry patterns may be considered, contingent upon the application's requirements and the nature of the errors encountered. One such pattern is the Circuit Breaker pattern, which introduces a temporary pause in retries if a certain threshold of consecutive failures is reached. This can be advantageous in scenarios where immediately retrying after multiple failures is unlikely to succeed, allowing the network or the application to recover before attempting further retries. Another pattern is the Bulkhead pattern, which limits the number of concurrent retries to prevent overwhelming the system or network resources. This can be particularly beneficial in applications with a high volume of transactions or when dealing with resource-intensive operations. ## The full code Here, you can find the full implementation of SPL Token transfer plus retry logic. To learn how to set up and run the project, check out [Transferring SPL tokens on Solana: A step-by-step TypeScript tutorial](/docs/transferring-spl-tokens-on-solana-typescript). ```typescript TypeScript theme={"system"} import { getOrCreateAssociatedTokenAccount, createTransferInstruction, } from "@solana/spl-token"; import { Connection, PublicKey, TransactionMessage, VersionedTransaction, Keypair, ParsedAccountData, ComputeBudgetProgram } from "@solana/web3.js"; import bs58 from "bs58"; import "dotenv/config"; // Fetches the number of decimals for a given token to accurately handle token amounts. async function getNumberDecimals( mintAddress: PublicKey, connection: Connection ): Promise { const info = await connection.getParsedAccountInfo(mintAddress); const decimals = (info.value?.data as ParsedAccountData).parsed.info .decimals as number; console.log(`Token Decimals: ${decimals}`); return decimals; } // Initializes a Keypair from the secret key stored in environment variables. Essential for signing transactions. function initializeKeypair(): Keypair { const privateKey = new Uint8Array(bs58.decode(process.env.PRIVATE_KEY!)); const keypair = Keypair.fromSecretKey(privateKey); console.log( `Initialized Keypair: Public Key - ${keypair.publicKey.toString()}` ); return keypair; } // Sets up the connection to the Solana cluster, utilizing environment variables for configuration. function initializeConnection(): Connection { const rpcUrl = process.env.SOLANA_RPC!; const connection = new Connection(rpcUrl, { commitment: "confirmed", wsEndpoint: process.env.SOLANA_WSS, }); // Redacting part of the RPC URL for security/log clarity console.log(`Initialized Connection to Solana RPC: ${rpcUrl.slice(0, -32)}`); return connection; } async function main() { const retryCount = Number(process.env.MAX_RETRY_FUNCTION); // Default retry count set to 5 for (let attempt = 1; attempt <= retryCount; attempt++) { try { console.log(`Attempt ${attempt}: Starting Token Transfer Process`); const connection = initializeConnection(); const fromKeypair = initializeKeypair(); const destinationWallet = new PublicKey( "CzNGm14nMopjGYyycMbWqEF2e1aEHcJLKk2CHw9BiZwC" ); const mintAddress = new PublicKey( "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" ); // Config priority fee and amount to transfer const PRIORITY_RATE = 12345; // MICRO_LAMPORTS const transferAmount = 0.01; // This will need to be adjusted based on the token's decimals // Instruction to set the compute unit price for priority fee const PRIORITY_FEE_INSTRUCTIONS = ComputeBudgetProgram.setComputeUnitPrice({microLamports: PRIORITY_RATE}); console.log("----------------------------------------"); const decimals = await getNumberDecimals(mintAddress, connection); let sourceAccount = await getOrCreateAssociatedTokenAccount( connection, fromKeypair, mintAddress, fromKeypair.publicKey ); console.log(`Source Account: ${sourceAccount.address.toString()}`); let destinationAccount = await getOrCreateAssociatedTokenAccount( connection, fromKeypair, mintAddress, destinationWallet ); console.log( `Destination Account: ${destinationAccount.address.toString()}` ); console.log("----------------------------------------"); const transferAmountInDecimals = transferAmount * Math.pow(10, decimals); const transferInstruction = createTransferInstruction( sourceAccount.address, destinationAccount.address, fromKeypair.publicKey, transferAmountInDecimals ); let latestBlockhash = await connection.getLatestBlockhash("confirmed"); const messageV0 = new TransactionMessage({ payerKey: fromKeypair.publicKey, recentBlockhash: latestBlockhash.blockhash, instructions: [PRIORITY_FEE_INSTRUCTIONS, transferInstruction], }).compileToV0Message(); const versionedTransaction = new VersionedTransaction(messageV0); versionedTransaction.sign([fromKeypair]); const txid = await connection.sendTransaction(versionedTransaction, { skipPreflight: false, maxRetries: Number(process.env.MAX_RETRY_WEB3JS), preflightCommitment: "confirmed", }); console.log(`Transaction Submitted: ${txid}`); const confirmation = await connection.confirmTransaction( { signature: txid, blockhash: latestBlockhash.blockhash, lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, }, "confirmed" ); if (confirmation.value.err) { throw new Error("🚨Transaction not confirmed."); } console.log( `Transaction Successfully Confirmed! 🎉 View on SolScan: https://solscan.io/tx/${txid}` ); return; // Success, exit the function } catch (error) { console.error(`Attempt ${attempt} failed with error: ${error}`); if (attempt === retryCount) { // Last attempt failed, throw the error throw new Error(`Transaction failed after ${retryCount} attempts.`); } // Wait for 2 seconds before retrying await new Promise((resolve) => setTimeout(resolve, 2000)); } } } main() ``` ## Conclusion In this article, we extend the functionality of our Solana SPL token transfer application by implementing simple retry logic. By adding this retry mechanism, we significantly improve our application's reliability and resilience. Failed transactions are automatically retried multiple times, mitigating the impact of transient network issues or node overload. We started by understanding the importance of retry logic in blockchain applications, particularly in the Solana blockchain and the `TransactionExpiredBlockheightExceededError` context. We then implemented the retry logic by wrapping the transaction send logic in a loop with maximum retry attempts, handling different error types, introducing a delay or backoff strategy between retries, and logging retry attempts for better visibility. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Error reference Source: https://docs.chainstack.com/docs/error-reference Reference of HTTP, WebSocket, and JSON-RPC error codes returned by Chainstack nodes — what each code means and how to fix it. **TLDR:** * HTTP `4xx` = a problem with your request (auth, payload size, rate limit). HTTP `5xx` = a problem on the server side; the same client request might succeed on retry. * WebSocket close code `1006` is a transport-level disconnect — your code must reconnect. * JSON-RPC error codes follow the [JSON-RPC 2.0 spec](https://www.jsonrpc.org/specification#error_object); `-32000` is a node-implementation error and the message tells you what's wrong. ## HTTP status codes | Code | Message | Cause and fix | | - | - | - | | `400` | Bad Request | Your request body is malformed. Validate the JSON and the JSON-RPC envelope. | | `401` | Authorization Required | Wrong endpoint key or password. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). | | `404` | Not Found | The endpoint path doesn't exist on this node. Verify the URL. If the URL is correct, [contact support](https://chainstack.com/contact/) — the gateway may be unhealthy. | | `404` | `DEPTH_ZERO_SELF_SIGNED_CERT` | TLS cert chain issue on the gateway. [Contact support](https://chainstack.com/contact/). | | `413` | Request Entity Too Large | Default request body cap is **1 MB**. Split the payload (e.g., paginate `eth_getLogs` ranges) or batch fewer JSON-RPC calls per request. | | `429` | Too Many Requests | You hit an RPS or connection limit. See [Throughput guidelines](/docs/limits) and [Requests per second (RPS) plan limits](/docs/rps-plan-limits). Consider [Unlimited Node](/docs/unlimited-node) for sustained high-throughput workloads. | | `499` | Client Closed Request | Your client closed the connection before the node responded — usually a too-short client-side timeout. Increase the timeout. | | `500` | Internal Server Error | Gateway or node-side fault. Retry with backoff. If it persists, [contact support](https://chainstack.com/contact/). | | `500` | `ECONNREFUSED` | Same — gateway or node refused the connection. [Contact support](https://chainstack.com/contact/). | | `502` | Bad Gateway | Node or upstream is unreachable from the gateway. Often resolves on retry; if not, [contact support](https://chainstack.com/contact/). | | `503` | Service Temporarily Unavailable | Node is under resource pressure. Retry with backoff. If sustained, [contact support](https://chainstack.com/contact/). | | `504` | Gateway Timeout | Node didn't respond within the gateway timeout. Heavy methods (traces, wide-range `eth_getLogs`) are common causes — paginate or move to a [Dedicated Node](/docs/dedicated-node). | ## WebSocket close codes | Code | Message | Cause and fix | | - | - | - | | `1006` | Abnormal closure (often `ECONNRESET`) | The shared gateway dropped the connection (NGINX reload, idle timeout). Implement automatic reconnect in your client. See [Handle real-time data with WebSockets](/docs/handle-real-time-data-using-websockets-with-javascript-and-python) for a reconnect pattern. | ## JSON-RPC error codes These follow the [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification#error_object). The node returns them inside the response body even when the HTTP status is `200`. | Code | Message | Cause and fix | | - | - | - | | `-32700` | Parse error | The server couldn't parse your JSON. Validate the request body. | | `-32600` | Invalid Request | The JSON is valid but the JSON-RPC envelope is malformed (missing `jsonrpc`, `method`, or `id`). | | `-32601` | Method not found | The method isn't enabled on this node or isn't supported on this protocol. Check the protocol's [methods reference](/docs/protocols-tooling-introduction) and confirm the [debug/trace APIs](/docs/debug-and-trace-apis) are enabled if you need them. | | `-32602` | Invalid params | Parameter type, count, or value is wrong. Check the per-method reference in [API reference](/reference). | | `-32603` | Internal error | Server-side JSON-RPC error. Usually wraps a more specific `-32000` error below. | | `-32000` | Server error | Implementation-defined error from the node client. The `message` field has the details — for example `missing trie node`, `nonce too low`, `execution reverted`. See [EIP-1474](https://eips.ethereum.org/EIPS/eip-1474) for common Ethereum-side codes. | | `-32012` | Scan aborted | A Solana account-scan method (`getProgramAccounts`, `getTokenAccountsByOwner`, `getTokenAccountsByDelegate`, `getSupply`) was stopped. If the scan exceeded its execution-time or memory limit, narrow the query — for example with `dataSize` or `memcmp` filters on `getProgramAccounts`. If the message says `scan abandoned after waiting`, the scan waited too long in the node's queue and never ran — retry with backoff. See [Solana scan method limits](/docs/limits#solana-scan-method-limits). | | `-32612` | Custom tracers are disabled by default | You requested a tracer that isn't enabled on this node. Custom raw-JavaScript tracers need a [Dedicated Node](/docs/dedicated-node); native tracers beyond the defaults, such as `flatCallTracer`, are enabled per protocol (currently Ethereum and Polygon). See [Custom tracers on EVMs](/docs/limits#custom-tracers-on-evms). | ### Common `-32000` messages | Message contains | What it means | Where to look | | - | - | - | | `missing trie node` | You queried historical state on a full node. | [EVM node returns "Missing trie node"](/docs/evm-node-returns-missing-trie-node) | | `nonce too low` | Transaction's nonce has already been mined. | Use the latest confirmed nonce from `eth_getTransactionCount(... "latest")`. | | `replacement transaction underpriced` | You tried to replace a pending tx without raising the gas price enough. | Bump the gas price by at least the `pricebump` threshold (default 10%). | | `execution reverted` | Smart contract reverted. | Decode the revert reason; check input parameters and contract preconditions. | | `Only replay-protected (EIP-155) transactions allowed over RPC` | Transaction was signed without a `chainId`. | Add the `chainId` field when signing. See [EIP-155](https://eips.ethereum.org/EIPS/eip-155). | ## See also * [Throughput guidelines](/docs/limits) * [Best practices for error handling in API requests](/docs/best-practices-for-error-handling-in-api-requests) * [EVM node returns "Missing trie node"](/docs/evm-node-returns-missing-trie-node) * [Fix Ethereum's filter not found error](/docs/understanding-ethereums-filter-not-found-error-and-how-to-fix-it) # Ethereum Dencun: Rundown with examples Source: https://docs.chainstack.com/docs/ethereum-dencun-rundown-with-examples Explore Ethereum Dencun upgrade features including EIP-4844 blob transactions, transient storage opcodes, and new precompile changes with code examples. **TLDR:** * Dencun brings several EIPs that streamline both execution and consensus layers, especially reducing costs for rollups (EIP-4844) and introducing transient storage for more efficient contract logic (EIP-1153). * Block data can now reference the beacon chain (EIP-4788) and handle data blobs separately for \~18 days (EIP-4844), which cuts down on rollup fees. * Consensus updates (EIPs 7044, 7045, 7514) simplify validator exits, extend attestation windows, and limit validator churn. * Other highlights include new EVM opcodes for memory copying (EIP-5656), restricting SELFDESTRUCT usage (EIP-6780), and offering a `BLOBBASEFEE` opcode (EIP-7516). ## Main article The Dencun upgrade activates on the mainnet at [epoch 269568](https://beaconcha.in/epoch/269568), so here's a quick rundown on all the EIPs included in the hardfork. The EIPs here follow the same order as they are in the meta [EIP-7569: Hardfork Meta - Dencun](https://eips.ethereum.org/EIPS/eip-7569) — an EIP that lists all the EIPs of the hardfork & other hardfork details. ## EIP-1153: Transient storage opcodes Execution layer. [EIP-1153 link](https://eips.ethereum.org/EIPS/eip-1153). This EIP adds a new type to storage: transient storage. With the EIP-1153 activation on the network, there are now three types of storage (previously two): * temporary memory storage — byte-level storage that persists data during a function execution and is then discarded * transient storage — data persists within one transaction (across all function executions) and is then discarded * permanent storage — data persists across transactions & blocks The transient storage opcodes are `TSTORE` and `TLOAD`. Having no transient storage contributed to extra gas consumption quirks in Ethereum; the most well known one being users charged extra for interacting with the contracts implementing OpenZeppelin's Reentrancy Guard. In practice, you will pay less on the execution level when interacting with the contracts that have functions that add no state change, like, again Reentrancy Guard. Check out [this nice deep dive into EIP-1153](https://medium.com/@organmo/demystifying-eip-1153-transient-storage-faeabbadd0d). ## EIP-4788: Beacon block root in the EVM Execution layer. [EIP-4788 link](https://eips.ethereum.org/EIPS/eip-4788). EVM can now see the parent consensus layer (beacon chain) block root. Practically, running an [eth\_getBlockByNumber | Ethereum](/reference/ethereum-getblockbynumber) after Dencun adds `parentBeaconBlockRoot` in the block details: ```javascript Javascript theme={"system"} "baseFeePerGas": "0x17357a9e30", "blobGasUsed": "0x0", "difficulty": "0x0", "excessBlobGas": "0x4c80000", "extraData": "0x34353131353565", "gasLimit": "0x1c9c380", "gasUsed": "0x9ee9e4", "hash": "0x632c2cf1d7d980760507f679d5bd3f07f1e767f63e28b193cbb2efbc86c95e53", "logsBloom": "0x42014...", "miner": "0x0c06b6d4ec451987e8c0b772ffcf7f080c46447a", "mixHash": "0xab9d05372e79c60cf348746f5772c9b28aad1164a1736611584b97080dca4999", "nonce": "0x0000000000000000", "number": "0x538e0e", "parentBeaconBlockRoot": "0x877cfcbcc3624e070c89287db70841834b9284dc135d674921e11a4c8fc68784", // <-- HERE "parentHash": "0x54ed6b5344a035b71cce3bd96ad84283fd2b56f63c76f8d3aab8e52974e7c6ba", "receiptsRoot": "0x3378df8d38bead81edb3f081f1f63a77a32f6e1f872c275ed8907aef59d0ba02", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0xb7fd", "stateRoot": "0xfece204505a4cd70af610900fb5d116c291407c55d24d066177738edcf4c6431", "timestamp": "0x65f15304", "totalDifficulty": "0x3c656d23029ab0" ``` ## EIP-4844: Shard Blob Transactions Consensus layer. Execution layer. [EIP-4844 link](https://eips.ethereum.org/EIPS/eip-4844). This is your favorite one that's been covered multiple times by everyone, so here's a TLDR and a practical example. **TLDR** EIP-4844 makes rollup transactions cheaper because the EVM originally wasn't designed to execute the rollup batches and store them on the execution layer as part of a smart contract state. This is where the majority of your rollup transaction fees used to go to — to execute & store the data on the execution layer as part of the contract state. So EIP-4844 removes this "quirk" and implements as a sane version — rollup batches can now be submitted as a special transaction type as pure data not executable by the EVM and the data is stored on the consensus layer for roughly 18 days. These are called data blobs or "sidecars" attached to blocks. This makes submitting & keeping the rollup data on Ethereum much cheaper and consequently the rollup transaction fees cheaper. Let's now a have a practical walkthrough and get the data. (On Sepolia where the Dencun upgrade is already live). **Walkthrough with Arbitrum Sepolia** Check out the batches of transactions rolled up that Arbitrum is sending to Ethereum: [Rollup batches](https://sepolia.arbiscan.io/batches). Pick a batch. For example, [batch 90169](https://sepolia.arbiscan.io/batch/90169?isnitro=true). See that the batch's L1 block number is [5475939](https://sepolia.etherscan.io/block/5475939). Now let's do an [eth\_getBlockByNumber | Ethereum](/reference/ethereum-getblockbynumber) on block `5475939` to get the block's `parentBeaconBlockRoot` as discussed previously: ```shell Shell theme={"system"} curl --request POST \ --url https://ethereum-sepolia.core.chainstack.com/1fc2ff7e068591f6b44db1a454232d3d \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "eth_getBlockByNumber", "params": [ "5475939", false ] } ' | jq -r '.result.parentBeaconBlockRoot' ``` Use the `parentBeaconBlockRoot` to identify that it's [slot 4548475](https://sepolia.beaconcha.in/slot/0xbb7ef08755c4f394fd2e2cb325ac2228aa933d8a581e8e19e26f31f610ab118b) on the consensus layer. Since it's parent, we need the next slot: [slot 4548476](https://sepolia.beaconcha.in/slot/4548476). Use the slot number in a [Retrieve blobs by block ID](/reference/getblobsbyblockid) call to the consensus layer: ```shell Shell theme={"system"} curl -X 'GET' \ 'https://ethereum-sepolia.core.chainstack.com/beacon/1fc2ff7e068591f6b44db1a454232d3d/eth/v1/beacon/blob_sidecars/4548476' \ -H 'accept: application/json' ``` The response is the rolled up batch 90169 of Arbitrum Sepolia transactions as referenced in block 5475939 as a blob. Note that the blob lives for about 18 days, so make sure you use your own values. For a detailed walkthrough and explanation, see [Blob transactions the hard way](/docs/blob-transactions-the-hard-way). ## EIP-5656: MCOPY - Memory copying instruction Execution layer. [EIP-5656 link](https://eips.ethereum.org/EIPS/eip-5656). Basically a new EVM operation that's also efficient & saves gas costs: where the EVM used to do `MSTORE` & `MLOAD`, the same thing can now be done with `MCOPY`. This depends on the smart contract developer implementation however. ## EIP-6780: SELFDESTRUCT only in same transaction Execution layer. [EIP-6780 link](https://eips.ethereum.org/EIPS/eip-6780). `SELFDESTRUCT`, originally introduced as an understandable house cleaning opcode for developers — to remove the no longer needed code from the Ethereum state, actually made metamorphic contracts possible through the use of `CREATE2` & `SELFDESTRUCT`. You can deploy a contract, destroy the code, and then deploy a different contract to the same address. [Example implementation](https://ethereum-blockchain-developer.com/110-upgrade-smart-contracts/12-metamorphosis-create2/). [Vitalik's thoughts on the issue](https://hackmd.io/@vbuterin/selfdestruct). This EIP makes the use of `SELFDESTRUCT` much more limited: * it can either be called with the full set of instructions (recovering funds to the target and deleting contract code) within the contract creation transaction * OR it's limited to only recovering the funds and NOT deleting the contract code ## EIP-7044: Perpetually Valid Signed Voluntary Exits Consensus layer. [EIP-7044 link](https://eips.ethereum.org/EIPS/eip-7044). The EIP simplifies the process of exiting from staking positions for users doing non-custodial delegated staking. Before EIP-7044, users in non-custodial delegated staking arrangements depended on the process required a voluntary exit message to be signed by the validator's signing key, which is typically controlled by the validator operator. Pre-signed voluntary exits were valid only for the current and previous consensus layer fork versions, creating a dependency on the validator operator to process exit requests in a timely manner. After EIP-7044 activation, signed voluntary exit messages become perpetually valid, eliminating the need for them to be re-signed after future consensus layer upgrades. This change removes the uncertainty and reliance on validator operators for executing exit requests. Stakers now have more autonomy and assurance that they can exit their positions and access their funds at any time, without worrying about the validity of their pre-signed exit messages being affected by future network upgrades. ## EIP-7045: Increase Max Attestation Inclusion Slot Consensus layer. [EIP-7045 link](https://eips.ethereum.org/EIPS/eip-7045). This EIP extends the timeframe within which validator attestations can be included in a block on the consensus layer. Previously, attestations had to be included within the window of 1 epoch (approximately 6.4 minutes, given that an epoch consists of 32 slots and each slot is 12 seconds). This was considered too narrow of a window. EIP-7045 expands the window from 1 epoch to 2 epochs. ## EIP-7514: Add Max Epoch Churn Limit Consensus layer. [EIP-7514 link](https://eips.ethereum.org/EIPS/eip-7514). EIP-7514 adds a max epoch churn limit to regulate the number of validators that can join or leave the network within a given epoch. It caps the churn limit at 8 validators per epoch to prevents the state-size bloat and the potential strain on network resources as the number of active validators increases. The churn limit, before this EIP, was variable with a minimum value of four and increased as more validators joined, but with EIP-7514, it's now fixed, preventing the active validator set from growing too rapidly. ## EIP-7516: BLOBBASEFEE opcode Execution layer. [EIP-7516 link](https://eips.ethereum.org/EIPS/eip-7516). Tightly related to EIP-4844 (see above), this is a simple opcode `BLOBBASEFEE` allows contracts to manage data costs dynamically, especially for rollups. `BLOBBASEFEE` only costs 2 gas to execute and provides the value of the blob base-fee directly from the block header. Track the [eth\_blobBaseFee](https://github.com/ethereum/go-ethereum/pull/29140) implementation in Go Ethereum. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # EIP-7702: delegate an EOA and batch transactions Source: https://docs.chainstack.com/docs/ethereum-eip-7702-tutorial Use EIP-7702 set-code transactions (type 0x04, Pectra) to give an Ethereum EOA smart-contract code: delegate to a contract, batch calls atomically, sponsor gas, read and revoke the delegation. Verified viem examples on Chainstack. **TLDR:** * EIP-7702 (live since the Pectra upgrade, May 7, 2025) lets an externally owned account (EOA) temporarily run a smart contract's code through a new transaction type, `0x04`. * You sign an authorization naming a contract; the network writes a 23-byte delegation designator — `0xef0100` followed by the contract address — into the EOA's code. `eth_getCode` then returns that designator instead of `0x`. * The delegate runs in the EOA's own storage and balance context, which unlocks batching, gas sponsorship, and session keys without moving funds to a new address. * All examples below use [viem](https://viem.sh/docs/eip7702) and run against a Chainstack node on Sepolia (chain ID `11155111`), where Pectra has been live since March 5, 2025. ## What is EIP-7702 EIP-7702, "Set code for EOAs," is the account-abstraction feature shipped in Ethereum's Pectra upgrade. It introduces a new [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) transaction type, `0x04` (the set-code transaction), that carries an `authorization_list`. Each authorization lets an EOA delegate its code to a smart contract — so the EOA can do things only contracts could before (batch multiple calls atomically, let someone else pay gas, enforce custom signing rules) while keeping the same address, balance, and history. Unlike [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337), which runs through a separate mempool and bundlers, EIP-7702 is a native transaction type — you send it to any standard JSON-RPC endpoint. ## How EIP-7702 works A set-code transaction carries an authorization list. Each authorization is a signed tuple: | Field | Meaning | | | | - | - | - | - | | `chain_id` | The chain the authorization is valid on, or `0` to make it valid on every EVM chain that supports EIP-7702. | | | | `address` | The contract to delegate to. Set it to the zero address to revoke. | | | | `nonce` | Must match the authorizing account's nonce at validation time. | | | | `y_parity`, `r`, `s` | A secp256k1 signature over \`keccak256(0x05 | | rlp(\[chain\_id, address, nonce]))\`. | When a valid authorization is processed, the EVM writes a **delegation designator** to the EOA's code — exactly 23 bytes: ```text theme={"system"} 0xef0100 || <20-byte contract address> ``` The `0xef` prefix is a banned opcode ([EIP-3541](https://eips.ethereum.org/EIPS/eip-3541)), so clients treat the designator as a pointer rather than executable code. Two consequences matter in practice: * The delegate executes in the **EOA's** storage and balance context — inside the contract, `address(this)` is the EOA's address. * The delegation **persists across transactions** until you change it. It is not undone at the end of the block. > [!NOTE] > > The authorization signature uses the magic byte `0x05`, which is different from the transaction type byte `0x04`. viem and other libraries handle this for you. ### Self-sponsor vs relayer There are two ways to submit a set-code transaction: * **Self-sponsor** — the EOA both signs the authorization and sends the transaction. Because the transaction consumes the EOA's nonce, the authorization's nonce must be `transaction.nonce + 1`. viem handles this off-by-one automatically when you pass `executor: 'self'`. * **Relayer (sponsored)** — a separate account pays gas and submits the transaction; the EOA only signs the authorization. The authorization's nonce equals the EOA's current nonce. ## Prerequisites You need an Ethereum node endpoint on a network where Pectra is live. For development, use Sepolia. **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy an Ethereum Sepolia node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Then install viem (version 2.x has stable EIP-7702 support): ```bash theme={"system"} npm install viem ``` ## Delegate an EOA to a contract This self-sponsored flow signs an authorization and sends the type-`0x04` transaction from the same EOA. Point the transport at your Chainstack Sepolia endpoint. ```javascript index.js theme={"system"} import { createWalletClient, createPublicClient, http } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { sepolia } from "viem/chains"; const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY"); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const walletClient = createWalletClient({ account, chain: sepolia, transport: http(NODE_URL) }); const publicClient = createPublicClient({ chain: sepolia, transport: http(NODE_URL) }); // The contract whose code the EOA will run (deploy your own; see "Batch transactions" below) const DELEGATE = "0xYOUR_DELEGATE_CONTRACT"; // 1. Sign the authorization. executor: 'self' sets authorization.nonce = tx.nonce + 1 const authorization = await walletClient.signAuthorization({ account, contractAddress: DELEGATE, executor: "self", }); // 2. Send the type-0x04 set-code transaction const hash = await walletClient.sendTransaction({ authorizationList: [authorization], to: account.address, }); console.log("set-code tx:", hash); ``` After the transaction confirms, its receipt `type` is `eip7702`, and the EOA now carries the delegation designator. ## Read and revoke a delegation Read the EOA's code with [eth\_getCode](/reference/ethereum-getcode) — a delegated account returns the 23-byte designator instead of `0x`: ```javascript index.js theme={"system"} const code = await publicClient.getCode({ address: account.address }); console.log(code); // 0xef0100ca11bde05977b3631167028862be2a173976ca11 // ^^^^^^ prefix ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ delegate address ``` To revoke, sign an authorization for the zero address and send another set-code transaction. The EOA's code resets to empty: ```javascript index.js theme={"system"} const revoke = await walletClient.signAuthorization({ account, contractAddress: "0x0000000000000000000000000000000000000000", executor: "self", }); await walletClient.sendTransaction({ authorizationList: [revoke], to: account.address }); const after = await publicClient.getCode({ address: account.address }); console.log(after); // 0x (delegation removed) ``` > [!NOTE] > > Revoking removes the code pointer, but it does **not** clear the EOA's storage. State written by a previous delegate remains in the account's storage slots. ## Batch transactions from an EOA The headline EIP-7702 use case is executing several calls atomically — for example `approve` and `transfer` in a single transaction. Delegate to a minimal batch executor: ```solidity BatchCallDelegation.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract BatchCallDelegation { struct Call { bytes data; address to; uint256 value; } function execute(Call[] calldata calls) external payable { // Runs in the EOA's storage context: add require(msg.sender == address(this)) // in production to prevent arbitrary callers from spending the EOA's balance. for (uint256 i = 0; i < calls.length; i++) { Call memory call = calls[i]; (bool success, bytes memory result) = call.to.call{value: call.value}(call.data); if (!success) { assembly { revert(add(result, 32), mload(result)) } } } } } ``` Then delegate and execute the batch in one transaction with `writeContract`, which also accepts an `authorizationList`: ```javascript index.js theme={"system"} import { createWalletClient, http, encodeFunctionData } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { sepolia } from "viem/chains"; const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY"); const walletClient = createWalletClient({ account, chain: sepolia, transport: http("YOUR_CHAINSTACK_ENDPOINT") }); const erc20Abi = [ { name: "approve", type: "function", inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ type: "bool" }] }, { name: "transfer", type: "function", inputs: [{ name: "to", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ type: "bool" }] }, ]; const batchAbi = [ { name: "execute", type: "function", inputs: [{ name: "calls", type: "tuple[]", components: [{ name: "data", type: "bytes" }, { name: "to", type: "address" }, { name: "value", type: "uint256" }] }], outputs: [] }, ]; // 1. Authorize the deployed BatchCallDelegation contract const authorization = await walletClient.signAuthorization({ account, contractAddress: "0xYOUR_BATCH_EXECUTOR", executor: "self", }); // 2. Build the calls: approve, then transfer const calls = [ { to: "0xTOKEN", value: 0n, data: encodeFunctionData({ abi: erc20Abi, functionName: "approve", args: ["0xSPENDER", 1000000n] }) }, { to: "0xTOKEN", value: 0n, data: encodeFunctionData({ abi: erc20Abi, functionName: "transfer", args: ["0xRECIPIENT", 1000000n] }) }, ]; // 3. One atomic transaction: delegate + run the batch on the EOA's own address const hash = await walletClient.writeContract({ abi: batchAbi, address: account.address, authorizationList: [authorization], functionName: "execute", args: [calls], }); console.log("batch tx:", hash); ``` Once the EOA is delegated, later calls do not need to re-authorize — the code is already set until you revoke it. ## Sponsor gas with a relayer In the relayer pattern, a separate account pays gas and submits the transaction while the EOA only signs the authorization. Omit `executor: 'self'` and put the relayer on the wallet client: ```javascript index.js theme={"system"} import { createWalletClient, http } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { sepolia } from "viem/chains"; const relay = privateKeyToAccount("0xRELAY_PRIVATE_KEY"); // pays gas const eoa = privateKeyToAccount("0xEOA_PRIVATE_KEY"); // delegates, pays nothing const walletClient = createWalletClient({ account: relay, chain: sepolia, transport: http("YOUR_CHAINSTACK_ENDPOINT") }); // EOA signs the authorization (no executor: 'self') const authorization = await walletClient.signAuthorization({ account: eoa, contractAddress: "0xYOUR_DELEGATE_CONTRACT" }); // Relay submits the set-code transaction and pays the gas const hash = await walletClient.sendTransaction({ authorizationList: [authorization], to: eoa.address }); ``` ## Security considerations EIP-7702 hands a contract control over your account's storage and balance. By mid-2025 there were already real losses — a \$1.54M batch-transaction phishing attack and a \$146K drainer variant — so treat delegation with care. > [!WARNING] > > Only delegate to contracts you have audited or fully trust. A malicious or buggy delegate can drain the EOA, because it runs with the account's full balance and storage access. * **Storage collisions** — changing delegation does not clear storage. If a new delegate uses a storage slot differently than the previous one, it reads corrupted data. Use [ERC-7201](https://eips.ethereum.org/EIPS/eip-7201) namespaced storage in any contract meant to be a delegate. * **Broken `tx.origin` checks** — a delegated EOA can run contract logic while still being `tx.origin`, so `require(tx.origin == msg.sender)` no longer proves "called by an EOA." Replace such guards with an explicit reentrancy guard. * **Constructors do not run** — delegation never calls the contract's constructor. Initialize through an explicit `initialize()` guarded against re-initialization. * **Bundle initialization** — if you split delegation and initialization into two transactions, an attacker can front-run the init. Encode the init call in the same set-code transaction's `data`. * **`chain_id = 0`** — an authorization signed with chain ID `0` is valid on every EIP-7702 chain. Only use it deliberately. See the [ethereum.org EIP-7702 guidelines](https://ethereum.org/roadmap/pectra/7702/) and [OpenZeppelin's EOA delegation docs](https://docs.openzeppelin.com/contracts/5.x/eoa-delegation) for production patterns. ## Networks EIP-7702 is available wherever Pectra has activated. | Network | Chain ID | Pectra activation | | - | - | - | | Ethereum mainnet | `1` | May 7, 2025 | | Sepolia | `11155111` | Mar 5, 2025 | | Hoodi | `560048` | Mar 2025 | Chainstack serves Ethereum mainnet, Sepolia, and Hoodi — see [Ethereum API reference](/reference/ethereum-getting-started). ## Frequently asked questions ### What is the EIP-7702 delegation designator? It is the 23-byte value `0xef0100` followed by the 20-byte delegate contract address, written to the EOA's code when a valid authorization is processed. `eth_getCode` returns it for a delegated account; a plain EOA returns `0x`. ### How do I revoke an EIP-7702 delegation? Send another set-code transaction with an authorization whose `address` is the zero address (`0x0000000000000000000000000000000000000000`). The EOA's code resets to empty. Note that storage written by the previous delegate is not cleared. ### What is the difference between self-sponsor and relayer mode? In self-sponsor mode the EOA signs the authorization and sends its own transaction, so `authorization.nonce` must be `tx.nonce + 1` (viem's `executor: 'self'` handles this). In relayer mode a different account pays gas and submits the transaction, and `authorization.nonce` equals the EOA's current nonce. ### Can I use ethers.js or viem for EIP-7702? viem 2.x has first-class, stable support (`signAuthorization`, `authorizationList` on `sendTransaction`/`writeContract`), which is what this guide uses. ethers added support more recently; viem is the most mature today. ### Is EIP-7702 live on mainnet? Yes — it shipped with Pectra on Ethereum mainnet on May 7, 2025, and on Sepolia on March 5, 2025. Use Sepolia for development. ### Does a delegation expire? No. The delegation designator persists across transactions and blocks until you change it or revoke it with a new set-code transaction. A single signed authorization delegates indefinitely. ## Related resources * [Ethereum API reference](/reference/ethereum-getting-started) — endpoints, methods, and networks * [Ethereum tooling](/docs/ethereum-tooling) — ethers.js, viem, and framework setup * [eth\_getCode](/reference/ethereum-getcode) — read an account's code (and its delegation designator) * [eth\_sendRawTransaction](/reference/ethereum-sendrawtransaction) — broadcast signed transactions * [Ethereum Dencun: rundown with examples](/docs/ethereum-dencun-rundown-with-examples) — the previous upgrade's features * [EIP-7702 specification](https://eips.ethereum.org/EIPS/eip-7702) — the canonical spec * [viem EIP-7702 documentation](https://viem.sh/docs/eip7702) — full API reference # Ethereum: How to analyze pending blocks Source: https://docs.chainstack.com/docs/ethereum-how-to-analyze-pending-blocks Analyze pending Ethereum blocks and mempool transactions using eth_getBlockByNumber with the pending tag through a Chainstack node for MEV research. **TLDR:** * **What is a pending block?** It’s the block a node (whether proposer or not) believes will be next in line for inclusion on the chain. A proposer’s pending block is built from their local mempool and is what they’ll broadcast. A non-proposer’s pending block, however, is only a “best guess” since the proposer might have a different mempool or prioritization strategy. * **How does Geth create it?** Up to version 1.14.0, Geth continuously managed a pending block in the background, updating it as new transactions arrived. Now, Geth builds a pending block on demand, caches it briefly (2 seconds by default), then discards or updates it when a new block is published or if a reorg happens. * **Why do transaction sets differ?** Nodes prioritize local transactions first (if they meet gas requirements) and then sort remote ones by their effective gas tip. The block proposer might see different transactions altogether and order them differently based on fees, local config, or simply having a newer view of the mempool. * **What does the code show?** By comparing the “pending” block to the finalized block with the same block number, you can see which transactions made it and how their positions changed. In practice, you’ll often find: * Some transactions in the pending block don’t appear in the finalized block. * The finalized block may include additional transactions. * Transaction orders may differ because the proposer’s actual block can diverge from your local pending snapshot. ## Main article Overall, monitoring the pending block gives insights into potential arbitrage or flash loan opportunities, but it’s never a hard guarantee that your transactions will be included exactly as you see them. In the Ethereum blockchain, understanding the concept of a pending block is crucial for seizing arbitrage opportunities, executing flash loans, and managing risks. Monitoring pending blocks allows users to identify large transactions or price discrepancies across decentralized exchanges. When a user queries the pending block through a node client, several processes occur under the hood to assemble and manage this block. Since Geth stays [the most popular Ethereum execution client](https://www.ethernodes.org/), let's explore how Geth specifically constructs the pending block. ## Introduction The pending tag of blocks allows developers to get information on the transactions expected to be included in the next block. According to the [JSON-RPC specification](https://github.com/ethereum/execution-apis/blob/main/src/schemas/block.yaml) for Ethereum clients, there are five block tags that the clients must use: 1. **Earliest**. Refers to the lowest numbered block available to the client. 2. **Pending**. Refers to the next block anticipated by the client, built atop the latest and including transactions typically sourced from the local mempool. 3. **Latest**. Points to the most recent block in the client's observed canonical chain, subject to potential reorganizations even under normal conditions. 4. **Safe**. Represents the latest block secure from reorganizations under assumptions of an honest majority and specific synchronicity conditions. 5. **Finalized**. Denotes the most recent crypto-economically secure block, immune to reorganizations except under manual intervention coordinated by the community. It must be noted that if the node is a block proposer, then the pending block is the one the node intends to broadcast later. This pending block is built using the latest transactions from the local mempool, prioritized, and validated according to the consensus rules. On the other hand, if the node is not a block proposer (whether it is a validator or a non-validator), the pending block still consists of transactions from the local mempool. However, these transactions are included based on the node’s expectations of what might be in the next block, but there is no guarantee that these transactions will be included. **The actual inclusion of transactions depends on the block proposer who might have a different mempool state and transaction selection criteria.** ## Geth implementation Let’s explore the pending block on the example of the Geth execution client. Recently, [the client has been updated](https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0) and a pending block is created upon request (whenever this block tag is used) and cached for a period set in the pendingTTL (time-to-live) property of the miner ([currently, it’s set to 2 sec](https://github.com/ethereum/go-ethereum/blob/master/miner/pending.go)). Before the update, the pending block was managed continuously in the background. Geth would keep updating the pending block as new transactions arrived in the mempool, and the state of the blockchain changed. To check which client version you are connected to, you can use the [web3\_clientVersion RPC method](/reference/ethereum-clientversion). Let’s examine the lifecycle of the pending block before the update has been introduced (Geth \< 1.14.0). The component of the Geth client that is responsible for managing the pending block is [the miner](https://github.com/ethereum/go-ethereum/blob/v1.13.15/miner/worker.go). After receiving the request to interact with a pending block, the Geth checks if a pending block is already available and up-to-date. If it is not available, the validator fetches, validates, sorts pending transactions in the local mempool, and then includes them in the pending block. From now on, it generates and maintains the pending block continuously. If a pending block is available, the worker ensures it is updated with any new transactions or state changes that have occurred. Here are the scenarios that can occur to an existing pending block: 1. **Update**. When new transactions arrive in the mempool, they are validated, sorted, and included in the pending block. 2. **Discarding**. When a new block is built and added to the blockchain or a blockchain reorganization occurs, the current pending block may become invalid due to changes in the state. A new pending block is generated based on the new blockchain state. To better understand the construction of a pending block, we need to examine how transactions are filtered and ordered in the mempool (or txpool, as it's called in Geth). ### Mempool When transactions are added to the mempool, they undergo several [validation](https://github.com/ethereum/go-ethereum/blob/master/core/txpool/validation.go) steps to ensure they meet network conditions and node-specific configurations. Two relevant gas checks related to the article's topic are listed below. GasFeeCap ≥ GasTipCap and GasTipCap ≥ MinGasTip Once validated, transactions are dynamically filtered and sorted to maintain the mempool's efficiency and relevance. **Filtering** Transactions remain in the mempool if their gas fee cap exceeds the current base fee. Additionally, the effective gas tip must be greater than or equal to the minimum gas tip specified in the node configuration. GasFeeCap ≥ BaseFee and EffectiveGasTip ≥ MinGasTip EffectiveGasTip = min(GasFeeCap - BaseFee, GasTipCap) **Sorting** Transactions in the mempool are primarily sorted by their gas price, specifically their effective gas tip, which is the difference between the gas fee cap and the base fee or the gas tip cap, whichever is lower. > [!NOTE] > > ### EIP-1559 > > Gas fee cap and gas tip cap were introduced with EIP-1559 transactions. Legacy transactions, also known as Type-0 transactions, do not have these properties and only specify a gas price. These legacy transactions are sorted [by gas price alone](https://github.com/ethereum/go-ethereum/blob/7cfff30ba3a67de767a9b2a7405b91f120873d10/core/types/tx_legacy.go#L101), without the need for calculating an effective gas tip. For transactions from the same account, they are sorted by nonce to ensure they can be executed sequentially. If necessary, the time when the transaction was first seen can also be considered to help order transactions with the same gas price. ### Pending block When requesting a pending block, transactions are sourced from the mempool. Geth employs filtering and sorting logic mentioned the mempool section to ensure that pending transactions adhere to the current network conditions and node configuration. [Local transactions](https://github.com/ethereum/go-ethereum/blob/master/miner/worker.go#L415) are given the highest priority and are included in the block before considering remote ones, regardless of their effective gas tip, as long as it exceeds the minimum gas tip set in the node configuration. > [!NOTE] > > ### Local transactions > > Local transactions in Geth are those originating from addresses [explicitly added to the node's configuration](https://geth.ethereum.org/docs/fundamentals/command-line-options) or transactions submitted directly by the node itself. ## Code walkthrough Now let's have a hands-on walkthrough in Python. Here's what we are going to do: 1. Check the version of the node we are connected to. 2. Fetch a pending block and its number. 3. Fetch the latest block with the same number. 4. Compare transactions in the pending and latest blocks. ### Prerequisites 1. Log in to your [Chainstack account](https://console.chainstack.com/) and get a [reliable Ethereum RPC endpoint](https://chainstack.com/build-better-with-ethereum/). You can get by with a full node for this exercise. 2. Open your terminal (Command Prompt for Windows or Terminal for macOS/Linux) and run the following command to clone the GitHub repository. ```bash Bash theme={"system"} git clone https://github.com/smypmsa/geth-pending-latest-block.git cd your-repository ``` 3. Set up your Python virtual environment. ```bash macOS/Linux theme={"system"} python3 -m venv venv source venv/bin/activate ``` ```Text Windows theme={"system"} python -m venv venv venv\Scripts\activate ``` 4. With the virtual environment activated, run the following command to install the required dependencies. ```bash Bash theme={"system"} pip install -r requirements.txt ``` 5. Create the environment variable file (.env) and paste your Chainstack endpoint URL there. ```python Python theme={"system"} ETH_NODE_URL= ``` [GitHub repository](https://github.com/smypmsa/geth-pending-latest-block) for all the scripts. ### Connect to the node Load the configuration file (.env), read its values and connect to the Ethereum node. ```python Python theme={"system"} import os import time from dotenv import load_dotenv from web3 import Web3 # Load environment variables load_dotenv() ETH_NODE_URL = os.getenv("ETH_NODE_URL") # Create connection to Ethereum node web3 = Web3(Web3.HTTPProvider(ETH_NODE_URL)) ``` ### Fetch a pending block Check that the connection to the Ethereum node is established, get the node version and fetch a pending block. ```python Python theme={"system"} def main(): if not web3.is_connected(): print("\nFailed to connect to the Ethereum node.") return print("\nConnected to the Ethereum node.") print("Node version:", web3.client_version) # Fetch pending block pending_block = web3.eth.get_block('pending') pending_block_number = pending_block.number ``` ### Fetch a finalized block To fetch a finalized block with the same number as the pending block we fetched earlier, a few retries may be needed. ```python Python theme={"system"} # Retry fetching the finalized block with the same block number as the pending block finalized_block = None start_time = time.time() while True: try: finalized_block = web3.eth.get_block(pending_block_number, full_transactions=True) break except Exception as e: if time.time() - start_time > 60: print(f"Failed to fetch the finalized block within 60 seconds: {e}") return time.sleep(2) ``` ### Compare transactions Get transactions that are common for both blocks and that are unique for each block. ```python Python theme={"system"} # Compare transactions in the pending block and finalized block print(f"\nComparing txs in pending block and finalized block (block number: {pending_block_number}):") pending_block_tx_hashes = {tx.hex() for tx in pending_block.transactions} finalized_block_tx_hashes = {tx['hash'].hex() for tx in finalized_block.transactions} common_txs = pending_block_tx_hashes.intersection(finalized_block_tx_hashes) pending_only_txs = pending_block_tx_hashes - finalized_block_tx_hashes finalized_only_txs = finalized_block_tx_hashes - pending_block_tx_hashes ``` Let’s also compare the positions of transactions in the pending block with their positions in the latest block. ```python Python theme={"system"} position_changes = { "same_position": 0, "higher_position": 0, "lower_position": 0 } for tx_hash in common_txs: pending_index = [tx.hex() for tx in pending_block.transactions].index(tx_hash) finalized_index = [tx['hash'].hex() for tx in finalized_block.transactions].index(tx_hash) if pending_index == finalized_index: position_changes["same_position"] += 1 elif pending_index > finalized_index: position_changes["higher_position"] += 1 else: position_changes["lower_position"] += 1 ``` ### Putting it all together ```python Python theme={"system"} import os import time from dotenv import load_dotenv from web3 import Web3 # Load environment variables load_dotenv() ETH_NODE_URL = os.getenv("ETH_NODE_URL") # Create connection to Ethereum node web3 = Web3(Web3.HTTPProvider(ETH_NODE_URL)) def main(): if not web3.is_connected(): print("\nFailed to connect to the Ethereum node.") return print("\nConnected to the Ethereum node.") print("Node version:", web3.client_version) # Fetch pending block pending_block = web3.eth.get_block('pending') pending_block_number = pending_block.number # Retry fetching the finalized block with the same block number as the pending block finalized_block = None start_time = time.time() while True: try: finalized_block = web3.eth.get_block(pending_block_number, full_transactions=True) break except Exception as e: if time.time() - start_time > 60: print(f"Failed to fetch the finalized block within 60 seconds: {e}") return time.sleep(2) # Compare transactions in the pending block and finalized block print(f"\nComparing txs in pending block and finalized block (block number: {pending_block_number}):") pending_block_tx_hashes = {tx.hex() for tx in pending_block.transactions} finalized_block_tx_hashes = {tx['hash'].hex() for tx in finalized_block.transactions} common_txs = pending_block_tx_hashes.intersection(finalized_block_tx_hashes) pending_only_txs = pending_block_tx_hashes - finalized_block_tx_hashes finalized_only_txs = finalized_block_tx_hashes - pending_block_tx_hashes print(f"\nTxs in pending block: {len(pending_block.transactions)}") print(f"Txs in latest block: {len(finalized_block.transactions)}") position_changes = { "same_position": 0, "higher_position": 0, "lower_position": 0 } for tx_hash in common_txs: pending_index = [tx.hex() for tx in pending_block.transactions].index(tx_hash) finalized_index = [tx['hash'].hex() for tx in finalized_block.transactions].index(tx_hash) if pending_index == finalized_index: position_changes["same_position"] += 1 elif pending_index > finalized_index: position_changes["higher_position"] += 1 else: position_changes["lower_position"] += 1 print(f"\nTxs with higher position in the latest: {position_changes['higher_position']}") print(f"Txs with the same position in the latest: {position_changes['same_position']}") print(f"Txs with lower position in the latest: {position_changes['lower_position']}") print(f"Txs not included in the latest: {len(pending_only_txs)}") if __name__ == "__main__": main() ``` ## Pending and latest blocks analysis If you run the code above, you will notice that the transactions in the pending block differ from those in the final block added to the blockchain. The point is that the pending block is constructed by any node (validator or non-validator), but the finalized block is constructed by the block proposer (validator). **The block proposer may have a different view of the mempool, apply different prioritization criteria, or include different transactions based on network conditions and their own transaction pool.** When examining the transactions in the pending block versus the finalized block for block number 20212119, we can observe some interesting patterns and differences: | Metric | Number of transactions | | - | - | | Pending block | 122 | | Finalized block | 189 | | Transactions with higher positions in the latest block | 0 | | Transactions with the same position in the latest block | 0 | | Transactions with lower positions in the latest block | 120 | | Transactions not included in the latest block | 2 | ### Key observations 1. The finalized block contains significantly more transactions than the pending block. This means that additional transactions were included by the block proposer after the pending block snapshot was taken. 2. Almost all transactions (120 out of 122) from the pending block ended up in a lower position in the finalized block. 3. Two transactions from the pending block were not included in the finalized block at all. This could be due to various reasons such as insufficient fees, reorganization, or replacement by higher-priority transactions. ## Conclusion The goal of this article was to demystify the concept of a pending block in the Ethereum blockchain, specifically focusing on how Geth, the most popular Ethereum execution client, constructs pending blocks. ### Key findings Block proposer and non-block proposer limitations: * Block proposers construct the pending block using the latest transactions from the local mempool, ensuring they are prioritized and validated according to consensus rules. These blocks are proposed to the network for inclusion in the blockchain. * Non-block proposers (which can include validators when they are not proposing a block) construct a pending block based on their expectations of the next block's transactions but lack the guarantee of actual inclusion, as the final decision rests with the block proposer. Transaction ordering in a pending block: * Transactions in the pending block are sourced from the mempool and primarily sorted by their effective gas tip, calculated as the difference between the gas fee cap and the base fee or the gas tip cap. * Local transactions are prioritized over remote transactions, regardless of their effective gas tip, as long as they meet the minimum gas tip requirement. Handling of legacy transactions: * Legacy transactions (Type-0) are sorted by gas price alone, without considering the effective gas tip. This ensures backward compatibility and straightforward inclusion based on gas price. ### About author > [card] Anton Sauchyk > > ![Anton Sauchyk](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1817926677730664448/3nNn0T2p_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1d13d3584862f1be3a84fa3f8e38fea7) > > Developer Advocate @ Chainstack > \ > Multiple years of software development and Web3 expertise > > [https://github.com/smypmsa](https://github.com/smypmsa) > > [https://x.com/sensuniama](https://x.com/sensuniama) > > [https://www.linkedin.com/in/anton-sauchyk/](https://www.linkedin.com/in/anton-sauchyk/) # Ethereum logs tutorial series: Logs and filters Source: https://docs.chainstack.com/docs/ethereum-logs-tutorial-series-logs-and-filters Master Ethereum event logs and filters with practical examples covering eth_getLogs, topics encoding, and smart contract event monitoring on Chainstack. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR** * Explains how Ethereum logs provide an immutable record of on-chain events, while filters allow targeted retrieval of these logs based on addresses, topics, or block ranges. * Demonstrates retrieving historical logs with getPastLogs for one-time queries, and real-time subscription to new logs with eth\_subscribe for reactive DApps. * Highlights best practices such as indexing event parameters, breaking large block ranges, caching, and throttling requests. * Concludes that logs + filters combine to power event-driven, scalable Ethereum applications. ## Introduction Alright, so why do programmers never get lost in the woods?! Because they always follow the trail of logs they've left behind (ba-dum-tss). In the world of software development, logs are an indispensable tool for understanding and troubleshooting the behavior of applications. By acting as a detailed journal of system events, errors, and other relevant information, logs provide developers with crucial insights into the inner workings of their programs. As applications continue to grow more complex and interconnected, the importance of logs becomes increasingly apparent. In Ethereum, logs take on a distinct role compared to those in traditional software development. They provide a mechanism to store and access data generated by smart contracts during their execution. These logs, which are stored as immutable entries within transaction receipts, provide insights into events, state changes, and data storage related to smart contracts. Now, the utilization of the said information is only possible through convenient means of access to these logs. This is where filters come in. Filters are mechanisms that allow external applications, developers, and users to efficiently search, access, and monitor logs generated by the Ethereum blockchain. By applying specific criteria to filter logs, they enable targeted retrieval of relevant information from the vast amount of data stored in the form of logs. One can think of Ethereum logs as newspaper articles. They contain recorded information about specific events, stories, or data, just as logs hold information about events and data generated by smart contracts. Newspapers provide a means of communication and record-keeping, much like logs in the Ethereum ecosystem. On the other hand, filters can be compared to search engines that help users find relevant articles or information from newspapers based on specific criteria or keywords. Filters in Ethereum allow developers and DApps to efficiently access specific data from logs or other aspects of the blockchain without having to go through the entire blockchain, just as search engines enable users to find the exact information they are looking for without reading all available newspapers. Now, what follows is an in-depth analysis of Ethereum logs, filters, and their working. Whether you're a seasoned Ethereum developer or just getting started with the platform, this in-depth analysis will provide you with the knowledge you need to make the most of Ethereum logs and filters. Let's get started! ## Analyzing Ethereum logs Ethereum logs are data structures created as a result of events emitted by smart contracts during their execution. The key difference between logs and events is that events are a programming feature in Solidity that enables smart contracts to communicate and interact with external applications, while logs are the data structures generated by the Ethereum Virtual Machine (EVM) when events are emitted. Smart contracts can emit events to indicate specific occurrences or state changes, which are logged and stored as part of transaction receipts. Ethereum logs primarily serve two main purposes: event logging and data storage. Developers and DApps can access these logs to trigger further actions or to track important events. Additionally, logs offer a cost-effective way to store data off-chain while maintaining a reference on the blockchain, reducing gas costs and enhancing efficiency. > [!NOTE] > > To get a more in-depth idea about event logs, check out [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) To understand the basic components of an Ethereum log, let's consider a simple example of a token transfer event log. The `Transfer` event is used in a simple token smart contract to record and communicate token transfers between addresses: ```javascript Javascript theme={"system"} /** * @dev Emitted when `value` tokens are moved from one account (`from`) * to another (`to`). * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); ``` The event takes three parameters: the sender's address, the recipient's address, and the transferred token value. When the `transfer` function is called within the smart contract, the `Transfer` event is emitted, generating the following Ethereum log entry: ```javascript Javascript theme={"system"} { address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', topics: [ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', '0x00000000000000000000000037b2b98ea5d620a4064fc954e9f374c8a28e2125', '0x000000000000000000000000dc412bbc1875e588166211defa9c84ac195094cf' ], data: '0x0000000000000000000000000000000000000000000000000000000047868c00', blockNumber: 14673517, transactionHash: '0x63aff25171e502638926200a480fbd0365ca0c76d036acaa866f8af5d26e9a61', transactionIndex: 311, blockHash: '0xfa470ae2ccbe4d1a83b2ab039e300b4fc6d2904db80f8f8a0f4ab890f4240a09', logIndex: 531, removed: false, id: 'log_3cd63a01' } ``` The basic components of the given Ethereum log include: 1. `address` — the address of the contract that generated this log. 2. `topics` — an array of topics associated with the log. Topics are indexed event parameters that can be used to filter logs. 3. `data` — the log data as a hexadecimal string. This contains the non-indexed event parameters. 4. `blockNumber` — the number of the block in which the log was created. 5. `transactionHash` — the hash of the transaction that generated this log. 6. `transactionIndex` — the index of the transaction within the block. 7. `blockHash` — the hash of the block that contains this log. 8. `logIndex` — the index of the log within the block. In this case, the log index is `531`. 9. `removed` — a boolean value that indicates whether the log was removed due to a chain reorganization. 10. `id` — a unique identifier for this log entry. > [!NOTE] > > Find more explanations and examples in the [Chainstack API reference | eth\_getLogs](/reference/ethereum-getlogs). ## Understanding filters Ethereum filters are essential tools for developers when working with Ethereum-based applications. They facilitate real-time monitoring and interaction with the Ethereum blockchain, offering developers the ability to listen for specific events, state changes, or transactions. Filters help create responsive, event-driven applications that continuously adapt to changes on the blockchain. There are three primary types of filters in Ethereum: > [card] [Log filters](/reference/ethereum-newfilter) > [card] [Block filters](/reference/ethereum-newblockfilter) > [card] [Pending transaction filters](/reference/ethereum-newpendingtransactionfilter) ### Log filters Log filters are essential for monitoring and reacting to events generated by smart contracts on the Ethereum blockchain. These events can include but are not limited to, transferring tokens, updating contract states, or invoking specific contract functions. Log filters allow developers to fetch logs based on certain conditions, such as: * **Contract address**: Monitor events emitted by a specific smart contract. * **Event signature**: Filter logs by the event's unique signature, which is the Keccak-256 hash of the event definition (e.g., `Transfer(address,address,uint256)`). * **Block range**: Limit the logs fetched to a specific range of blocks. * **Indexed event parameters**: Filter logs based on specific indexed event parameters, which are used to narrow down the logs relevant to the application's needs. Logs contain valuable information, including the event signature, contract address, block number, transaction hash, and event parameters. By utilizing log filters, developers can track contract events and update their applications accordingly. ### Block filters Block filters focus on monitoring newly generated blocks on the Ethereum blockchain. These filters are useful for applications that need to react to new blocks, such as: * Fetching the latest gas prices * Verifying transactions included in a block * Analyzing block data, such as miner rewards, difficulty, and uncle blocks Block filters provide developers with real time updates on new blocks mined, allowing them to retrieve block header data. This data includes the block number, miner, timestamp, parent hash, and other pertinent information. By monitoring new blocks, developers can ensure their applications stay up-to-date with the latest blockchain data. ### Pending transaction filters Pending transaction filters are designed to monitor and track pending transactions in the Ethereum network. These filters are beneficial for applications that require information about unconfirmed transactions, such as: * Displaying pending transactions in a user's wallet * Analyzing transaction behavior, like gas price bidding or transaction propagation * Estimating transaction confirmation times Pending transaction filters notify developers when a new transaction enters the pending state, providing the transaction hash. Developers can then fetch the transaction data using the hash, which includes the sender, recipient, value, gas price, and other relevant transaction details. ## Accessing logs using filters ### Prerequisites Access an Ethereum node: 1. [Head over to Chainstack](https://console.chainstack.com/user/account/create) and create an account. 2. [Deploy an Ethereum Mainnet node](/docs/manage-your-networks) in Chainstack. Set up a Node project: 1. Install the following dependencies in your system: * [Node (version ≥ 16)](https://nodejs.org/en/download) and the corresponding npm * A code editor (VS Code, preferably) Once you have everything, the next step is to set up a Node project and install the web.js library. 2. Create a new directory. 3. Open a terminal in the directory. 4. Use the following command to initialize a Node project: ```shell Shell theme={"system"} npm init -y ``` 5. Once the project is initialized, use the following command to install the web.js package: ```shell Shell theme={"system"} npm install web3 ``` 6. Within the project, create a new JavaScript file, `fetchLogs.js`, and add the following lines of code: ```javascript fetchLogs.js theme={"system"} const Web3 = require('web3'); // Add your Chainstack node endpoint (HTTPS) const chainstackRpcUrl = 'YOUR_CHAINSTACK_ENDPOINT'; const web3 = new Web3(new Web3.providers.HttpProvider(chainstackRpcUrl)); // USDC token contract address on Ethereum mainnet const usdcContractAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; // The Transfer event signature for ERC20 tokens (keccak256 hash of "Transfer(address,address,uint256)") const transferEventSignature = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; // Define the filter options for fetching USDC transfer logs const filterOptions = { fromBlock: 'latest', // Start from the genesis block address: usdcContractAddress, topics: [transferEventSignature], }; // Fetch logs using the filter options web3.eth.getPastLogs(filterOptions).then(logs => { // Define the Transfer event ABI const transferEventInputs = [ { type: 'address', name: 'from', indexed: true }, { type: 'address', name: 'to', indexed: true }, { type: 'uint256', name: 'value', indexed: false }, ]; // Process each log logs.forEach(log => { // Decode the log data const decodedLog = web3.eth.abi.decodeLog(transferEventInputs, log.data, log.topics.slice(1)); // Log the decoded transfer event data console.log('USDC Transfer:'); console.log(' From:', decodedLog.from); console.log(' To:', decodedLog.to); console.log(' Value (in USDC):', parseInt(decodedLog.value) / 1e6); }); }).catch(error => { console.error('Error:', error); }); ``` In this code example, we demonstrate how to fetch USDC token transfer event logs using log filters in web3.js. The purpose of this code is to provide a clear and concise example for developers to understand how to interact with the Ethereum blockchain and obtain event logs, specifically for the USDC stablecoin. This information can be useful for applications that need to track token transfers or analyze token transaction history. The code starts by importing the `Web3` library, which is the primary library for interacting with Ethereum nodes. We then provide the Chainstack node endpoint, which is used to connect to the Ethereum Mainnet. The USDC token contract address and the `Transfer` event signature are defined to be used in the log filter. We create a `filterOptions` object, which contains the necessary filter parameters, such as the starting and ending blocks (`fromBlock` and `toBlock`), the contract address, and the event signature. This object is used as input for the `web3.eth.getPastLogs()` method, which fetches logs matching the filter options. Upon successful retrieval of the logs, the function iterates through each log entry, decoding the log data using the `Transfer` event ABI. The decoded log information is then printed to the console, showing the sender, recipient, and value of each USDC transfer. ### Using raw JSON-RPC If you're not using a high-level library, you can call the JSON-RPC methods directly. Three methods cover the common cases: `eth_getLogs` for one-shot reads, `eth_newFilter` + `eth_getFilterLogs` for filter-based reads. #### eth\_getLogs Retrieve logs matching a filter in a single call: ```bash cURL theme={"system"} curl -X POST 'https://ethereum-mainnet.core.chainstack.com/AUTH_KEY' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "eth_getLogs", "params": [{ "fromBlock": "0xCD0E64", "toBlock": "latest", "address": "0x514910771af9ca656af840dff83e8264ecf986ca", "topics": [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" ] }] }' ``` This example reads all ERC-20 `Transfer` events for the [Chainlink (LINK) token contract](https://etherscan.io/token/0x514910771af9ca656af840dff83e8264ecf986ca) starting at [block 13,438,564](https://etherscan.io/block/13438564) (`0xCD0E64` in hex). The topic hash `0xddf2…3b3ef` is the Keccak-256 of `Transfer(address,address,uint256)`. Block numbers must be hex with a `0x` prefix, or the tags `latest`, `earliest`, `pending`, `safe`, `finalized`. #### eth\_newFilter + eth\_getFilterLogs When you want to register a filter once and read it back later (or repeatedly with [`eth_getFilterChanges`](/reference/ethereum-getfilterchanges)), create the filter first: ```bash Create the filter theme={"system"} curl -X POST 'https://ethereum-mainnet.core.chainstack.com/AUTH_KEY' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "eth_newFilter", "params": [{ "fromBlock": "0xCD0E64", "toBlock": "latest", "address": "0x514910771af9ca656af840dff83e8264ecf986ca", "topics": [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" ] }] }' # → {"jsonrpc":"2.0","id":1,"result":"0xb85dcf61893301a80392d3551aebbfa6"} ``` The response `result` is your filter ID. Use it with `eth_getFilterLogs` to get all matching logs in one shot: ```bash Read filter logs theme={"system"} curl -X POST 'https://ethereum-mainnet.core.chainstack.com/AUTH_KEY' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "eth_getFilterLogs", "params": ["0xb85dcf61893301a80392d3551aebbfa6"] }' ``` Filters expire after \~5 minutes of inactivity — see [Fix Ethereum's filter not found error](/docs/understanding-ethereums-filter-not-found-error-and-how-to-fix-it) for the recovery pattern. ## Subscribing to logs In the above-given code, we use the `web3.eth.getPastLogs()` to perform a one-time query to the Ethereum node to retrieve logs matching the given filters. The result is a collection of logs from the specified block range that match the filter criteria. This approach is useful for obtaining historical event data or performing a one-time analysis of contract events. To monitor the logs in real time, we need to use the `web3.eth.subscribe()` method. When we use the `subscribe` method, we create an ongoing subscription to a specific event, such as logs, new block headers, or pending transactions. The Ethereum node pushes new data to the subscriber in real time whenever the subscribed event occurs. This approach is useful for building event-driven applications that need to react to changes on the blockchain as they happen. To use the `subscribe` function, we need to get the WSS endpoint of your Chainstack node. > [!NOTE] > > You can find more examples and explanations about the subscriptions method in the [Chainstack API reference](/reference/ethereum-web3js-subscriptions-methods). Once you fetch the endpoint, use the following code to *subscribe* to event logs: ```javascript Javascript theme={"system"} const Web3 = require('web3'); // Add your Chainstack node endpoint (WSS) const chainstackRpcUrl = 'YOUR_CHAINSTACK_ENDPOINT'; // Configure reconnect options for WebsocketProvider const websocketOptions = { clientConfig: { reconnect: { auto: true, // Automatically attempt to reconnect delay: 5000, // Reconnect after 5 seconds maxRetries: 10, // Max number of retries }, }, }; const web3 = new Web3(new Web3.providers.WebsocketProvider(chainstackRpcUrl, websocketOptions)); // USDC token contract address on Ethereum mainnet const usdcContractAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; // The Transfer event signature for ERC20 tokens (keccak256 hash of "Transfer(address,address,uint256)") const transferEventSignature = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; // Define the filter options for fetching USDC transfer logs const filterOptions = { address: usdcContractAddress, topics: [transferEventSignature], }; // Subscribe to logs and process USDC transfer events web3.eth.subscribe('logs', filterOptions, (error, log) => { if (error) { console.error('Error:', error); return; } // Define the Transfer event ABI const transferEventInputs = [ { type: 'address', name: 'from', indexed: true }, { type: 'address', name: 'to', indexed: true }, { type: 'uint256', name: 'value', indexed: false }, ]; // Decode the log data const decodedLog = web3.eth.abi.decodeLog(transferEventInputs, log.data, log.topics.slice(1)); // Log the decoded transfer event data console.log('USDC Transfer:'); console.log(' From:', decodedLog.from); console.log(' To:', decodedLog.to); console.log(' Value (in USDC):', parseInt(decodedLog.value) / 1e6); }); ``` The given code leverages the `subscribe` method available in the web3.js library to enable real-time monitoring of logs. The `subscribe` method sets up a WebSocket connection with the Ethereum node, allowing for efficient and continuous updates on new events without the need for polling. In this specific example, the code creates a subscription to the 'logs' event, applying a filter to focus on the USDC token contract's `Transfer` events. Upon receiving a new log event matching the filter criteria, the callback function is triggered, processing and decoding the event data to extract valuable information about the USDC transfers occurring on the Ethereum network. The code is also designed to automatically reconnect to the Ethereum node if the WebSocket connection is lost or interrupted. This is important to ensure that the script continues to monitor USDC transfers without manual intervention in case of connection issues. The reconnection logic is implemented using the `clientConfig` option when creating a new instance of`Web3.providers.WebsocketProvider`. This option allows you to customize the behavior of the WebSocket client, allowing automatic reconnection attempts following the delay and maximum retries specified in the `websocketOptions`. ## Making the best out of Ethereum logs and filters When developing Ethereum applications, it's important to ensure that your code is efficient, secure, and scalable. To help you build reliable and performant applications, we have compiled a list of best practices for retrieving and using event logs and filters in Ethereum. These recommendations cover various aspects of development, such as safe block range, optimal coding practices, and more. By following these guidelines, you can improve your application's performance and ensure seamless interactions with the Ethereum network. Here are a few best practices that you can follow while using Ethereum logs and filters: 1. **Use the latest version of web3.js**. Make sure to always use the most recent version of the web3.js library, as it includes the latest features, optimizations, and security fixes. 2. **Choose a safe block range**. When retrieving event logs, it is essential to choose a reasonable block range. Requesting too large a range may lead to timeouts and slow response times. If you need to retrieve logs from a wide range, consider breaking the request into smaller chunks. Chainstack does not impose strict limitations on the block range you can query. However, for optimal performance, on the Ethereum network, we suggest keeping the block range within 5,000 blocks. 3. **Filter events by indexed parameters**. Using indexed parameters in your event filters can significantly improve efficiency by allowing you to target specific logs. Make use of indexed event parameters whenever possible. 4. **Monitor for new events**. Use the web3.js library's built-in event subscription feature to monitor new events in real-time. This can help you avoid the need to constantly poll for updates. 5. **Throttle your requests**. To avoid overwhelming your Ethereum provider, throttle your requests to a reasonable rate. You can use libraries like `lodash` to implement request throttling easily. 6. **Use the `fromBlock` parameter wisely**. When retrieving historical event logs, be mindful of the `fromBlock` parameter. It is often more efficient to start from a block that you know contains relevant events rather than querying from the genesis block. 7. **Cache logs locally**. Cache event logs locally to minimize the need for frequent API calls. This can improve your application's performance and reduce the load on your Ethereum provider. 8. **Monitor gas usage**. Keep track of gas usage for your contract interactions. Optimizing your contract's gas consumption can save your users money and improve the overall user experience. 9. **Secure your Ethereum provider**. For higher security, ensure that you use properly secure connections with API keys and other security measures in your Chainstack nodes. ## Conclusion In conclusion, logs and filters are essential components of Ethereum-based applications. Logs provide valuable information about contract events, while filters allow developers to monitor and interact with the blockchain in real time. By leveraging these tools, developers can build event-driven applications that respond to changes on the blockchain and provide a seamless user experience. It's important to follow best practices when working with logs and filters to ensure that your code is efficient, secure, and scalable. By keeping these recommendations in mind, you can improve your application's performance and provide reliable interactions with the Ethereum network. > [!NOTE] > > ### See also > > > [card] [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) > > > [card] [Uncovering the power of eth\\\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) > > > [card] [Expanding your blockchain horizons: The eth\\\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) > > > [card] [Deep dive into Merkle proofs and eth\\\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) > > > [card] [Geth vs Erigon: Deep dive into RPC methods on Ethereum clients](/docs/geth-vs-erigon-deep-dive-into-rpc-methods-on-ethereum-clients) ### About the author > [card] Sethu Raman Omanakuttan > > ![Sethu Raman Omanakuttan](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/u/44131280.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=a94536fa650a30812be2fef8e02ed093) > > Developer Advocate @ Chainstack > \ > BUIDLs on Ethereum, NEAR, Graph Protocol and Oasis > \ > Majored in computer science and technology > > [https://github.com/SethuRamanOmanakuttan](https://github.com/SethuRamanOmanakuttan) > > [https://twitter.com/Sethu\_Raman\_O](https://twitter.com/Sethu_Raman_O) > > [https://www.linkedin.com/in/sethuraman-omanakuttan](https://www.linkedin.com/in/sethuraman-omanakuttan) # Ethereum methods Source: https://docs.chainstack.com/docs/ethereum-methods Complete reference of supported Ethereum JSON-RPC methods on Chainstack, including eth, debug, trace, engine, and web3 namespace availability. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [Interactive Ethereum API call examples](/reference/ethereum-getting-started). ## Execution layer | Method | Availability | Comment | | - | - | - | | eth\_accounts | | Will always return empty | | eth\_blobBaseFee | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_simulateV1 | | No archive data support | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | txpool\_content | | | | txpool\_inspect | | | | txpool\_contentFrom | | | | txpool\_status | | | | web3\_clientVersion | | | | web3\_sha3 | | | | erigon\_blockNumber | | | | erigon\_forks | | | | erigon\_getBlockByTimestamp | | | | erigon\_getBlockReceiptsByBlockHash | | | | erigon\_getHeaderByHash | | | | erigon\_getHeaderByNumber | | | | erigon\_getLatestLogs | | | | erigon\_getLogsByHash | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_getTrieFlushInterval | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceCallMany | | | | debug\_traceTransaction | | | | trace\_block | | | | trace\_call | | | | trace\_callMany | | | | trace\_filter | | | | trace\_rawTransaction | | | | trace\_replayBlockTransactions | | | | trace\_replayTransaction | | | | trace\_transaction | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | ## Consensus layer (Beacon Chain) | Method | Availability | Comment | | - | - | - | | /eth/v1/beacon/blocks/\{block\_id}/attestations | | | | /eth/v1/beacon/blocks/\{block\_id}/root | | | | /eth/v1/beacon/blob\_sidecars/\{\{block\_id}} | | | | /eth/v1/beacon/genesis | | | | /eth/v1/beacon/headers | | | | /eth/v1/beacon/headers/\{block\_id} | | | | /eth/v1/beacon/states/\{state\_id}/committees | | | | /eth/v1/beacon/states/\{state\_id}/finality\_checkpoints | | | | /eth/v1/beacon/states/\{state\_id}/fork | | | | /eth/v1/beacon/states/\{state\_id}/root | | | | /eth/v1/beacon/states/\{state\_id}/sync\_committees | | | | /eth/v1/beacon/states/\{state\_id}/validator\_balances | | | | /eth/v1/beacon/states/\{state\_id}/validators | | | | /eth/v1/beacon/states/\{state\_id}/validators/\{validator\_id} | | | | /eth/v1/beacon/rewards/sync\_committee/\{block\_id} | | | | /eth/v1/beacon/rewards/blocks/\{block\_id} | | | | /eth/v1/beacon/rewards/attestations/\{epoch} | | | | /eth/v1/config/deposit\_contract | | | | /eth/v1/config/spec | | | | /eth/v1/events | | | | /eth/v1/node/peer\_count | | | | /eth/v1/node/peers | | | | /eth/v1/node/syncing | | | | /eth/v1/node/version | | | | /eth/v1/validator/aggregate\_attestation | | | | /eth/v1/validator/blinded\_blocks/\{slot} | | | | /eth/v1/validator/duties/attester/\{epoch} | | | | /eth/v1/validator/duties/proposer/\{epoch} | | | | /eth/v1/validator/sync\_committee\_contribution | | | | /eth/v2/beacon/blocks/\{block\_id} | | | | /eth/v2/debug/beacon/states/\{state\_id} | | | # Monitor an Ethereum address for incoming transactions in real time Source: https://docs.chainstack.com/docs/ethereum-monitor-address-transactions Watch an Ethereum address for incoming ETH and ERC-20 transfers in real time with ethers v6 WebSocket subscriptions over a Chainstack WSS endpoint. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). This tutorial shows you how to watch an Ethereum address for incoming transfers as they land, using ethers v6 and a Chainstack WebSocket (WSS) endpoint. By the end you have two small Node.js scripts — one that catches incoming Ether (ETH) and one that catches incoming ERC-20 token transfers — that print each transfer the moment it is included in a block. **TLDR** * Subscribe to a Chainstack Ethereum WSS endpoint with ethers v6 to watch an address for incoming transfers in real time. * Catch native ETH transfers by subscribing to new blocks (`newHeads`) and scanning each block's transactions for your address. * Catch ERC-20 token transfers by subscribing to the token's `Transfer` logs filtered to your address, so the node does the filtering for you. * Includes the raw `eth_subscribe` (`newHeads` / `logs`) equivalent for stacks that don't use ethers. ## How real-time address monitoring works Ethereum gives you two real-time streams over a WebSocket connection, and which one you use depends on the type of transfer you want to catch. * Native ETH transfers — a plain transaction with a `value` and a `to` field. ETH transfers are not events, so there is no log to filter. You subscribe to new blocks, fetch each block's transactions, and keep the ones whose `to` is your address. * ERC-20 token transfers — emitted by the token contract as a `Transfer` event log, not as a transaction addressed to you. You subscribe to the `logs` stream filtered by the token contract and an indexed `to` topic, so the node returns only the transfers addressed to your account. Both streams need a persistent connection, which is why address monitoring uses a WSS endpoint rather than HTTPS. Learn more about the tradeoff in [Handle real-time data with WebSockets in JS & Python](/docs/handle-real-time-data-using-websockets-with-javascript-and-python). ## Get your Chainstack WebSocket endpoint You need a WSS endpoint from an Ethereum node. Deploy a node on Chainstack, then copy the WSS endpoint from the node's **Access and credentials** tab. > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) > [!NOTE] > > For always-on monitoring, a [Trader Node](/docs/trader-node) gives dedicated resources and regional placement; a [Global Node](/docs/global-elastic-node) works on every plan. See [Ethereum tooling](/docs/ethereum-tooling) for the full library and endpoint reference. ## Set up the project Create a project directory and install ethers: ```bash Shell theme={"system"} mkdir eth-monitor && cd eth-monitor npm init -y npm install ethers ``` > [!WARNING] > > This tutorial uses **ethers v6**. Confirm your version with `npm ls ethers`. In v6 the provider class is `ethers.WebSocketProvider`; in v5 it was `ethers.providers.WebSocketProvider`. The code here does not run on v5. ## Monitor incoming ETH transfers To catch incoming ETH, subscribe to new blocks and, for each block, check every transaction's `to` against your address. Save the following as `monitor-native.js`, then paste your WSS endpoint into `WSS_ENDPOINT`. The example watches the Wrapped Ether (WETH) contract because it receives ETH constantly, so you see results within seconds — replace it with the address you want to monitor. ```javascript monitor-native.js theme={"system"} const { ethers } = require("ethers"); const WSS_ENDPOINT = "YOUR_CHAINSTACK_WSS_ENDPOINT"; const WATCHED_ADDRESS = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; // replace with the address you monitor const provider = new ethers.WebSocketProvider(WSS_ENDPOINT); const watched = WATCHED_ADDRESS.toLowerCase(); provider.on("block", async (blockNumber) => { try { // getBlock(blockNumber, true) prefetches full transaction objects. const block = await provider.getBlock(blockNumber, true); if (!block) return; const txs = block.prefetchedTransactions; console.log(`Block ${blockNumber} — scanning ${txs.length} transactions`); for (const tx of txs) { if (tx.to && tx.to.toLowerCase() === watched && tx.value > 0n) { console.log("Incoming ETH transfer:"); console.log(` tx: ${tx.hash}`); console.log(` from: ${tx.from}`); console.log(` value: ${ethers.formatEther(tx.value)} ETH`); } } } catch (err) { if (err.code !== "UNSUPPORTED_OPERATION") console.error("Scan error:", err.message); } }); console.log(`Watching ${WATCHED_ADDRESS} for incoming ETH...`); async function shutdown() { await provider.destroy(); process.exit(0); } process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); ``` How the script works: * `provider.on("block", ...)` — fires once per new block with the block number. This is the ethers equivalent of a `newHeads` subscription. * `getBlock(blockNumber, true)` — the second argument prefetches full transaction objects, exposed as `block.prefetchedTransactions`. Without it you get only transaction hashes. * `tx.value` — a `bigint`, so compare it with `0n` and format it with `ethers.formatEther`. * `try/catch` — swallows the `UNSUPPORTED_OPERATION` error that an in-flight `getBlock` throws when the provider is shutting down. Run it: ```bash Shell theme={"system"} node monitor-native.js ``` Example output: ```text theme={"system"} Watching 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 for incoming ETH... Block 25498621 — scanning 890 transactions Incoming ETH transfer: tx: 0x36bd59565352d406dc7c4dc87f8eae944fda53e376b9eeccc4219741d69e35fd from: 0xf76A07f67e1F6f9db8EbBa3eE9Acb6B8933F89F0 value: 1.0 ETH Block 25498622 — scanning 341 transactions Block 25498623 — scanning 526 transactions ``` > [!NOTE] > > Block scanning catches direct ETH sends where the transaction's `to` is your address. ETH moved to your address inside a contract call — an internal transfer — is not a top-level transaction and does not show up here; detecting those requires tracing. See [Ethereum tooling](/docs/ethereum-tooling) for trace methods. ## Monitor incoming ERC-20 token transfers To catch incoming tokens, subscribe to the token's `Transfer` logs with the indexed `to` topic set to your address. The node streams only the matching transfers, so you never scan a full block. Save the following as `monitor-token.js`. The example watches USDC transfers to an active address so you see results quickly — replace `WATCHED_ADDRESS` with the address you monitor, and set `TOKEN_ADDRESS` and `TOKEN_DECIMALS` for the token you care about. ```javascript monitor-token.js theme={"system"} const { ethers } = require("ethers"); const WSS_ENDPOINT = "YOUR_CHAINSTACK_WSS_ENDPOINT"; const WATCHED_ADDRESS = "0x0000000aa232009084Bd71A5797d089AA4Edfad4"; // replace with the address you monitor const TOKEN_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; // USDC const TOKEN_DECIMALS = 6; const provider = new ethers.WebSocketProvider(WSS_ENDPOINT); const transferTopic = ethers.id("Transfer(address,address,uint256)"); // Only Transfer events whose indexed `to` equals the watched address. const filter = { address: TOKEN_ADDRESS, topics: [transferTopic, null, ethers.zeroPadValue(WATCHED_ADDRESS, 32)], }; provider.on(filter, (log) => { const from = ethers.getAddress("0x" + log.topics[1].slice(26)); const value = ethers.formatUnits(BigInt(log.data), TOKEN_DECIMALS); console.log("Incoming token transfer:"); console.log(` block: ${log.blockNumber}`); console.log(` tx: ${log.transactionHash}`); console.log(` from: ${from}`); console.log(` value: ${value}`); }); console.log(`Watching ${WATCHED_ADDRESS} for incoming token transfers...`); async function shutdown() { await provider.destroy(); process.exit(0); } process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); ``` How the filter works: * `ethers.id("Transfer(address,address,uint256)")` — the keccak-256 hash of the event signature, which is topic 0 for every ERC-20 `Transfer`. * `topics: [transferTopic, null, ethers.zeroPadValue(WATCHED_ADDRESS, 32)]` — position 0 matches the event, `null` matches any sender, and position 2 pins the indexed `to` to your address. Addresses are left-padded to 32 bytes in topics, which is what `zeroPadValue` does. * Decoding — the sender is in `log.topics[1]` and the amount is in `log.data`. Recover the address with `ethers.getAddress`, and format the amount with `ethers.formatUnits` and the token's decimals. * `TOKEN_DECIMALS` — set this to the token's decimals. USDC uses 6; most ERC-20 tokens use 18. Run it: ```bash Shell theme={"system"} node monitor-token.js ``` Example output: ```text theme={"system"} Watching 0x0000000aa232009084Bd71A5797d089AA4Edfad4 for incoming token transfers... Incoming token transfer: block: 25498602 tx: 0x1b2bb56ba54a4554c801bb8d5b070579fa3f24598c0154a2549b20f3f9389574 from: 0x000000000004444c5dc75cB358380D2e3dE08A90 value: 494.56227 Incoming token transfer: block: 25498605 tx: 0x48c0facc1f08bee72a9215cedc3a488ddfe9d94e147a11ac987344a29739104b from: 0x000000000004444c5dc75cB358380D2e3dE08A90 value: 461.309863 ``` > [!NOTE] > > This log filter runs on the node, so you receive only the transfers addressed to you — more efficient than scanning every block. To watch several tokens, create one filter per token, or omit the `to` topic to receive every `Transfer` and filter client-side. For a deeper look at logs and topics, see [Ethereum logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters). ## Handle disconnections and shut down cleanly WebSocket connections can drop, so a production monitor needs reconnect logic; and when you stop a monitor, close the provider so the process exits cleanly. * Reconnects — a dropped socket (codes like `1006`, or `ECONNRESET`) ends the subscription silently. Re-create the `WebSocketProvider` and re-attach your handlers when the socket closes, or use a Trader Node [dedicated gateway](/docs/trader-node#dedicated-gateways). The [WebSockets tutorial](/docs/handle-real-time-data-using-websockets-with-javascript-and-python) has a full reconnect pattern. * Clean shutdown — call `await provider.destroy()` to close the socket and detach listeners. Do not call `provider.removeAllListeners()` right before `destroy()` — that races the in-flight `eth_unsubscribe` against the socket teardown and throws `provider destroyed; cancelled request`. Calling `destroy()` on its own is enough. ## Subscribe with raw JSON-RPC (eth\_subscribe) If you are not using ethers, subscribe directly with the `eth_subscribe` method over the WSS connection — `newHeads` for blocks and `logs` for token transfers. This is what the ethers subscriptions do under the hood. Connect with [wscat](https://github.com/websockets/wscat): ```bash Shell theme={"system"} npm install -g wscat wscat -c YOUR_CHAINSTACK_WSS_ENDPOINT ``` Subscribe to new block headers: ```json theme={"system"} {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]} ``` Subscribe to incoming USDC transfers for an address — the third topic is your address left-padded to 32 bytes: ```json theme={"system"} {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["logs",{"address":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",null,"0x0000000000000000000000000000000aa232009084bd71a5797d089aa4edfad4"]}]} ``` The node replies with a subscription id, then pushes an `eth_subscription` message for each new block or matching log. For the full method reference, see [eth\_subscribe ("newHeads")](/reference/ethereum-native-subscribe-newheads), [eth\_subscribe ("logs")](/reference/ethereum-native-subscribe-logs), and [eth\_getLogs](/reference/ethereum-getlogs) for backfilling history. ## What's next > [card] [Ethereum logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters) > > Go deeper on event topics, indexed parameters, and log filtering. > [card] [Tracking Bored Apes: event logs](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) > > A worked example of decoding contract event logs end to end. > [card] [Real-time data with WebSockets](/docs/handle-real-time-data-using-websockets-with-javascript-and-python) > > Connection limits and a production reconnect pattern. > [card] [Ethereum tooling](/docs/ethereum-tooling) > > Libraries, endpoints, and trace methods for Ethereum development. # Monitor Uniswap swaps in real time over WebSocket Source: https://docs.chainstack.com/docs/ethereum-monitor-uniswap-swaps-websocket Stream and decode live Uniswap trades on Ethereum by subscribing to a pool's Swap event logs over a WebSocket endpoint with ethers v6. **TLDR:** * Connect the ethers v6 `WebSocketProvider` to a Chainstack Ethereum WSS endpoint, and pass `staticNetwork` so the log subscription keeps delivering. * Attach a `Contract` listener for the pool's `Swap` event — the node pushes each matching log the moment the block lands onchain, with no block scanning or polling. * Decode `amount0` and `amount1` from the event: a negative amount is the token the pool paid out (what the trader received), a positive amount is the token it took in (what the trader paid). * This tutorial monitors the USDC/WETH 0.05% Uniswap V3 pool `0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640`, whose Swap `topic0` is `0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67`. ## What you'll build A small Node.js script that opens one WebSocket connection to Ethereum mainnet and prints every swap on a Uniswap pool as it happens — the traded token amounts, the direction of the trade, and the transaction hash. The same pattern works for any Uniswap V3 pool or V2 pair by changing one address and the event definition. Subscribing to the pool's `Swap` event is the direct way to watch trades. The node streams you only the logs you asked for, so you react the instant a swap is mined instead of pulling full blocks and filtering transactions yourself. ## Prerequisites * Node.js 18 or later. * ethers v6 — install it with `npm install ethers`. This tutorial uses the v6 API (`ethers.WebSocketProvider`), which differs from v5's `ethers.providers.WebSocketProvider`. * An Ethereum mainnet WSS endpoint from Chainstack. ## How Uniswap emits swaps Every Uniswap pool contract emits a `Swap` event log each time a trade settles against it. Watching that one event over a WebSocket connection is the most direct way to track trades in real time: the node pushes each matching log as soon as the block is mined, so you never poll or download whole blocks. Each event is identified by its `topic0` — the keccak256 hash of the event signature. Filtering a subscription by the pool address and this `topic0` tells the node to send you that pool's swaps and nothing else. This tutorial uses the USDC/WETH 0.05% Uniswap V3 pool at `0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640`. Its Swap event signature and `topic0` are: * Signature — `Swap(address,address,int256,int256,uint160,uint128,int24)` * topic0 — `0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67` Uniswap V2 pairs emit a differently shaped `Swap` event with its own `topic0`. To watch a V2 pair, see [Monitor a different pool or a Uniswap V2 pair](#monitor-a-different-pool-or-a-uniswap-v2-pair). ## Get your Ethereum WebSocket endpoint A WebSocket (WSS) endpoint keeps a single connection open so the node can push new logs to you, which is what makes real-time subscriptions possible. Get one from Chainstack: **1. Sign in to Chainstack** Log in to your [Chainstack account](https://console.chainstack.com/). **2. Deploy an Ethereum node** Create a project, then deploy an Ethereum mainnet node. [Global Nodes](/docs/global-elastic-node) deploy instantly and are available on every plan. **3. Copy the WSS endpoint** Open the node and, on the **Access and credentials** tab, copy the **WSS endpoint** — it starts with `wss://`. Keep the endpoint private and out of source control — anyone with it can send requests against your node. ## Subscribe to the pool's Swap events The script below connects to the pool, reads the two tokens once so it can label and scale the amounts, then listens for `Swap` events and decodes each one. Save it as `monitor.js` and set `WSS_ENDPOINT` to your Chainstack endpoint. ```javascript monitor.js theme={"system"} const { ethers } = require("ethers"); // Your Ethereum mainnet WebSocket endpoint from Chainstack const WSS_ENDPOINT = "YOUR_CHAINSTACK_WSS_ENDPOINT"; // USDC/WETH 0.05% Uniswap V3 pool const POOL_ADDRESS = "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"; const POOL_ABI = [ "function token0() view returns (address)", "function token1() view returns (address)", "event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)", ]; const ERC20_ABI = [ "function symbol() view returns (string)", "function decimals() view returns (uint8)", ]; async function main() { // Pin the network so ethers does not re-poll the chain ID on every call. // On a load-balanced endpoint that polling can starve the log subscription. const network = ethers.Network.from("mainnet"); const provider = new ethers.WebSocketProvider(WSS_ENDPOINT, network, { staticNetwork: network, }); const pool = new ethers.Contract(POOL_ADDRESS, POOL_ABI, provider); // Read the two tokens once so we can label and scale the amounts. const [token0, token1] = await Promise.all([pool.token0(), pool.token1()]); const c0 = new ethers.Contract(token0, ERC20_ABI, provider); const c1 = new ethers.Contract(token1, ERC20_ABI, provider); const [sym0, dec0, sym1, dec1] = await Promise.all([ c0.symbol(), c0.decimals(), c1.symbol(), c1.decimals(), ]); console.log(`Monitoring ${sym0}/${sym1} pool ${POOL_ADDRESS}`); console.log("Waiting for swaps...\n"); pool.on("Swap", (sender, recipient, amount0, amount1, sqrtPriceX96, liquidity, tick, event) => { // amount0 and amount1 are signed deltas from the pool's point of view: // negative = the pool paid the token out (the trader received it), // positive = the pool took the token in (the trader paid it). const a0 = ethers.formatUnits(amount0, dec0); const a1 = ethers.formatUnits(amount1, dec1); const direction = amount1 < 0n ? `bought ${sym1} with ${sym0}` : `sold ${sym1} for ${sym0}`; console.log(`Swap in block ${event.log.blockNumber}`); console.log(` tx: ${event.log.transactionHash}`); console.log(` ${sym0}: ${a0}`); console.log(` ${sym1}: ${a1}`); console.log(` trader ${direction}, recipient ${recipient}\n`); }); } main().catch(console.error); ``` The `Swap` event reports two signed amounts from the pool's point of view. In one swap, exactly one amount is negative and the other positive — a negative amount is the token the pool paid out (what the trader received), and a positive amount is the token the pool took in (what the trader paid). The code reads `token0` and `token1` from the pool so it can attach the right symbol and scale each amount by that token's decimals with `ethers.formatUnits`. Dividing the two scaled amounts gives the trade's execution price. The event also carries `sqrtPriceX96`, `liquidity`, and `tick`, which describe the pool state after the swap. This tutorial focuses on the traded amounts. > [!NOTE] > > Passing `staticNetwork` is what keeps the subscription alive. Without it, ethers v6 periodically re-checks the chain ID; on a load-balanced endpoint those checks can reset the connection state and the log subscription silently stops delivering events. ## Run the monitor Run the script with Node: ```bash theme={"system"} node monitor.js ``` It prints a line per swap as trades settle on the pool. A short live run against the USDC/WETH pool produced the following — your output will show whatever swaps happen during your run: ```text theme={"system"} Monitoring USDC/WETH pool 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640 Waiting for swaps... Swap in block 25498633 tx: 0xcfb97eb4cd1accedbf1b40f7c47e71edb71b41baffe09ad4acaece1aae03b3f0 USDC: -51098.837883 WETH: 29.403937142747500544 trader sold WETH for USDC, recipient 0xBdb3ba9ffe392549E1f8658DD2630c141fDF47B6 Swap in block 25498633 tx: 0x7eafee6687d32598fe2659d7eb7dae3dd8577699e1fbe184234563d1cf673d29 USDC: -3474.837572 WETH: 2.0 trader sold WETH for USDC, recipient 0x46cec41E8c0f30F84c0A520ACa42D780345f8b0B Swap in block 25498634 tx: 0x7f7dbc41a2c4cc820a32c70f00fb101f1d93558a449e876c7b0ebcd2a57501db USDC: -35445.646081 WETH: 20.40471086773936128 trader sold WETH for USDC, recipient 0xBdb3ba9ffe392549E1f8658DD2630c141fDF47B6 Swap in block 25498635 tx: 0xdb3a7ce4c4df1bad8579590ff58c97fbc15062a8709820882c8464620b482178 USDC: -0.008659 WETH: 0.000004985410412159 trader sold WETH for USDC, recipient 0xA56Bf66fe8C07D8799b50f98477537BCE7c34Cd7 ``` Each line reflects a real settled trade: in the first swap the pool paid out 51,098.84 USDC and took in 29.40 WETH, so the trader sold WETH for USDC at roughly 1,738 USDC per WETH. ## Keep the WebSocket connection alive WebSocket connections drop — from idle timeouts, network blips, or node restarts. A monitor that must run unattended should recreate the provider when the socket closes. Wrap the setup in a function and reconnect on the `close` event: ```javascript theme={"system"} const { ethers } = require("ethers"); const WSS_ENDPOINT = "YOUR_CHAINSTACK_WSS_ENDPOINT"; const POOL_ADDRESS = "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"; const network = ethers.Network.from("mainnet"); const POOL_ABI = [ "event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)", ]; function startMonitor() { const provider = new ethers.WebSocketProvider(WSS_ENDPOINT, network, { staticNetwork: network, }); const pool = new ethers.Contract(POOL_ADDRESS, POOL_ABI, provider); pool.on("Swap", (sender, recipient, amount0, amount1, sqrtPriceX96, liquidity, tick, event) => { console.log(`Swap in block ${event.log.blockNumber}: ${event.log.transactionHash}`); // Decode the amounts here, as in monitor.js. }); // Recreate the provider if the socket drops. provider.websocket.onclose = () => { console.warn("WebSocket closed — reconnecting in 3s"); pool.removeAllListeners(); setTimeout(startMonitor, 3000); }; } startMonitor(); ``` `pool.removeAllListeners()` clears the handlers from the old provider before the new connection re-registers them, which avoids duplicate logging after a reconnect. ## Subscribe with a raw topic filter If you want to filter by `topic0` directly — without a `Contract` listener, or when you only care about the raw log — subscribe with an event filter. Under the hood ethers issues an `eth_subscribe` for `logs`, and you decode each log with an `Interface`: ```javascript theme={"system"} const { ethers } = require("ethers"); const WSS_ENDPOINT = "YOUR_CHAINSTACK_WSS_ENDPOINT"; const POOL_ADDRESS = "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"; const iface = new ethers.Interface([ "event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)", ]); const swapTopic = iface.getEvent("Swap").topicHash; // 0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67 const network = ethers.Network.from("mainnet"); const provider = new ethers.WebSocketProvider(WSS_ENDPOINT, network, { staticNetwork: network, }); provider.on({ address: POOL_ADDRESS, topics: [swapTopic] }, (log) => { const { args } = iface.parseLog(log); console.log(`Swap in block ${log.blockNumber} tx ${log.transactionHash}`); console.log(` amount0: ${args.amount0}`); console.log(` amount1: ${args.amount1}`); }); ``` Computing `swapTopic` from the event signature — rather than pasting the hash — keeps the filter correct if you change the event. The value it produces is `0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67`. ## Monitor a different pool or a Uniswap V2 pair To watch another Uniswap V3 pool, change `POOL_ADDRESS` to that pool's contract. Every V3 pool emits the same `Swap` event, so the `topic0` and decoding stay the same — only the token symbols and decimals differ, and the script reads those from the pool. You can find a pool's address on a block explorer or through the Uniswap factory's `getPool(tokenA, tokenB, fee)`. Uniswap V2 pairs emit a different `Swap` event, so a V2 monitor needs a different definition and `topic0`: * Signature — `Swap(address,uint256,uint256,uint256,uint256,address)` * topic0 — `0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822` A V2 pair reports four unsigned amounts instead of two signed ones — `amount0In`, `amount1In`, `amount0Out`, and `amount1Out`. The token that flows in has a non-zero `In` amount, and the token that flows out has a non-zero `Out` amount. Swap the event string in the ABI for the V2 shape and read the four fields: ```javascript theme={"system"} const PAIR_ABI = [ "event Swap(address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to)", ]; ``` ## Troubleshooting **No events arrive, but the script stays connected.** This is almost always the network-detection issue. Construct the provider with a static network — `new ethers.WebSocketProvider(url, network, { staticNetwork: network })` — so ethers stops re-polling the chain ID and the log subscription keeps delivering. **The connection drops after a while.** WebSocket connections are not permanent. Recreate the provider on the socket's `close` event so the monitor reconnects on its own. **No swaps show up at all.** Confirm the pool address and `topic0` are correct, and that the pool is actually trading — a low-volume pool can sit quiet for minutes. Check the pool on a block explorer to confirm recent `Swap` events. The USDC/WETH 0.05% pool used here trades many times per minute. **The amounts look wrong.** Scale each amount by the correct token decimals — USDC uses 6, WETH uses 18 — and make sure you're using the right event shape: Uniswap V3 emits two signed amounts, while Uniswap V2 emits four unsigned ones. ## Next steps * Track several pools at once by creating a listener per pool on the same provider. * Persist each swap to a database, or push large trades to an alerting channel. * Compute rolling volume and price from the decoded amounts. Learn more about the node behind this tutorial in [Global Nodes](/docs/global-elastic-node). # Ethereum: Redundant event listener with Python Source: https://docs.chainstack.com/docs/ethereum-redundant-event-listener-python-version Build a redundant Ethereum event listener in Python with automatic WebSocket reconnection and failover across multiple Chainstack node endpoints. **TLDR** * This tutorial shows how to create a redundant event listener in Python to reliably capture Ethereum events by connecting to multiple endpoints. * Using web3.py, it continuously checks for WETH “Transfer” events, mitigating the risk of missing events due to node downtime or latency issues. * Each event is tracked by a unique hash to avoid duplicates, ensuring only the first instance gets processed. * The result is a more robust, fault-tolerant DApp setup that remains accurate and responsive even during endpoint failures. ## Main article Event listeners play an important role in blockchain technology, enabling applications to respond to specific events emitted by smart contracts. These events are vital for decentralized applications (DApps), such as tracking token transfers and monitoring contract executions. In the Ethereum ecosystem, events are logged on the blockchain. They can be listened to by off-chain applications to trigger specific actions or update their state based on the latest blockchain activity. This tutorial aims to build a resilient Ethereum event listener using Python. By leveraging multiple endpoints across different regions, we aim to achieve better consistency in catching events and ensure redundancy in case of endpoint failures. This approach enhances the reliability and robustness of the event listener, making it more effective in real-world scenarios where network issues or endpoint downtimes might occur. This tutorial teaches you how to set up, configure, and run a resilient event listener that can handle events efficiently and reliably. > [!NOTE] > > ### JavaScript version > > We also have a JavaScript version of this tutorial, find it here: [Ethereum: BUIDLing a redundant event listener with ethers and web3.js](/docs/ethereum-redundant-event-llstener-ethers-web3js). ## Prerequisites Before diving into the tutorial, let's go over the basic setup: Ensure you have the following setup before starting the tutorial: * **Python:** Make sure Python is installed on your machine. You can download it from the [official Python website](https://www.python.org/). * **Virtual environment:** Using a virtual environment to manage your project dependencies is a good practice. To set up and activate a virtual environment, run the following commands in the terminal in the directory where you want to create the project: ```bash Bash theme={"system"} $ python -m venv venv $ source venv/bin/activate # On Windows, use `venv\Scripts\activate` ``` * **web3.py library:** Install the web3.py library using pip. Run the following command from the terminal in the directory where you want to create the project: ```bash Bash theme={"system"} pip install web3 ``` * **Ethereum node endpoints:** > [!NOTE] > > You need at least a Chainstack paid plan to deploy multiple nodes. Check the [Chainstack pricing](https://chainstack.com/pricing/) page for a coupon. ## The code Now that your environment is ready let's go over the code. In the directory where your project is create a new file named `main.py` and paste the following code: ```python main.py theme={"system"} import os import asyncio import logging from web3 import Web3 from web3.middleware import geth_poa_middleware # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') # List of endpoints endpoints = [ "ENDPOINT_1", "ENDPOINT_2", "ENDPOINT_3" ] # Filter for WETH transfer events logs_filter = { 'address': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', # WETH contract address 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], # Transfer event signature } # Set to track seen event identifiers to prevent duplicates seen_events = set() async def handle_event(event, endpoint): event_id = f"{event['transactionHash'].hex()}-{event['logIndex']}" if event_id not in seen_events: seen_events.add(event_id) logging.info(f"Event received first from {endpoint}: {event_id}") async def subscribe_to_logs(endpoint): while True: try: web3 = Web3(Web3.HTTPProvider(endpoint)) web3.middleware_onion.inject(geth_poa_middleware, layer=0) if not web3.is_connected(): logging.warning(f"Failed to connect to endpoint {endpoint}") await asyncio.sleep(5) continue logging.info(f"Connected to endpoint {endpoint}") event_filter = web3.eth.filter(logs_filter) while True: for event in event_filter.get_new_entries(): await handle_event(event, endpoint) await asyncio.sleep(1) except Exception as e: logging.error(f"Error when subscribing to logs from {endpoint}: {e}") await asyncio.sleep(5) # Wait before retrying async def main(): tasks = [subscribe_to_logs(endpoint) for endpoint in endpoints] await asyncio.gather(*tasks) if __name__ == "__main__": asyncio.run(main()) ``` ### Step-by-step code breakdown Let's break down the code step-by-step to understand how it works: 1. **Import libraries** The first step is to import the necessary libraries. We import `os`, `asyncio`, and `logging` for general purpose use, and `Web3` and `geth_poa_middleware` from the `web3` library to interact with the Ethereum blockchain. ```python Python theme={"system"} import os import asyncio import logging from web3 import Web3 from web3.middleware import geth_poa_middleware ``` 2. **Configure logging** Logging is configured to display informational messages with a specific format, which includes the timestamp, log level, and message. This helps in tracking the application's behavior and debugging issues. ```python Python theme={"system"} logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') ``` 3. **Define endpoints** A list of Ethereum node endpoints is defined. These endpoints should be URLs of your RPC nodes. Replace `ENDPOINT_1`, `ENDPOINT_2`, and `ENDPOINT_3` with the actual URLs of your Ethereum nodes. ```python Python theme={"system"} endpoints = [ "ENDPOINT_1", "ENDPOINT_2", "ENDPOINT_3" ] ``` 4. **Set up event filter** The event filter is configured to listen for WETH transfer events. The `address` field specifies the WETH contract address and the `topics` field contains the signature for the Transfer event. ```python Python theme={"system"} logs_filter = { 'address': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', # WETH contract address 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], # Transfer event signature } ``` 5. **Track seen events** A set named `seen_events` is used to track event identifiers that have already been processed. This helps in preventing duplicate handling of the same event. ```python Python theme={"system"} seen_events = set() ``` 6. **Handle events** The `handle_event` function processes the received events. It constructs a unique event identifier from the transaction hash and log index. If the event has not been seen before, it logs the event and adds the identifier to the `seen_events` set. ```python Python theme={"system"} async def handle_event(event, endpoint): event_id = f"{event['transactionHash'].hex()}-{event['logIndex']}" if event_id not in seen_events: seen_events.add(event_id) logging.info(f"Event received first from {endpoint}: {event_id}") ``` 7. **Subscribe to logs** The `subscribe_to_logs` function continuously tries to connect to an Ethereum node, sets up a filter for the specified events, and processes new entries as they arrive. If the connection fails, it waits for 5 seconds before retrying. ```python Python theme={"system"} async def subscribe_to_logs(endpoint): while True: try: web3 = Web3(Web3.HTTPProvider(endpoint)) web3.middleware_onion.inject(geth_poa_middleware, layer=0) if not web3.is_connected(): logging.warning(f"Failed to connect to endpoint {endpoint}") await asyncio.sleep(5) continue logging.info(f"Connected to endpoint {endpoint}") event_filter = web3.eth.filter(logs_filter) while True: for event in event_filter.get_new_entries(): await handle_event(event, endpoint) await asyncio.sleep(1) except Exception as e: logging.error(f"Error when subscribing to logs from {endpoint}: {e}") await asyncio.sleep(5) # Wait before retrying ``` 8. **Run the main function** The `main` function creates tasks for each endpoint and runs them concurrently using `asyncio.gather`. The script starts by calling `asyncio.run(main())`. ```python Python theme={"system"} async def main(): tasks = [subscribe_to_logs(endpoint) for endpoint in endpoints] await asyncio.gather(*tasks) if __name__ == "__main__": asyncio.run(main()) ``` ### Run the code To run the code, simply add your endpoints in the `endpoints` list and run the command in the terminal: ``` python3 main.py ``` It will start listening and logging; here is an example: ``` 2024-05-16 17:07:09,452 - INFO - Connected to endpoint https://nd-974-620-518.p2pify.com/ 2024-05-16 17:07:11,345 - INFO - Connected to endpoint https://nd-777-597-727.p2pify.com/ 2024-05-16 17:07:12,776 - INFO - Connected to endpoint https://ethereum-mainnet.core.chainstack.com/ 2024-05-16 17:07:13,805 - INFO - Event received first from https://ethereum-mainnet.core.chainstack.com/: 0xcab7994c8ff1495136db4966f4ed4556513540c6cf08dbd22e09fb3496acadef-1 2024-05-16 17:07:13,805 - INFO - Event received first from https://ethereum-mainnet.core.chainstack.com/: 0x649a1c6138ae7f3135d9ec2a24068ced7d1d2f00fd63781fa11153915d3f22b4-4 2024-05-16 17:07:13,805 - INFO - Event received first from https://ethereum-mainnet.core.chainstack.com/: 0x52c3c874bc8a8a7c34cdcfbf7e0adad89164b2069d1b445feb44504d350dee59-7 2024-05-16 17:07:13,805 - INFO - Event received first from https://ethereum-mainnet.core.chainstack.com/: 0x8755f753006a4bcf6b436bf1b377ca41d39c33219658426e8ae6a63b914279c3-30 2024-05-16 17:07:13,805 - INFO - Event received first from https://ethereum-mainnet.core.chainstack.com/: 0x4b5d3072d599cb4fc148a09c38b85bfa2729f5ad95e2b676cd15c8ebd4cff76d-43 2024-05-16 17:07:13,805 - INFO - Event received first from https://ethereum-mainnet.core.chainstack.com/: 0x54ebab7ec57c78c8182ab6797bc4a41f31c852e4d206ea13c59166592c44f41a-91 2024-05-16 17:07:13,805 - INFO - Event received first from https://ethereum-mainnet.core.chainstack.com/: 0x8f69c9aa219fcc6d29edec88d72de91d7ab4bbb2f3854e7d4c339deb313badf9-128 ``` You can find the full event in `event` in `handle_event` and you can use it for further processing: ``` async def handle_event(event, endpoint): event_id = f"{event['transactionHash'].hex()}-{event['logIndex']}" if event_id not in seen_events: seen_events.add(event_id) logging.info(f"Event received first from {endpoint}: {event_id}") ``` ## Conclusion Building a resilient Ethereum event listener is crucial for maintaining reliable and consistent decentralized applications. Using multiple endpoints across different regions can ensure better event capture and redundancy, making your event listener robust against network issues and endpoint downtimes. This tutorial guided you through setting up, configuring, and running a resilient event listener using Python and web3.py. With this setup, you are well-equipped to handle blockchain events efficiently and reliably, enhancing the effectiveness of your DApps. # Ethereum: Redundant event listener with ethers/web3.js Source: https://docs.chainstack.com/docs/ethereum-redundant-event-llstener-ethers-web3js Build a redundant Ethereum event listener using ethers.js and web3.js with automatic reconnection and failover across multiple Chainstack endpoints. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). * Monitoring Ethereum events is crucial for accurate data and a smooth user experience. * Subscribing to multiple nodes in parallel prevents missed events during node downtime, ensuring reliability. * Using web3.js or ethers.js with environment variables streamlines setup and protects your endpoints. * Redundant event listeners are key for mission-critical DApps, providing robust real-time updates. ## Main article When building on Ethereum, reliably monitoring and reacting to events is important. Events like token transfers often trigger downstream actions or application updates. Missing important events can lead to data inconsistencies, incorrect user balances, and poor user experience. While Ethereum nodes provide access to the blockchain and its events, relying on a single node for event listening can be risky. Nodes can experience downtime, connectivity issues, or other failures, resulting in missed events and application functionality disruptions. This challenge is amplified in mission-critical applications or those handling high-value transactions, where missing events can have severe consequences. > [!NOTE] > > ### Python version > > We also have a Python version of this tutorial, find it here: [How to set up a redundant Ethereum event listener with Python](/docs/ethereum-redundant-event-listener-python-version). To address this issue, the concept of a redundant event listener emerges as a solution. By subscribing to multiple Ethereum nodes simultaneously, a redundant event listener increases the chances of receiving events even if one or more nodes fail. This approach introduces redundancy and fault tolerance, ensuring that important events are not missed and that the application remains responsive and up-to-date. This tutorial will explore how to BUIDL a redundant Ethereum event listener using Node.js and the popular web3.js and ethers.js libraries using WSS RPC nodes. Chainstack Global Nodes already have redundancy and fault tolerance built in, but we can improve the robustness of this event listener by using Chainstack [Trader Node](/docs/trader-node). This architecture will allow your app to place event listeners in various regions worldwide, ensuring redundancy and accuracy. > [!NOTE] > > Learn everything about event logs in [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial). ## Project overview This tutorial shows you how to build a redundant Ethereum event listener using Node.js, web3.js, and ethers.js. This application aims to ensure reliable and fault-tolerant monitoring of events on the Ethereum blockchain, specifically the transfer events of the Wrapped Ether (WETH) contract. The application achieves redundancy by establishing multiple WebSocket connections to Ethereum RPC nodes, using Chainstack global and regional infrastructure. By subscribing to multiple nodes simultaneously, the application increases its chances of receiving events even if one or more nodes experience downtime or connectivity issues. The logic behind the application is as follows: 1. It defines an array of WebSocket endpoints from various Ethereum node providers. 2. It creates a filter object that specifies the contract address of the WETH contract and the topic (event signature) for the "Transfer" event, which is the event we want to listen for. 3. It initializes a `Set` data structure to track unique event identifiers and prevent duplicate event processing. 4. The application defines a function called `subscribeToLogs` that takes an endpoint as input, creates a new Web3 instance with that endpoint, and sets up a WebSocket subscription to listen for logs (events) matching the defined filter. 5. When a new event is received, the function checks if the event identifier (a combination of the transaction hash and log index in web3.js and transaction hash and block in ethers) has been seen before. If not, it logs the event data to the console and adds the event identifier to the `Set` to mark it as processed. 6. The function also handles subscription errors by logging them to the console. By implementing this redundant event listener, the application ensures that important events, such as WETH transfers, are not missed, even in the face of node failures or connectivity issues. This increased reliability and fault tolerance can be crucial for applications that rely heavily on monitoring and reacting to Ethereum events in real-time. ## Prerequisites To start with a JavaScript development project, you'll need to install `node.js`, a powerful JavaScript runtime environment that enables developers to run JavaScript code outside a web browser. For this project, it's recommended to use at least version 18. You can [download it from their website](https://nodejs.org/en/download/). With `node.js` installed, you're ready to start using JavaScript. Now, you can set up your nodes. > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) > [!NOTE] > > Keep in mind that you need at least a Chainstack paid plan to deploy multiple nodes. Check the [Chainstack pricing](https://chainstack.com/pricing/) page for a coupon. For this project, it is recommended to deploy at least three RPC nodes, one [Global Node](/docs/global-elastic-node), and two [Trader Nodes](/docs/trader-node). Remember that there is no limit on how many nodes you can use in the project, and can also mix different providers. Once the infrastructure is set up, you can work on a new project. ## Create a new JavaScript project First, we'll create a new Node.js project and initialize it with a `package.json` file. Open your terminal, navigate to the desired directory, and run the following command: ``` npm init -y ``` This will create a `package.json` file with default settings. Next, we'll install the required dependencies, which are the `web3.js`, `ethers.js` and the `dotenv` package for loading environment variables from a `.env` file: ``` npm install web3 ethers dotenv ``` After installing the dependencies, create a new file called `.env` in the root directory of your project. This file will store your environment variables, including the WebSocket endpoints for the Ethereum nodes. In the `.env` file, you can define the endpoints like this: ``` ENDPOINT_1=YOUR_CHAINSTACK_GLOBAL_NODE ENDPOINT_2=YOUR_CHAINSTACK_TRADER_NODE ENDPOINT_3=YOUR_CHAINSTACK_TRADER_NODE ``` > [!NOTE] > > Rememeber that we are using WSS endpoints. In your Node.js script, you'll need to load the environment variables from the `.env` file using the `dotenv` package. At the top of your script, add the following line: ```javascript Javascript theme={"system"} require('dotenv').config(); ``` This will load the environment variables from the `.env` file and make them accessible in your script using `process.env`. Now, instead of hardcoding the endpoints in your script, you can read them from the environment variables: ```javascript Javascript theme={"system"} const endpoints = [ process.env.ENDPOINT_1, process.env.ENDPOINT_2, process.env.ENDPOINT_3, ]; ``` By following these steps, you'll have a basic Node.js project set up with the required dependencies, and you'll be able to store and access your WebSocket endpoints securely using environment variables in a `.env` file. > [!NOTE] > > Check out [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project) to learn more about managing Node.js projects. ## Web3.js code This section will guide you through this project using web3.js, and the following section will go over ethers.js. This way, you have options based on your favorite library. The project is already set up, so let's create a new file named `index.js` and paste the following code: ```javascript index.js theme={"system"} const { Web3 } = require("web3"); require('dotenv').config(); // Add this in case you didn't earlier // List of RPC endpoints const endpoints = [ process.env.ENDPOINT_1, process.env.ENDPOINT_2, process.env.ENDPOINT_3, ]; // Filter for WETH transfer events const logsFilter = { address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH contract address topics: [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", ], // Transfer event signature }; // Set to track seen event identifiers to prevent duplicates const seenEvents = new Set(); async function subscribeToLogs(endpoint) { const web3 = new Web3(endpoint); try { const subscription = await web3.eth.subscribe("logs", logsFilter); console.log(`Subscription created with ID: ${subscription.id}`); subscription.on("data", (log) => { const eventId = `${log.transactionHash}-${log.logIndex}`; if (!seenEvents.has(eventId)) { seenEvents.add(eventId); console.log(`Event received first from ${endpoint.slice(0, 33)}:`, log); } }); subscription.on("error", (error) => { console.error(`Error when subscribing to logs from ${endpoint}:`, error); }); } catch (error) { console.error(`Error setting up subscription from ${endpoint}:`, error); } } // Initialize subscriptions for all endpoints endpoints.forEach((endpoint) => { subscribeToLogs(endpoint); }); ``` ### Web3 code explanation This code sets up a redundant Ethereum event listener using the Web3.js library and environment variables for storing WebSocket endpoints. Here's what's happening: 1. The code starts by importing the `Web3` object from the `web3` library and configuring the `dotenv` package to load environment variables from a `.env` file. 2. The WebSocket endpoints for various Ethereum nodes are read from the environment variables `ENDPOINT_1`, `ENDPOINT_2`, and `ENDPOINT_3` and stored in the `endpoints` array. 3. The `logsFilter` object is defined, which specifies the contract address of the Wrapped Ether (WETH) contract and the topic (event signature) for the "Transfer" event. This filter will be used to subscribe to only the desired events. 4. A `Set` data structure called `seenEvents` is initialized to track unique event identifiers and prevent duplicate event processing. 5. The `subscribeToLogs` async function is defined, which takes an endpoint as input and sets up a WebSocket subscription to listen for logs (events) matching the `logsFilter`. 6. Inside the `subscribeToLogs` function, a new `Web3` instance is created using the provided endpoint, and the `web3.eth.subscribe("logs", logsFilter)` method is called to create a subscription. 7. When a new event is received through the subscription, the `subscription.on("data", ...)` callback is triggered. The callback generates a unique event identifier using the transaction hash and log index and checks if this identifier has been seen before in the `seenEvents` Set. 8. If the event identifier is new, it is added to the `seenEvents` Set and the event data is logged to the console along with the endpoint from which the event was received. 9. The `subscription.on("error", ...)`callback is set up to handle any errors during the subscription process, logging the error into the console. 10. Finally, the `endpoints.forEach(subscribeToLogs)` line iterates over the `endpoints` array and calls the `subscribeToLogs` function for each endpoint creates multiple WebSocket subscriptions to different Ethereum nodes. By implementing this redundant event listener, the application can monitor WETH transfer events reliably and consistently, even if one or more Ethereum nodes experience downtime or connectivity issues. Using environment variables to store endpoints makes managing and updating endpoint configurations easier without modifying the code directly. ## Ethers.js code Here, we'll cover the ethers.js version of this project; you can create an entire new Node.js project or just make a new JavaScript file, then paste this code: ``` const { ethers } = require("ethers"); require('dotenv').config(); // List of RPC endpoints const endpoints = [ process.env.ENDPOINT_1, process.env.ENDPOINT_2, process.env.ENDPOINT_3, ]; const contractAddress = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; // WETH contract address const contractABI = [ "event Transfer(address indexed from, address indexed to, uint amount)", ]; // Set to track seen event identifiers to prevent duplicates const seenEvents = new Set(); async function subscribeToEvents(endpoint) { const provider = new ethers.WebSocketProvider(endpoint); const contract = new ethers.Contract(contractAddress, contractABI, provider); // Subscribe to the Transfer event contract.on("Transfer", (from, to, amount, event) => { const eventId = `${event.log.transactionHash}-${event.log.blockNumber}`; if (!seenEvents.has(eventId)) { seenEvents.add(eventId); console.log(`Event received first from ${endpoint.slice(0, 33)}:`); console.log( `Transfer from ${from} to ${to} of ${ethers.formatEther( amount.toString() )} ETH` ); // console.log("Transaction details:", event); } }); // Handle errors provider.on("error", (error) => { console.error(`WebSocket error from ${endpoint}:`, error); }); } // Initialize subscriptions for all endpoints endpoints.forEach((endpoint) => { subscribeToEvents(endpoint); }); ``` > [!NOTE] > > Ethers.js now supports `ChainstackProvider`, learn more about it by reading [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ### Ethers.js code breakdown This code sets up a redundant Ethereum event listener using the `ethers.js` library and environment variables for storing WebSocket endpoints. Here's what's happening: 1. The code starts by importing the `ethers` object from the `ethers.js` library and configuring the `dotenv` package to load environment variables from a `.env` file. 2. The WebSocket endpoints for various Ethereum nodes are read from the environment variables `ENDPOINT_1`, `ENDPOINT_2`, and `ENDPOINT_3` and stored in the `endpoints` array. 3. The contract address of the Wrapped Ether (WETH) contract and its contract ABI (Application Binary Interface) are defined as constants. 4. A `Set` data structure called `seenEvents` is initialized to track unique event identifiers and prevent duplicate event processing. 5. The `subscribeToEvents` async function is defined, which takes an endpoint as input and sets up an event subscription for the WETH contract's "Transfer" event. 6. Inside the `subscribeToEvents` function, a new `WebSocketProvider` instance is created using the provided endpoint, and a `Contract` instance is created using the contract address, ABI, and provider. 7. The `contract.on("Transfer", ...)` method is used to subscribe to the "Transfer" event of the WETH contract. 8. When a new "Transfer" event is received, the callback function is triggered, generating a unique event identifier using the transaction hash and block number. 9. If the event identifier is new, it is added to the `seenEvents` Set, and the event details (sender, recipient, and amount transferred) are logged to the console along with the endpoint from which the event was received. 10. The `provider.on("error", ...)` callback is set up to handle any errors during the WebSocket connection, logging the error to the console. 11. Finally, the `endpoints.forEach(subscribeToEvents)` line iterates over the `endpoints` array and calls the `subscribeToEvents` function for each endpoint creates multiple WebSocket connections and event subscriptions to different Ethereum nodes. By implementing this redundant event listener with `ethers.js`, the application can monitor WETH transfer events reliably and consistently, even if one or more Ethereum nodes experience downtime or connectivity issues. Using environment variables to store endpoints makes managing and updating endpoint configurations easier without modifying the code directly. ## Conclusion Building reliable and fault-tolerant systems is crucial when developing applications on the Ethereum blockchain. By implementing a redundant event listener using Node.js and libraries like web3.js or ethers.js, you can consistently ensure that your application receives important events, such as token transfers, without disruptions. This approach mitigates the risks associated with relying on a single node. It introduces redundancy by subscribing to multiple nodes simultaneously, increasing the chances of receiving events even if some nodes fail. The tutorial provided a step-by-step guide to setting up a project, configuring environment variables, and implementing the redundant event listener using web3.js and ethers.js. With this solution, your Ethereum-based application can remain responsive and up-to-date and provide a seamless user experience, even in node failures or connectivity issues. # Simulate a Uniswap swap before broadcasting Source: https://docs.chainstack.com/docs/ethereum-simulate-a-uniswap-swap Preview a Uniswap swap's output amount and confirm the trade would succeed before you send it, using eth_call, Uniswap V2 getAmountsOut, V3 QuoterV2, and eth_simulateV1 with ethers.js v6 on a Chainstack Ethereum node. **TLDR:** * Simulating a swap previews its output and confirms it would succeed before you sign, spend gas, or broadcast anything — it is a read-only [`eth_call`](/reference/ethereum-ethcall) against your Ethereum node. * For a price, call the Uniswap V2 router `getAmountsOut` or the Uniswap V3 `QuoterV2` `quoteExactInputSingle` — both return the expected output amount for your input. * To confirm the exact trade would go through, `eth_call` the router's `swapExactETHForTokens` with your real `amountOutMin` and deadline — a revert such as `UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT` means the real transaction would fail too. * To preview exact asset changes or simulate from an account that doesn't hold the funds yet, use `eth_simulateV1` with state overrides and `traceTransfers`. * Every example uses ethers.js v6 and runs against Ethereum mainnet through a Chainstack node. ## How swap simulation works Simulating a swap runs the trade against the current chain state and returns what would happen — the output amount and whether it would revert — without a signature, without spending gas, and without broadcasting a transaction. You do it with a single `eth_call` (or `eth_simulateV1`) to your node, so you can check a trade as often as you like at no cost and with no onchain footprint. A swap simulation answers three separate questions, and each has its own tool: * How many tokens will I receive — a quote from the Uniswap V2 router `getAmountsOut` or the V3 `QuoterV2`. * Will the exact swap succeed with my slippage limit and deadline — an `eth_call` against the router's swap function, which reverts with the same reason a real transaction would. * What exactly changes, and can I preview it from an account that doesn't hold the funds yet — `eth_simulateV1` with state overrides and asset-change tracing. The examples below buy USDC with 1 ETH on Ethereum mainnet. They use these mainnet addresses: * WETH — `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` (18 decimals) * USDC — `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` (6 decimals) * Uniswap V2 router 02 — `0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D` * Uniswap V3 QuoterV2 — `0x61fFE014bA17989E743c5F6cB21bF9697530B21e` > [!NOTE] > > The output amounts shown come from live mainnet runs. Pool reserves change with every block, so your exact numbers will differ. ## Prerequisites * Node.js 18 or later. * The ethers.js v6 library — install it with `npm install ethers`. * A Chainstack Ethereum node endpoint — [deploy a node](/docs/manage-your-networks) and copy the HTTPS endpoint from the node's **Access and credentials** tab. See [view node access and credentials](/docs/manage-your-node) for the exact location. Set the endpoint as `RPC_URL` in each script below. Keep it private — treat it like a password. ## Preview the output with the Uniswap V2 router The fastest quote is the Uniswap V2 router's `getAmountsOut` — a view function that reads the pool reserves and returns the amount you would receive for a given input along a token path. Calling it is a read-only `eth_call`, so it needs no account, no funds, and no gas. `getAmountsOut` returns an array of amounts, one per hop in the path. The first element is your input and the last is your output. ```js v2-quote.js theme={"system"} const { ethers } = require("ethers"); const RPC_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(RPC_URL); const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; const V2_ROUTER = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"; const routerAbi = [ "function getAmountsOut(uint256 amountIn, address[] path) view returns (uint256[] amounts)" ]; async function main() { const router = new ethers.Contract(V2_ROUTER, routerAbi, provider); const amountIn = ethers.parseEther("1"); // 1 WETH, 18 decimals const path = [WETH, USDC]; const amounts = await router.getAmountsOut(amountIn, path); const amountOut = amounts[amounts.length - 1]; console.log(`Input: ${ethers.formatUnits(amounts[0], 18)} WETH`); console.log(`Output: ${ethers.formatUnits(amountOut, 6)} USDC`); // USDC has 6 decimals } main().catch(console.error); ``` Example output: ```text theme={"system"} Input: 1.0 WETH Output: 1740.067173 USDC ``` The `router.getAmountsOut(...)` call is an `eth_call` under the hood. By default it runs against the latest block; pass a block tag to simulate against past state — for example `router.getAmountsOut(amountIn, path, { blockTag: 25498586 })`. > [!TIP] > > For a route through more than one pool, add the intermediate tokens to `path`, for example `[WETH, USDC, someToken]`. `getAmountsOut` returns the amount after every hop. ## Preview the output with the Uniswap V3 QuoterV2 For Uniswap V3, the `QuoterV2` contract simulates a swap through the pool's concentrated-liquidity math and returns the output amount plus the resulting price. Unlike a V2 reserve formula, it walks the pool's ticks, so it accounts for how your trade moves the price. `quoteExactInputSingle` takes a single struct and returns four values — the output amount, the pool price after the swap (`sqrtPriceX96After`), how many initialized ticks the swap crossed, and a gas estimate. It is not a `view` function — it reverts internally to return its result — so call it with `staticCall` to run it through `eth_call`. Uniswap V3 pools exist at several fee tiers — 100 (0.01%), 500 (0.05%), 3000 (0.3%), and 10000 (1%). WETH/USDC is deepest at 500. Try the tiers you care about and take the best quote. ```js v3-quote.js theme={"system"} const { ethers } = require("ethers"); const RPC_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(RPC_URL); const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; const QUOTER_V2 = "0x61fFE014bA17989E743c5F6cB21bF9697530B21e"; const quoterAbi = [ "function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96) params) returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)" ]; async function main() { const quoter = new ethers.Contract(QUOTER_V2, quoterAbi, provider); const amountIn = ethers.parseEther("1"); // 1 WETH // QuoterV2 is not a view function, so use staticCall to run it through eth_call. const [amountOut, sqrtPriceX96After, ticksCrossed, gasEstimate] = await quoter.quoteExactInputSingle.staticCall({ tokenIn: WETH, tokenOut: USDC, amountIn, fee: 500, // 0.05% pool sqrtPriceLimitX96: 0n // 0 = no price limit }); console.log(`Output: ${ethers.formatUnits(amountOut, 6)} USDC`); console.log(`Ticks crossed: ${ticksCrossed}`); console.log(`Gas estimate: ${gasEstimate}`); } main().catch(console.error); ``` Example output: ```text theme={"system"} Output: 1740.414231 USDC Ticks crossed: 1 Gas estimate: 90075 ``` ## Confirm the swap would succeed with eth\_call A quote tells you the price, but it does not run the swap you are about to send. To confirm the exact transaction would go through — with your slippage guard, deadline, and recipient — simulate the router's swap function itself with `eth_call`. If the call returns, the real transaction would succeed and you get the exact output; if it reverts, the real transaction would revert with the same reason. This example encodes `swapExactETHForTokens` and sends it through `provider.call`, which is `eth_call`. Because you buy with ETH, `value` carries the input and the path starts with WETH — the router wraps the ETH for you. ```js simulate-swap.js theme={"system"} const { ethers } = require("ethers"); const RPC_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(RPC_URL); const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; const V2_ROUTER = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"; // The wallet you would swap from. const FROM = "0xYourWalletAddress"; const router = new ethers.Interface([ "function swapExactETHForTokens(uint256 amountOutMin, address[] path, address to, uint256 deadline) payable returns (uint256[] amounts)" ]); async function main() { const amountIn = ethers.parseEther("1"); // 1 ETH in const path = [WETH, USDC]; const deadline = Math.floor(Date.now() / 1000) + 1200; // 20 minutes // amountOutMin is your slippage guard. Set it to the minimum you will accept, // for example the quote minus 1%: quotedOut * 99n / 100n. const amountOutMin = 0n; const data = router.encodeFunctionData("swapExactETHForTokens", [ amountOutMin, path, FROM, deadline ]); try { const result = await provider.call({ from: FROM, to: V2_ROUTER, value: amountIn, data }); const [amounts] = router.decodeFunctionResult("swapExactETHForTokens", result); console.log("Simulation succeeded - the swap would go through."); console.log(`Output: ${ethers.formatUnits(amounts[amounts.length - 1], 6)} USDC`); } catch (err) { console.log("Simulation reverted - the swap would fail:"); console.log(err.reason ?? err.shortMessage ?? err.message); } } main().catch(console.error); ``` Example output when the trade is viable: ```text theme={"system"} Simulation succeeded - the swap would go through. Output: 1740.067173 USDC ``` Set `amountOutMin` above the quote — for example `ethers.parseUnits("999999999", 6)` — and the same call reverts with the reason the real transaction would return: ```text theme={"system"} Simulation reverted - the swap would fail: UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT ``` `eth_call` runs the swap logic against current state without moving any funds. Whether the `value` must be backed by a real balance depends on your node's configuration, so setting `from` to a funded wallet is the reliable choice. To simulate from an account that does not hold the ETH, override its balance with `eth_simulateV1`. ## Preview asset changes with eth\_simulateV1 `eth_simulateV1` runs a full transaction (or a sequence of them) and can override account state first — balances, code, storage — then report every asset movement. Use it to simulate a swap from any address without funding it, and to read the exact amounts that would move. This example gives a chosen address 10 ETH with a state override, simulates the swap, and reads the USDC received from the transfer logs. `traceTransfers: true` surfaces every asset movement — including native ETH — as a `Transfer` log, so you can total the deltas directly. ```js simulate-v1.js theme={"system"} const { ethers } = require("ethers"); const RPC_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(RPC_URL); const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; const V2_ROUTER = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"; const ME = "0x2222222222222222222222222222222222222222"; // any address you want to simulate as const iface = new ethers.Interface([ "function swapExactETHForTokens(uint256 amountOutMin, address[] path, address to, uint256 deadline) payable returns (uint256[] amounts)" ]); async function main() { const amountIn = ethers.parseEther("1"); const path = [WETH, USDC]; const deadline = Math.floor(Date.now() / 1000) + 1200; const data = iface.encodeFunctionData("swapExactETHForTokens", [0n, path, ME, deadline]); const [block] = await provider.send("eth_simulateV1", [ { blockStateCalls: [ { // Override state: give ME 10 ETH so the call has funds, no signing needed. stateOverrides: { [ME]: { balance: "0x" + ethers.parseEther("10").toString(16) } }, calls: [ { from: ME, to: V2_ROUTER, value: "0x" + amountIn.toString(16), data } ] } ], traceTransfers: true // include asset movements as logs }, "latest" ]); const call = block.calls[0]; console.log(`Status: ${call.status === "0x1" ? "success" : "revert"}`); console.log(`Gas used: ${parseInt(call.gasUsed, 16)}`); const [amounts] = iface.decodeFunctionResult("swapExactETHForTokens", call.returnData); console.log(`Output: ${ethers.formatUnits(amounts[amounts.length - 1], 6)} USDC`); // traceTransfers surfaces every asset movement as a Transfer log. const TRANSFER = ethers.id("Transfer(address,address,uint256)"); for (const log of call.logs) { const isUsdc = log.address.toLowerCase() === USDC.toLowerCase(); if (log.topics[0] === TRANSFER && isUsdc) { const to = ethers.getAddress("0x" + log.topics[2].slice(26)); if (to.toLowerCase() === ME.toLowerCase()) { console.log(`Asset change: +${ethers.formatUnits(BigInt(log.data), 6)} USDC to ${to}`); } } } } main().catch(console.error); ``` Example output: ```text theme={"system"} Status: success Gas used: 113105 Output: 1739.113587 USDC Asset change: +1739.113587 USDC to 0x2222222222222222222222222222222222222222 ``` > [!WARNING] > > If you set `validation: true`, `eth_simulateV1` enforces the base-fee rule, so each call must also carry `maxFeePerGas` and `maxPriorityFeePerGas` — otherwise it returns `max fee per gas less than block base fee`. For a read-only preview, leave validation off (the default). ## Choose the right method | Method | Answers | Executes the swap? | Needs funds? | | - | - | - | - | | V2 `getAmountsOut` | Expected output for a V2 route | No — reads reserves, applies the V2 formula | No | | V3 `quoteExactInputSingle` | Expected output and price impact for a V3 pool | Simulated — reverts internally to return the quote | No | | `eth_call` on the swap | Whether the exact swap would succeed, and its output | Yes | Only if your node enforces balances | | `eth_simulateV1` | Output and every asset change, from any account | Yes | No — override balances and allowances | Start with a quote to see the price, then simulate the swap with `eth_call` to confirm it would succeed with your slippage and deadline. Reach for `eth_simulateV1` when you need exact asset changes or want to simulate from an account without first funding it. ## Wrapping up You now have four ways to inspect a Uniswap trade before committing to it — a V2 quote, a V3 quote, a full `eth_call` execution check, and an `eth_simulateV1` asset-change preview — all read-only against your Chainstack Ethereum node. Compute `amountOutMin` from a fresh quote and your slippage tolerance, confirm the swap with a simulation, and only then sign and broadcast the real transaction. # Ethereum tooling Source: https://docs.chainstack.com/docs/ethereum-tooling Set up MetaMask, Hardhat, Foundry, and development tools to build and deploy smart contracts on Ethereum through your Chainstack RPC node endpoints. > [!NOTE] > > ### Run nodes on Chainstack > > [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required. You can sign up with your GitHub, X, Google, or Microsoft account. Get started with a [reliable Ethereum RPC endpoint](https://chainstack.com/build-better-with-ethereum/) to use the tools below. ## Wallets Wallets allow the addition of custom RPC endpoints either by directly modifying the settings of an existing network (e.g., Ethereum) or by adding a custom network and specifying a custom RPC endpoint. To obtain the address of your RPC endpoint, navigate to the node details on the Chainstack console and locate the Execution client HTTPS endpoint in the Access and credentials section. When adding a new network, you need a chain ID which you can on resources like [chainlist.org](http://chainlist.org/) or [chainlist.wtf](http://chainlist.wtf). ### MetaMask ![MetaMask screenshot](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/b22e56c-image.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=7ada1bdd0ca436ea57927f93b0c0d808) In the section **Access and credentials** of a Chainstack Node, press **Connect wallet**. This will prompt you to confirm a new network details. To add a network manually, go to **Networks** and add a new network with a required chain ID and your Chainstack RPC endpoint. ### Trust Wallet ![TW screenshot](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/0cdce86-image.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=8e64b091f885a648f72de225f2ae7cf3) To add a custom RPC to Trust Wallet, open the wallet and navigate to the **Settings** section. Look for the **Network** section to add a custom network with your Chainstack RPC endpoint. ### Rainbow ![Rainbow screenshot](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/66cf357-image.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=e60ccc8043227eb2083c9dd0b868d592) To add a custom RPC to Rainbow Wallet, open the wallet and navigate to the **Settings** section. Look for the **Networks** to add your Chainstack RPC endpoint. ### Rabby ![Rabby screenshot](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/6830bdd-image.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=c04ad8ff2b69bf58c03254230e288f12) To add a custom RPC to Rabby, open the wallet and go to **Settings** > **Modify RPC URL** to replace the node Rabby uses for a chain it already supports. See [Adding a custom RPC endpoint to Rabby](/docs/adding-a-custom-rpc-endpoint-to-rabby). ### Frame Desktop ![Frame screenshot](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/0e40729-image.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1970870ba2747dcdce9d81c82d609097) To add a custom RPC to Frame Desktop, open the wallet and navigate to the **Chains** section. Click on the chain details to add your Chainstack RPC endpoint. ## IDEs Cloud-based IDEs provide the flexibility to use injected providers. MetaMask is the most commonly used one. By adding a Chainstack RPC node in MetaMask and connecting to the wallet in your IDE, you can seamlessly interact with the network through a Chainstack node. ### Remix IDE To enable Remix IDE to interact with the network through a Chainstack node, you can follow these steps: 1. Install and set up MetaMask to use a Chainstack node for interaction. You can refer to the guide on Interacting through MetaMask for detailed instructions. 2. Open Remix IDE and navigate the [Connect wallet](https://remix.ethereum.org/) button. Here, select **Injected Web3 Provider** and then **MetaMask**. ## Programming languages and libraries ### Communication protocols WebSockets and HTTP are essential communication protocols in web applications. WebSockets enable two-way, persistent communication between a client and a server, useful for real-time price feeds, live transaction monitoring, and event notifications. In contrast, HTTP follows a one-way, request-response model, ideal for retrieving periodic price updates and transaction histories. ### ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js) and Ethereum nodes deployed with Chainstack. 1. Install ethers.js ```bash Bash theme={"system"} npm install ethers ``` 2. Initialize HTTP or WebSocket provider ```javascript Javascript theme={"system"} import { ethers } from 'ethers'; // Using HTTP provider const httpProvider = new ethers.JsonRpcProvider("/*YOUR_HTTP_CHAINSTACK_ENDPOINT*/"); httpProvider.getBlockNumber().then(console.log); // Using WebSocket provider const wsProvider = new ethers.WebSocketProvider("/*YOUR_WS_CHAINSTACK_ENDPOINT*/"); wsProvider.getBlockNumber().then((blockNumber) => { console.log(blockNumber); wsProvider.destroy(); }); ``` ### web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Ethereum nodes deployed with Chainstack. 1. Install web3.py ```bash Bash theme={"system"} pip install web3 ``` 2. Initialize HTTP or WebSocket provider ```python Python theme={"system"} from web3 import Web3 # Using HTTP provider http_web3 = Web3(Web3.HTTPProvider("/*YOUR_HTTP_CHAINSTACK_ENDPOINT*/")) print(http_web3.eth.block_number) # Using WebSocket provider with Web3(Web3.WebsocketProvider("/*YOUR_WS_CHAINSTACK_ENDPOINT*/")) as ws_web3: print(ws_web3.eth.block_number) ``` ### Nethereum (.NET) Build DApps using [Nethereum](https://github.com/Nethereum/Nethereum) and Ethereum nodes deployed with Chainstack. 1. Install Nethereum 2. Initialize HTTP or WebSocket provider ```csharp C# theme={"system"} using Nethereum.JsonRpc.WebSocketClient; using Nethereum.Web3; namespace TutorialWeb3 { internal class Program { static void Main(string[] args) { // Using HTTP provider var httpClient = new Web3("/*YOUR_HTTP_CHAINSTACK_ENDPOINT*/"); var httpBlockNumber = httpClient.Eth.Blocks.GetBlockNumber.SendRequestAsync().GetAwaiter().GetResult(); Console.WriteLine($"HTTP Block Number: {httpBlockNumber}"); // Using WebSocket provider using (var wsClient = new WebSocketClient("/*YOUR_WS_CHAINSTACK_ENDPOINT*/")) { var web3Ws = new Web3(wsClient); var wsBlockNumber = web3Ws.Eth.Blocks.GetBlockNumber.SendRequestAsync().GetAwaiter().GetResult(); Console.WriteLine("WS Block Number: " + wsBlockNumber); } } } } ``` ### viem Build DApps using [viem](https://github.com/wevm/viem) and Ethereum nodes deployed with Chainstack. 1. Install viem 2. Initialize HTTP or WebSocket provider ```typescript Typescript theme={"system"} import { createPublicClient, http, webSocket } from 'viem'; import { mainnet } from 'viem/chains'; // Using HTTP provider const httpClient = createPublicClient({ chain: mainnet, transport: http("/*YOUR_HTTP_CHAINSTACK_ENDPOINT*/"), }); const blockNumber = await httpClient.getBlockNumber(); console.log(blockNumber); // Using WebSocket provider const wsClient = createPublicClient({ chain: mainnet, transport: webSocket("/*YOUR_WS_CHAINSTACK_ENDPOINT*/"), }) const wsBlockNumber = await wsClient.getBlockNumber(); console.log(wsBlockNumber); await wsClient.transport.getRpcClient().then((rpcClient) => { rpcClient.close(); }); ``` ## Development frameworks and toolkits ### Foundry Configure [Foundry](https://github.com/foundry-rs/foundry) to deploy contracts using Chainstack Ethereum nodes. 1. Install Foundry ```bash Bash theme={"system"} curl -L https://foundry.paradigm.xyz | bash foundryup ``` 2. Configure environment in Foundry Create a new Foundry project: ```bash Bash theme={"system"} forge init my_project cd my_project ``` Create a `.env` file in your project root and add your Chainstack endpoint and private key: ```bash Bash theme={"system"} CHAINSTACK_MAINNET_ENDPOINT=https://your-chainstack-mainnet-url CHAINSTACK_DEVNET_ENDPOINT=https://your-chainstack-devnet-url PRIVATE_KEY_MAINNET=your-mainnet-private-key PRIVATE_KEY_DEVNET=your-devnet-private-key ``` You need to load the environment variables into your shell session: ```bash Bash theme={"system"} source .env echo $CHAINSTACK_DEVNET_ENDPOINT echo $PRIVATE_KEY_DEVNET ``` Update your `foundry.toml` file to include the Chainstack network: ```toml TOML theme={"system"} [profile.default] src = 'src' out = 'out' libs = ['lib'] [rpc_endpoints] mainnet = "${CHAINSTACK_MAINNET_ENDPOINT}" devnet = "${CHAINSTACK_DEVNET_ENDPOINT}" ``` 3. Before deploying, ensure that your contracts are compiled. This step is crucial to avoid deployment issues. ```bash Bash theme={"system"} forge build ``` 4. To use the Chainstack endpoint for deployment, you can use the following command: ```bash Bash theme={"system"} forge create --rpc-url ${CHAINSTACK_DEVNET_ENDPOINT} --private-key ${PRIVATE_KEY_DEVNET} src/YourContract.sol:YourContract ``` Alternatively, you can create a deployment script in the `script` folder, for example `Deploy.s.sol`: ```solidity solidity theme={"system"} // script/Deploy.s.sol // Specify the Solidity version pragma solidity ^0.8.20; // Import the Forge script library import "forge-std/Script.sol"; // Import your contract import "../src/YourContract.sol"; contract DeployScript is Script { function run() external { // Retrieve the deployer's private key from the environment variables uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY_DEVNET"); // Change to PRIVATE_KEY_MAINNET for mainnet // Start broadcasting transactions using the deployer's private key vm.startBroadcast(deployerPrivateKey); // Deploy the contract YourContract yourContract = new YourContract(); // Stop broadcasting transactions vm.stopBroadcast(); // Log the deployed contract address console2.log("Contract deployed at:", address(yourContract)); } } ``` Then run the script with: ```bash Bash theme={"system"} forge script script/Deploy.s.sol:DeployScript --rpc-url devnet--broadcast ``` 5. Interact with the blockchain using Cast. Use the following command to query the balance of an Ethereum address: ```bash Bash theme={"system"} cast balance --rpc-url $CHAINSTACK_DEVNET_ENDPOINT ``` Replace \ with the actual Ethereum address you want to query. ### Hardhat Configure [Hardhat](https://github.com/NomicFoundation/hardhat) to deploy contracts using Chainstack Ethereum nodes. 1. Install Hardhat and other dependencies ```bash Bash theme={"system"} npm init -y npm install --save-dev hardhat @nomiclabs/hardhat-ethers ethers dotenv ``` 2. Configure environment in Hardhat Create a `.env` file in your project root and add your Chainstack endpoint and private key: ```bash Bash theme={"system"} CHAINSTACK_MAINNET_ENDPOINT=https://your-chainstack-mainnet-url CHAINSTACK_DEVNET_ENDPOINT=https://your-chainstack-devnet-url PRIVATE_KEY_MAINNET=your-mainnet-private-key PRIVATE_KEY_DEVNET=your-devnet-private-key ``` Initiate a project: ```bash Bash theme={"system"} npx hardhat init ``` Update your `hardhat.config.js` file to use the Chainstack endpoint: ```javascript Javascript theme={"system"} require('@nomiclabs/hardhat-ethers'); require('dotenv').config(); module.exports = { solidity: "0.8.20", // Specify the required compiler version networks: { mainnet: { url: process.env.CHAINSTACK_MAINNET_ENDPOINT, accounts: [process.env.PRIVATE_KEY_MAINNET] // Mainnet private key }, devnet: { url: process.env.CHAINSTACK_DEVNET_ENDPOINT, accounts: [process.env.PRIVATE_KEY_DEVNET] // Devnet private key } } }; ``` 3. To deploy your contracts using the Chainstack endpoint, you can create a deployment script in the `scripts` folder, for example deploy.js: ```javascript Javascript theme={"system"} const hre = require("hardhat"); async function main() { // Retrieve the deployer account from the list of signers const [deployer] = await hre.ethers.getSigners(); console.log("Deploying contracts with the account:", deployer.address); // Load the contract factory from the contracts folder const Token = await hre.ethers.getContractFactory("MyToken"); // Replace "MyToken" with your contract name // Specify the initial supply for the token (adjust as necessary) const initialSupply = hre.ethers.utils.parseUnits("1000", 18); // Adjust the supply and decimals as needed // Deploy the contract and wait for it to be mined const token = await Token.deploy(initialSupply); await token.deployed(); // Output the contract address and transaction hash once deployed console.log("Contract deployed at address:", token.address); console.log("Transaction hash:", token.deployTransaction.hash); } // Execute the main function and handle potential errors main() .then(() => process.exit(0)) .catch((error) => { console.error("Error during deployment:", error); process.exit(1); }); ``` 4. Compile the contracts ```tsx Bash theme={"system"} npx hardhat compile ``` 5. Use the appropriate network flag (mainnet or devnet) to deploy to the respective network ```bash Bash theme={"system"} npx hardhat run scripts/deploy.js --network devnet ``` ### Scaffold-ETH 2 Configure [Scaffold-ETH 2](https://github.com/scaffold-eth/scaffold-eth-2) to deploy contracts using Chainstack Ethereum nodes. 1. Clone the Scaffold-ETH 2 repository ```bash Bash theme={"system"} git clone https://github.com/scaffold-eth/scaffold-eth-2.git cd scaffold-eth-2 ``` 2. Install dependencies ```bash Bash theme={"system"} yarn install ``` 3. Create a .env.local file in the root directory of your project ```bash Bash theme={"system"} touch .env.local ``` 4. Add your Chainstack endpoint and private key to the .env.local file ``` CHAINSTACK_MAINNET_ENDPOINT=https://your-chainstack-mainnet-url CHAINSTACK_DEVNET_ENDPOINT=https://your-chainstack-devnet-url DEPLOYER_PRIVATE_KEY=your-private-key-here ``` Note: by default, Scaffold-ETH 2 uses Alchemy RPC nodes and it assumes the config contains an Alchemy API key. We can override this setting Chainstack RPC nodes and updating some other configs. 5. Modify the packages/hardhat/hardhat.config.ts file to configure the Chainstack endpoint. Locate the networks section and add or modify the configuration for the Chainstack networks ```javascript Bash theme={"system"} const config: HardhatUserConfig = { solidity: "0.8.20", // Ensure to specify the correct Solidity version networks: { mainnet: { url: process.env.CHAINSTACK_MAINNET_ENDPOINT || `https://eth-mainnet.g.alchemy.com/v2/${providerApiKey}`, // Use the Chainstack mainnet endpoint // accounts: [process.env.DEPLOYER_PRIVATE_KEY_MAINNET as string], // Mainnet private key }, sepolia: { url: process.env.CHAINSTACK_DEVNET_ENDPOINT || `https://eth-sepolia.g.alchemy.com/v2/${providerApiKey}`, // Use the Chainstack devnet endpoint // accounts: [process.env.DEPLOYER_PRIVATE_KEY_DEVNET as string], // Devnet private key }, // ... other network configurations }, }; ``` 6. Compile contracts using Hardhat ```bash Bash theme={"system"} cd packages/hardhat yarn hardhat compile ``` 7. To deploy your contracts using the Chainstack endpoint, run ```bash theme={"system"} yarn deploy --network sepolia ``` 8. To start the Next.js app with the Chainstack configuration ```bash theme={"system"} yarn start ``` ### Truffle (no longer maintained) Configure [Truffle](https://github.com/trufflesuite/truffle) to deploy contracts using Chainstack Ethereum nodes. 1. Install Truffle and other dependencies ```bash theme={"system"} # Initialize a new npm project npm init -y # Install Truffle as a local development dependency npm install --save-dev truffle @truffle/hdwallet-provider dotenv ``` 2. Configure environment in Truffle Initialize a new Truffle project: ```bash Bash theme={"system"} npx truffle init ``` Create a `.env` file in your project root and add your Chainstack endpoint and private key: ```bash Bash theme={"system"} CHAINSTACK_MAINNET_ENDPOINT=https://your-chainstack-mainnet-url CHAINSTACK_DEVNET_ENDPOINT=https://your-chainstack-devnet-url PRIVATE_KEY_MAINNET=your-mainnet-private-key PRIVATE_KEY_DEVNET=your-devnet-private-key ``` Update your `truffle-config.js` file to use the Chainstack endpoint: ```javascript Javascript theme={"system"} require('dotenv').config(); const HDWalletProvider = require('@truffle/hdwallet-provider'); module.exports = { networks: { // Configuration for Mainnet mainnet: { provider: () => new HDWalletProvider(process.env.PRIVATE_KEY_MAINNET, process.env.CHAINSTACK_MAINNET_ENDPOINT), network_id: 1, // Mainnet's network ID }, // Configuration for Devnet devnet: { provider: () => new HDWalletProvider(process.env.PRIVATE_KEY_DEVNET, process.env.CHAINSTACK_DEVNET_ENDPOINT), network_id: 11155111, // Sepolia }, }, compilers: { solc: { version: "0.8.20", // Specify the Solidity compiler version }, }, }; ``` 3. Create a migration script in the migrations folder to deploy your contract (migrations/1\_deploy\_contract\_name.js) ```javascript Javascript theme={"system"} // Import your contract artifact const YourContract = artifacts.require("CONTRACT_NAME"); module.exports = function (deployer) { deployer.deploy(YourContract); }; ``` 4. Compile the contracts ```jsx Bash theme={"system"} npx truffle compile ``` 5. To deploy your contract using the Chainstack endpoint, run ```bash Bash theme={"system"} truffle migrate --network devnet ``` ### Ape Configure [Ape Framework](https://github.com/ApeWorX/ape) to deploy contracts using Chainstack Ethereum nodes. Before performing the following steps, you can set up and activate a virtual environment. 1. Install Ape and dependencies ```bash Bash theme={"system"} pip install eth-ape ``` 2. Configure environment in Ape Initialize a project: ```bash Bash theme={"system"} ape init ``` Create or update your `ape-config.yaml` file in your project root: ```yaml yaml theme={"system"} name: ape-1 contracts_folder: contracts # Default is contracts default_ecosystem: ethereum # Default is ethereum node: ethereum: mainnet: uri: CHAINSTACK_MAINNET_ENDPOINT sepolia: uri: CHAINSTACK_SEPOLIA_ENDPOINT dependencies: - name: OpenZeppelin github: OpenZeppelin/openzeppelin-contracts/token/ERC20/ERC20.sol # Example version: 4.4.2 ``` 3. Import an account. You'll be prompted to enter a private key and pass phrase to encrypt it. ```bash Bash theme={"system"} ape accounts import devnet_deployer ``` 4. To deploy your contracts using the Chainstack endpoint, you can create a deployment script, for example scripts/deploy.py ```python Python theme={"system"} from ape import accounts, project def main(): # Load the account for deployment # Change 'devnet_deployer' to 'mainnet_deployer' when deploying to mainnet account = accounts.load("devnet_deployer") # Deploy the contract contract = account.deploy(project.MyContract) # Replace 'MyContract' with your contract's name and add its arguments # Output the contract address print(f"Contract deployed at: {contract.address}") # Output the transaction hash print(f"Transaction hash: {contract.txn_hash.hex()}") if __name__ == "__main__": main() ``` 5. Compile contracts. Before deploying, ensure your contracts are compiled. Ape automatically compiles contracts when deploying, but you can manually compile them if needed ```bash Bash theme={"system"} ape compile ``` 6. Run the deployment script using ```bash Bash theme={"system"} ape run scripts/deploy.py --network ethereum:sepolia ``` ### Brownie Configure [Brownie](https://github.com/eth-brownie/brownie) to deploy contracts using Chainstack Ethereum nodes. 1. Create a Python virtual environment and install Brownie ```bash Bash theme={"system"} python -m venv venv source venv/bin/activate # On Windows, use venv\Scripts\activate pip install eth-brownie ``` 2. Configure environment in Brownie Create a new Brownie project (add `--force` if a folder is not empty): ```bash Bash theme={"system"} brownie init ``` In your project directory, create a `.env` file and add your Chainstack endpoint and private key: ```bash Bash theme={"system"} CHAINSTACK_MAINNET_ENDPOINT=https://your-chainstack-mainnet-url CHAINSTACK_DEVNET_ENDPOINT=https://your-chainstack-devnet-url PRIVATE_KEY_MAINNET=your-mainnet-private-key PRIVATE_KEY_DEVNET=your-devnet-private-key ``` Ensure that your terminal session loads these environment variables. You can manually source the .env file: ```bash Bash theme={"system"} source .env ``` Update the `brownie-config.yaml` file in your project root: ```yaml yaml theme={"system"} dotenv: .env networks: default: development chainstack-mainnet: host: ${CHAINSTACK_MAINNET_ENDPOINT} chainid: 1 explorer: https://etherscan.io chainstack-devnet: host: ${CHAINSTACK_DEVNET_ENDPOINT} chainid: 1337 # Example chain ID for devnet; adjust if different ``` Add a custom network to Brownie's network list: ```bash Bash theme={"system"} brownie networks add Ethereum chainstack-mainnet host=${CHAINSTACK_MAINNET_ENDPOINT} chainid=1 brownie networks add Ethereum chainstack-devnet host=${CHAINSTACK_DEVNET_ENDPOINT} chainid=1337 ``` To use your private key for deployments, you can create a `scripts/deploy.py` file: ```python Python theme={"system"} import os from brownie import accounts, Counter # Replace 'Counter' with your contract's class name def main(): # Load the deployer's account using the private key from the environment variable acct = accounts.add(os.getenv("PRIVATE_KEY_DEVNET")) # Use PRIVATE_KEY_MAINNET for mainnet # Deploy the contract (Replace 'Counter' with the actual name of your contract) contract = Counter.deploy({'from': acct}) # Output the contract address and transaction hash print(f"Contract deployed at: {contract.address}") print(f"Transaction hash: {contract.tx.txid}") ``` 3. To deploy your contracts using the Chainstack endpoint, run ```bash Bash theme={"system"} brownie run scripts/deploy.py --network chainstack-devnet ``` # Ethereum Trader Nodes with Warp transactions Source: https://docs.chainstack.com/docs/ethereum-trader-nodes Use Ethereum Trader Nodes with Warp transactions for faster block inclusion through bloXroute's high-speed relay network and staked connections. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). > [!NOTE] > > The Warp transactions feature is available starting from the [paid plans](https://chainstack.com/pricing/). > > Warp transactions (not requests, just the transactions) consumed are billed separately. For details, see [Pricing](https://chainstack.com/pricing/) with the [extra usage](/docs/manage-your-billing#manage-the-extra-usage-setting) setting enabled. ## Benefits with Chainstack Chainstack nodes with Warp transactions combine reliable nodes for pre-transaction operations with bloXroute's high-speed propagation for the transaction itself. Switching to a Chainstack node with Warp transactions (available on both Trader Nodes and Global Nodes) is as simple as changing the endpoint in your code. All calls go through Chainstack's infrastructure, except for [`eth_sendRawTransaction`](/reference/ethereum-sendrawtransaction), which is routed directly to bloXroute. To enable Warp transactions, install the [Warp transactions](/docs/warp-transactions) add-on on your Trader or Global Node's **Add-ons** tab. ## Sample use case 1. A token liquidity is deployed on Ethereum. 2. You receive a signal through your Ethereum node by monitoring the Uniswap contracts. 3. Your bot acts on the signal, sending a transaction via a Chainstack node with Warp transactions enabled. 4. Transaction is quickly propagated, increasing chances of winning the race. ## Availability and usage * Available from the [paid plans](https://chainstack.com/pricing). * Warp transactions (not requests, just the transactions) consumed are billed separately. For details, see [Pricing](https://chainstack.com/pricing/) with the [extra usage](/docs/manage-your-billing#manage-the-extra-usage-setting) setting enabled. * Only `eth_sendRawTransaction` calls are consumed as Warp transactions. * Deploy nodes close to your bot for best performance. See also [Sending Trader Node Warp transaction with web3.js, ethers.js, web3.py, and ethClient.go](/docs/sending-warp-transaction-with-web3js-ethersjs-web3py-and-ethclientgo). # Ethereum: Academic certificates with Truffle Source: https://docs.chainstack.com/docs/ethereum-tutorial-academic-certificates-with-truffle Deploy an academic certificate verification smart contract on Ethereum using Truffle and a Chainstack node for tamper-proof credential management. **TLDR:** * You’ll build and deploy a contract-based DApp that generates and verifies academic certificates on Ethereum. * You’ll use Truffle locally to create, test, and compile your contracts. * You’ll integrate with Chainstack to run your own Ethereum Sepolia node and migrate your contracts. * By the end, you’ll have a fully working certificate-issuing DApp ready for a testnet deployment. ## Main article In this tutorial, you will: * Create a DApp that generates an academic certificate. * Deploy the DApp on a public Ethereum node using Chainstack. The contract and the Truffle configuration are in the [GitHub repository](https://github.com/chainstack/ethereum-certificates-tutorial). ## Prerequisites * [Chainstack account](https://console.chainstack.com/user/login) to deploy a [reliable Ethereum RPC endpoint](https://chainstack.com/build-better-with-ethereum/) * [Truffle Suite](https://trufflesuite.com/docs/truffle/how-to/install/) to create and deploy contracts ## Overview To get from zero to a deployed DApp on the Ethereum Sepolia testnet, do the following: 1. With Chainstack, create a public chain project (public chain project- a project to join public networks). 2. With Chainstack, join the Ethereum Sepolia testnet. 3. With Chainstack, access your Ethereum node credentials. 4. With Truffle, create and compile the DApp contract. 5. With Truffle, deploy the contract to your local development network. 6. With Truffle, interact with the contract on your local development network. 7. With Truffle, create and run the contract test. 8. With Truffle, deploy the contract to your Ethereum node running with Chainstack. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Ethereum Sepolia testnet See [Join a public network](/docs/manage-your-networks). ### Get your Ethereum node access and credentials See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Get Sepolia testnet ether from a faucet In your MetaMask, fund each account with Sepolia ether from our [Sepolia Ethereum faucet](https://faucet.chainstack.com). ### Create and compile the contracts 1. On your machine, create a directory for the contract. Initialize Truffle in the directory: ```shell Shell theme={"system"} truffle init ``` This will generate the Truffle boilerplate structure: ```shell Shell theme={"system"} . ├── contracts │ └── .gitkeep ├── migrations │ └── .gitkeep ├── test │ └── .gitkeep └── truffle-config.js ``` 2. Go to the `contracts` directory. In the directory, create two files: `Ownable.sol` and `DocStamp.sol`. ```sol Ownable.sol theme={"system"} //SPDX-License-Identifier:MIT // Ownable.sol pragma solidity ^0.5.0; contract Ownable { address public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor() public { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner); _; } function transferOwnership(address newOwner) onlyOwner public { require(newOwner != address(0)); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } } ``` This is an ownable contract. The contract implementation is the following: * Only an authority can generate a certificate. On contract deployment, the authority is the account that deploys the contract. The authority is the contract owner. * The contract owner can transfer their authority. ```sol DocStamp.sol theme={"system"} //SPDX-License-Identifier:MIT // DocStamp.sol pragma solidity ^0.5.0; import './Ownable.sol'; contract DocStamp is Ownable { mapping (bytes32 => address) public records; event CertificateIssued(bytes32 indexed record, uint256 timestamp, bool returnValue); function issueCertificate(string calldata name, string calldata details) external onlyOwner { bytes32 certificate = keccak256(abi.encodePacked(name, details)); require(certificate != keccak256(abi.encodePacked(""))); records[certificate] = msg.sender; emit CertificateIssued(certificate, block.timestamp, true); } function owningAuthority() external view returns (address) { return owner; } function verifyCertificate(string calldata name, string calldata details, bytes32 certificate) external view returns (bool) { bytes32 certificate2 = keccak256(abi.encodePacked(name, details)); // are certificates the same? if (certificate == certificate2) { // does the certificate exist on the blockchain? if (records[certificate] == owner) { return true; } } return false; } } ``` This is the main contract. The contract handles the generation and verification of certificates. * `issueCertificate()` — generates a certificate by calculating a hash of the student name and details. * Can be called only by the owner. * Emits a certificate generation event with the timestamp. * The issuer puts the certificate on the blockchain by storing it in the global variable records by passing `records[certificate] = msg.sender`. * `owningAuthority()` — returns the address of issuer/authority. * `verifyCertificate()` — calculates a hash of the student name and details, and checks if the contract is on the blockchain. * Can be called by anyone. 3. Create `2_deploy_contracts.js` in the `migrations` directory. ```javascript Javascript theme={"system"} var DocStamp = artifacts.require("./DocStamp.sol"); module.exports = function(deployer) { deployer.deploy(DocStamp); }; ``` > [!NOTE] > > ### Deployment details > > Since DocStamp inherits from Ownable, Ownable will be deployed together with DocStamp. 4. Compile the contracts: ```shell Shell theme={"system"} truffle compile ``` This will compile the contracts and put them in your `build/contracts` directory in the `.json` format. If the contract does not compile, check the compiler version in your `truffle-config.js` file and ensure that your compiler version matches the pragma solidity version of the contract. ### Deploy the contract to your local development network 1. Start the development network on your machine: ```shell Shell theme={"system"} truffle develop ``` 2. Without exiting the Truffle console, deploy the contract to the local development network: ```shell Shell theme={"system"} truffle(develop)> migrate ``` This will deploy the contract to the development network as specified in the`truffle-config.js`. ### Interact with the contract on your local development network 1. In your Truffle console, create an instance of the deployed contract: ```javascript Javascript theme={"system"} let instance = await DocStamp.deployed() ``` You can run `instance` to see the contract object ABI, bytecode, and methods. 2. Declare the contract owner: ```javascript Javascript theme={"system"} let owner = await instance.owningAuthority() ``` You can run `owner` to see the account that deployed the contract and owns the contract. 3. Issue the certificate: ```javascript Javascript theme={"system"} let result = await instance.issueCertificate("John", "graduate", {from: owner}) ``` This issues the certificate. Run `result.logs` to view the full certificate details. > [!NOTE] > > ### Getting certificate details > > Running result will not print the certificate details in Truffle console. You must run `result.logs`. > > See also [Processing transaction results](https://www.trufflesuite.com/docs/truffle/getting-started/interacting-with-your-contracts#processing-transaction-results). Example output: ```shell Shell theme={"system"} logIndex: 0, transactionIndex: 0, transactionHash: '0xb3ef241d76bd4d3a3d92ad4fd382785589033a4f561baa2895136a3315b3561b', blockHash: '0x29343b9fc5b88bb8c85287463a37a00e8fecce36553880365ca5395d9fb18eeb', blockNumber: 7, address: '0x3113Aa54D455142a254b43b83FB16c18eD30ba33', type: 'mined', id: 'log_dbbbec7e', event: 'CertificateIssued', args: Result { '0': '0x837e31a66aa8eec0d7adfd41f84175803ddcae69afd451598f2672f652b2c153', '1': [BN], '2': true, __length__: 3, record: '0x837e31a66aa8eec0d7adfd41f84175803ddcae69afd451598f2672f652b2c153', timestamp: [BN], returnValue: true ``` Note the `record` value in the output. This is the hash of the certificate values: name and details. You will need this hash to create the contract test later in this tutorial. 4. Run the certificate verification: ```javascript Javascript theme={"system"} let verify = await instance.verifyCertificate("NAME", "DETAILS", "CERTIFICATE_HASH", {from: owner}) ``` where * NAME — the student name on the certificate used on the issuing step. * DETAILS — any details * CERTIFICATE\_HASH — the hash of DETAILS and NAME. You should have received this hash in the record field at the previous step by running `result.logs`. Example: ```javascript Javascript theme={"system"} let verified = await instance.verifyCertificate("John", "graduate", "0x837e31a66aa8eec0d7adfd41f84175803ddcae69afd451598f2672f652b2c153", {from: owner}) ``` Running `verify` will now print `true` if there is a match, and `false` if there is no match. ### Test the contract 1. Navigate to the `test` directory. 2. Create a `test.js` file: ```javascript Javascript theme={"system"} const DocStamp = artifacts.require('./DocStamp.sol') contract('DocStamp', function(accounts) { it('should issue a certificate', async function() { const account = accounts[0] try { const instance = await DocStamp.deployed() await instance.issueCertificate("NAME", "DETAILS") const authority = await instance.owningAuthority() assert.equal(authority, account) } catch(error) { assert.equal(error, undefined) } }) it('should verify a certificate', async function() { const account = accounts[0] try { const instance = await DocStamp.deployed() const verified = await instance.verifyCertificate("NAME", "DETAILS", "CERTIFICATE_HASH") assert.equal(verified, true) } catch(error) { assert.equal(error, undefined) } }) }) ``` where * NAME — the student name on the certificate used on the issuing step. * DETAILS — any details * CERTIFICATE\_HASH — the hash of DETAILS and NAME. You should have received this hash in the record field at one of the previous steps by running `result.logs`. Example: ```javascript Javascript theme={"system"} const DocStamp = artifacts.require('./DocStamp.sol') contract('DocStamp', function(accounts) { it('should issue a certificate', async function() { const account = accounts[0] try { const instance = await DocStamp.deployed() await instance.issueCertificate("John", "graduate") const authority = await instance.owningAuthority() assert.equal(authority, account) } catch(error) { assert.equal(error, undefined) } }) it('should verify a certificate', async function() { const account = accounts[0] try { const instance = await DocStamp.deployed() const verified = await instance.verifyCertificate("John", "graduate", "0x837e31a66aa8eec0d7adfd41f84175803ddcae69afd451598f2672f652b2c153") assert.equal(verified, true) } catch(error) { assert.equal(error, undefined) } }) }) ``` 3. Run the test: ```shell Shell theme={"system"} truffle test ``` The test run output should be `Passing`. > [!NOTE] > > ### See also > > [Truffle: Writing Tests in JavaScript](https://trufflesuite.com/docs/truffle/how-to/debug-test/write-tests-in-javascript/) ### Deploy the contract to your Ethereum node 1. Install `HDWalletProvider`. [HDWalletProvider](https://github.com/trufflesuite/truffle/tree/develop/packages/hdwallet-provider) is Truffle's separate npm package used to sign transactions. Run: ```shell Shell theme={"system"} npm install @truffle/hdwallet-provider ``` 2. Edit `truffle-config.js` to add: * HDWalletProvider * Your Ethereum node access and credentials. Example: ```javascript Javascript theme={"system"} const HDWalletProvider = require("@truffle/hdwallet-provider"); const mnemonic = "misery walnut expose ..."; module.exports = { networks: { development: { host: "127.0.0.1", port: 9545, network_id: "5777" }, sepolia: { provider: () => new HDWalletProvider(mnemonic, "YOUR_CHAINSTACK_ENDPOINT"), network_id: 11155111, gas: 4500000, gasPrice: 10000000000 } } }; ``` where * `sepolia` — any network name that you will pass to the `truffle migrate --network` command. * `HDWalletProvider` — Truffle's custom provider to sign transactions * `mnemonic` — your mnemonic that generates your accounts. You can also generate a mnemonic online with [Mnemonic Code Converter](https://iancoleman.io/bip39/). Make sure you generate a 15-word mnemonic. * YOUR\_CHAINSTACK\_ENDPOINT — your Chainstack node endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) and [Ethereum tooling](/docs/ethereum-tooling). * `network_id` — the Ethereum Sepolia testnet network ID: `5`. 3. Run: ```shell Shell theme={"system"} truffle migrate --network sepolia ``` This will engage `2_deploy_contracts.js` and deploy the contract to the Ethereum Sepolia testnet as specified in `truffle-config.js`. > [!WARNING] > > ### Get testnet ether > > You must get the Sepolia testnet ether to deploy the contract to the testnet. > [!NOTE] > > See also: > > * [Ethereum: Asset tokenization with Embark](/docs/ethereum-tutorial-asset-tokenization-with-embark) > * [Ethereum: Trust fund account with Remix](/docs/ethereum-tutorial-trust-fund-account-with-remix) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Ethereum: Asset tokenization with Embark Source: https://docs.chainstack.com/docs/ethereum-tutorial-asset-tokenization-with-embark Tokenize real-world assets on Ethereum using the Embark framework and a Chainstack node to deploy and manage ERC-20 asset-backed token contracts. **TLDR:** * You'll build a simple contract in Embark that tokenizes assets with a fixed supply of 1,000 tokens. * You'll set a price of 0.1 ether per token and let anyone exchange ether for asset tokens on Sepolia. * You'll use Chainstack to deploy and run your own Sepolia node, and Geth to manage your accounts. * You'll confirm everything works by testing the contract with Embark's Cockpit and by sending ether from MetaMask. ## Main article This tutorial will guide you through creating a tokenized asset contract and deploying it on Sepolia testnet. For illustration purposes, this contract does the following: * Creates a total supply of 1,000 tokens specific to the asset. * Sets the token price to 0.1 ether. * Let anyone exchange ether for asset tokens. This tutorial uses [Embark](https://framework.embarklabs.io/) to test and deploy the contract. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Ethereum RPC endpoint](https://chainstack.com/build-better-with-ethereum/) for Sepolia testnet. * [Embark](https://framework.embarklabs.io/) to test and deploy the contract. * [Geth](https://geth.ethereum.org/) to create an Ethereum account that will deploy the contract. * [MetaMask](https://metamask.io/) to interact with the contract. ## Overview To get from zero to an asset tokenization contract running on Sepolia, do the following: 1. With Chainstack, create a public chain project (public chain project- a project to join public networks). 2. With Chainstack, deploy a Sepolia testnet node. 3. With Embark, create a project and the contract. 4. With Geth, create a new account. 5. Import the account in MetaMask and fund the account with Sepolia ether. 6. With Embark, deploy the contract through the Sepolia node. 7. With Embark, check the contract with Cockpit. 8. Interact with the contract through MetaMask. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Deploy a Sepolia node See [Join a public network](/docs/manage-your-networks). ### Get Sepolia testnet ether from a faucet In your MetaMask, fund each account with Sepolia ether [Chainstack's Sepolia faucet](https://faucet.chainstack.com). ### Create an Embark project and the contract 1. Create a new Embark project: ```bash Shell theme={"system"} embark new asset ``` This will create an Embark directory called `asset`. 2. Change to the `contracts` directory of the Embark project. 3. Create an `AssetTokenized.sol` file in the `contracts` directory: ```solidity solidity theme={"system"} pragma solidity = 0.5.0; contract AssetTokenized{ uint public supply; uint public pricePerEth; mapping( address => uint ) public balance; constructor() public { supply = 1000; // There are a total of 1000 tokens for this asset pricePerEth = 100000000000000000; // One token costs 0.1 ether } function check() public view returns(uint) { return balance[msg.sender]; } function () external payable { balance[msg.sender] += msg.value/pricePerEth; // adds asset tokens to how much ether is sent by the investor supply -= msg.value/pricePerEth; //subtracts the remaining asset tokens from the total supply } } ``` ### Create an Ethereum account You will use this account to deploy the contract. 1. Create the account: ```bash Shell theme={"system"} geth account new ``` 2. Check the path to the keystore file created for the new account: ```bash Shell theme={"system"} geth account list ``` ### Import the account in MetaMask and fund the account 1. In MetaMask, click **Import Account** > **JSON File.** 2. Select the keystore file that you created earlier. 3. Fund the account with [Sepolia ether](https://faucet.paradigm.xyz/). ### Deploy the contract 1. In your Embark project directory, change to `config`. 2. Append `contracts.js` with the following configuration: ```js JavaScript theme={"system"} chainstack: { deployment:{ accounts: [ { privateKeyFile:"//root/.ethereum/keystore/UTC--2019-08-01T07-24-17.754471456Z- -73236c8d8aaee5263e8a32c71171030dd7a3e8e6", password:"PASSWORD" } ], host:"YOUR_CHAINSTACK_ENDPOINT", port:false, protocol:"https", type:"rpc" }, dappConnection: [ "$WEB3", // uses pre-existing Web3 object if available (e.g in Mist) "ws://localhost:8546", "" ], gas: "auto", } ``` where * `//root/.ethereum/keystore/...` — the location of the keystore file * PASSWORD — the password you provided when creating the Ethereum account with Geth * YOUR\_CHAINSTACK\_ENDPOINT — your Chainstack node endpoint. See also [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). 3. Deploy the contract with Embark: ```bash Shell theme={"system"} embark run chainstack ``` where `chainstack` — the argument from the configuration file `contracts.js` This will deploy the contract on Sepolia. ### Check the contract with Cockpit On contract deployment, Embark runs Cockpit which is a front-end application to test the contract. In your browser, open: `` where CONTRACT\_NAME — the name of your contract. In this tutorial, the path is ``. This will also display the contract address in the `Deployed at` line. Test the contract by calling: * `supply()` — to check the remaining supply of tokens on the contract. * `check()` — to check the amount of tokens owned by the Ethereum address you are using to call the contract. * `pricePerEth()` — to check the token price in wei. ### Interact with the contract 1. In MetaMask, send an amount of Sepolia ether to the contract address. 2. In Cockpit, call the contract functions `supply()` and `check()` after a few seconds to see a change in values returned. > [!NOTE] > > ### See also > > > [card] [Ethereum: Academic certificates with Truffle](/docs/ethereum-tutorial-academic-certificates-with-truffle) > > > [card] [Ethereum: Trust fund account with Remix](/docs/ethereum-tutorial-trust-fund-account-with-remix) > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Ethereum: Trust fund account with Remix Source: https://docs.chainstack.com/docs/ethereum-tutorial-trust-fund-account-with-remix Create a time-locked trust fund smart contract on Ethereum using Remix IDE and a Chainstack node endpoint for secure, scheduled fund release. **TLDR:** * You'll build a trust fund contract that can accept deposits from anyone, but only the owner can withdraw. * Ownership can be transferred to another address, making it useful for simple DeFi scenarios. * You'll rely on Chainstack for an Ethereum Sepolia node, and Remix + MetaMask for contract compilation and deployment. * By the end, you'll have a fully functional trust fund contract deployed to Sepolia and ready to receive or withdraw Ether. ## Main article Unlike legacy finance systems, where you need to rely on a well-established third party, you can build your own financial instrument on Ethereum. The objective of this tutorial is to show how easy it is to build and run your own instance of a simple decentralized finance example, or DeFi. In this tutorial, you will: * Create a basic Trust Fund account smart contract with the following interaction options: * Fund the account from any Ethereum address * Withdraw all funds from the account only from the account owner's address * Withdraw partial funds from the account only from the account owner's address * Transfer the account ownership to any Ethereum address * Compile the smart contract with Remix IDE. * Deploy the smart contract to the Ethereum Sepolia testnet through a Chainstack node. * Interact with the smart contract through Remix IDE and a Chainstack node. > [!NOTE] > > ### Ethereum Sepolia testnet > > For illustration purposes, this tutorial uses Ethereum Sepolia testnet. > > For Ethereum mainnet, the steps are exactly the same, except you need to use mainnet ether. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Ethereum RPC endpoint](https://chainstack.com/build-better-with-ethereum/). * [Remix IDE](https://remix.ethereum.org/) to compile the contract and deploy it through MetaMask. * [MetaMask](https://metamask.io/) to deploy the contract through the Chainstack node and interact with the contract. ## Overview To get from zero to a deployed Trust Fund account on the Ethereum Sepolia testnet, do the following: 1. With Chainstack, create a public chain project (public chain project- a project to join public networks) . 2. With Chainstack, join the Ethereum Sepolia testnet. 3. With Chainstack, access your Ethereum node credentials. 4. Set up your MetaMask to work through a Chainstack node. 5. With Remix IDE, create and compile the Trust Fund smart contract. 6. Set up your Remix IDE to work through a Chainstack node. 7. With Remix IDE, deploy the contract to the Ethereum Sepolia testnet. 8. With Remix IDE, interact with the contract on the Ethereum Sepolia testnet. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Ethereum Sepolia testnet See [Join a public network](/docs/manage-your-networks). ### Get your Ethereum node access and credentials See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Set up MetaMask See [Ethereum tooling: MetaMask](/docs/ethereum-tooling#metamask). Having set up your MetaMask to interact through a Chainstack node, your Remix IDE will also interact with the network through a Chainstack node. Create at least two accounts in MetaMask. You need two accounts to transfer the contract ownership from one to another. In your MetaMask, fund each account with Sepolia ether [Chainstack's Sepolia faucet](https://faucet.chainstack.com). ### Create and compile the Trust Fund smart contract 1. Open [Remix IDE](https://remix.ethereum.org/). 2. On the home page, click **Environments** > **Solidity**. 3. On the left pane, click **File explorers** > **+**. 4. In the modal, give any name to your contract. For example, `transferableTrustFund.sol`. 5. Put in the contract code: ```sol transferableTrustFund.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title TransferableTrustFundAccount * @dev A trust fund account that can be transferred to a new owner */ contract TransferableTrustFundAccount { address payable public owner; // The current owner of the contract /** * @dev Modifier to restrict access to owner-only functions */ modifier onlyOwner() { require(msg.sender == owner, "Caller is not the owner"); _; } /** * @dev Initializes the contract by setting the initial owner */ constructor() { owner = payable(msg.sender); } /** * @dev Withdraws the entire contract balance to the owner */ function withdrawAll() public onlyOwner { uint256 balance = address(this).balance; require(balance > 0, "Insufficient funds"); owner.transfer(balance); } /** * @dev Withdraws a specified amount to the owner * @param amount The amount to withdraw */ function withdrawAmount(uint256 amount) public onlyOwner { uint256 balance = address(this).balance; require(balance >= amount, "Insufficient funds"); owner.transfer(amount); } /** * @dev Transfers ownership of the contract to a new account * @param newOwner The address of the new owner */ function transferAccount(address payable newOwner) public onlyOwner { require(newOwner != address(0), "Invalid address"); owner = newOwner; } /** * @dev Gets the current balance of the contract * @return The current balance of the contract */ function getBalance() public view returns (uint256) { return address(this).balance; } /** * @dev Gets the current owner of the contract * @return The address of the current owner */ function getOwner() public view returns (address) { return owner; } /** * @dev Fallback function to accept incoming transfers */ receive() external payable {} } ``` This is your Trust Fund account smart contract: * The contract belongs to the Ethereum account that deploys the contract through: ```sol transferableTrustFund.sol theme={"system"} contract TransferableTrustFundAccount { address payable public owner; ... } ``` * The `onlyOwner` modifier is used to restrict access to certain functions in the smart contract to only the owner of the contract. A modifier is like a wrapper that can be applied to a function, and it modifies the behavior of the function in some way: ```sol transferableTrustFund.sol theme={"system"} modifier onlyOwner() { require(msg.sender == owner, "Caller is not the owner"); _; } ``` * Only the contract owner can withdraw all funds from the contract through: ```sol transferableTrustFund.sol theme={"system"} function withdrawAll() public onlyOwner { uint256 balance = address(this).balance; require(balance > 0, "Insufficient funds"); owner.transfer(balance); } ``` * Only the contract owner can withdraw partial funds from the contract through: ```sol transferableTrustFund.sol theme={"system"} function withdrawAmount(uint256 amount) public onlyOwner { uint256 balance = address(this).balance; require(balance >= amount, "Insufficient funds"); owner.transfer(amount); } ``` * Anyone can deposit funds to the contract through: ```sol transferableTrustFund.sol theme={"system"} receive() external payable {} ``` * Only the contract owner can transfer the contract ownership to any other Ethereum account through: ```sol transferableTrustFund.sol theme={"system"} function transferAccount(address payable newOwner) public onlyOwner { require(newOwner != address(0), "Invalid address"); owner = newOwner; } ``` * Retrieve the current balance on the smart contract and the current owner with: ```sol transferableTrustFund.sol theme={"system"} function getBalance() public view returns (uint256) { return address(this).balance; } function getOwner() public view returns (address) { return owner; } ``` 6. Compile the contract. On the left pane, click **Solidity compiler** > **Compile**: > [!NOTE] > > ### Compilation failed? > > If the contract does not compile, make sure to change the compiler version to 0.8.0 or higher. ### Set up Remix IDE to work through your Chainstack node On the left pane, click **Deploy** and switch to **Injected Provider - Metamask**: This will engage your MetaMask and interact with the network through the Chainstack node provided in MetaMask. ### Deploy the Trust Fund smart contract On the left pane, click **Deploy & run transactions** > **Deploy:** This will engage your MetaMask to deploy the contract to the Ethereum Sepolia testnet through your currently selected MetaMask account. Click **Confirm** in the MetaMask modal. ### Interact with the Trust Fund smart contract Once the contract is deployed, fund the contract: 1. Copy the contract address under **Deployed Contracts**. 2. Open your MetaMask and send Sepolia ether to the copied contract address. Now that your contract is funded, you can interact with it. Expand the contract under **Deployed Contracts**: * `withdrawAmount` — enter any amount that is less than the current contract balance to withdraw partial funds. * `withdrawAll` — click to withdraw all funds from the contract. * `transferAccount` — enter any Ethereum address to transfer the contract ownership. For this example, enter the address of your second account in MetaMask. * `getBalance` — click to display the current ether balance in the smart contract; the balance is shown in Wei. * `getOwner` — click to display the address currently owning the Trust fund smart contract. > [!NOTE] > > ### See also > > > [card] [Ethereum: Academic certificates with Truffle](/docs/ethereum-tutorial-academic-certificates-with-truffle) > > > [card] [Ethereum: Asset tokenization with Embark](/docs/ethereum-tutorial-asset-tokenization-with-embark) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # ethers.js ChainstackProvider: Multi-chain balance checker Source: https://docs.chainstack.com/docs/ethersjs-chainstackprovider-how-to-multi-chain-wallet-balance-aggregator Build a multi-chain wallet balance aggregator using ethers.js ChainstackProvider to query balances across Ethereum, Polygon, and BNB Smart Chain. **TLDR** * `ChainstackProvider` in ethers.js lets you connect to multiple blockchains (Ethereum, Polygon, Arbitrum, etc.) with minimal setup. * This Next.js DApp demonstrates fetching wallet balances from multiple chains via a simple API endpoint using ChainstackProvider for each network. * Keeping providers on the backend helps protect your endpoints, with the frontend only calling your API endpoint. * Easily scale to more EVM chains or extend functionality by adjusting the provider config and adding UI elements. ## Main article ethers.js recently added support for `ChainstackProvider`. This allows you to leverage Chainstack nodes in ethers out of the box. This tutorial will guide you through setting up a Next.js project, integrating ethers.js with `ChainstackProvider` building a fully functional DApp that cretrieves wallet balances from multiple blockchain networks. > [!NOTE] > > Learn more about `ChainstackProvider` and the supported chains on the [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ### What is ethers.js? ethers.js is a JavaScript library designed to interact with EVM blockchains. It provides a lightweight and easy-to-use API for connecting to various blockchains, making transactions, interacting with smart contracts, and handling cryptographic functions. Due to its simplicity, flexibility, and robust features, Ethers.js is popular among developers, making it a go-to choice for building Ethereum-based applications. ## Prerequisites Spinning up a DApp with `ChainstackProvider` is so simple that you only need an IDE and Node.js installed. Install Node.js from the [official website](https://nodejs.org/en) if you don't have it yet. > [!NOTE] > > Find the repository for this DApp on the [Chainstack Labs GitHub](https://github.com/chainstacklabs/ethers-chainstackprovider-demo-nextjs) and see a functioning and deployed version of the DApp: [Multi-chain wallet balance aggregator DApp](https://ethers-chainstackprovider-demo-nextjs.vercel.app/) At this stage, let's go ahead and set up the Next.js project; create a new directory for your DApp and run the command to initialize Next.js: ``` npx create-next-app@14.2.3 ``` This project uses Next 14; select the following options during the setup: ``` ✔ What is your project named? … balances ✔ Would you like to use TypeScript? … No ✔ Would you like to use ESLint? … Yes ✔ Would you like to use Tailwind CSS? … Yes ✔ Would you like to use `src/` directory? … Yes ✔ Would you like to use App Router? (recommended) … Yes ? Would you like to customize the default import alias (@/*)? › No ``` Then, move into the newly created project and install the ethers library. To have access to the `ChainstackProvider` you need to at least ethers `V6.12.0`. ``` npm i ethers ``` At this point, you are ready to code. ## How to use ethers.js' `ChainstackProvider`? Let's briefly talk about the `ChainstackProvider`. It provides a very convenient way to spin up DApps and prototypes but keep in mind that the endpoints used are heavily throttled and will not be suitable for a production environment. The good news is that `ChainstackProvider` supports your own Chainstack nodes as well; you just need to deploy a [Global Node](/docs/global-elastic-node) from the Chainstack console and initialize the provider with the node authorization key. Follow these steps to sign up on Chainstack, deploy a node, and find your endpoint credentials: > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) > [!NOTE] > > You must deploy a Global Node to use the authorization key in `ChainstackProvider`. Once deployed, your node RPC ULR will look like this: > [https://ethereum-mainnet.core.chainstack.com/AUTH\_KEY](https://ethereum-mainnet.core.chainstack.com/AUTH_KEY) Now you can add the `AUTH_KEY` to the `ChainstackProvider` instance: ```javascript ethers theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet", "AUTH_KEY"); ``` > [!NOTE] > > Check out the supported chains on the [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ## Project overview ### Core DApp functionality 1. **Address input**: Users enter an Ethereum wallet address into a text field. 2. **Fetch balances**: Upon submitting the address, the DApp communicates with a backend API to fetch the wallet balances from various blockchain networks. 3. **Display results**: The DApp displays the retrieved balances in a user-friendly format. To achieve this functionality, ywork with two main components: 1. **Frontend UI (page.js)**: * This file handles the user interface, including the input field for the wallet address and the button to trigger the balance check. * It also displays the fetched balances and handles loading and error states. 2. **Backend API (route.js)**: * This file manages the logic for connecting to your blockchain networks using ChainstackProvider. * It defines an API endpoint the frontend can call to fetch the balances. * Since you use the backend to communicate with a chain, you don't expose any endpoints on the front end, preserving good security practices. Focusing on these two components streamlines the development process and demonstrates the simplicity of integrating ethers.js with `ChainstackProvider` in a Next.js project. ## The front-end code Let's first work on the UI. Once your project is initialized, go to `...src/app/page.js` and paste the following code: ```javascript page.js theme={"system"} "use client"; import { useState } from "react"; import { ethers } from "ethers"; export default function Home() { const [walletAddress, setWalletAddress] = useState(""); const [balances, setBalances] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const handleCheckBalances = async () => { if (!ethers.isAddress(walletAddress)) { setError("Invalid Ethereum address"); return; } setLoading(true); setError(null); setBalances(null); // Clear previous balances try { const response = await fetch(`/api/balances`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ walletAddress }), }); if (!response.ok) throw new Error("Failed to fetch balances"); const data = await response.json(); setBalances(data); } catch (err) { setError(err.message); } setLoading(false); }; return (

Chainstack Powered

Multi-chain Wallet Balance Aggregator

This simple DApp uses the ChainstackProvider from ether.js to interact with those chains.

Learn more about the ChainstackProvider from ether.js on the{" "} Chainstack documentation .

setWalletAddress(e.target.value)} className="w-full p-2 mb-4 border rounded bg-gray-700 border-gray-600 text-white" /> {error &&

{error}

} {balances && (

Ethereum:{" "} {parseFloat(balances.ethereum).toFixed(5)} ETH

Polygon:{" "} {parseFloat(balances.polygon).toFixed(5)} MATIC

BNB Smart Chain:{" "} {parseFloat(balances.bnb).toFixed(5)} BNB

Arbitrum:{" "} {parseFloat(balances.arbitrum).toFixed(5)} ETH

)}
); } ``` ### Breakdown of `page.js` The `page.js` file is responsible for the user interface of your DApp. It allows users to input their Ethereum wallet address and fetches the wallet balances from multiple blockchain networks by interacting with your backend API. Here’s a simple breakdown of the important parts: #### Import required modules ```javascript Javascript theme={"system"} "use client"; import { useState } from "react"; import { ethers } from "ethers"; ``` You import `useState` from React for managing state within the component and `ethers` from the ethers.js library to validate Ethereum addresses. #### State management ```javascript Javascript theme={"system"} const [walletAddress, setWalletAddress] = useState(""); const [balances, setBalances] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); ``` You define several state variables: * `walletAddress` to store the user-inputted wallet address. * `balances` to store the fetched balances. * `loading` to indicate the loading state while fetching data. * `error` to display any errors that occur during the fetching process. #### Handle balance check ```javascript Javascript theme={"system"} const handleCheckBalances = async () => { if (!ethers.isAddress(walletAddress)) { setError("Invalid Ethereum address"); return; } setLoading(true); setError(null); setBalances(null); // Clear previous balances try { const response = await fetch(`/api/balances`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ walletAddress }), }); if (!response.ok) throw new Error("Failed to fetch balances"); const data = await response.json(); setBalances(data); } catch (err) { setError(err.message); } setLoading(false); }; ``` This function handles the balance-checking process and calls the API that uses ethers and the `ChainstackProvider`, you take care of that in the following section: 1. **Validation**: Checks if the input is a valid Ethereum address. 2. **Loading state**: Sets the loading state to true and clears previous balances and errors. 3. **API interaction**: Sends a `POST` request to the `/api/balances` endpoint with the wallet address. 4. **Response handling**: Processes the response, updating the `balances` state or sets an error message if the request fails. 5. **Loading state**: Resets the loading state to false after the request completes. #### User interface ```javascript Javascript theme={"system"} return (

Chainstack Powered

Multi-chain Wallet Balance Aggregator

This simple DApp uses the ChainstackProvider from ether.js to interact with those chains.

Learn more about the ChainstackProvider from ether.js on the{" "} Chainstack documentation .

setWalletAddress(e.target.value)} className="w-full p-2 mb-4 border rounded bg-gray-700 border-gray-600 text-white" /> {error &&

{error}

} {balances && (

Ethereum:{" "} {parseFloat(balances.ethereum).toFixed(5)} ETH

Polygon:{" "} {parseFloat(balances.polygon).toFixed(5)} MATIC

BNB Smart Chain:{" "} {parseFloat(balances.bnb).toFixed(5)} BNB

Arbitrum:{" "} {parseFloat(balances.arbitrum).toFixed(5)} ETH

)}
); ``` The UI consists of: * A header with titles and descriptions. * An input field for the wallet address. * A button to trigger the balance check. * Conditional rendering to display errors or balances. This simple breakdown focuses on the key functionality: interacting with the backend API to fetch and display wallet balances. Next, you get into the backend logic in `route.js` to understand how the balances are retrieved from multiple blockchain networks using ChainstackProvider. ## The backend API Now let's work on the heart of the DApp, the API endpoint using the `ChainstackProvider` to fetch data from the chains. In `src/app` create a new directory for the API route. The final path will be `...src/app/api/balances/route.ts`. Create each directory, `api`, `balances`, and then the `route.js` file in it. Once you have `route.js` paste this code into it: ```javascript route.ts theme={"system"} const ethers = require("ethers"); const providers = { ethereum: new ethers.ChainstackProvider("mainnet"), polygon: new ethers.ChainstackProvider("matic"), bnb: new ethers.ChainstackProvider("bnb"), arbitrum: new ethers.ChainstackProvider("arbitrum"), }; async function getBalance(network, walletAddress) { const provider = providers[network]; const balance = await provider.getBalance(walletAddress); return ethers.formatEther(balance); // Convert balance from wei to ether } async function getAllBalances(walletAddress) { const balances = {}; for (const network in providers) { balances[network] = await getBalance(network, walletAddress); } return balances; } export async function POST(req) { const { walletAddress } = await req.json(); if (!walletAddress) { return new Response( JSON.stringify({ error: "Wallet address is required" }), { status: 400 } ); } try { const balances = await getAllBalances(walletAddress); return new Response(JSON.stringify(balances), { status: 200 }); } catch (error) { console.error("Error fetching balances:", error); return new Response(JSON.stringify({ error: "Internal Server Error" }), { status: 500, }); } } ``` ### Breakdown of `route.ts` The `route.ts` file handles the backend logic for your DApp. It interacts with multiple blockchain networks using `ChainstackProvider` to fetch the wallet balances. Here’s a detailed breakdown of the important parts of the code: #### Import ethers.js ```javascript Javascript theme={"system"} const ethers = require("ethers"); ``` You import the ethers library, which provides the necessary tools to interact with Ethereum and other blockchain networks. #### Initialize providers ```javascript Javascript theme={"system"} // Initialize a provider for each supported chain const providers = { ethereum: new ethers.ChainstackProvider("mainnet"), polygon: new ethers.ChainstackProvider("matic"), bnb: new ethers.ChainstackProvider("bnb"), arbitrum: new ethers.ChainstackProvider("arbitrum"), }; ``` You create an object `providers` that holds instances of `ChainstackProvider` for each supported blockchain network. This allows you to connect to Ethereum, Polygon, BNB Smart Chain, and Arbitrum networks. #### Fetch balance for a single network ```javascript Javascript theme={"system"} async function getBalance(network, walletAddress) { const provider = providers[network]; const balance = await provider.getBalance(walletAddress); return ethers.formatEther(balance); // Convert balance from wei to ether } ``` The `getBalance` function takes a network name and a wallet address as parameters. It uses the corresponding provider to fetch the balance of the given wallet address and converts the balance from `wei` to `ether` using `ethers.formatEther`. #### Fetch balances for all networks ```javascript Javascript theme={"system"} async function getAllBalances(walletAddress) { const balances = {}; for (const network in providers) { balances[network] = await getBalance(network, walletAddress); } return balances; } ``` The `getAllBalances` function takes a wallet address as a parameter. It iterates over all the networks defined in the `providers` object, fetches the balance for each network using the `getBalance` function, and stores the results in the `balances` object. #### Handle the POST request ```javascript Javascript theme={"system"} export async function POST(req) { const { walletAddress } = await req.json(); if (!walletAddress) { return new Response( JSON.stringify({ error: "Wallet address is required" }), { status: 400 } ); } try { const balances = await getAllBalances(walletAddress); return new Response(JSON.stringify(balances), { status: 200 }); } catch (error) { console.error("Error fetching balances:", error); return new Response(JSON.stringify({ error: "Internal Server Error" }), { status: 500, }); } } ``` The `POST` function handles incoming POST requests to your API endpoint: 1. **Parse the request**: It extracts the `walletAddress` from the request body. 2. **Validate**: If the `walletAddress` is missing; it returns a 400 status with an error message. 3. **Fetch balances**: It calls the `getAllBalances` function to fetch the balances for all networks. 4. **Return the response**: It returns the balances with a 200 status if successful. If an error occurs, it logs it and returns a 500 status with an error message. The `route.ts` file handles the backend logic for your DApp. It connects to multiple blockchain networks using the`ChainstackProvider` to fetch the wallet balances and returns the results to the front end. This is a good way to keep your endpoints secure, as the front end does not call the RPC node directly but calls something similar to a proxy server instead. ## Run the DApp And now you have a complete DApp; it was indeed super easy with the `ChainstackProvider`. Now, to run it, simply start the development server: ``` npm run dev ``` Your DApp is now running on `http://localhost:3000`. You can use this address to test it: `0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE`. ## Conclusion In this tutorial, you've walked through creating a multi-chain wallet balance aggregator DApp using Ethers.js and `ChainstackProvider`. By integrating these powerful tools into a Next.js project, you've seen for yourself how easy it is to fetch and display wallet balances from multiple blockchain networks. This project showcases the simplicity and efficiency of using ChainstackProvider to interact with various blockchain networks, making it an excellent choice for developers looking to build decentralized applications. # EVM MCP server Source: https://docs.chainstack.com/docs/evm-mcp-server Query balances, read contracts, and interact with Ethereum and EVM-compatible blockchains through the EVM MCP server for AI-assisted development. > [!WARNING] > > **Deprecated.** Use the unified [Chainstack MCP server](/docs/chainstack-mcp-server) at `https://mcp.chainstack.com/mcp` instead. It provides node deployment, documentation search, and platform management in a single remote server — no local setup required. The **EVM MCP server** is part of Chainstack's RPC Nodes MCP server suite, providing AI models with direct access to Ethereum and EVM-compatible blockchain networks. This server enables real-time blockchain interactions, smart contract calls, and comprehensive blockchain data queries. ## Repository The EVM MCP server is available in the [chainstacklabs/rpc-nodes-mcp](https://github.com/chainstacklabs/rpc-nodes-mcp) repository, which contains both EVM and Solana blockchain interaction capabilities. ## Supported networks The EVM MCP server supports all EVM-compatible networks available on Chainstack, including: * **Ethereum** (Mainnet, Sepolia) * **Polygon** (Mainnet, Amoy testnet) * **BNB Smart Chain** (Mainnet, testnet) * **Arbitrum** (One, Sepolia) * **Optimism** (Mainnet, Sepolia) * **Base** (Mainnet, Sepolia) * **Avalanche** C-Chain * **And many more** EVM-compatible networks ## Available functions The EVM MCP server provides comprehensive blockchain interaction capabilities: ### Network information * **Get supported blockchains** - Retrieve list of available networks * **Get client version** - Check node client information * **Get network version** - Retrieve network ID * **Get chain ID** - Fetch the chain identifier ### Block operations * **Get latest block number** - Retrieve current block height * **Get block by number/hash** - Fetch complete block information * **Get block transaction count** - Count transactions in a block * **Get block receipts** - Retrieve all transaction receipts for a block ### Account and balance queries * **Get account balance** - Check ETH/native token balance * **Get transaction count** - Retrieve account nonce * **Get contract code** - Fetch smart contract bytecode * **Get storage values** - Read contract storage slots ### Transaction operations * **Get transaction by hash** - Retrieve transaction details * **Get transaction receipt** - Fetch transaction execution results * **Estimate gas** - Calculate gas requirements for transactions * **Get gas price** - Retrieve current gas pricing * **Get fee history** - Analyze historical fee data ### Smart contract interactions * **Call contract methods** - Execute read-only contract calls * **Simulate transactions** - Test transactions without execution * **Get event logs** - Query contract event emissions * **Get proofs** - Retrieve Merkle proofs for accounts and storage ### Advanced features * **Debug transaction traces** - Detailed execution analysis * **Trace calls and blocks** - Comprehensive execution tracing * **State overrides** - Simulate with modified blockchain state * **Batch requests** - Execute multiple calls efficiently ## Use cases The EVM MCP server enables AI applications to: ### DeFi and trading analysis * **Monitor token balances** and portfolio values * **Track transaction flows** and trading patterns * **Analyze gas usage** and optimization opportunities * **Query DeFi protocol states** and liquidity metrics ### Smart contract development * **Test contract interactions** before deployment * **Debug transaction failures** with detailed traces * **Simulate complex scenarios** with state overrides * **Validate contract behavior** across different conditions ### Blockchain analytics * **Extract event data** from smart contracts * **Analyze block composition** and network activity * **Track address interactions** and transaction patterns * **Generate reports** on blockchain metrics ### Real-time monitoring * **Watch for specific events** or transactions * **Monitor contract state changes** in real-time * **Track network congestion** and gas price trends * **Alert on important blockchain events** ## Integration with AI models See the [RPC nodes MCP README file](https://github.com/chainstacklabs/rpc-nodes-mcp/blob/main/README.md) for more information on how to integrate the EVM MCP server with AI models. The EVM MCP server seamlessly integrates with popular AI development environments: ### Cursor Configure the server in your [Cursor settings](https://docs.cursor.com/context/model-context-protocol) to access Chainstack's knowledge base directly within your conversations. ### Claude Desktop Configure the server in your [Claude Desktop settings](https://modelcontextprotocol.io/quickstart/user) to access Chainstack's knowledge base directly within your conversations. ### Claude Code Configure the server in your [Claude Code settings](https://docs.anthropic.com/en/docs/claude-code/mcp) to access Chainstack's knowledge base directly within your conversations. ### Custom AI applications Use the MCP protocol to integrate Chainstack's documentation into your own AI-powered development tools and assistants. ## Next steps > [card] [Solana MCP server](/docs/solana-mcp-server) > > Add Solana blockchain capabilities > [card] [Developer Portal MCP server](/docs/developer-portal-mcp-server) > > Access documentation and guides > [card] [GitHub Repository](https://github.com/chainstacklabs/rpc-nodes-mcp) > > View source code and installation instructions > [card] [Ethereum API Reference](/reference/ethereum-getting-started) > > Explore available [Ethereum methods](/docs/ethereum-methods) # EVM node connection: HTTP vs WebSocket Source: https://docs.chainstack.com/docs/evm-node-connection-http-vs-websocket Choose between HTTP and WebSocket when connecting to a Chainstack EVM node. Compare timeouts, connection reuse, and use cases for eth_call, eth_getLogs, and subscriptions. **TLDR:** * **HTTP** is unidirectional and stateless — each request opens a new connection. Best for short-lived calls. * **WebSocket** is bidirectional and persistent — one connection is reused for many requests and supports subscriptions. Best for long-running or real-time workloads. * Default to WebSocket for any workload with subscriptions, long-running queries, or sustained traffic from the same client. ## How the protocols differ | | HTTP | WebSocket | | - | - | - | | Direction | Unidirectional (request → response) | Bidirectional | | Connection lifecycle | New connection per request, closed on response | Single persistent connection, reused | | Server push | No | Yes (subscriptions) | | Typical use | One-shot reads and writes (`eth_call`, `eth_sendRawTransaction`, `eth_getLogs`) | Subscriptions (`eth_subscribe`), sustained polling | In client-server communication, each HTTP request from the client establishes a new TCP/TLS connection and the connection closes after the response. A WebSocket connection is made once and reused until either the server or the client terminates it — the same socket can carry many JSON-RPC requests and push notifications. ## When to use HTTP * All one-shot reads and writes — `eth_call`, `eth_sendRawTransaction`, `eth_getBalance`, `eth_blockNumber`, `eth_getLogs`, traces. * Stateless batch jobs and serverless functions where each invocation makes a few requests and then exits. * HTTP/2 keep-alive in modern clients amortises the per-request connection cost. * For wide-range `eth_getLogs` queries, paginate the block range rather than reaching for WebSocket — the call is request/response and doesn't benefit from a persistent socket. ## When to use WebSocket * Subscriptions via [`eth_subscribe`](/reference/ethereum-web3js-subscriptions-methods) — `newHeads`, `logs`, `newPendingTransactions`. These require server push, which HTTP can't do. * Sustained traffic from the same client where connection reuse and keep-alive eliminate per-request handshake overhead. * Real-time event listening — see [Handle real-time data with WebSockets in JS & Python](/docs/handle-real-time-data-using-websockets-with-javascript-and-python). > [!NOTE] > > WebSocket subscription notifications are billable: each push your server sends counts as one [request unit](/docs/request-units), just like a regular RPC response. For sustained high-volume subscriptions, consider an [Unlimited Node](/docs/unlimited-node) which uses RPS-tiered flat-fee pricing instead of per-request billing. ## Connection reuse with WebSocket When using WebSocket from a client library, open the connection once and reuse it for every request. Re-opening per request is a common mistake that defeats the protocol's advantage and quickly exhausts the connection pool. ```python web3.py — incorrect theme={"system"} # Anti-pattern: opens a new WebSocket per call def get_latest_block(): w3 = Web3(Web3.LegacyWebSocketProvider("wss://ethereum-mainnet.core.chainstack.com/AUTH_KEY")) return w3.eth.block_number ``` ```python web3.py — correct theme={"system"} # Open once, reuse w3 = Web3(Web3.LegacyWebSocketProvider("wss://ethereum-mainnet.core.chainstack.com/AUTH_KEY")) def get_latest_block(): return w3.eth.block_number ``` ## See also * [Handle real-time data with WebSockets in JS & Python](/docs/handle-real-time-data-using-websockets-with-javascript-and-python) * [Request units](/docs/request-units) * [Throughput guidelines](/docs/limits) * [Best practices for error handling in API requests](/docs/best-practices-for-error-handling-in-api-requests) # EVM node returns "Missing trie node" Source: https://docs.chainstack.com/docs/evm-node-returns-missing-trie-node Resolve the 'missing trie node' error on EVM nodes by querying historical state with an archive node instead of a full node, which retains only the last 128 blocks. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * The `missing trie node` error means you're requesting historical state from a node that doesn't have it. * Full nodes on EVM chains retain only the most recent \~128 blocks of state; everything older needs an [archive node](/docs/protocols-modes-and-types). * The fix: query through an archive endpoint, or fall back to the latest state with a block tag like `"latest"`. ## The error You query an EVM node for state at a specific historical block and get back: ``` missing trie node ``` For example, getting an account balance at a past block in the Geth console: ```javascript Geth console theme={"system"} > eth.getBalance("0xc94770007dda54cF92009BFF0dE90c06F603a09f", 8228625) Missing trie node ``` The same error surfaces through JSON-RPC clients: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0xc94770007dda54cF92009BFF0dE90c06F603a09f","0x7d8e91"],"id":1}' # {"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"missing trie node ..."}} ``` ## Cause Most EVM full nodes retain state only for a sliding window of the most recent blocks — Geth's default `gcmode=full` window is roughly 128 blocks. The exact window varies by client (Geth, Reth, Nethermind, Besu) and chain. State queries — [`eth_getBalance`](/reference/ethereum-getbalance), [`eth_getStorageAt`](/reference/ethereum-getstorageat), [`eth_call`](/reference/ethereum-ethcall), [`eth_getCode`](/reference/ethereum-getcode), traces — against any block older than this window return `missing trie node`. Erigon is the main exception: its staged-sync architecture stores all historical state by default, so an Erigon-based "full" node can answer historical queries that a Geth full node can't. Archive nodes keep the full state trie from genesis on all clients, so they answer historical queries at any block. This also affects [`eth_simulateV1`](/reference/ethereum-simulatev1) on EVM full nodes — it can only simulate against the most recent \~128 blocks; older targets return `missing trie node`. ## Solutions ### Use an archive endpoint Deploy a node in archive mode and switch your endpoint to it. On Chainstack, archive mode is available across all node types — [Global Nodes](/docs/global-elastic-node), [Trader Nodes](/docs/trader-node), [Dedicated Nodes](/docs/dedicated-node), and [Unlimited Nodes](/docs/unlimited-node) — on [paid plans](https://chainstack.com/pricing/) for supported protocols. See [Protocols, modes, and types](/docs/protocols-modes-and-types). ### Query the latest state If you don't actually need historical state, use the `"latest"` block tag (or omit the block parameter): ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0xc94770007dda54cF92009BFF0dE90c06F603a09f","latest"],"id":1}' ``` ### Use logs or receipts instead For event-driven workloads, [`eth_getLogs`](/reference/ethereum-getlogs) and `eth_getTransactionReceipt` work on full nodes regardless of block age and don't touch the state trie. See [Understanding `eth_getLogs` limitations](/docs/understanding-eth-getlogs-limitations). ## See also * [Protocols, modes, and types](/docs/protocols-modes-and-types) — full vs archive * [Throughput guidelines](/docs/limits) — related EVM range limits * [Debug & Trace APIs](/docs/debug-and-trace-apis) # Only replay-protected (EIP-155) transactions allowed over RPC Source: https://docs.chainstack.com/docs/evm-only-replay-protected-eip-155-transactions-error Fix the EVM node 'only replay-protected (EIP-155) transactions allowed over RPC' error by adding chainId to your signed transaction. Includes examples in ethers.js, viem, and web3.py. **TLDR:** * EVM nodes refuse to broadcast legacy (pre-EIP-155) transactions because they could be replayed on other EVM chains. * The fix: include `chainId` in the transaction object before signing. * All modern signing libraries do this automatically when you pass `chainId` (ethers.js, viem) or set the network/wallet to a specific chain (web3.py with a chain-aware wallet). ## The error You try to send a raw transaction through an EVM node and get: ``` only replay-protected (EIP-155) transactions allowed over RPC ``` ## Cause [EIP-155](https://eips.ethereum.org/EIPS/eip-155) made the chain ID part of the signed transaction so a signature for Ethereum mainnet can't be replayed as-is on, say, BNB Smart Chain. Most modern EVM clients refuse to accept non-EIP-155 transactions over public RPC. If your signing code doesn't include `chainId`, the resulting transaction is "legacy" pre-EIP-155 and the node rejects it. ## Solution Include `chainId` in the transaction object before signing. The value is the numeric chain ID of the target network — look it up on [chainlist.org](https://chainlist.org/). ```typescript ethers.js theme={"system"} import { Wallet, JsonRpcProvider, parseEther, parseUnits } from "ethers"; const provider = new JsonRpcProvider("https://ethereum-mainnet.core.chainstack.com/AUTH_KEY"); const wallet = new Wallet(process.env.PRIVATE_KEY!, provider); // ethers picks up chainId automatically from the provider. // For manual construction, include it explicitly: const tx = await wallet.sendTransaction({ to: "0xRecipientAddress", value: parseEther("0.1"), gasLimit: 21000, maxFeePerGas: parseUnits("50", "gwei"), maxPriorityFeePerGas: parseUnits("2", "gwei"), chainId: 1, // Ethereum mainnet }); ``` ```typescript viem theme={"system"} import { createWalletClient, http, parseEther, parseGwei } from "viem"; import { mainnet } from "viem/chains"; import { privateKeyToAccount } from "viem/accounts"; const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); const client = createWalletClient({ account, chain: mainnet, // chain object carries the chain ID transport: http("https://ethereum-mainnet.core.chainstack.com/AUTH_KEY"), }); const hash = await client.sendTransaction({ to: "0xRecipientAddress", value: parseEther("0.1"), maxFeePerGas: parseGwei("50"), maxPriorityFeePerGas: parseGwei("2"), }); ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("https://ethereum-mainnet.core.chainstack.com/AUTH_KEY")) account = w3.eth.account.from_key(PRIVATE_KEY) tx = { "to": "0xRecipientAddress", "value": w3.to_wei(0.1, "ether"), "gas": 21000, "maxFeePerGas": w3.to_wei(50, "gwei"), "maxPriorityFeePerGas": w3.to_wei(2, "gwei"), "nonce": w3.eth.get_transaction_count(account.address, "latest"), "chainId": w3.eth.chain_id, # fetched once and reused } signed = account.sign_transaction(tx) tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction) ``` > [!NOTE] > > In ethers.js and viem, `chainId` is taken from the provider/chain object — you usually don't have to set it manually. The error typically shows up when you're hand-crafting a transaction or using a low-level signer that doesn't have a chain context bound. ## See also * [EIP-155: Simple replay attack protection](https://eips.ethereum.org/EIPS/eip-155) * [chainlist.org](https://chainlist.org/) — registry of EVM chain IDs * [Error reference](/docs/error-reference) # EVM transaction pool: pending vs queued Source: https://docs.chainstack.com/docs/evm-transaction-pool-pending-vs-queued Understand pending vs queued transactions in an EVM node's local pool. Inspect the pool with txpool_status, subscribe to new pending transactions, and fix nonce-gap queued transactions. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * **Pending** transactions are in the pool ready to be included in a block. * **Queued** transactions are sitting locally because their account nonce is out of sequence; they don't propagate until the gap is filled. * Inspect the pool with `txpool_status` / `txpool_content`; subscribe to new pending transactions with `eth_subscribe`; fix a queued transaction by filling the nonce gap. > [!NOTE] > > `txpool_content` and `txpool_inspect` need a [Dedicated Node](/docs/dedicated-node) — Global Nodes and Trader Nodes return `-32601` for them. `txpool_status` depends on the network and mode, and the `eth_subscribe` part works on every node type. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ## Pending vs queued A transaction submitted through an EVM node has one of two states before it lands in a block: * **Pending** — in the node's transaction pool and ready to be included in the next block. * **Queued** — held in the local pool because the account nonce is out of sequence. Each transaction has an **account nonce** (not the block nonce). The account nonce starts at `0` and must increase by one per transaction from that address. A transaction whose nonce is in sequence goes straight to **pending**. A transaction with a nonce gap waits in **queued** until the missing nonces are filled. ### Example | Step | From | Nonce | State | | - | - | - | - | | 1 | `0x123...` | 0 | Pending → mined | | 2 | `0x123...` | 1 | Pending → mined | | 3 | `0x123...` | 3 | **Queued** (nonce 2 is missing) | | 4 | `0x123...` | 2 | Pending → mined. Nonce 3 now moves to Pending and is mined too. | ## Inspect the local pool Use the [`txpool`](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool) JSON-RPC namespace to inspect the mempool of a node you have access to. On Chainstack, reading the pool contents with `txpool_inspect` or `txpool_content` requires a [Dedicated Node](/docs/dedicated-node) — see [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access) for which methods each node type serves, and [Mempool configurations](/docs/mempool-configuration) for protocol-by-protocol availability. ```bash txpool_status theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"txpool_status","params":[],"id":1}' # {"jsonrpc":"2.0","id":1,"result":{"pending":"0x4a","queued":"0x3"}} ``` ```bash txpool_inspect theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"txpool_inspect","params":[],"id":1}' ``` ```bash txpool_content theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"txpool_content","params":[],"id":1}' ``` To check the current confirmed nonce for an address: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["0xYourAddress","latest"],"id":1}' ``` ## Subscribe to new pending transactions For real-time delivery of new pending transactions, subscribe over WebSocket: ```python web3.py (WebSocket) theme={"system"} import asyncio from web3 import AsyncWeb3, WebSocketProvider WSS = "wss://ethereum-mainnet.core.chainstack.com/AUTH_KEY" async def main(): async with AsyncWeb3(WebSocketProvider(WSS)) as w3: subscription_id = await w3.eth.subscribe("newPendingTransactions") async for payload in w3.socket.process_subscriptions(): print(payload["result"]) # transaction hash asyncio.run(main()) ``` ```bash cURL (raw subscription) theme={"system"} wscat -c wss://ethereum-mainnet.core.chainstack.com/AUTH_KEY > {"jsonrpc":"2.0","method":"eth_subscribe","params":["newPendingTransactions"],"id":1} ``` > [!NOTE] > > Each push notification over WebSocket counts as one [request unit](/docs/request-units). For sustained high-volume pending-transaction subscriptions, consider an [Unlimited Node](/docs/unlimited-node) which uses RPS-tiered flat-fee pricing instead of per-request billing. ### HTTP-polling alternative If you can't use WebSocket, poll an HTTP filter with [`eth_newPendingTransactionFilter`](/reference/ethereum-newpendingtransactionfilter) and [`eth_getFilterChanges`](/reference/ethereum-getfilterchanges): ```bash Create filter theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_newPendingTransactionFilter","params":[],"id":0}' # {"jsonrpc":"2.0","id":0,"result":"0xb05a471a87c111bccadc9671f5b23b6a"} ``` ```bash Poll for new entries theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_getFilterChanges","params":["0xb05a471a87c111bccadc9671f5b23b6a"],"id":1}' ``` Filters expire after about five minutes of inactivity — see [Fix Ethereum's filter not found error](/docs/understanding-ethereums-filter-not-found-error-and-how-to-fix-it). ## Fix a queued transaction If you have a queued transaction stuck behind a nonce gap, fill the gap by sending the missing nonces. The simplest path is to send no-op self-transfers at the missing nonces until the queued nonce becomes the next pending one. ### Diagnose the gap 1. Get the confirmed nonce: `eth_getTransactionCount("0xYourAddress","latest")`. 2. Get the queued transaction's nonce from `txpool_content` or from the transaction object: [`eth_getTransactionByHash`](/reference/ethereum-gettransactionbyhash). 3. The gap is everything between them. ### Fill the gap Send a transaction at each missing nonce. A 0-value self-transfer with explicit `nonce` is enough: ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) account = w3.eth.account.from_key("0xYOUR_PRIVATE_KEY") confirmed = w3.eth.get_transaction_count(account.address, "latest") stuck_nonce = 12 # nonce of your queued transaction for nonce in range(confirmed, stuck_nonce): tx = { "from": account.address, "to": account.address, "value": 0, "nonce": nonce, "gas": 21000, "maxFeePerGas": w3.to_wei(50, "gwei"), "maxPriorityFeePerGas": w3.to_wei(2, "gwei"), "chainId": w3.eth.chain_id, } signed = account.sign_transaction(tx) w3.eth.send_raw_transaction(signed.raw_transaction) ``` Once the missing nonces confirm, the previously queued transaction moves to **pending** and is mined. ### Alternative: cancel by replacement If you'd rather drop the queued transaction altogether, send a replacement at the same nonce with a higher gas price — most clients require the new gas price to be at least 10% higher (`txpool.pricebump`). ## See also * [Mempool configurations](/docs/mempool-configuration) — Chainstack support per protocol * [Debug and trace APIs](/docs/debug-and-trace-apis) * [Request units](/docs/request-units) * [Fix Ethereum's filter not found error](/docs/understanding-ethereums-filter-not-found-error-and-how-to-fix-it) # eth_getBlockReceipts emulator for blockchain data Source: https://docs.chainstack.com/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator Build eth_getBlockReceipts emulator with Node.js and web3.js using Chainstack. Replicate transaction receipt retrieval for EVM chains without native support. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR** * Demonstrates building a stand-in for the eth\_getBlockReceipts method (available natively on Erigon and newer Geth versions) using the eth\_getBlockByNumber and eth\_getTransactionReceipt calls on any EVM chain. * Uses Node.js and web3.js to fetch all transaction receipts for a specified block, grouping them into a single array – effectively replicating the one-call convenience of eth\_getBlockReceipts. * Highlights the parsing of receipt logs for more user-friendly output and includes a helper function to selectively extract specific fields (e.g. gasUsed) from each receipt. * Offers a practical solution to unify transaction + receipt data retrieval on clients that lack the native eth\_getBlockReceipts endpoint. ## Main article The [`eth_getBlockReceipts`](/reference/ethereum-getblockreceipts) method is a powerful tool in the Ethereum ecosystem that offers a window into the inner workings of the network. Retrieving the receipts of all transactions within a block provides insight into the status and outcome of each transaction. Whether you're developing a decentralized exchange, a contract auditing tool, or just curious about the Ethereum network, the `eth_getBlockReceipts` method is an essential resource that can help you uncover the details and outcomes of transactions on the blockchain. It's like having an x-ray of the Ethereum network, revealing what's happening beneath the surface and providing a clear picture of the network's activity. The caveat is that this method is only available by querying nodes running the [Erigon client](/docs/protocols-clients). This guide will show you how you can emulate this method in essentially any EVM-compatible network, even if the node is not running on Erigon. Read the [Erigon vs. Geth](https://chainstack.com/ethereum-clients-geth-and-erigon/) guide to get a better understanding of these two popular Ethereum clients. This article is code-centered, and you should read the [Uncovering the power of eth\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) guide to have more theory insights. > [!NOTE] > > ### Update on eth\_getBlockReceipts > > As of September 2023, the `eth_getBlockReceipts` method is also available on the Geth client from V [1.13.0](https://github.com/ethereum/go-ethereum/releases/tag/v1.13.0). ## Prerequisites To start with a JavaScript development project, you'll need to install `node.js`, a powerful JavaScript runtime environment that enables developers to run JavaScript code outside a web browser. For this project, it's recommended to use at least version 16. You can [download it from their website](https://nodejs.org/en/download/). With `node.js` installed, you're ready to start using JavaScript. However, you'll need access to a blockchain node to query the data. Here's where Chainstack comes in to save the day. Simply follow these steps to sign up and deploy your own blockchain node with Chainstack for free: > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) > [!NOTE] > > ### Disclaimer > > This tool was developed and tested using an Avalanche endpoint, but you can choose any EVM-compatible network. ## The project Now that you have all the tools required, you are ready to create your `eth_getBlockReceipt` emulator. ## Initialize an npm project An npm (Node Package Manager) project is a software project managed using the `npm` platform. npm is the default package manager for the JavaScript runtime environment node.js and is needed to manage the dependencies and packages used in a project. At the heart of an `npm` project is the `package.json` file, a blueprint that outlines the packages and dependencies your project requires. This file acts as a roadmap, guiding npm in managing your project's dependencies. `npm` makes it easy for developers to share and reuse code, and its vast library of packages can be easily installed and used in any project. This allows developers to focus on writing their own code and let `npm` manage external dependencies. To initialize an `npm` project, open your terminal in the root directory of your project and run the following command: ```bash Shell theme={"system"} npm init ``` This command will prompt a few questions, answer them, and it will create a `package.json` file. Note that these answers do not affect the functionality of your project, so don’t stress too much about what you input. You can also run the command with the `-y` flag, which will skip all questions. ```bash Shell theme={"system"} npm init -y ``` You have now created an `npm` project and are ready to start development. ## Install the dependencies As previously mentioned, creating this tool requires some npm packages. Specifically the `dotenv` and `web3` packages. During development, we used the following versions: * dotenv ^16.0.3 * web3 ^1.8.1 Install those packages by running the following: ```bash Shell theme={"system"} npm i web3 dotenv ``` [web3.js](https://web3js.readthedocs.io/en/v1.8.2/) is a tool for interacting with an EVM-compatible blockchain, while dotenv is a package for managing configuration settings in a secure and convenient manner. Together, these two packages provide a powerful and flexible toolkit for developing DApps. ## The code This `eth_getBlockReceipt` emulator will be developed on a single JavaScript file, so go ahead and create a file named `index.js` in the root directory of your project. The tool will do the following: * Create a provider instance and import packages. * Generate an eth\_getBlockReceipt-like object with a function. * Loop into the object and extract specific fields with a function. * Run the program with the main function. ### Import packages and create a provider instance The first step of any tool using the web3.js library is to import the necessary packages and create a provider instance. So add the following code at the top of your `index.js` file. ```js index.js theme={"system"} const Web3 = require("web3"); require('dotenv').config(); // Create provider instance const NODE_URL = process.env.CHAINSTACK_NODE_URL; const web3 = new Web3(NODE_URL); ``` This creates a `web3` instance that can access a high-level API for interacting with a blockchain; essentially, it’s your connection to the blockchain node. This tool uses an environment variable to import the node URL to keep sensitive information safe, so it is more difficult to inadvertently share it. This is done using the `dotenv` package. Create a file named `.env` in the root directory and create a variable holding your Chainstack node URL. ```js .env theme={"system"} CHAINSTACK_NODE_URL="YOUR_ENDPOINT_URL_HERE" ``` > [!WARNING] > > It is important that the environment variable has the same name as the variable imported in the `index.js` file. ### Generate an eth\_getBlockReceipt-like object Now, let’s start working on the bulk of the logic for this project. Create a new `async` function named `getBlockReceipts` which takes two parameters: the `provider` and the target `block` for retrieving receipts. Then paste the following code into the `index.js` file: ```js index.js theme={"system"} async function getBlockReceipts(provider, block) { const extractTransactions = await provider.eth.getBlock(block, false) let receipts = []; for (const transaction of extractTransactions.transactions) { // Get the transaction receipt const txReceipt = await web3.eth.getTransactionReceipt(transaction); for (let log of txReceipt.logs) { let logData = { address: log.address, topics: log.topics, data: log.data, blockNumber: log.blockNumber, transactionHash: log.transactionHash, transactionIndex: log.transactionIndex, blockHash: log.blockHash, logIndex: log.logIndex, removed: log.removed, id: log.id } txReceipt.logs = logData; } // replace the original logs array with the logData receipts.push(txReceipt); } return receipts; } ``` Let’s go over the logic here. The first step is to retrieve all of the transactions hashed from the desired block. This can be achieved by utilizing the [`eth_getBlockByNumber`](/reference/ethereum-getblockbynumber) method from the Ethereum JSON-RPC API. When calling this method, set the *full transactions* flag to `false` to ensure that only the transaction hashes are returned in an array format. The transaction hashes retrieved in this step will be stored in the `extractTransactions` constant for further processing. ```js JavaScript theme={"system"} const extractTransactions = await provider.eth.getBlock(block, false) ``` The next step will loop through the array of hashes and call the [`eth_getTransactionReceipt`](/reference/ethereum-gettransactionreceipt) method on each hash, do some parsing, and store the result in an array called `receipts`. ```js JavaScript theme={"system"} let receipts = []; for (const transaction of extractTransactions.transactions) { // Get the transaction receipt const txReceipt = await web3.eth.getTransactionReceipt(transaction); for (let log of txReceipt.logs) { let logData = { address: log.address, topics: log.topics, data: log.data, blockNumber: log.blockNumber, transactionHash: log.transactionHash, transactionIndex: log.transactionIndex, blockHash: log.blockHash, logIndex: log.logIndex, removed: log.removed, id: log.id } txReceipt.logs = logData; } // replace the original logs array with the logData receipts.push(txReceipt); } ``` The second loop for `(let log of txReceipt.logs)` will extract the logs data and replace the `logs object` from the original receipt. This step aims to provide a more user-friendly representation of the logs data. Without this processing, the `logs` data in the transaction receipt would only be returned as an array of objects, `[object, object]`, which may require additional steps from the end-user to extract the relevant information. By replacing the original logs object with the `logData` object, the final response includes a more intuitive representation of the log data, eliminating the need for additional processing. Once all the logs in the transaction receipt have been processed, the code proceeds to the next iteration of the loop and repeats the same process for the next transaction. The final step pushes the unpacked transaction receipt into the `receipts` array, and after all of the transactions have been processed, the function returns the `receipts` array. ### Support function to isolate fields This script also includes a support function called `getElement` which takes the receipts array returned by `getBlockReceipts` and the name of a field as the input parameters. Paste the following function into the `index.js` file. ```js index.js theme={"system"} // Extract a specific field and return an array async function getElement(receipts, field) { return receipts.map(receipt => receipt[field]); } ``` The function returns an array that contains the values of the specified field from each object in the `receipts` array. This is achieved through the `map` method, which iterates over each element in the `receipts` array and returns the specified field's value for that element. In essence, the `getElement` function provides a convenient way to extract a specific field from a collection of objects and returns the extracted values as an array. For example: ```js JavaScript theme={"system"} const logData = await getElement(receipts, 'gasUsed'); // Response = [ 396022, 1442653, 664107 ] ``` ### Main function and full code The final step of the script is the `main()` function, which runs the full code and demonstrates the logic. Paste the following at the bottom of the file. ```js index.js theme={"system"} async function main() { const blockNumber = 25792736; // Retrieve the transactions receipts const receipts = await getBlockReceipts(web3, blockNumber); console.log(receipts); // Use the getElement function to extract a specific field from each receipt. const logData = await getElement(receipts, 'to'); console.log(logData); } main(); ``` This function can be seen as the execution of the program where: * A block number is specified as input to the function. * The `getBlockReceipts` function is invoked within the function. * The results of the `getBlockReceipts` function are output to the console. * The second part of the function calls the `getElement` function. * The `to` field is extracted from each receipt using the `getElement` function. ### Full code The previous sections show the script broken down into all its components to make it easier to understand. Here, you can find the full code: ```js index.js theme={"system"} const Web3 = require("web3"); require('dotenv').config(); // Create provider instance const NODE_URL = process.env.CHAINSTACK_NODE_URL; const web3 = new Web3(NODE_URL); // Create receipts object async function getBlockReceipts(provider, block) { // Extract the transactions from the block const extractTransactions = await provider.eth.getBlock(block, false) let receipts = []; for (const transaction of extractTransactions.transactions) { // Get the transaction receipt const txReceipt = await web3.eth.getTransactionReceipt(transaction); // Parse the logs information for (let log of txReceipt.logs) { let logData = { address: log.address, topics: log.topics, data: log.data, blockNumber: log.blockNumber, transactionHash: log.transactionHash, transactionIndex: log.transactionIndex, blockHash: log.blockHash, logIndex: log.logIndex, removed: log.removed, id: log.id } txReceipt.logs = logData; } // replace the original logs array with the logData receipts.push(txReceipt); } return receipts; } // Extract a specific field and return an array async function getElement(receipts, field) { return receipts.map(receipt => receipt[field]); } async function main() { const blockNumber = 25792736; const receipts = await getBlockReceipts(web3, blockNumber); console.log(receipts); // Use the getElement function to extract a specific field from each receipt. const logData = await getElement(receipts, 'to'); console.log(logData); } main(); ``` ## Run the program Save the file and run the program by executing the following command: ```bash Shell theme={"system"} node index ``` If this program is run on block `25792736` on the Avalanche mainnet, the response will be the following: ```bash Shell theme={"system"} [ { blockHash: '0x451155957eee73e4ea17edd5a26e4aeaff30cc828b2a4a81f2197d7d980cd00e', blockNumber: 25792736, contractAddress: null, cumulativeGasUsed: 396022, effectiveGasPrice: 27500000000, from: '0x6e752dcb0acb921c1fa446992c590a28661f27ca', gasUsed: 396022, logs: { address: '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7', topics: [Array], data: '0x0000000000000000000000000000000000000000000000027eaff286463ffb12', blockNumber: 25792736, transactionHash: '0x3708f39015b7816a156d0fc24ab7f658bcac130ebdd63e62ae93b9c8093ad41e', transactionIndex: 0, blockHash: '0x451155957eee73e4ea17edd5a26e4aeaff30cc828b2a4a81f2197d7d980cd00e', logIndex: 10, removed: false, id: 'log_e7f37c48' }, logsBloom: '0x00000000000000000020000000000001000000040000000020000000000000000000000000400040004000000000000100000000000020020000420000200000040080000000080800000008000000008000000000400000000000000408020000000011000008400000000000004000000000000000040000000010000800010008000002000000080000000000080000000010000000000000000000000000020400000000000000000000000004000000000000004100000000000000000080000002021004000000000001000000000000000000008000001802000000000010000000000004000400000008000000000000000200020000000000000100', status: true, to: '0x1111111254eeb25477b68fb85ed929f73a960582', transactionHash: '0x3708f39015b7816a156d0fc24ab7f658bcac130ebdd63e62ae93b9c8093ad41e', transactionIndex: 0, type: '0x0' }, { blockHash: '0x451155957eee73e4ea17edd5a26e4aeaff30cc828b2a4a81f2197d7d980cd00e', blockNumber: 25792736, contractAddress: null, cumulativeGasUsed: 1838675, effectiveGasPrice: 26500000000, from: '0x0d6c6017b639c3ee31c79f8a300acd5cbd1ab866', gasUsed: 1442653, logs: { address: '0x83a283641C6B4DF383BCDDf807193284C84c5342', topics: [Array], data: '0x00000000000000000000000000000000000000000000003291a743cf2f536ff5', blockNumber: 25792736, transactionHash: '0x182eab9950cf7d6711222a437331995e9484b15126abd575c529bda13cc26017', transactionIndex: 1, blockHash: '0x451155957eee73e4ea17edd5a26e4aeaff30cc828b2a4a81f2197d7d980cd00e', logIndex: 15, removed: false, id: 'log_98091197' }, logsBloom: '0x00000000000000000000000000040000001000000000000000000000000000000080001008020000000000000040800000000000000000000000000000000000000040000000000000000008020000000000000000000000000000000000008000000000000008000000008010020001000000000000000000000110000000000000000000020000100000000000080000000090000000000000000000002800000000008000000000000040000000000000000000000010000000020400000000040002000000000000000000000000200000010000100000000080000000000000040000040000008000800000000000000000002000000000000000000000', status: true, to: '0x0efc8ef83d7318121449e9c5dbdf7135bcc1fa90', transactionHash: '0x182eab9950cf7d6711222a437331995e9484b15126abd575c529bda13cc26017', transactionIndex: 1, type: '0x2' }, { blockHash: '0x451155957eee73e4ea17edd5a26e4aeaff30cc828b2a4a81f2197d7d980cd00e', blockNumber: 25792736, contractAddress: null, cumulativeGasUsed: 2502782, effectiveGasPrice: 26500000000, from: '0xab1d3dd66e0f0799d09ca530c30e8f0b90d87f85', gasUsed: 664107, logs: { address: '0xc8cEeA18c2E168C6e767422c8d144c55545D23e9', topics: [Array], data: '0x0000000000000000000000000000000000000000000000034f3202e0e51db900', blockNumber: 25792736, transactionHash: '0x9cdcd3970e0666dabbbdbc386425f1760830087215be4b495c7a4d4a27a596a7', transactionIndex: 2, blockHash: '0x451155957eee73e4ea17edd5a26e4aeaff30cc828b2a4a81f2197d7d980cd00e', logIndex: 40, removed: false, id: 'log_a3adc356' }, logsBloom: '0x000080002000000000000000000000000080001000004000000000000001010200000000000080000000000002000000000000000000000000040000002400000001000000000000000000080000000000000000008400100000000080008000000004000200000000000000000008000000000000080000000000100000000100400010800000000000000000000000000000010000000000000800000000800200000000000000000000000000000000000200000000000002000040000000000400020000000000000000000200010000000000000000000000000000600001100c0000000000000000000004000000200000040000400000000000000000', status: true, to: '0xc8ceea18c2e168c6e767422c8d144c55545d23e9', transactionHash: '0x9cdcd3970e0666dabbbdbc386425f1760830087215be4b495c7a4d4a27a596a7', transactionIndex: 2, type: '0x2' } ] [ '0x1111111254eeb25477b68fb85ed929f73a960582', '0x0efc8ef83d7318121449e9c5dbdf7135bcc1fa90', '0xc8ceea18c2e168c6e767422c8d144c55545d23e9' ] ``` ## Conclusion In conclusion, this script effectively leverages the power of the Web3 library and its methods. With this function, you can now use `eth_getBlockReceipts` even if your node is not running the Erigon client. At the same time, you just learned that, if a specific method is not available, you can always build it yourself. > [!NOTE] > > ### See also > > > [card] [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) > > > [card] [Uncovering the power of eth\\\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) > > > [card] [Deep dive into Merkle proofs and eth\\\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) > > > [card] [Geth vs Erigon: Deep dive into RPC methods on Ethereum clients](/docs/geth-vs-erigon-deep-dive-into-rpc-methods-on-ethereum-clients) > > > [card] [Ethereum logs tutorial series: Logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters) ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Exploring Bitcoin transactions with `getrawtransaction` Source: https://docs.chainstack.com/docs/exploring-bitcoin-transactions-with-getrawtransaction Decode and analyze Bitcoin transactions using getrawtransaction with a Chainstack node to inspect inputs, outputs, scripts, and confirmation status. **TLDR** * `getrawtransaction` retrieves raw or decoded Bitcoin transaction data by `txid`, optionally with a specific block hash. * Verbose mode (`true`) returns a detailed JSON, while omitting it yields the raw hex string. * Transactions typically remain in the mempool for around 14 days; dropped transactions require resubmission with adequate fees. * Bitcoin Core runs many operations single-threaded, so complex or parallel requests may slow under heavy load. ## Main article In Bitcoin blockchain development, the **`getrawtransaction`** method serves as a fundamental tool for retrieving detailed information about specific transactions. This method provides developers with insights into transaction details, offering a closer look into the intricacies of Bitcoin's decentralized ledger. Before you begin testing the **`getrawtransaction`** method, it's essential to have access to a functioning Bitcoin node. Chainstack offers a convenient and efficient way to deploy and manage Bitcoin nodes. Follow these detailed steps to sign up on Chainstack, deploy your node, and access your endpoint credentials: > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) ## Exploring **`getrawtransaction`** The **`getrawtransaction`** method is a crucial tool in Bitcoin blockchain development, allowing developers to access detailed information about transactions. This method is used to retrieve data either in a raw format or as a decoded, more readable JSON object, depending on the parameters provided. ### Parameters * `txid`: The transaction ID, a unique identifier for the transaction. * `verbose` (optional): A boolean flag. When set to `true`, the method returns a JSON object with detailed transaction data. If set to `false` or omitted, it returns the raw transaction data in hexadecimal format. #### Example Usage To fetch the detailed transaction data using cURL: ```bash Bash theme={"system"} curl --location 'YOUR_CHAINSTACK_ENDPOINT' \ --header 'content-type: text/plain;' \ --data '{"jsonrpc": "1.0", "id": "1", "method": "getrawtransaction", "params": ["dd3025c6da8f546fcdb059428b74bf560efe0b360e90e46bd428de0905fdb3f2", true]}' ``` > [!NOTE] > > Replace `YOUR_CHAINSTACK_ENDPOINT` with the endpoint from your [Console](/docs/manage-your-node#view-node-access-and-credentials). ### Response The response object varies based on the `verbose` parameter: * If `verbose` is `false` or omitted, the response is a string containing the raw transaction data in hexadecimal format. * If `verbose` is `true`, the response is a JSON object with detailed transaction information, including: * `txid`: The transaction ID. * `hash`: The transaction hash; differs from `txid` for segwit transactions. * `version`: The version of the transaction. * `size`: The size of the transaction in bytes. * `vsize`: The virtual size of the transaction (considering segwit discount). * `weight`: A metric for the transaction's size. * `locktime`: The locktime of the transaction. * `vin`: An array of input objects, each containing details like scripts and sequences. * `vout`: An array of output objects, detailing where the bitcoins are going, including value and scripts. * Additional fields like `blockhash`, `confirmations`, and `time` are included if the transaction is confirmed. ### Understanding the parameters #### 1. **Detailed Transaction Information: `verbose` Parameter** The **`verbose`** parameter is optional and determines the format of the response. When set to **`true`**, it returns a JSON object with detailed transaction information, including inputs, outputs, and transaction metadata. If set to **`false`** or omitted, the response is the raw hexadecimal transaction data. ##### Example without `verbose`: ```bash Bash theme={"system"} curl --location 'YOUR_CHAINSTACK_ENDPOINT' \ --header 'content-type: text/plain;' \ --data '{"jsonrpc": "1.0", "id": "1", "method": "getrawtransaction", "params": ["dd3025c6da8f546fcdb059428b74bf560efe0b360e90e46bd428de0905fdb3f2"]}' ``` This command returns the raw transaction data in hexadecimal format. To decode it, use the `decoderawtransaction` method. ```jsx jsx theme={"system"} curl --location 'YOUR_CHAINSTACK_ENDPOINT' \ --header 'content-type: text/plain;' \ --data '{"jsonrpc": "1.0", "id": "1", "method": "decoderawtransaction", "params": ["020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff31031f960c04affc9b652f466f756e6472792055534120506f6f6c202364726f70676f6c642f2a9e8f693dfb000000000000ffffffff023df0692a0000000016001435f6de260c9f3bdee47524c473a6016c0c055cb90000000000000000266a24aa21a9edc3d6ba5653cb4443ee0ef0f6778e5cee98e4e2eac4282120750f91f41a1e087a0120000000000000000000000000000000000000000000000000000000000000000000000000"]}' ``` ### 2. **Fetch Transaction by Block: `blockhash` Parameter** The **`blockhash`** parameter is used to retrieve a transaction by specifying the hash of the block in which it's included. This is particularly useful for accessing transactions within a specific block. #### Example using `blockhash`: ```bash Bash theme={"system"} curl --location 'YOUR_CHAINSTACK_ENDPOINT' \ --header 'content-type: text/plain;' \ --data '{"jsonrpc": "1.0", "id": "1", "method": "getrawtransaction", "params": ["dd3025c6da8f546fcdb059428b74bf560efe0b360e90e46bd428de0905fdb3f2" , true, "000000000000000000015c7c5ce593387df1adbb494cf2a9d261bb56095d1769"]}' ``` This command fetches detailed information about the specified transaction within the given block. ### Real-World Applications of `getrawtransaction` The `getrawtransaction` method is not just a theoretical tool; it finds practical applications in various real-world scenarios. For instance: 1. **Transaction Auditing**: Financial institutions and auditors use `getrawtransaction` to verify the authenticity of transactions. They can inspect details such as input and output addresses, transaction values, and fees to ensure compliance with regulatory standards. 2. **Wallet Functionality**: Cryptocurrency wallet applications leverage this method to fetch transaction details. When a user receives or sends Bitcoin, the wallet can display comprehensive transaction information, enhancing user transparency and trust in the wallet's operations. 3. **Blockchain Analytics**: Companies specializing in blockchain analytics use `getrawtransaction` to gather data for analysis. By decoding transaction details, they can identify patterns, track asset flows, and detect suspicious activities, contributing to anti-money laundering (AML) efforts. 4. **Network Fee Estimation**: Services that provide fee estimation for Bitcoin transactions often analyze past transactions using `getrawtransaction`. By understanding the fee structures of recent transactions, they can more accurately suggest optimal transaction fees for users. ### Limited Lifespan of Transactions in the Mempool It's crucial to understand that Bitcoin's mempool, which is the collection of all unconfirmed transactions waiting to be included in a block, does not retain transactions indefinitely. Typically, transactions remain in the mempool for about 14 days, though this can vary based on node configuration and network conditions. Detailed information about this can be found on our Mempool Configuration Page at [Chainstack Mempool Configuration](/docs/mempool-configuration). Unconfirmed transactions that linger for an extended period may be dropped from the mempool. This is particularly relevant during times of network congestion or if the transaction fee is too low. To prevent transactions from expiring, developers should ensure they are either confirmed in a timely manner or resubmitted with an appropriate fee. ### Single-Threaded Nature of Bitcoin Core An important architectural detail of Bitcoin Core, the reference implementation of the Bitcoin protocol, is its single-threaded nature for processing many of its critical functions. This includes the validation of transactions and blocks. Consequently, Bitcoin Core handles operations sequentially, which may lead to limitations in processing multiple requests concurrently. This design choice impacts how quickly the node can process transactions and blocks, especially under heavy load. It's a factor to consider when building applications that interact with Bitcoin Core, as response times can vary depending on the node's current workload. ### Conclusion The `getrawtransaction` method is an invaluable tool in Bitcoin nodes, providing developers with the ability to retrieve specific transaction information. When used with its parameters - `txid` for the transaction ID, `verbose` for detailed transaction data, and `blockhash` for specifying a transaction within a particular block — it offers comprehensive insights into individual Bitcoin transactions. However, developers must be mindful of the nuances of Bitcoin's network and node operation, such as the mempool's transaction retention policy and the single-threaded nature of Bitcoin Core. These factors underscore the importance of efficient transaction management and a thorough understanding of the underlying system for effective blockchain development. # Extended history Source: https://docs.chainstack.com/docs/extended-nodes Extended Global Nodes are full nodes that keep more history than a standard full node of the protocol, but not a complete archive. Requests are billed the same as on a Full node. An Extended [Global Node](/docs/global-elastic-node) is a full node that keeps more history than a standard full node of the same protocol, but not a complete archive from genesis. Extended does not change request-unit billing. ## Which nodes are Extended Extended applies only to Global Nodes. The protocols and networks that offer Extended Global Nodes show Extended in the Global Node Mainnet Support and Global Node Testnet Support columns on [Networks](/docs/protocols-networks). Each protocol's history depth is listed in [How much history each protocol keeps](#how-much-history-each-protocol-keeps). ## Billing Extended is not a separate billing class. Requests to an Extended Global Node are billed the same as requests to a Full Global Node of the protocol. See [Full vs archive by protocol](/docs/request-units#full-vs-archive-by-protocol). ## How much history each protocol keeps History depth depends on the protocol and the network, and it can change over time. Each protocol reports its current range at runtime, so read the range from the node instead of assuming a fixed depth: * Sui — the gRPC `LedgerService/GetServiceInfo` method reports `lowestAvailableCheckpoint`, the oldest checkpoint the node holds. Plan against the window from that checkpoint to the tip. See [Checkpoint retention](/docs/sui-grpc-endpoint#checkpoint-retention). On Mainnet, GraphQL reports its windows through `serviceConfig.availableRange`, and its filtered transaction and event queries keep a shorter window — see [How far back GraphQL history goes](/docs/sui-graphql-endpoint#how-far-back-graphql-history-goes). * XRP Ledger — keeps more recent ledgers than a default `rippled` server. `server_info` reports the range the node holds in `complete_ledgers`. See [How much ledger history your node holds](/docs/xrpl-tooling#how-much-ledger-history-your-node-holds). * Stellar — `getHealth` reports the retention window, from `oldestLedger` to `latestLedger`, which bounds transaction and event queries on both networks. On Mainnet, `getLedgers` also serves ledgers older than the window, back to ledger 2. On Testnet, `getLedgers` is limited to the window. See [Historical data availability](/docs/stellar-tooling#historical-data-availability). # Fantom methods Source: https://docs.chainstack.com/docs/fantom-methods Complete reference of supported Fantom JSON-RPC methods on Chainstack, including eth, debug, trace, and net namespace method availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Fantom API call examples](/reference/getting-started-fantom). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | Not supported by the Sonic node client | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | ftm\_currentEpoch | | | | trace\_block | | | | trace\_transaction | | | | trace\_filter | | | | trace\_get | | | | dag\_getEvent | | | | dag\_getEventPayload | | | | dag\_getHeads | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Fantom tooling Source: https://docs.chainstack.com/docs/fantom-tooling Complete Fantom tooling guide for Chainstack nodes. Use MetaMask, Hardhat, ethers.js, Foundry, Remix IDE, and more to build dApps on Fantom blockchain. Get started with a [reliable Fantom RPC endpoint](https://chainstack.com/build-better-with-fantom/) to use the tools below. ## Node.js You can build a web app to query data using Node.js and [axios](https://www.npmjs.com/package/axios): ```javascript Javascript theme={"system"} const axios = require('axios'); const main = async () => { try { const result = await axios.post( 'YOUR_CHAINSTACK_ENDPOINT', { query: ` QUERY ` } ); console.log(result.data); } catch(error) { console.error(error); } } main(); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node GraphQL endpoint * QUERY — your GraphQL query See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). See also [Using GraphQL with EVM-compatible nodes](https://support.chainstack.com/hc/en-us/articles/4409604331161-Using-GraphQL-with-EVM-compatible-nodes). ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Fantom nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Fantom nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Fantom nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Php theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Fantom nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint * USERNAME — your node access username * PASSWORD — your node access password See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Fantom nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Fantom network ID: * Mainnet: `250` * Testnet: `4002` See [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — Fantom network ID: * Mainnet: `250` * Testnet: `4002` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Brownie 1. Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). 2. Use the `brownie networks add` command with the node endpoint: ```shell Shell theme={"system"} brownie networks add Fantom ID name="NETWORK_NAME" host= YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `chainstack-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password * NETWORK\_ID — Fantom network ID: * Mainnet: `250` * Testnet: `4002` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Faucet Source: https://docs.chainstack.com/docs/faucets Get free testnet tokens for Ethereum Sepolia, Polygon Amoy, BNB Smart Chain, and other supported networks from the Chainstack faucet for development. The [Chainstack faucet](https://faucet.chainstack.com/) dispenses free testnet tokens for development and testing — no Twitter or social login required. ### Supported networks * Sepolia testnet * Hoodi testnet * Solana devnet * BNB Smart Chain testnet * Base Sepolia testnet * Monad testnet * Amoy testnet * Plasma testnet * Hyperliquid testnet * TON testnet * zkSync Sepolia testnet * Robinhood Chain testnet ### Request tokens via the faucet UI 1. Open [faucet.chainstack.com](https://faucet.chainstack.com/) and sign in to your Chainstack account. 2. Select a testnet. 3. Paste the destination address and submit. The faucet tops up your address to a per-network cap. On each network, you can get one top-up every 24 hours: a request is refused if your organization or the destination address already got funds on that network in that time. Other networks have separate limits. ### Request tokens from the console The faucet is also built into the Chainstack console, so you can request tokens without a separate sign-in. 1. Open [**Faucet**](https://console.chainstack.com/faucet) in the console. 2. Select a testnet, paste the destination address, and submit. You can save the wallets you fund most often. Each saved wallet can carry a recurring auto-claim, which tops it up on the schedule you set instead of you returning to the faucet by hand. ### Request tokens via the MCP server The [Chainstack MCP server](/docs/chainstack-mcp-server) exposes a `request_testnet_funds` tool so AI agents can fund a testnet address directly from your coding environment. Authentication uses your Chainstack API key. # Features availability Source: https://docs.chainstack.com/docs/features-availability-across-subscription-plans Overview of Chainstack features availability across different subscription plans, including node customization options and enterprise-specific offerings. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). ## Trader Node transactions [Trader Node transactions](/docs/warp-transactions) (ex. Warp transactions) are available starting from the [paid plans](https://chainstack.com/pricing/) for the following protocols: * Ethereum * Solana * BNB Smart Chain ## Debug and trace APIs [Debug and trace APIs](/docs/debug-and-trace-apis) are available starting from the [paid plan](https://chainstack.com/pricing/). For per protocol availability, see [Available clouds, regions, and locations](/docs/nodes-clouds-regions-and-locations). ## Multichain API [Multichain API](/docs/multichain-api) is available on every plan, including the Developer plan. ## Mempool See [Mempool configurations](/docs/mempool-configuration). ## Node customization Node customization is available on the [Enterprise plan](https://chainstack.com/pricing/) for [Dedicated Nodes](/docs/dedicated-node). Below you can find the current customizations that can be used for your node. > [!NOTE] > > Feel free to check with us for any additional customization that's not on the list, as these are just examples. ### Transaction settings > [!TIP] > > * **txpool.pricebump** — the minimum increase in transaction fee required to replace a pending transaction with the same nonce. The default value is `10%`. Applicable for EVM-based protocols. > * **rpc.txfeecap** — a limit on the transaction fee. The default value is `100 base currency`. Applicable for EVM-based protocols. > * **rpc.gascap** — a limit on the gas that can be used to run `eth_call` and `eth_estimateGas`. The default value is `0.5 base currency`. Applicable for EVM-based protocols. > * **txlookuplimit** — the maximum number of blocks whose transaction indices are reserved. By default, for Ethereum and Polygon, the number is `2350000`. > [!NOTE] > > **Base currency** is the native token of a protocol. ### Node and network customizations > [!TIP] > > * **Dedicated gateway** — provides a [dedicated connection to the node](/docs/trader-node#dedicated-gateways), eliminating some limitations, such as the need to reconnect. > * **Custom EVM tracers** — We can enable [custom JavaScript tracers](/reference/custom-js-tracing-ethereum) for your dedicated EVM node. > * **Scaling node resources** — options to scale up node resources like storage, CPU, and RAM. > * **Tailored load balancing** — a dedicated gateway that balances the request load between multiple Dedicated Nodes. > * **Bitcoin address indexing** — [electrs](https://github.com/Blockstream/electrs) indexer for address-level queries (transaction history, balance at a past block) not available via vanilla Bitcoin Core JSON-RPC. For any customizations not listed here, contact [Chainstack support](https://support.chainstack.com/). We are always happy to explore your case and help you. # Fetch address transactions with eth_getBlockByNumber Source: https://docs.chainstack.com/docs/fetching-transactions-to-and-from-a-specific-address-with-eth_getblockbynumber Filter transactions by address using eth_getBlockByNumber to scan Ethereum blocks and extract all incoming and outgoing transfers via a Chainstack node. **TLDR:** * Fetching Ethereum transactions for a specific address can be done with Python, the Web3 library, and a Chainstack node. * Use eth\_getBlockByNumber to inspect each block’s transactions, filter by from and to addresses, then compile relevant tx hashes. * You can speed up retrieval by parallelizing block fetches – multi-threading can significantly cut down the total runtime. * This approach is crucial for auditing, real-time monitoring, or any scenario requiring granular, address-based blockchain data. ## Introduction In the rapidly evolving world of blockchain technology, Ethereum has emerged as a leading platform for decentralized applications (DApps), smart contracts, and token transactions. As the ecosystem grows, so does the need for tools and methods that provide insights into network activity. One such tool is the ability to fetch transactions to and from specific Ethereum addresses, an invaluable capability for a range of applications—from auditing and analytics to monitoring and compliance. Whether you're a DApp developer looking to debug contract interactions, a financial analyst tracking asset flows, or an Ethereum user wanting to keep tabs on your transactions, understanding how to fetch transaction data programmatically is essential. This article aims to fill that knowledge gap by providing a step-by-step guide on how to fetch transactions for a specific Ethereum address using Python and Chainstack. We'll walk you through the entire process, from setting up your development environment to customizing and optimizing your code for various use cases. ## Setting up the environment Before diving into the code, it's crucial to set up a proper development environment. This ensures you have all the necessary tools and configurations to run the code smoothly. Below are the components you'll need and the steps to set them up. ### Python and Web3 library 1. **Install Python**. If you haven't already, download and install Python from the [official website](https://www.python.org/downloads/). 2. **Create a virtual environment**. In your project directory, create a new virtual environment: ```shell Shell theme={"system"} python3 -m venv tx_monitor ``` Then activate it: ```shell Shell theme={"system"} source tx_monitor/bin/activate ``` 3. **Install the Web3 library**. Open your terminal and run the following command to install the Web3 library: ```shell Shell theme={"system"} pip install web3 ``` ### Blockchain RPC endpoint with Chainstack You'll need access to an RPC node to interact with a blockchain. Chainstack provides a straightforward way to deploy and manage blockchain nodes. 1. **Sign up for Chainstack**. Visit the [Chainstack signup page](https://console.chainstack.com/user/account/create) and follow the instructions to create an account. > [!NOTE] > > Try out the new [social login](https://twitter.com/ChainstackHQ/status/1709988403893223599) feature. 2. **Deploy a node**. Once your account is set up, you can deploy an Ethereum node. Follow the [Chainstack documentation](/docs/manage-your-networks) for a step-by-step guide on how to do this. 3. **Access node credentials**. After deploying your node, you'll need the RPC URL to interact with the Ethereum network. You can find this information in the Chainstack dashboard. For more details, see [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). In this article, we use a BNB Smart Chain node as an example. ## The code’s logic Now, let’s go over the logic we implement in this project. The code will take the following steps: 1. **Initialization**. Set up the BNB Smart Chain node connection and specify the block range and address to monitor. 2. **Core functionality**, Use the `eth_getBlockByNumber` method indirectly through Web3's `get_block` function to fetch each block within the specified range. > [!NOTE] > > Learn more about [eth\_getBlockByNumber](/reference/ethereum-getblockbynumber). 3. **Transaction filtering**. For each fetched block, scan through the list of transactions and identify those that involve the specified Ethereum address. It does this by checking the `from` and `to` fields in each transaction. 4. **Result compilation**. All transactions involving the specified address are compiled into a list printed out at the end. ### Importance of `eth_getBlockByNumber` The `eth_getBlockByNumber` method is crucial here as it allows the script to fetch the entire block data, including the full list of transactions within each block. By doing so, the script can then iterate through these transactions to identify those that involve the specified address. This method provides a way to access historical data on the blockchain, making it a valuable tool for auditing and monitoring activities. In a new file, paste the following code: ```python Python theme={"system"} from web3 import Web3 from web3.middleware import geth_poa_middleware rpc_url = "YOUR_CHAINSTACK_ENDPOINT" # Change it to your Chainstack's node URL from_block = 32720146 # Define the block interval you have interest on to_block = 32720209 your_address = "0x2D4C407BBe49438ED859fe965b140dcF1aaB71a9" # Define the address of interest w3 = Web3(Web3.HTTPProvider(rpc_url)) w3.middleware_onion.inject(geth_poa_middleware, layer=0) # This is needed for some specific protocols given some ExtraBytes in the response. For polygon, for example, this is needed def get_transactions_for_address(address, from_block, to_block): transactions = [] for block_number in range(from_block, to_block + 1): print(f'Inspecting block {block_number}') block = w3.eth.get_block(block_number, full_transactions=True) if block is not None and 'transactions' in block: for tx in block['transactions']: if address in [tx['from'], tx['to']]: transactions.append(tx['hash'].hex()) print(f'TX involving {your_address} here') return transactions if __name__ == "__main__": print(f'Scanning for TXs involving {your_address}') transactions = get_transactions_for_address(your_address, from_block, to_block) if transactions: print(f"Transactions involving address {your_address}:") for tx_hash in transactions: print(tx_hash) else: print(f"No transactions found for address {your_address} in the specified block range.") ``` ## Fetching transactions with precision At the heart of our script is the `get_transactions_for_address` function, a meticulously designed piece of logic that scans the blockchain to identify transactions involving a specific address within a user-defined range of blocks. Here's an overview of its operation: * The function embarks on a block-by-block journey, traversing from the starting block number defined in `from_block` to the ending block number in `to_block`. * The function scrutinizes every transaction within each block, examining the sender (`from`) and recipient (`to`) addresses. The transaction hash is captured and stored in a list if it identifies a transaction where the specified Ethereum address is either the sender or the recipient. This function is a robust yet straightforward mechanism for compiling a transaction history for a given Ethereum address. Doing so offers invaluable insights for auditing, monitoring, or any other use cases requiring a detailed transaction history. ## The significance of transaction tracking ### Transaction monitoring: A multi-faceted utility The capability to accurately retrieve transactions associated with a particular address is not just a feature—it's an essential tool with diverse applications, such as: * **For wallet users**. This functionality enables wallet users to meticulously monitor incoming and outgoing transactions, thereby providing a reliable way to verify that all transactions have been executed as expected. * **For developers**. This tool is especially beneficial for developers who must keep tabs on interactions with their deployed smart contracts. It serves as a real-time monitoring system and an auditing mechanism to ensure the contracts operate as designed. ### Real-world applications To further illustrate the utility of this tool, let's dive into some scenarios where it can be particularly impactful: * Token asset management — the ability to track token transfers to and from a specific address is invaluable for managing your digital assets effectively. It provides a transparent view of your token portfolio's activity. * Transaction verification — for businesses and individual users, confirming the successful receipt of funds is paramount. This tool simplifies that process by providing a straightforward way to verify transaction completion. * Smart contract oversight — for developers and auditors alike, this tool is important for continuously monitoring and auditing smart contract interactions. It provides a granular view of transactional data, aiding in both development and compliance efforts. ## Fine-tuning and performance enhancement ### Strategic block range selection One cornerstone of maximizing this script's utility is the judicious selection of the `from_block` and `to_block` parameters. These values define the scope of your transaction search, and their optimal setting depends on your specific use case. Whether you're interested in a brief snapshot of recent activity or an exhaustive historical audit, adjusting these parameters allows you to focus your query accordingly. ### Advanced transaction filtering For those looking to go beyond basic transaction tracking, the script can be further customized by implementing additional filters. For example, you could refine your search by filtering transactions based on their value or by isolating only those transactions that interact with a particular smart contract. This level of granularity enables you to extract precisely the data you're interested in, making your monitoring efforts more targeted and efficient. ## Leveraging parallel computing for efficiency Performance optimization becomes a key consideration because the `get_block` method can be resource-intensive—especially when dealing with a large range of blocks. One effective strategy for speeding up the data retrieval process is through parallelization. By employing multi-threading, you can distribute the workload across multiple threads, reducing overall runtime. The script includes a parallelized version of the core function, demonstrating how multi-threading can significantly improve performance. > [!NOTE] > > Learn more about multithreading in Python in [Mastering multithreading in Python for Web3 requests: A comprehensive guide](/docs/mastering-multithreading-in-python-for-web3-requests-a-comprehensive-guide). ```python Python theme={"system"} from web3 import Web3 from web3.middleware import geth_poa_middleware import concurrent.futures import time # Use the same RPC URL as in your original code rpc_url = "YOUR_CHAINSTACK_ENDPOINT" # Use the same block range as in your original code from_block = 32720146 to_block = 32720209 # Use the same Ethereum address as in your original code your_address = "0x2D4C407BBe49438ED859fe965b140dcF1aaB71a9" w3 = Web3(Web3.HTTPProvider(rpc_url)) w3.middleware_onion.inject(geth_poa_middleware, layer=0) def get_transactions_for_block_range(address, from_block, to_block, block_number, num_threads): transactions = [] for block_num in range(from_block + block_number, to_block + 1, num_threads): block = w3.eth.get_block(block_num, full_transactions=True) if block is not None and 'transactions' in block: for tx in block['transactions']: if address in [tx['from'], tx['to']]: transactions.append(tx['hash'].hex()) print(f'TX involving {address} found') return transactions if __name__ == "__main__": latencies = [] for num_threads in [1, 2, 4, 8]: start_time = time.time() with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor: futures = [] for i in range(num_threads): futures.append( executor.submit( get_transactions_for_block_range, your_address, from_block, to_block, i, num_threads ) ) transactions = [] for future in concurrent.futures.as_completed(futures): transactions.extend(future.result()) end_time = time.time() latency = end_time - start_time latencies.append((num_threads, latency)) if transactions: print(f"Transactions involving address {your_address} with {num_threads} threads:") for tx_hash in transactions: print(tx_hash) else: print(f"No transactions found for address {your_address} in the specified block range.") print("\nPerformance Metrics:") for num_threads, latency in latencies: print(f"Threads: {num_threads}, Latency: {latency:.2f} seconds") ``` This code will output the difference found while simultaneously using 1, 2, 4, and 8 threads. Here is the result we found: ```python Python theme={"system"} Performance Metrics: Threads: 1, Latency: 22.53 seconds Threads: 2, Latency: 12.35 seconds Threads: 4, Latency: 6.62 seconds Threads: 8, Latency: 3.33 seconds ``` The code tests the performance with 1, 2, 4, and 8 threads, allowing us to observe how the system scales with increasing threads. Here's a breakdown of the results: * **Single-threaded (1 thread)**. When running the code with a single thread, it took 22.53 seconds to complete the task. This serves as our baseline for performance comparison. * **Dual-threaded (2 threads)**. With two threads, the latency dropped to 12.35 seconds. This is almost a 45% reduction in time compared to the single-threaded execution, showcasing the benefits of parallelization. * **Quad-threaded (4 threads)**. Utilizing four threads further reduced the latency to 6.62 seconds. This is roughly a 70% reduction compared to the single-threaded baseline. * **Octa-threaded (8 threads)**. Finally, with eight threads, the latency was minimized to 3.33 seconds. This is an astonishing reduction of about 85% compared to the single-threaded execution. These results demonstrate the power of multithreading in optimizing computational tasks. The more threads we use, the lower the latency, up to a point. This is a classic example of how parallel computing can significantly speed up inherently parallelizable tasks, like fetching transactions from different blocks in a blockchain. ### Hardware constraints The number of threads you can effectively use is often limited by the hardware on which the code is running. Most modern CPUs have multiple cores, and each core can run one or more threads. It's generally a good idea to align the number of threads with the number of available CPU cores for optimal performance. ### Scalability testing The numbers 1, 2, 4, and 8 were chosen to provide a broad spectrum for scalability testing. Starting with a single thread provides a baseline performance metric. Doubling the number of threads at each step (1 to 2, 2 to 4, and 4 to 8) allows us to observe how the system scales with increasing threads. This is a common approach to gauge the "speedup" factor and to understand if the application benefits from parallelization. ### Diminishing returns It's essential to note that adding more threads doesn't always result in linear performance improvement. Due to factors like thread management overhead and resource contention, there's a point beyond which adding more threads may yield diminishing returns or even degrade performance. That's why it's useful to test with various numbers of threads to find the "sweet spot." ### Task granularity The granularity of the task at hand also influences the optimal number of threads. If the task can be broken down into smaller, independent sub-tasks (as is the case with fetching transactions from different blocks), it's more likely to benefit from multi-threading. However, if the task involves many shared states or resources, adding more threads might lead to issues like race conditions or deadlocks. ## Conclusion The capacity to retrieve transactions associated with a particular address is an indispensable tool for anyone engaged in the blockchain space. This utility transcends roles, offering valuable insights for wallet users, developers, and auditors alike. It not only enhances your understanding of transactional flows but also fortifies the transparency and accountability that are the cornerstones of blockchain technology. Through thoughtful customization and optimization, you can fine-tune this code to serve many applications. Whether you're tracking fund movements, auditing smart contracts, or monitoring network activity, the code provides a robust foundation upon which you can build. The introduction of multi-threading options showcases how performance can be significantly improved, allowing for more efficient data retrieval and analysis. This adaptability underscores the code's versatility, making it a reliable real-time monitoring and historical data analysis solution. # Fetch BAYC transfer events with web3.js getPastEvents Source: https://docs.chainstack.com/docs/fetching-transfer-events-with-getpastevents-for-a-bayc-nft Retrieve BAYC NFT transfer history using web3.js getPastEvents method with event log filtering through a Chainstack Ethereum node for NFT analytics. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR** * Set up a node.js script using web3.js to connect to Ethereum via Chainstack endpoints. * Fetch real-time crypto prices through Chainlink’s AggregatorV3Interface contracts, removing reliance on external APIs. * Conversion involves retrieving integer values from the contract and formatting them into human-readable outputs. * Call prices at intervals to keep data updated without single-point-of-failure risks. ## Main article As the Ethereum ecosystem matures and sees more advanced DApps and smart contract interactions, efficiently retrieving historical data becomes paramount. One of the tools developers can leverage for this purpose is the `getPastEvents` function from the web3.js library. This tool, while powerful, has nuances and limitations that developers must be aware of to ensure efficient application development. ## About `getPastEvents` The `getPastEvents` function is a part of the web3.js library, which provides an interface for developers to interact with the Ethereum blockchain. It's specifically tailored to fetch past events emitted by Ethereum smart contracts, making it a go-to method for DApp developers needing historical contract event data. > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about event logs. ## Best practices when using `getPastEvents` To maximize the efficiency and reliability of your event data retrieval, follow these guidelines: 1. **Limit the block range**. Always adhere to recommended block range limits. This minimizes the risk of time-consuming queries or receiving excessively large data sets. 2. **Use Filters**. `getPastEvents` offers filtering options. Leverage them to refine the events you want, making queries more efficient. 3. **Pagination**. If you must retrieve many events, consider breaking down your requests—query in chunks to prevent potential timeouts or massive responses. 4. **Error Handling**. Be prepared for potential failures due to network glitches or other unforeseen issues. Implement robust error handling and a retry mechanism. By following these guidelines and understanding the intricacies of `getPastEvents`, developers can efficiently and effectively integrate historical event data retrieval into their DApps. > [!NOTE] > > Check out the [Understanding eth\_getLogs limitations](/docs/understanding-eth-getlogs-limitations) guide to learn more about event retrieval best practices. ## Real-world example: Retrieving `Transfer` events for a specific Bored Ape Bored Ape Yacht Club (BAYC) is a well-known collection of NFTs on the Ethereum blockchain. Each Bored Ape is a unique digital asset; ownership transfers are recorded as `Transfer` events on the blockchain. In this example, we'll demonstrate using the `getPastEvents` function to retrieve all `Transfer` events for a Bored Ape. ## Prerequisites > [card] [Download Node.js](https://nodejs.org/en/download) > [card] [Web3.js Library](https://docs.web3js.org/) > [card] [Chainstack Ethereum Node](/docs/manage-your-networks) > [!NOTE] > > Read [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project) to learn how to manage a node.js project. ## Get an Ethereum node Follow these steps to deploy an Ethereum node: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Setup For this example, we will be using node.js, so let’s set up a project. ### Step 1. Setup Initialize a new node.js project by running: ```shell Shell theme={"system"} npm init -y ``` Install the web3.js library: ```shell Shell theme={"system"} npm install web3 ``` ### Step 2. Writing the script In the directory where you created the new project, create a new file named `index.js` and paste the following code into it. This will set up the BAYC ABI, initialize the contract, and fetch `Transfer` events. ```jsx index.js theme={"system"} const { Web3 } = require("web3"); const url = "YOUR_CHAINSTACK_NODE"; // Replace with your Chainstack Ethereum node endpoint const web3 = new Web3(new Web3.providers.HttpProvider(url)); // ABI for the Bored Ape Yacht Club contract, only including the Transfer event const BAYC_ABI = [ { anonymous: false, inputs: [ { indexed: true, internalType: "address", name: "from", type: "address", }, { indexed: true, internalType: "address", name: "to", type: "address", }, { indexed: true, internalType: "uint256", name: "tokenId", type: "uint256", }, ], name: "Transfer", type: "event", }, ]; const BAYC_CONTRACT_ADDRESS = "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"; const contract = new web3.eth.Contract(BAYC_ABI, BAYC_CONTRACT_ADDRESS); async function fetchTransfersForTokenId(tokenId) { try { // Fetch the latest block number and calculate the target block const latestBlock = await web3.eth.getBlockNumber(); const target = Number(latestBlock) - 10000; // Fetch Transfer events for the given token ID const events = await contract.getPastEvents("Transfer", { filter: { tokenId: tokenId }, fromBlock: target, toBlock: "latest", }); console.log( `Total transfers for Bored Ape ${tokenId}: ${events.length} transfers` ); // Iterate through the events and log details for (let event of events) { console.log( `From: ${event.returnValues.from} To: ${event.returnValues.to} at block: ${event.blockNumber}` ); } } catch (error) { console.error(`Error fetching transfers for token ${tokenId}:`, error); } } // Fetch bids for BAYC NFT ID 7924 fetchTransfersForTokenId(7924); ``` ### Step 3. Run the Script Execute the script with: ```shell Shell theme={"system"} node index.js ``` This will display the transfer history of the past 10,000 blocks for the BAYC token with ID 7924, detailing from which address to which address the token was transferred and the block at which the transfer occurred. Note that there might not be any transfers as we are only querying the past 10,000 blocks. ## Understanding the code This code provides a practical example of using web3.js to fetch and analyze specific contract events on the Ethereum blockchain, demonstrating a common pattern used in blockchain development and analysis. 1. **Defining BAYC ABI**. The ABI (application binary interface) for the BAYC contract is defined, focusing on the `Transfer` event. The ABI is a critical component that enables the script to interact with the smart contract's functions and events. 2. **Defining BAYC contract address**. The address of the BAYC contract on the Ethereum blockchain is specified. 3. **Initializing contract instance**. A contract instance is created using the ABI and contract address. This instance provides methods to interact with the contract, including fetching past events. 4. **Defining the main function (`fetchTransfersForTokenId`)**: * **Fetching latest block number**. The script fetches the latest block number from the Ethereum blockchain. * **Calculating target block**. The target block is calculated by subtracting 10,000 from the latest block number. This defines the range of blocks to query for past events. * **Fetching transfer events**. The `getPastEvents` method is called on the contract instance to fetch all `Transfer` events related to the specified token ID within the block range. * **Logging transfers**. The script logs the total number of transfers for the specified Bored Ape and iterates through the events, logging details such as the sender, receiver, and block number. * **Error handling**. If an error occurs during the process, it's caught and logged to the console. 5. **Calling the main function**. Finally, the `fetchTransfersForTokenId` function is called with a specific token ID (7924) to fetch and log the transfer events for that Bored Ape. > [!NOTE] > > Note how we are checking the past 10,000 blocks, not the entire chain. If you want to index the events from the entire chain, you will need multiple rounds. ## Conclusion Fetching historical event data from the Ethereum blockchain is a common requirement for developers working with DApps and smart contracts. The `getPastEvents` function in the web3.js library offers a powerful and flexible way to retrieve such data, but it also comes with nuances and limitations that must be carefully managed. In this guide, we explored the `getPastEvents` function, focusing on its application in retrieving `Transfer` events for a specific Bored Ape Yacht Club NFT. We discussed best practices for `getPastEvents`, including limiting the block range, using filters, implementing pagination, and handling errors. We also provided a step-by-step example to demonstrate how to set up a node.js project, initialize the web3.js library, and write a script to fetch and display transfer events for a BAYC NFT. ### About the author > [card] Edin Drazevicanin > > ![Edin Drazevicanin](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/juOi73j.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=8d80cce654921d5a7a6373a939ebf2af) > > Technical Support Engineer @ Chainstack > \ > JUST BUIDL IT! > > [https://github.com/0x6564](https://github.com/0x6564) > > [https://twitter.com/edeenn22](https://twitter.com/edeenn22) > > [https://www.linkedin.com/in/edindr/](https://www.linkedin.com/in/edindr/) # Filecoin tooling Source: https://docs.chainstack.com/docs/filecoin-tooling Set up MetaMask and development tools to interact with Filecoin through Chainstack nodes. Filecoin support has been deprecated on the platform. > [!WARNING] > > ### No Filecoin support > > Chainstack deprecated support for Filecoin nodes. This page here is for legacy and in case you may find it useful. ## MetaMask You can set your [MetaMask](https://metamask.io/) to interact through your Filecoin nodes. 1. Open your MetaMask and click the network selector. 2. In the network selector, click **Custom RPC**. 3. In the **New RPC URL** field, enter the node endpoint. 4. In the **Chain ID** field, enter the ID of the network: * Calibration Testnet: `314159` 5. Click **Save**. ## Remix IDE To make Remix IDE interact with the network through a Filecoin node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Filecoin node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Deploy & run transactions**. This will engage MetaMask and make Remix IDE interact with the network through a Filecoin node. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Filecoin nodes. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('FILECOIN_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * FILECOIN\_ENDPOINT — your node HTTPS endpoint * HOSTNAME — your node HTTPS endpoint hostname ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('FILECOIN_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * FILECOIN\_ENDPOINT — your node WSS endpoint * HOSTNAME — your node WSS endpoint hostname See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Filecoin nodes. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'FILECOIN_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * FILECOIN\_ENDPOINT — your node HTTPS endpoint * NETWORK\_ID — Filecoin network ID: * Calibration Testnet: `314159` ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('FILECOIN_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * FILECOIN\_ENDPOINT — your node WSS endpoint * NETWORK\_ID — Filecoin network ID: * Calibration Testnet: `314159` # Filecoin: Deploy a deal-making contract using Hardhat Source: https://docs.chainstack.com/docs/filecoin-tutorial-deploy-a-deal-making-contract-on-filecoin-with-hardhat Deploy a deal-making smart contract on Filecoin Calibration testnet using Hardhat and a Chainstack RPC endpoint. Filecoin support is now deprecated. > [!WARNING] > > ### Filecoin support is deprecated > > Chainstack deprecated support for Filecoin nodes. This page here is for legacy and in case you may find it useful. ## Introduction This tutorial will show you how to deploy a deal-making smart contract on the Calibration Testnet. The Calibration Testnet supports the FEVM implementation. ### Filecoin Virtual Machine and Filecoin EVM The **Filecoin Virtual Machine**, or FVM, serves as the backbone of the Filecoin network, providing a powerful runtime environment for the execution of smart contracts, known as **actors**. Actors can be written in Solidity and, in the future, in any language that compiles to WebAssembly, empowering developers to establish and enforce a set of rules to store and retrieve data on the Filecoin network. The FVM acts as a gatekeeper, ensuring the integrity of stored data and enforcing the terms of storage deals, such as data retention and retrieval times, making the Filecoin network a safe and reliable platform for decentralized data storage. The **Filecoin Ethereum Virtual Machine**, or FEVM, brings the power of the Ethereum Virtual Machine (EVM) to the Filecoin network. The FEVM is virtualized as a runtime layer on top of the Filecoin Virtual Machine, allowing for the execution of EVM smart contracts on the network. With the FEVM, developers can quickly and easily start writing actors on the Filecoin blockchain, utilizing all of the familiar tools, packages, and languages they are used to while having access to Filecoin's unique storage capabilities, opening up new possibilities and opportunities for DApp development. Follow the FEVM implementation progress on the [Filecoin docs](https://docs.filecoin.io/developers/smart-contracts/concepts/filecoin-evm/#fevm-and-native-fvm). ### Filecoin actors In the Filecoin network, **actors** are a crucial component and play a role analogous to smart contracts in the Ethereum Virtual Machine. These actors are integral to the system's operations, as any change to the state of the Filecoin blockchain necessitates the initiation of an actor method invocation. The code that defines an actor is divided into various methods. When messages are sent to an actor, they include information specifying which method(s) to call and the input parameters for those methods. Moreover, actor code interacts with a runtime object that contains information about the overall state of the network, such as the current epoch, cryptographic signatures, and proof validations. Just like smart contracts on other blockchain platforms, actors in the Filecoin network must pay a gas fee. This fee, denominated in Filecoin's native cryptocurrency (FIL), is used to offset the cost of a transaction, which includes the network resources used to process the transaction. Every actor in the Filecoin network has several attributes associated with it: a Filecoin balance, a state pointer, a code that identifies its type, and a nonce, which tracks the number of messages sent by this actor. Actors in the Filecoin network fall into two categories: 1. **Built-in actors** — these are pre-programmed actors that the Filecoin network team writes and deploys directly into the network. They come pre-installed and are designed to execute specific functions within the network. For instance, the `StorageMinerActor` is a built-in actor that handles storage mining operations and the collection of proofs. 2. **User actors** — these are akin to smart contracts that developers can create and deploy on the Filecoin network via the FVM. User actors are developed by third-party developers and offer a broad spectrum of functionalities within the network. They provide the flexibility for developers to create custom solutions and applications on the Filecoin network. ## Prerequisites * A Filecoin node * [Hardhat](https://hardhat.org/) to create and deploy contracts * [yarn installed](https://classic.yarnpkg.com/lang/en/docs/install/) on your machine ## Overview To get from zero to an emitted deal proposal on the Calibration Testnet, do the following: 1. Get a Filecoin node endpoint. 2. Clone the FEVM deal-making kit from Filecoin's GitHub. 3. Fund your wallet from the Filecoin faucet. 4. Prepare files for storage on the Filecoin network, converting them to `.car` format. 5. Deploy the deal-making contract on the Filecoin Calibration Testnet through a node. 6. Make a deal proposal for the Boost storage providers to pick up. > [!NOTE] > > Add your [Filecoin endpoint to MetaMask](/docs/filecoin-tooling#metamask). ### Clone the FEVM deal-making kit from Filecoin’s GitHub The Filecoin team has developed a comprehensive FEVM deal-making kit. This kit provides developers with a suite of tools designed to facilitate the deployment of deal-making smart contracts and the creation of deal proposals, thereby streamlining the process of engaging with the Filecoin network. Clone the [FEVM deal-making kit](https://github.com/filecoin-project/fvm-starter-kit-deal-making) repository. Note that this kit includes a submodule to convert files into `.car`; use the following command to clone the submodule as well: ```shell Shell theme={"system"} git clone --recurse-submodules https://github.com/filecoin-project/fvm-starter-kit-deal-making.git ``` Move the terminal into the root folder of the project: ```shell Shell theme={"system"} cd fvm-starter-kit-deal-making ``` Then install the dependencies running the yarn command: ```shell Shell theme={"system"} yarn install ``` ### Set up environment variables and network configuration In the `.env.example` file, delete its content and paste the following: ```yaml .env.example theme={"system"} PRIVATE_KEY="YOUR_PRIVATE_KEY" CALIBRATION_URL="FILECOIN_ENDPOINT" CHAIN_ID:314159 ``` Add the private key of the account you intend to use for deploying the smart contract and interacting with the network, along with the URL of the Calibration Testnet. This step is crucial for establishing a secure connection using environment variables. Finally, save the file and rename it to `.env`. ### Fund your wallet from the Filecoin faucet Before proceeding further, ensure you have some testnet funds from the Filecoin faucet. * [Filecoin faucet](https://faucet.calibration.fildev.network/funds.html) Clicking the above link will direct you to a page where you can input your wallet address. This faucet accepts both Ethereum format addresses and Filecoin's f4address format. The FEVM deal-making kit comes equipped with a Hardhat task that allows for the conversion of an Ethereum address to an f4address using a private key. If you wish to determine your f4address, execute the command below after configuring your private key in the `.env` file. ```shell Shell theme={"system"} yarn hardhat get-address ``` You will receive a response like the following: ```shell Shell theme={"system"} Ethereum address (this addresss should work for most tools): 0x8f8e7012F8F974707A8F11C7cfFC5d45EfF5c2Ae f4address (also known as t4 address on testnets): f410fr6hhaexy7f2ha6upchd477c5ixx7lqvoim54aeq ``` You can input either the Ethereum address or the f4address into the faucet. The faucet will provide a message ID. This ID can be used in the [Filecoin Explorer](https://calibration.filscout.com/en) to view the details of the transaction. ### Prepare files for storage on the Filecoin network Files intended for upload to the Filecoin network must be in the `.car` format. Prior to proposing a deal, these files need to be converted, and certain necessary information must be obtained. The most straightforward method to accomplish this is by utilizing the [FVM Data Depot](https://data.lighthouse.storage/) page, which we will explore in this tutorial. Alternatively, you can use the included submodule in the repository to generate `.car` files. Instructions for this process can be found within the [repository](https://github.com/filecoin-project/fvm-starter-kit-deal-making#option-b-use-the-generate-car-tool-locally). Login into the FVM Data Depot page and click **Upload New File**. Once the process is done, you can access the page with the information we’ll need. To propose a deal, we will need the following: * **Piece CID** * **Piece Size** * **CAR Size** * **URL** ### Edit `hardhat.config.js` Before proceeding with the contract deployment, it is necessary to modify the config file in order to incorporate the Calibration endpoint from the environment variables. To accomplish this, please follow the steps below: 1. Locate the hardhat.config.js file. 2. Remove all existing content from the file. 3. Copy and paste the following code into the hardhat.config.js file: ``` require("@nomicfoundation/hardhat-toolbox") require("hardhat-deploy") require("hardhat-deploy-ethers") require("./tasks") require("dotenv").config() const PRIVATE_KEY = process.env.PRIVATE_KEY /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: { version: "0.8.17", settings: { optimizer: { enabled: true, runs: 1000, details: { yul: false }, }, }, }, defaultNetwork: "Calibration", networks: { Calibration: { chainId: process.env.CHAIN_ID, url: process.env.CALIBRATION_URL, accounts: [PRIVATE_KEY], }, FilecoinMainnet: { chainId: 314, url: "https://api.node.glif.io", accounts: [PRIVATE_KEY], }, }, paths: { sources: "./contracts", tests: "./test", cache: "./cache", artifacts: "./artifacts", }, } ``` This setup will pick up the Calibration endpoint from the environment variables. ### Deploy the deal-making contract on the Filecoin Calibration Testnet Now it’s time to deploy the smart contract, which you can find in the `contracts` directory. You will be deploying the `DealClient.sol` smart contract. Here is a high-level overview of the contract: 1. **Imports and structs**. The contract imports several libraries and contracts, including types and utilities, from the `@zondax/filecoin-solidity` package. It also defines several structs to model deal requests and extra parameters associated with these requests. 2. **DealClient contract**. The main contract, `DealClient`, is designed to facilitate the creation and management of storage deals on the Filecoin network. It includes several mappings to keep track of deal requests, piece requests, piece providers, piece deals, and piece statuses. 3. **Deal proposal creation**. The `makeDealProposal` function allows the contract owner to create a new deal proposal. The proposal is stored in the `dealRequests` array, and various mappings are updated to track the new proposal. 4. **Deal proposal retrieval**. The `getDealProposal` function allows anyone to retrieve a deal proposal given its ID. It returns a CBOR-encoded representation of the proposal. 5. **Deal authentication and notification**. The `authenticateMessage` and `dealNotify` functions are designed to be called by the Filecoin market actor as part of the deal publishing process. They validate the deal proposal and update the contract's state to reflect the published deal. 6. **Deal activation status**. The `updateActivationStatus` function can be called to retrieve the activation status of a deal and update the contract's state accordingly. 7. **Balance management**. The `addBalance` and `withdrawBalance` functions allow the contract owner to add funds to the storage market actor's escrow and withdraw funds from it. 8. **DataCap reception**. The `receiveDataCap` function is designed to be called by the Filecoin datacap actor. It emits an event to signal that datacap has been received. 9. **Filecoin method handling**. The `handle_filecoin_method` function is a universal entry point for calls coming from built-in Filecoin actors. It dispatches these calls to the appropriate function based on the method number. The contract is deployed by the `0_deploy.js` script in the `deploy` directory. This is a standard Hardhat deploying script, and it will work to deploy your contract. If you want to also convert the new contract address to f4address to find it in the explorer, as it does not support Ethereum addresses, delete the code from the deploy file and paste the following: ```javascript 0_deploy.js theme={"system"} require("hardhat-deploy") require("hardhat-deploy-ethers") const fa = require("@glif/filecoin-address"); const { networkConfig } = require("../helper-hardhat-config") const private_key = network.config.accounts[0] const wallet = new ethers.Wallet(private_key, ethers.provider) module.exports = async ({ deployments }) => { console.log("Wallet Ethereum Address:", wallet.address) //deploy DealClient const DealClient = await ethers.getContractFactory('DealClient', wallet); console.log('Deploying DealClient...'); const dc = await DealClient.deploy(); await dc.deployed() //Convert Ethereum address to f4 address const f4Address = fa.newDelegatedEthAddress(dc.address).toString(); console.log('DealClient deployed to:', dc.address); console.log('f4Address:', f4Address); } ``` This code only adds a line to convert the Ethereum address to f4address, which you can use to find the contract in the [Filecoin explorer](https://calibration.filscout.com/en). Run the following command to deploy the `DealClient` smart contract: ```shell Shell theme={"system"} yarn hardhat deploy ``` The console will return something similar to the following: ```bash Bash theme={"system"} Wallet Ethereum Address: 0x8f8e7012F8F974707A8F11C7cfFC5d45EfF5c2Ae Deploying DealClient... DealClient deployed to: 0x702E0755450aFb6A72DbE3cAD1fb47BaF3AC525C f4Address: f410foaxaovkfbl5wu4w34pfnd62hxlz2yus4fdk622a ``` Now you can find the smart contract you deployed in the [Filecoin explorer](https://calibration.filscout.com/en). ### Make a deal proposal Before invoking the `makeDealProposal` function from the smart contract, it's important to understand how the payload is constructed and the various parameters involved. To this end, you'll find a struct named `DealRequest` that outlines these parameters. The `DealRequest` struct looks like this: ```solidity DealRequest theme={"system"} /* * The DealRequest struct represents a user's request for this contract to initiate a deal. * This structure is designed in alignment with Filecoin's Deal Proposal, with the exception * of the provider field. The provider is omitted because any provider can respond to a deal * broadcast by this contract. */ struct DealRequest { bytes piece_cid; uint64 piece_size; bool verified_deal; string label; int64 start_epoch; int64 end_epoch; uint256 storage_price_per_epoch; uint256 provider_collateral; uint256 client_collateral; uint64 extra_params_version; ExtraParamsV1 extra_params; } ``` Let’s go over the parameter: * `piece_cid` — a unique identifier of the piece obtained when converting the file to `.car`. * `piece_size` — represents the size of the piece in bytes. * `verified_deal` — indicates whether the deal has been allocated `DataCap` or not. `DataCap` boosts the "quality-adjusted power" of storage providers by a factor of 10, resulting in better block rewards. Notaries receive batches of `DataCap` that they can distribute to clients. `DataCap` is consumed when used for making storage deals. * `label` — refers to the `DataCID` obtained during the conversion of the file to `.car`. * `start_epoch` — represents the epoch at which you want the storage to begin. * `end_epoch` — signifies the epoch at which you want the storage to conclude. * `storage_price_per_epoch` — the price offered per epoch in `attoFil`, similar to wei in Ethereum. This price corresponds to the cost per gigabyte of storage every 30 seconds (one epoch). A value of `1000000000000000000` means the deal offers 1 FIL per gigabyte stored every 30 seconds. The price can be set to zero, indicating it is free. * `provider_collateral` — the collateral that the storage provider must provide for the deal. It can be set to zero. * `client_collateral` — the collateral that you, as the client, must provide for the deal. It can also be set to zero. * `extra_params_version` — generally set to 1. * `ExtraParamsV1` — includes additional parameters: * `car_link` — the link to the `.car` file obtained during the conversion. * `car_size` — the size of the `.car` file obtained during the conversion. * `skip_ipni_announcement` — indicates whether the deal should be announced to IPNI indexers or not. * `remove_unsealed_copy` — specifies whether the storage provider should remove an unsealed copy. Now that you have a good idea about the deal-making parameters, we can send it to the network. The Filecoin kit includes a Hardhat task to facilitate these steps; in the terminal, send the following command: ```shell Shell theme={"system"} yarn hardhat make-deal-proposal --contract 0x702E0755450aFb6A72DbE3cAD1fb47BaF3AC525C --piece-cid baga6ea4seaqcgnwcifpjmcxqxpmmvcayct2msrj2wzhyrm7we6jl4c5i3gq7eda --piece-size 2097152 --verified-deal false --label "baga6ea4seaqcgnwcifpjmcxqxpmmvcayct2msrj2wzhyrm7we6jl4c5i3gq7eda" --start-epoch 587734 --end-epoch 600000 --storage-price-per-epoch 1000000000000000000 --provider-collateral 2000000000000000000 --client-collateral 0 --extra-params-version 1 --location-ref "YOUR_CAR_FILE_LINK" --car-size 1282438 --skip-ipni-announce false --remove-unsealed-copy false ``` Make sure to edit this request, including the address of the contract you just deployed and the info gathered when converting the file to `.car`. This will return a message with your proposal ID: ```bash Bash theme={"system"} Making deal proposal on network Calibration Complete! Event Emitted. ProposalId is: 0xfd6419d07e4c269e58d0c63969756c2124155b4a8d6dd08b8cd46e3a9acbf625 ``` To track the progress of your transaction, you can use the Filecoin explorer and review the details of the [transaction made for this tutorial](https://calibration.filscout.com/en/message/bafy2bzaceccg4fmbd4rna5ohthc7t2nui3jz7t54ahlafxxe3rd7tgkhj4npa). This will provide you with insights into how your transaction was executed. Once your transaction is confirmed, the [Boost storage providers](https://boost.filecoin.io/) will be able to proceed and pick up your deal. ## Conclusion In this tutorial, we walked through the deployment of a deal-making smart contract on the Filecoin Calibration Testnet, leveraging the Filecoin Ethereum Virtual Machine (FEVM). We delved into the concepts of the Filecoin Virtual Machine (FVM) and actors in the Filecoin network, equipping you with an understanding of the underlying architecture. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Flashblocks on Base: 200ms preconfirmations Source: https://docs.chainstack.com/docs/flashblocks-on-base Enable Flashblocks on Base through Chainstack for 200ms transaction preconfirmations, making your dApp experience up to 10x faster on the network. > [!WARNING] > > Base's [Denim upgrade](https://docs.base.org/upgrades/denim/overview) replaces Flashblocks with canonical 200ms blocks. Denim activated on Base Sepolia on Oct 21, 2026, at 18:00 (UTC), so Flashblocks subscriptions and pending state are no longer available on Base Sepolia. Base Mainnet serves Flashblocks until Denim activates there in November 2026, which is Base's current target. To move your integration to `newHeads`, `logs`, and regular RPC calls, see Base's [migration guide](https://docs.base.org/upgrades/denim/migrate-from-flashblocks). Get started with a [reliable Base RPC endpoint](https://chainstack.com/build-better-with-base/) to use Flashblocks. Flashblocks is a block builder sidecar for Optimism Stack chains. This sidecar is called Rollup-Boost. It's designed and built by [Flashbots](https://www.flashbots.net/)—the same team that originally productized MEV on Ethereum. Co-designed with Uniswap Labs and OP Labs. The Base team has implemented Flashblocks on the Base chain. Flashblocks went live on the Base Mainnet in July 2025. The Flashblocks implementation on the Base chain is two-part: * Sequencer side — The Rollup-Boost sidecar is installed next to the Base sequencer by the Base team. * Node side — The Flashblocks module is implemented on the [Base fork of the Reth node](https://github.com/base/node-reth). This two-side architecture implements the following Flashblocks flow: * Base sequencer creates regular 2-second blocks and additionally creates sub-blocks called Flashblocks every 200ms. * Base sequencer streams these Flashblocks every 200ms over WebSocket to the Base Reth nodes that have the Flashblocks module enabled. * The sequencer-streamed Flashblocks & Flashblock states can then be retrieved by apps over the standard Base RPC endpoints & methods: * `eth_getBlockByNumber` with `pending` tag * `eth_getTransactionReceipt` * [`eth_getBalance`](/reference/base-getbalance) with `pending` tag * [`eth_getTransactionCount`](/reference/base-gettransactioncount) with `pending` tag * [`eth_getTransactionByHash`](/reference/base-gettransactionbyhash) with `pending` tag * `eth_call` with `pending` tag * `eth_sendRawTransactionSync` * The transaction ordering is done at the Flashblocks level instead of the block level. Additionally, the following WebSocket subscription methods are available exclusively on Flashblocks-enabled endpoints: * [`eth_subscribe newFlashblockTransactions`](/reference/base-subscribe-newflashblocktransactions) — stream individual transactions as they are pre-confirmed (\~200ms each) * [`eth_subscribe pendingLogs`](/reference/base-subscribe-pendinglogs) — stream filtered event logs from pre-confirmed transactions * [`eth_subscribe newFlashblocks`](/reference/base-subscribe-newflashblocks) — stream full Flashblock payload objects from the sequencer Each 2-second block on the Base chain is formed after 10 different Flashblocks have been processed—each Flashblock with its own transaction ordering. ## Quick Flashblock vs. full block data comparison Let's call an [eth\_getBlockByNumber](/reference/base-getblockbynumber) with the `pending` tag to get the latest block data on a Flashblocks-enabled and non-Flashblocks-enabled node at the same time as [block 33228756](https://basescan.org/block/33228756) was being formed on the Base mainnet. See the results in the Chainstack Labs [base-flashblocks-transaction-latency-test](https://github.com/chainstacklabs/base-flashblocks-transaction-latency-test) repository: * `eth-get-block-by-number-pending-examples/preconfirmed-flashblock.log` — the result of running `"method":"eth_getBlockByNumber","params":["pending",true]` on a Flashblocks-enabled node as [block 33228756](https://basescan.org/block/33228756) was forming. * `eth-get-block-by-number-pending-examples/confirmed-block.log` — the result of running `"method":"eth_getBlockByNumber","params":["pending",true]` on a non-Flashblocks-enabled node as [block 33228756](https://basescan.org/block/33228756) was formed. You will see the key differences in the results: * Transaction count — 52 in the Flashblock vs. 167 in the fully formed block. * `stateRoot` — empty in the Flashblock vs. computed in the fully formed block. An empty `stateRoot` at this point means there's no final chain state for the block, hence "preconfirmation" in the name. * `blockHash` — different in the Flashblock and the fully formed block. The finalized transactions will have the hash of the fully formed block attributed to them. * `receiptsRoot` — different in the Flashblock and the fully formed block. A quick view: > [tab] Preconfirmed 200ms Flashblock > > ```json theme={"system"} > "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", > "receiptsRoot": "0x5f2dd9fbe32e2c22e47d84edbe6a300e75a004495191ff9f6228ce5de8f7c6f8", > "blockHash": "0x61792b40d824cb6e99ad07de112c823e0f86995063c00486e8dd3ef9667b40fc", > ``` > [tab] Confirmed regular 2s block > > ```json theme={"system"} > "stateRoot": "0x16a1375def73d111a7dcfcd39acbcdbe935acf011e645c8f37af2a05af72af71", > "receiptsRoot": "0x26dc0c64a01f87aecf3e112fa92c06845757dbc72b708bf783c97b425828cdb5", > "blockHash": "0xb09354e0fd87cd9c379c456ab41ea9b4ff1c684bea7705e19bb237f621b588f0", > ``` ## Transaction latency test tool See the Chainstack Labs [base-flashblocks-transaction-latency-test](https://github.com/chainstacklabs/base-flashblocks-transaction-latency-test) repository for the latency test tool and the comprehensive README file. ## Performance expectations Flashblocks are produced at 200ms intervals, but actual confirmation times will be higher due to network latency: * Standard Base endpoint — \~2000ms (2-second block time) * Flashblocks-enabled endpoint — \~300-500ms (200ms Flashblock interval + network travel time) The actual confirmation time depends on: * Network latency to/from the Base node * Transaction processing time * Time until next Flashblock (up to 200ms) * Network travel time for confirmation response ## Additional resources * Chainstack blog: [Flashblocks is live on Chainstack: even faster Base RPC](https://chainstack.com/flashblocks-base-rpc/) * [Base Flashblocks transaction latency test](https://github.com/chainstacklabs/base-flashblocks-transaction-latency-test) repository * Base documentation: [Flashblocks](https://docs.base.org/base-chain/flashblocks/apps) * Base blog: [We're making Base 10x faster with Flashblocks](https://blog.base.dev/accelerating-base-with-flashblocks) * Flashbots blog: [Introducing Rollup-Boost](https://writings.flashbots.net/introducing-rollup-boost) * Original spec for Rollup-Boost idea: [External Block Production](https://github.com/ethereum-optimism/design-docs/blob/main/protocol/external-block-production.md) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Subblocks on Optimism: 200ms preconfirmations Source: https://docs.chainstack.com/docs/flashblocks-on-optimism Use Subblocks (formerly Flashblocks) on Optimism through Chainstack for 200ms transaction preconfirmations via standard JSON-RPC calls with the pending tag. Get started with a [reliable Optimism RPC endpoint](https://chainstack.com/build-better-with-optimism/) to use Subblocks. Subblocks are partial blocks that the Optimism sequencer streams about every 200 ms while it builds each 2-second block. Chainstack Optimism Mainnet nodes follow this stream and serve the preconfirmed state through standard JSON-RPC methods with the `pending` block tag. > [!NOTE] > > Subblocks were previously called Flashblocks. Subblocks replaced Flashblocks on Optimism Mainnet on August 31, 2026, which shortened the interval from 250 ms to 200 ms and kept the JSON-RPC interface the same. OP Labs builds and maintains Subblocks. Flashblocks, the earlier implementation, is built and maintained by [Flashbots](https://www.flashbots.net/). ## How Subblocks work on Optimism Subblocks on Optimism have two parts: * Sequencer side — the Optimism sequencer's execution client produces a subblock as it builds each block and publishes the subblocks over a WebSocket stream. * Node side — the Reth-based execution client running on Chainstack follows the stream, executes the transactions in each subblock, and serves the result under the `pending` block tag. This two-part architecture implements the following flow: * The Optimism sequencer creates regular 2-second blocks and, while building each block, publishes a subblock about every 200 ms. * Each subblock appends transactions to the block in progress. The transaction list only grows until the block is sealed. * Apps retrieve the preconfirmed state over the standard Optimism RPC endpoints and methods: * [`eth_getBlockByNumber`](/reference/optimism-getblockbynumber) with the `pending` tag * [`eth_call`](/reference/optimism-call) with the `pending` tag * [`eth_getBalance`](/reference/optimism-getbalance) with the `pending` tag * [`eth_getTransactionCount`](/reference/optimism-gettransactioncount) with the `pending` tag * [`eth_getCode`](/reference/optimism-getcode) with the `pending` tag * [`eth_getStorageAt`](/reference/optimism-getstorageat) with the `pending` tag * [`eth_estimateGas`](/reference/optimism-estimategas) with the `pending` tag * [`eth_getTransactionReceipt`](/reference/optimism-gettransactionreceipt) — returns the preconfirmed receipt as soon as the transaction is included in a subblock * [`eth_getTransactionByHash`](/reference/optimism-gettransactionbyhash) — returns the preconfirmed transaction as soon as it is included in a subblock * [`eth_sendRawTransactionSync`](/reference/optimism-sendrawtransactionsync) — submit a signed transaction and receive the preconfirmed receipt in the same response The 200 ms interval is a target, not a guarantee, so the number of subblocks in a 2-second block varies and is about 10. > [!NOTE] > > Unlike Base, Optimism does not expose preconfirmations as dedicated WebSocket subscriptions like `eth_subscribe newFlashblocks`. The customer-facing interface on Optimism is the standard JSON-RPC interface with the `pending` block tag, as defined in the [Optimism Subblocks specification](https://specs.optimism.io/protocol/subblocks.html). ## Compare preconfirmed subblocks with the sealed block Call [eth\_getBlockByNumber](/reference/optimism-getblockbynumber) with the `pending` tag on an Optimism endpoint while a 2-second canonical block is forming. The response changes with each subblock: the block number stays the same, the transaction list grows, and the block hash changes. The same canonical block number sampled about 500 ms apart while the block was being built on Chainstack, and then after it was sealed: > [tab] Preconfirmed subblock (early) > > ```json theme={"system"} > "number": "0x97806d8", > "hash": "0x8ef10a9d6049c93d205a91a416ea0989988d68b8f236f8e51bce31376334f70c", > "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", > "transactions": [ /* 2 transactions */ ] > ``` > [tab] Preconfirmed subblock (later) > > ```json theme={"system"} > "number": "0x97806d8", > "hash": "0x10a6f84ffa45dbbf6bc91aa431162bb002c067e73bddef2e990461cdb3a80b3d", > "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", > "transactions": [ /* 7 transactions */ ] > ``` > [tab] Sealed 2-second block > > ```json theme={"system"} > "number": "0x97806d8", > "hash": "0xe1d8b7956db9c4fc0996474306b376157729ed3106d32bd5d1b3438fc1edf3d9", > "stateRoot": "0x7ffd13ffee70dfc77e6da2fa81cf1a00358b438c1e886e55a9c403d2d721849e", > "transactions": [ /* 16 transactions */ ] > ``` The block number stays the same across all three samples. Only the transaction list grows and the preconfirmation hash changes. In a `pending` response, `stateRoot` is all zeros, because the state root is exact only once the block is sealed. The `pending` hash is not the hash of the sealed block either. Read the final `hash` and `stateRoot` from the block after it is sealed, and track transactions by their own hash. ## Verify Subblocks on your endpoint You can verify Subblocks on your Optimism endpoint by polling `eth_getBlockByNumber` with the `pending` tag and watching the response change within a single block number. Replace `YOUR_OPTIMISM_ENDPOINT` with your Chainstack Optimism node HTTPS endpoint: ```bash theme={"system"} for i in 1 2 3 4 5 6 7 8 9 10; do curl -s "YOUR_OPTIMISM_ENDPOINT" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["pending",false]}' \ | python3 -c "import sys,json; b=json.load(sys.stdin)['result']; print(int(b['number'],16), b['hash'], len(b['transactions']))" sleep 0.2 done ``` On an endpoint that follows Subblocks, you see multiple distinct hashes and growing transaction counts for the same block number within a 2-second window. ## Performance expectations Subblocks on Optimism are produced about every 200 ms, so a `pending` read reflects a transaction long before the 2-second block that includes it is sealed. A `latest` read reflects the transaction only after the block is sealed. The actual preconfirmation time depends on: * Network latency to and from the Optimism node. * Transaction processing time at the sequencer. * Time until the next subblock (up to about 200 ms). * Gas available in the next subblock. The sequencer divides a block's gas capacity across the block-building window, so a transaction with a large gas limit can wait for a later subblock or the next block. * Network travel time for the confirmation response. > [!WARNING] > > Subblock preconfirmations are not final. A preconfirmed receipt from `eth_getTransactionReceipt`, `eth_sendRawTransactionSync`, or a pending-tag call can be revoked if the Optimism sequencer abandons or reorganizes the block in progress. Wait for the transaction to be included in a finalized canonical block before releasing funds or considering a payment settled. ## Optimism Subblocks vs. Base Flashblocks at a glance | Aspect | Optimism | Base | | - | - | - | | Preconfirmation feature | Subblocks | Flashblocks | | Interval | 200ms | 200ms | | Canonical block time | 2s | 2s | | Preconfirmations per block | \~10 | \~10 | | Customer-facing interface | JSON-RPC with `pending` tag | JSON-RPC with `pending` tag and dedicated WebSocket subscriptions | | `eth_subscribe newFlashblocks` etc. | Not exposed | [Exposed](/docs/flashblocks-on-base) | ## Additional resources * [Flashblocks on Base](/docs/flashblocks-on-base) — the Base implementation, including dedicated WebSocket subscription methods * Optimism documentation: [Subblocks overview](https://docs.optimism.io/op-stack/features/subblocks) * Optimism documentation: [Integrate Subblocks in your app](https://docs.optimism.io/app-developers/guides/transactions/integrating-subblocks) * Optimism documentation: [Subblocks notice](https://docs.optimism.io/notices/subblocks) — the move from 250 ms to 200 ms and the zeroed stream fields * Optimism specification: [Subblocks](https://specs.optimism.io/protocol/subblocks.html) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Forking EVM-compatible mainnet with Hardhat Source: https://docs.chainstack.com/docs/forking-evm-compatible-mainnet-with-hardhat Fork an EVM mainnet locally with Hardhat using a Chainstack archive node. Configure a forked network in hardhat.config, pin to a specific block, and connect MetaMask to the fork. **TLDR:** * Hardhat can spin up a local EVM node that mirrors a live mainnet's state — perfect for testing DeFi interactions against real contracts without spending real funds. * You need an [archive node](/docs/protocols-modes-and-types) endpoint for the fork; full nodes don't retain enough history. * In Hardhat 3, declare the fork in `hardhat.config.ts` under a network of type `edr-simulated` and run `npx hardhat node`. * Point MetaMask at `http://127.0.0.1:8545` with chain ID `31337` to interact with the fork in a browser wallet. ## Why fork mainnet? A mainnet fork gives you a local EVM that starts from the real mainnet state — same contracts, same balances, same storage slots — and from that point onward runs only on your machine. It's the standard way to: * Test DeFi interactions against deployed contracts (Uniswap, Aave, Curve, etc.) without paying gas. * Reproduce a specific on-chain bug at the exact block where it happened. * Simulate upgrades or whale movements before deploying changes. This article applies to any EVM-compatible network — Ethereum, Polygon, BNB Smart Chain, Base, Arbitrum, Optimism, and others. ## Before you start You need: * An archive node endpoint on the network you want to fork. Deploy an [archive-mode node](/docs/protocols-modes-and-types) on Chainstack and copy the HTTPS endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). * Node.js installed locally. * A Hardhat project. If you don't have one yet: ```bash Bash theme={"system"} mkdir hardhat-fork && cd hardhat-fork npm init -y npm install --save-dev hardhat npx hardhat --init ``` This guide uses Hardhat 3 (current). If you're on Hardhat 2, jump to [Hardhat 2 syntax](#hardhat-2-syntax-legacy) below. ## Configure the fork In Hardhat 3, forking is declared as a network of type `edr-simulated` in your config file: ```typescript hardhat.config.ts theme={"system"} import hardhatToolboxViemPlugin from "@nomicfoundation/hardhat-toolbox-viem"; import { defineConfig } from "hardhat/config"; export default defineConfig({ plugins: [hardhatToolboxViemPlugin], solidity: "0.8.28", networks: { mainnetFork: { type: "edr-simulated", forking: { url: "https://ethereum-mainnet.core.chainstack.com/AUTH_KEY", // blockNumber: 18000000, // optional pin }, }, }, }); ``` ## Run the fork as a standalone node Start a local JSON-RPC node that wallets and external scripts can connect to: ```bash Bash theme={"system"} npx hardhat node --network mainnetFork ``` Output: ``` Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/ ``` The local fork is now running on port `8545`. Any transaction you send updates only the local state; the live mainnet sees nothing. ## Fork at a specific block To reproduce historical state — for example, debugging an exploit at a known block — pin the fork by setting `blockNumber` in the `forking` config: ```typescript hardhat.config.ts theme={"system"} networks: { mainnetFork: { type: "edr-simulated", forking: { url: "https://ethereum-mainnet.core.chainstack.com/AUTH_KEY", blockNumber: 18000000, }, }, }, ``` Pinning to a block is also faster than forking at HEAD because Hardhat doesn't have to keep up with new blocks. ## Run tests against the fork To run your tests against the forked network, pass it with `--network`: ```bash Bash theme={"system"} npx hardhat test nodejs --network mainnetFork ``` Or connect to the forked network only inside specific tests: ```typescript test/Example.ts theme={"system"} import { describe, it } from "node:test"; import { network } from "hardhat"; describe("Fork test", function () { it("uses the forked mainnet state", async function () { const { viem } = await network.create("mainnetFork"); // ... }); }); ``` See the [Hardhat 3 forking guide](https://hardhat.org/docs/guides/forking) for the full set of options including [Solidity forking](https://hardhat.org/docs/guides/forking#forking-in-solidity-tests) via cheatcodes. ## Connect MetaMask to the fork Once the Hardhat node is running, point MetaMask at it: 1. Open MetaMask, click the network selector, and choose **Add a network** → **Add a network manually**. 2. Fill in: | Field | Value | | - | - | | Network name | `Hardhat fork` | | New RPC URL | `http://127.0.0.1:8545` | | Chain ID | `31337` | | Currency symbol | `ETH` | 3. Click **Save**, then switch MetaMask to the Hardhat fork network. MetaMask now talks to your local fork. Hardhat ships with pre-funded test accounts — import one with its private key (printed in the Hardhat node output) to send transactions from MetaMask. See [Adding a custom network to MetaMask](/docs/adding-a-custom-network-to-metamask) for more on the network-add flow. ## Hardhat 2 syntax (legacy) If your project is still on Hardhat 2, forking uses a CLI flag instead of the config-driven `edr-simulated` network type: ```bash Bash theme={"system"} npx hardhat node \ --fork https://ethereum-mainnet.core.chainstack.com/AUTH_KEY \ --fork-block-number 18000000 ``` The Hardhat 2 config-file equivalent: ```javascript hardhat.config.js theme={"system"} module.exports = { solidity: "0.8.24", networks: { hardhat: { forking: { url: "https://ethereum-mainnet.core.chainstack.com/AUTH_KEY", blockNumber: 18000000, }, }, }, }; ``` The MetaMask connection step is identical (`http://127.0.0.1:8545`, chain ID `31337`). ## Common gotchas * **`Error: missing trie node`** — your endpoint isn't an archive node, or it doesn't retain state at the block you're forking from. Switch to an archive endpoint. See [EVM node returns "Missing trie node"](/docs/evm-node-returns-missing-trie-node). * **Slow fork startup.** Hardhat fetches state on demand. Forking at HEAD means every test run racks up RPC calls. Pin to a fixed block whenever possible. * **Nonce errors after restart.** Hardhat resets state when you stop the node — but MetaMask remembers nonces per-account. Reset the MetaMask activity (Settings → Advanced → Clear activity tab data) after each fork restart. ## See also * [Adding a custom network to MetaMask](/docs/adding-a-custom-network-to-metamask) * [EVM node returns "Missing trie node"](/docs/evm-node-returns-missing-trie-node) * [Protocols, modes, and types](/docs/protocols-modes-and-types) * [Hardhat 3 forking guide](https://hardhat.org/docs/guides/forking) # Geth vs Erigon: RPC method comparison on Ethereum Source: https://docs.chainstack.com/docs/geth-vs-erigon-deep-dive-into-rpc-methods-on-ethereum-clients Compare Geth and Erigon execution client RPC method support, performance characteristics, and debug/trace API differences on Chainstack Ethereum nodes. **TLDR** * Geth and Erigon share most Ethereum JSON-RPC API calls but differ in some advanced functionality. * Geth provides a few extra methods for mining and wallet-bound calls (e.g., eth\_sendTransaction), while Erigon supports others like eth\_getBlockReceipts and unique debugging/trace methods. * Identify which client your provider runs to ensure compatibility and avoid missing methods. * Explore the Chainstack API reference for more detail and use web3\_clientVersion to check your client programmatically. ## Main article The JSON-RPC API is an essential tool for blockchain development as it provides a standardized way to interact with blockchain networks through HTTP or WebSocket connections. At the time of writing, there are seven clients for Ethereum, which is a great achievement in terms of decentralization. However, this can cause a problem for developers because different clients often implement different RPC API sets, leading to confusion. For example, if your DApp was developed on Erigon and you plan to switch to a new RPC provider running Geth, it's possible that the core API used in your application won't be available on the new node. To avoid compatibility issues, it's important to identify the current client you're using and the available API methods. This article compares the available RPC APIs for two Ethereum clients, Erigon and Geth, that are available on Chainstack. You can [run Geth or Erigon via Chainstack](https://chainstack.com/build-better-with-ethereum/) on a Dedicated Node and pick the client that fits your RPC needs. It is important to note that all tests were performed using **Erigon version 2.42** and **Geth version 1.11.5**, which were the most up-to-date versions as of May 2023. It is possible that there may be changes in the future. ### Find what client your node is running With Chainstack, you can easily view this information in the console. However, if you prefer a programmatic approach, consider using the `web3_clientVersion` method. This RPC method returns the client version as a response. Discover how to use `web3_clientVersion` and explore code examples in the [Ethereum node API reference](/reference/ethereum-clientversion). ## Standard Ethereum JSON RPC methods available The Ethereum JSON-RPC is a standard collection of methods that all execution clients must implement. The detailed specification can be found in the [Ethereum documentation](https://ethereum.github.io/execution-apis/api-documentation/). Most of the standard methods are implemented in both Geth and Erigon. However, the Erigon team has either deprecated or not implemented a few of them. ### Methods available on both Erigon and Geth | | | | | | - | - | - | - | | eth\_blockNumber | eth\_syncing | eth\_gasPrice | eth\_maxPriorityFeePerGas | | eth\_feeHistory | eth\_getBlockTransactionCountByHash | eth\_getUncleCountByBlockHash | eth\_getUncleCountByBlockNumber | | eth\_getTransactionByHash | eth\_getTransactionByBlockHashAndIndex | eth\_getTransactionByBlockNumberAndIndex | eth\_getTransactionReceipt | | eth\_estimateGas | eth\_getBalance | eth\_getCode | eth\_getTransactionCount | | eth\_getStorageAt | eth\_call | eth\_createAccessList | eth\_newFilter | | eth\_newBlockFilter | eth\_newPendingTransactionFilter | eth\_getFilterLogs | eth\_getFilterChanges | | eth\_uninstallFilter | eth\_getLogs | eth\_sendRawTransaction | | > [!NOTE] > > Check the Chainstack [Ethereum node API reference](/reference/ethereum-getting-started) to learn how these methods work. ### Standard methods available on Geth only | | | | | | - | - | - | - | | eth\_accounts | eth\_sendTransaction | eth\_sign | eth\_signTransaction | | eth\_mining | eth\_hashrate | eth\_getWork | eth\_submitWork | | eth\_submitHashrate | eth\_sign | eth\_signTransaction | eth\_getProof | | eth\_sendTransaction | debug\_getRawBlock | debug\_getRawTransaction | debug\_getRawReceipts | | debug\_getRawHeader | debug\_getBadBlocks | | | The following methods require the client to be associated with a wallet address. They are either deprecated or not implemented by Erigon. * eth\_accounts * eth\_sendTransaction * eth\_sign * eth\_signTransaction The following methods are only available in miner mode: * eth\_mining * eth\_hashrate * eth\_getWork * eth\_submitWork * eth\_submitHashrate ## Non-standard methods available on both Geth and Erigon In addition to the standard methods, Geth and Erigon implement their own RPC methods. Erigon was originally a fork of Geth, so both clients share the majority of the RPC APIs. | | | | | - | - | - | | eth\_subscribe | eth\_unsubscribe | | | web3\_clientVersion | web3\_sha3 | | | txpool\_content | txpool\_status | | | net\_listening | net\_peerCount | net\_version | | eth\_getUncleByBlockHashAndIndex | eth\_getUncleByBlockNumberAndIndex | eth\_getRawTransactionByHash | | eth\_getRawTransactionByBlockHashAndIndex | eth\_getRawTransactionByBlockNumberAndIndex | eth\_createAccessList | | debug\_accountRange | debug\_getModifiedAccountsByNumber | debug\_getModifiedAccountsByHash | | debug\_traceBlockByNumber | debug\_traceBlockByHash | debug\_traceTransaction | | debug\_storageRangeAt | debug\_traceCall | | The `eth_subscribe` and `eth_unsubscribe` RPC methods, available exclusively through WebSocket Secure (WSS), allow subscribing to real-time data streams for events like new blocks and pending transactions. The `web3_clientVersion` function delivers client information, while `web3_sha3` calculates the Keccak hash for a given string. Utilize `txpool_content` and `txpool_status` RPC methods for accessing transactions in the mempool. RPC methods within the `net` namespace facilitate the monitoring of a node's P2P status. Leveraging non-standard methods in the `eth` namespace is advantageous for obtaining extra information that is not accessible via standard RPC methods but is highly valuable for developers. The `debug` methods are designed for advanced users and fulfill a range of purposes, including gathering execution traces for single or multiple transactions. ### Non-standard methods available on Geth only | | | | | | - | - | - | - | | txpool\_contentFrom | txpool\_inspect | | | | debug\_backtraceAt | debug\_blockProfile | debug\_chaindbCompact | debug\_chaindbProperty | | debug\_cpuProfile | debug\_dbAncient | debug\_dbAncients | debug\_dbGet | | debug\_dumpBlock | debug\_freeOSMemory | debug\_freezeClient | debug\_gcStats | | debug\_getAccessibleState | debug\_getBadBlocks | debug\_getRawBlock | debug\_getRawHeader | | debug\_getRawTransaction | debug\_getRawReceipts | debug\_goTrace | debug\_intermediateRoots | | debug\_memStats | debug\_mutexProfile | debug\_preimage | debug\_printBlock | | debug\_seedHash | debug\_setBlockProfileRate | debug\_setGCPercent | debug\_setHead | | debug\_setMutexProfileFraction | debug\_setTrieFlushInterval | debug\_stacks | debug\_standardTraceBlockToFile | | debug\_standardTraceBadBlockToFile | debug\_startCPUProfile | debug\_startGoTrace | debug\_stopCPUProfile | | debug\_stopGoTrace | debug\_traceBadBlock | debug\_traceBlock | debug\_traceBlockFromFile | | debug\_traceChain | debug\_verbosity | debug\_vmodule | debug\_writeBlockProfile | | debug\_writeMemProfile | debug\_writeMutexProfile | | | Geth provides two additional methods in the `txpool` namespace: `txpool_contentFrom` and `txpool_inspect`. `txpool_contentFrom` retrieves the transactions contained within the txpool, and `txpool_inspect` lists a textual summary of all transactions. In addition, Geth exposes many execution-time node tuning through the debug namespace. Some of these methods are destructive to the node itself so it should be used with caution. For example: * `debug_freezeClient` forces a temporary client freeze. * `debug_setHead` sets the current head of the local chain by block number. * `debug_setTrieFlushInterval` configures how often in-memory state tries are persisted to disk. If this value is set to 0, the node will essentially turn into an archive node. Geth also provides handy trace methods in the `debug` namespace. If you are interested in how they work, the guide [Deep Dive into Ethereum debug\_trace APIs](https://chainstack.com/deep-dive-into-ethereum-trace-apis/) may be useful for you. ### Non-standard methods available on Erigon only | | | | | | - | - | - | - | | eth\_getBlockReceipts | eth\_protocolVersion | eth\_callMany | eth\_callBundle | | debug\_accountAt | debug\_traceCallMany | | | | trace\_call | trace\_callMany | trace\_rawTransaction | trace\_replayBlockTransactions | | trace\_replayTransaction | trace\_block | trace\_filter | trace\_get | | trace\_transaction | | | | | erigon\_getHeaderByHash | erigon\_getBlockReceiptsByBlockHash | erigon\_getHeaderByNumber | erigon\_getLogsByHash | | erigon\_forks | erigon\_getBlockByTimestamp | erigon\_BlockNumber | erigon\_getLatestLogs | Erigon inherits some debug and trace methods from Nethermind and Flashbot, which provides more possibilities than a Geth node. For example, [`debug_traceCallMany`](/reference/ethereum-tracecallmany) and `trace_callMany` are handy methods that don't exist on Geth. These two methods perform the same function, allowing users to send multiple transactions in a batch to oversee their execution. The transactions are executed in sequence, with each transaction depending on the resulting state of the previous transactions. ## Last but not least If you would like to learn more about how these methods work in detail, you can visit Chainstack’s [API documentation](/reference/ethereum-getting-started). Additionally, there is a [Chainstack Postman collection](/reference/ethereum-rpc-methods-postman-collection) for you to try out these RPC methods. Hope this article is helpful for you. If you have any questions, feel free to ping me on my social media or in Chainstack's [Telegram](https://t.me/chainstack) or [Discord](https://discord.gg/Cymtg2f7pX). Happy coding, cheers! > [!NOTE] > > ### See also > > > [card] [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) > > > [card] [Uncovering the power of eth\\\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) > > > [card] [Expanding your blockchain horizons: The eth\\\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) > > > [card] [Deep dive into Merkle proofs and eth\\\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) > > > [card] [Ethereum logs tutorial series: Logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters) ### About the author > [card] Wuzhong Zhu > > ![Wuzhong Zhu](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1548860905064017921/xCKUgveS_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=075e8dc4db13d9384c63bf4904eb520d) > > Developer Advocate @ Chainstack > \ > BUIDLs on Ethereum, zkEVMs, and The Graph protocol > > [https://twitter.com/wuzhongzhu](https://twitter.com/wuzhongzhu) > > [https://www.linkedin.com/in/wuzhong-zhu-44563589/](https://www.linkedin.com/in/wuzhong-zhu-44563589/) > > [https://github.com/wuzhong-zhu](https://github.com/wuzhong-zhu) # Getting started with Foundry Source: https://docs.chainstack.com/docs/getting-started-with-foundry Set up Foundry for Ethereum smart contract development using forge, cast, and anvil with a Chainstack RPC node for compilation, testing, and deployment. * Foundry is a complete toolkit that simplifies Ethereum smart contract development with modular components like Forge (build/test), Anvil (local node), Cast (CLI interactions), and Chisel (Solidity REPL). * You can compile and deploy contracts using Solidity-based scripts, specify private keys via CLI flags, and switch between local or custom RPC endpoints. * Cast offers flexible commands for sending transactions, reading contract data, or converting values, making it a powerful tool for rapid iteration and debugging. * Built-in testing (Forge) supports local, forked, and coverage tests, making it straightforward to manage dependencies, run end-to-end scenarios, and ensure code reliability. ## Main article If you're diving into smart contract development, you'll find Foundry to be an incredibly handy companion. It's more than just a tool; it's a full-fledged framework that makes smart contracts' development, testing, and deployment smoother and more efficient. Whether you're new to blockchain or an experienced developer, Foundry is designed to be a reliable and comprehensive toolkit that fits right into your workflow. Foundry is comprised of several key components, each serving a distinct purpose in the smart contract development lifecycle: 1. **Forge**: Forge is the heart of the Foundry framework, acting as a powerful compilation and testing tool. It's instrumental in compiling smart contracts, running a suite of tests (including fuzz and property-based tests), and ensuring the contracts are robust and secure before deployment. 2. **Anvil**: Anvil is a local node tailored for development. This component is invaluable for developers who need a quick and easy way to test their contracts in a local blockchain environment. Anvil enables rapid iteration and debugging without connecting to the main networks or testnets. 3. **Cast**: Cast is a versatile tool within Foundry designed to interact with Ethereum. It facilitates a range of actions, from sending transactions and querying blockchain data to manipulating local Ethereum states. This utility makes interacting with deployed contracts easier and performs various blockchain-related tasks. 4. **Chisel**: Chisel enriches the Foundry suite as a Solidity REPL (Read-Eval-Print Loop), enabling developers to interactively test and experiment with Solidity code snippets. It's ideal for immediate feedback and debugging in real-time, and it's compatible both within and outside Foundry projects. This guide covers installing Foundry, setting up, compiling, deploying, and interacting with smart contracts. ## Initializing a Project ### Creating a New Project * In an empty directory, initialize a new Foundry project: ```bash Shell theme={"system"} forge init ``` * To create a new directory with the project: ```bash Shell theme={"system"} forge init PROJECT_NAME ``` * **Note:** The `src` directory is where the smart contracts are placed. ## Compiling Contracts * To compile the contracts, run either: ```bash Shell theme={"system"} forge build ``` or ```bash Shell theme={"system"} forge compile ``` * The `out` directory will generate a JSON file containing compilation data, such as ABI. ## Setting Up a Local Blockchain * Use Anvil to start a local blockchain for testing: ```bash Shell theme={"system"} anvil ``` * The local blockchain will run at `127.0.0.1:8545`, and you can add it to MetaMask for ease of testing. ## Deploying Contracts ### Deploying Locally or to a Custom RPC * To deploy smart contracts, use `forge create`. Forge defaults to the Anvil local blockchain, but other RPCs can be specified using the `--rpc-url` flag. Get a free RPC from [Chainstack](https://chainstack.com/). * Deploying locally with Anvil running: ```bash Shell theme={"system"} forge create CONTRACT_NAME ``` * Deploying to a custom endpoint: ```bash Shell theme={"system"} forge create CONTRACT_NAME --rpc-url YOUR_ENDPOINT ``` * This will likely not work because it needs a private key to deploy. ```bash Shell theme={"system"} Error: Error accessing local wallet. Did you set a private key, mnemonic or keystore? Run `cast send --help` or `forge create --help` and use the corresponding CLI flag to set your key via: --private-key, --mnemonic-path, --aws, --interactive, --trezor or --ledger. Alternatively, if you're using a local node with unlocked accounts, use the --unlocked flag and either set the `ETH_FROM` environment variable to the address of the unlocked account you want to use, or provide the --from flag with the address directly. ``` ### Options for Specifying Private Key * Run the create command with the `--interactive` flag for a prompt to add a private key: ```bash Shell theme={"system"} forge create CONTRACT_NAME --interactive ``` * Or, directly include the private key in the command: ```bash Shell theme={"system"} forge create CONTRACT_NAME --private-key YOUR_PRIVATE_KEY ``` ## Writing Deploy Scripts * Scripts in Foundry are written in Solidity. We'll use a Solidity script to deploy a contract. By convention, script files end with `.s.sol`. * **Example:** Deploying `SimpleStorage.sol`. ### Creating the Deploy Script * Create a file named `deploySimpleStorage.s.sol` with the following content: ```sol sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Script} from "forge-std/Script.sol"; import {SimpleStorage} from "../src/SimpleStorage.sol"; contract DeploySimpleStorage is Script { function run() external returns (SimpleStorage) { vm.startBroadcast(); SimpleStorage simpleStorage = new SimpleStorage(); vm.stopBroadcast(); return simpleStorage; } } ``` * Deploy the contract using the script: ```bash Shell theme={"system"} forge script script/DeploySimpleStorage.s.sol --rpc-url YOUR_RPC --broadcast --private-key YOUR_PRIVATE_KEY ``` Let's break down its key components and functionalities: 1. **Pragma Directive**: * `pragma solidity ^0.8.19;`: Specifies that the script is compatible with Solidity version 0.8.19 or any newer version of the 0.8 series but not version 0.9 or above. 2. **Imports**: * `import {Script} from "forge-std/Script.sol";`: Imports the `Script` class from the `forge-std` library, which is a part of Foundry, a development environment for Ethereum smart contracts. * `import {SimpleStorage} from "../src/SimpleStorage.sol";`: Imports the `SimpleStorage` contract, presumably a custom contract located in the `src` directory. 3. **Contract Declaration**: * `contract DeploySimpleStorage is Script`: Defines a new contract named `DeploySimpleStorage` that inherits from the `Script` class. This setup is typical for deployment scripts in Foundry. 4. **Function Definition**: * `function run() external returns (SimpleStorage)`: The `run` function is the main entry point for the deployment script. It's marked `external` as it's intended to be called externally, and it returns an instance of `SimpleStorage`. 5. **Deployment Process**: * `vm.startBroadcast();`: Initiates a transaction broadcast. The `vm` object is a special component in Foundry, providing various functionalities related to the Ethereum Virtual Machine (EVM). * `SimpleStorage simpleStorage = new SimpleStorage();`: Instantiates the `SimpleStorage` contract. * `vm.stopBroadcast();`: Ends the transaction broadcast. 6. **Return Statement**: * `return simpleStorage;`: Returns the deployed instance of `SimpleStorage`. This script is a typical example of a deployment script used in the Foundry environment for deploying Ethereum smart contracts. It's concise and follows the pattern of starting a broadcast, deploying the contract, and stopping it. The `SimpleStorage` contract, which is not detailed here, would contain the actual business logic or data storage mechanisms. ## Interacting with Contracts Using Cast ### Sending Transactions * To send transactions, use `cast send`: ```bash Shell theme={"system"} cast send ADDRESS FUNCTION_SIG PARAMS ``` * **Example:** ```bash Shell theme={"system"} cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 "store(uint256)" 3333 --rpc-url $RPC_URL --private-key $PRIVATE_KEY ``` ### Reading from Contracts * Use `cast call` for reading view functions: ```bash Shell theme={"system"} cast call ADDRESS FUNCTION_SIGNATURE ``` * **Example:** ```bash Shell theme={"system"} cast call 0x5FbDB2315678afecb367f032d93F642f64180aa3 "retrieve()" ``` You can use cast for conversions, for example, hex to dec: ```bash Shell theme={"system"} cast --to-base 0x0000000000000000000000000000000000000000000000000000000000000d05 dec ``` Or use the [Chainstack EVM Swiss Knife](https://web3tools.chainstacklabs.com/hexadecimal-decimal). ## Managing Dependencies ### Installing Smart Contract Dependencies * Use the following command to install dependencies from a repository: ```bash Shell theme={"system"} forge install smartcontractkit/chainlink-brownie-contracts --no-commit ``` * Dependencies are added to the `lib` directory. ### Remapping Dependencies * Add remappings in the `foundry.toml` file for syntax convenience: ```toml TOML theme={"system"} remappings = ['@chainlink/contracts/=lib/chainlink-brownie-contracts/contracts/'] ``` chainlink/contracts/=lib/chainlink-brownie-contracts/contracts/'] ## Writing and Running Tests ### Example Test Contract * Tests in Foundry are also written in Solidity. Here's an example: ```bash Bash theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import {Test, console} from "forge-std/Test.sol"; contract FundMeTest is Test { uint256 number = 33; function setUp() external { number = 3333; } function testDemo() public { console.log("The saved number is", number); assertEq(number, 3333); } } ``` * Run tests with: ```bash Shell theme={"system"} forge test -vv ``` * The `-vv` flag outputs detailed logs for better insight. Let's break down its components: 1. **License and Solidity Version Declaration**: * `// SPDX-License-Identifier: MIT`: This is a comment specifying the license under which this file is released, in this case, the MIT License. * `pragma solidity ^0.8.18;`: This line specifies the compiler version. The file is compatible with Solidity version 0.8.18 and above within the 0.8.x range. 2. **Imports**: * `import {Test, console} from "forge-std/Test.sol";`: This line imports two elements from the Forge standard library (`forge-std`): * `Test`: A base contract that provides testing functionalities. * `console`: A utility to log output to the console. This is particularly useful for debugging and tracking variable values during test execution. 3. **Test Contract Declaration**: * `contract FundMeTest is Test {`: This line declares a new contract `FundMeTest` which inherits from the `Test` contract. In the context of Forge, this means `FundMeTest` is a test suite. 4. **State Variable**: * `uint256 number = 33;`: A state variable `number` of type `uint256` (unsigned integer of 256 bits) is declared and initialized to 33. This variable is used to demonstrate state manipulation and assertion in the test. 5. **Setup Function**: * `function setUp() external { number = 3333; }`: The `setUp()` function is a special function in the Forge framework that runs before each test function. It's used for initializing or resetting the state. Here, it sets the `number` variable to 3333. 6. **Test Function**: * `function testDemo() public { ... }`: This is the actual test function. In Forge, any function with a name starting with `test` is considered a test case. * `console.log("The saved number is", number);`: This line logs the value of `number` to the console, which is useful for debugging or verifying the test state. * `assertEq(number, 3333);`: This is an assertion statement provided by the `Test` contract. It checks whether the value of `number` is equal to 3333. If the assertion fails (i.e., if `number` is not 3333), the test will fail. ## Testing on a Fork * Run tests on a forked network by adding an RPC URL: ```bash Shell theme={"system"} forge test -vvv --fork-url $SEPOLIA_RPC ``` ## Coverage Analysis * Use `forge coverage` to analyze how much of your contracts are tested: ```bash Shell theme={"system"} forge coverage --fork-url $SEPOLIA_RPC ``` It will display a nice table: ```bash Shell theme={"system"} [⠢] Compiling... [⠢] Compiling 26 files with 0.8.20 [⠆] Solc 0.8.20 finished in 4.07s Compiler run successful! Analysing contracts... Running tests... | File | % Lines | % Statements | % Branches | % Funcs | |---------------------------|---------------|---------------|---------------|--------------| | script/DeployFundme.s.sol | 0.00% (0/3) | 0.00% (0/3) | 100.00% (0/0) | 0.00% (0/1) | | src/FundMe.sol | 16.67% (2/12) | 23.53% (4/17) | 0.00% (0/4) | 25.00% (1/4) | | src/PriceConverter.sol | 0.00% (0/6) | 0.00% (0/11) | 100.00% (0/0) | 0.00% (0/2) | | Total | 9.52% (2/21) | 12.90% (4/31) | 0.00% (0/4) | 14.29% (1/7) | ``` # TON: Deploy a smart contract Source: https://docs.chainstack.com/docs/getting-started-with-ton-deploy-a-smart-contract Deploy TON smart contracts with Blueprint SDK and Chainstack. Learn TACT language, contract development, testing, deployment, and interaction on TON blockchain. **TLDR:** * You’ll configure a Hardhat project to interact with Aave V3 flash loans on Avalanche’s Fuji testnet. * You’ll use Chainstack for your Avalanche node endpoint and deploy a custom FlashLoan contract. * You’ll borrow USDC, then repay it plus fees in a single transaction, demonstrating a flash loan’s instant, collateral-free mechanics. * By the end, you’ll have a working Aave flash loan flow on a testnet environment ready for deeper custom logic. ## Main article The Open Network ([TON](https://docs.ton.org/)) is a decentralized and open platform comprising several components, including TON Blockchain, TON DNS, TON Storage, and TON Sites. Originally developed by the Telegram team, TON aims to provide fast, secure, and user-friendly decentralized services. Its core protocol, TON Blockchain, connects the underlying infrastructure to form the greater TON Ecosystem. TON is focused on achieving widespread cross-chain interoperability within a highly scalable and secure framework. Designed as a distributed supercomputer or “superserver,” TON provides various products and services to contribute to developing the decentralized vision for the new internet. This makes it well-suited for a wide range of decentralized applications. One of the unique features of TON smart contracts is that they can receive messages and do some action based on it; we'll leverage this in the smart contract we'll develop. This tutorial will guide you through creating, building, testing, deploying, and interacting with a simple storage contract on the TON blockchain. We'll use the [Blueprint SDK](https://docs.ton.org/develop/smart-contracts/sdk/javascript). The Blueprint SDK is an essential tool for developers working with the TON blockchain. It provides a comprehensive suite of tools and libraries that simplify the process of writing, testing, and deploying smart contracts. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a TON testnet node * [Node.js](https://nodejs.org/en) * A TON wallet, we used Tonekeeper. You can choose one on [ton.org](https://ton.org/wallets?locale=en\&pagination%5Blimit%5D=-1). * Some testnet tokens; get some tokens from the [TON faucet](https://t.me/testgiver_ton_bot). > [!NOTE] > > ### Run nodes on Chainstack > > [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required. You can sign up with your GitHub, X, Google, or Microsoft account. ## Project overview This will be an introductory project where we use the Blueprint SDK to develop, test, and deploy a simple storage-style smart contract where we can save a number and increment a counter. The smart contract is written in the TACT language, TON's smart contract language, and is comparable to Solidity for EVM-based chains. ## Getting started Let's create a new TON project and run the initialization command in a new directory. ```bash Shell theme={"system"} npm create ton@latest ``` Select an empty project in TACT. When you create a new project using the Blueprint SDK, the directory structure is organized to help you efficiently manage your smart contract development process. Here's a brief overview of the main directories and their purposes: * **build/**: This directory contains the compiled artifacts of your smart contracts. After you run the build command, the output files will be stored here, including the compiled contract bytecode and other related artifacts. * **contracts/**: This is where you write your smart contracts. All `.tact` files containing the contract code are placed in this directory. For instance, the `SimpleStorage` contract will reside here. * **scripts/**: This directory is used for deployment and interaction scripts. These scripts facilitate deploying your contracts to the TON blockchain and interacting with them. For example, scripts to deploy the contract or fetch data from the contract will be placed here. * **tests/**: This directory holds the test files for your smart contracts. Here, you can write tests to ensure your contracts behave as expected. The default test script verifies contract deployment, and you can extend it to test additional functionalities. * **wrappers/**: This directory contains the TypeScript wrappers generated for your contracts. These wrappers provide a convenient way to interact with your contracts in a type-safe manner from your scripts and tests. ## Contract development Now, we are ready to start developing the contract. You'll find a `.tact` contract in the `contracts` directory, paste the following code in it. ```sol simple_contract.tact theme={"system"} import "@stdlib/deploy"; // Allows the contract to receive a custom object of type "Save" specifying the number to save in the contract. message Save { amount: Int as uint32; } // This is an example of a simple storage contract. It has a function that increments the saved number by one when the function is called. contract SimpleContract with Deployable { // Declare variables // Variables structure: name: type id: Int as uint32; // This is an ID for contract deployment savedNumber: Int as uint32; counter: Int as uint32; // init is similar to a contructor in Solidity init(id: Int) { // Init the id assed from the contructor self.id = id; // Initialize the variables to zero when the contract is deployed self.savedNumber = 0; self.counter = 0; } // TON contracts can recevie messages // This function makes an action when a specific message is recevied // In this case, when the contract recevies the message "add 1" will increment the counter variable by 1 receive("add 1"){ self.counter = self.counter + 1; } // This allows the contract to recevie objects, in this case of type "Save" // Save a value in the contract receive(msg: Save){ self.savedNumber = msg.amount; } // Getter function to read the variable get fun Number(): Int { // Int is the type of value returned return self.savedNumber; } // Getter function to read the counter variable get fun Counter(): Int { // Int is the type of value returned return self.counter; } // Getter function for the ID get fun Id(): Int { return self.id; } } ``` Here is a breakdown of how the code works. ### Breakdown of the Contract 1. **Imports and Message Declaration** * The contract imports necessary modules using `import "@stdlib/deploy";`. * A custom message type `Save` is declared, which contains an `amount` field of type `Int` (aliased as `uint32`). ```tact tact theme={"system"} import "@stdlib/deploy"; message Save { amount: Int as uint32; } ``` 2. **Contract Declaration** * The `SimpleContract` is declared with the `Deployable` trait, allowing it to be deployed on the blockchain. * The contract contains three variables: `id`, `savedNumber`, and `counter`, all of type `Int` (aliased as `uint32`). ```tact tact theme={"system"} contract SimpleContract with Deployable { id: Int as uint32; savedNumber: Int as uint32; counter: Int as uint32; ``` 3. **Initialization** * The `init` function acts as a constructor and initializes the contract with an `id`. We need to give a custom ID because the smart contract address will be determined during deployment based on the code and initial status. * The variables `savedNumber` and `counter` are set to zero upon deployment. ```tact tact theme={"system"} init(id: Int) { self.id = id; self.savedNumber = 0; self.counter = 0; } ``` 4. **Message Handlers** * The contract can receive messages to perform specific actions. * The `receive("add 1")` handler increments the `counter` by 1 when it receives the message "add 1". ```tact tact theme={"system"} receive("add 1") { self.counter = self.counter + 1; } ``` * The `receive(msg: Save)` handler allows the contract to receive an object of type `Save` and store the `amount` in `savedNumber`. ```tact tact theme={"system"} receive(msg: Save) { self.savedNumber = msg.amount; } ``` 5. **Getter Functions** * The contract includes getter functions to retrieve the values of `savedNumber`, `counter`, and `id`. ```tact tact theme={"system"} get fun Number(): Int { return self.savedNumber; } get fun Counter(): Int { return self.counter; } get fun Id(): Int { return self.id; } ``` The `SimpleContract` is a straightforward example of a storage contract on the TON blockchain. It demonstrates basic functionalities such as initializing variables, handling messages to perform specific actions, and providing getter functions to retrieve stored values. In the next sections, we will build, test, deploy, and interact with this contract using the Blueprint SDK. ## Build the contract Once we have the contract, we can run the build command to compile it and ensure no error. ```bash Shell theme={"system"} npx blueprint build ``` It will compile and build the contract ```bash Shell theme={"system"} Using file: SimpleContract Build script running, compiling SimpleContract ⏳ Compiling... > 👀 Enabling debug > SimpleContract: tact compiler > SimpleContract: func compiler > SimpleContract: fift decompiler > Packaging > SimpleContract > Bindings > SimpleContract > Reports > SimpleContract ⚠️ Make sure to disable debug mode in contract wrappers before doing production deployments! Compiled successfully! Cell BOC result: { "hash": "8bb0916eb10debd617ebaba79be7097cc21e41597dc940d16af521dbed9dad25", "hashBase64": "i7CRbrEN69YX66unm+cJfMIeQVl9yUDRavUh2+2drSU=", "hex": "b5ee9c724102110100029f000114ff00f4a413f4bcf2c80b0102016202070298d001d0d3030171b0a301fa400120d74981010bbaf2e08820d70b0a208104ffbaf2d0898309baf2e088545053036f04f86102f862db3c59db3cf2e082c8f84301cc7f01ca000101cb1fc9ed54090301f6eda2edfb0192307fe07021d749c21f953020d70b1fde208210946a98b6ba8ea830d31f018210946a98b6baf2e081d33f0131c8018210aff90f5758cb1fcb3fc9f84201706ddb3c7fe0c0008e2af90182eb0d7299a100d9de4cf674453aeb6aa320067fc00702b62aa29243621d23217dba94a47fdb31e09130e27004013a6d6d226eb3995b206ef2d0806f22019132e2102470030480425023db3c0501cac87101ca01500701ca007001ca02500520d74981010bbaf2e08820d70b0a208104ffbaf2d0898309baf2e088cf165003fa027001ca68236eb3917f93246eb3e2973333017001ca00e30d216eb39c7f01ca0001206ef2d08001cc95317001ca00e2c901fb000600987f01ca00c87001ca007001ca00246eb39d7f01ca0004206ef2d0805004cc9634037001ca00e2246eb39d7f01ca0004206ef2d0805004cc9634037001ca00e27001ca00027f01ca0002c958cc020120080c020fbd10c6d9e6d9e18c090b013ced44d0d401f863d2000194d31f0131e030f828d70b0a8309baf2e089db3c0a0002700002200201200d0e00b9bbbd182705cec3d5d2cae7b1e84ec39d64a851b6682709dd6352d2b647cb322d3af2dfdf1623982702055c01b80676394ce583aae4725b2c382701bd49def954596f1c753d3de0559c32682709d974e5ab34ecb733a0e966d9466e8a480201480f100011b0afbb5134348000600075b26ee3435697066733a2f2f516d576165594177773744717159335651704e5136456232414146466d67416346323365383955655a7947327764820ab944fa3" } Wrote compilation artifact to build/SimpleContract.compiled.json ``` ## Test the contract Once the contract compiles, we can test it; the test file is in the `tests` directory. The default test script verifies that the contract deploys correctly. Let's edit it to also check that the counter and the save features work. Paste the following code. ```typescript SimpleContract.spec.ts theme={"system"} import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox'; import { toNano } from '@ton/core'; import { SimpleContract } from '../wrappers/SimpleContract'; import '@ton/test-utils'; // On TON we can test by creating a virtual chain describe('SimpleContract', () => { let blockchain: Blockchain; // Init a virtual chain let deployer: SandboxContract; let simpleContract: SandboxContract; // Init the smart contract instance const contractId = 1648n; // Id for deployment that will be passed in the contructor. Random value in this example beforeEach(async () => { blockchain = await Blockchain.create(); simpleContract = blockchain.openContract(await SimpleContract.fromInit(contractId)); // Init the deployer. It comes with 1M TON tokens deployer = await blockchain.treasury('deployer'); const deployResult = await simpleContract.send( deployer.getSender(), { value: toNano('0.05'), // Value to send to the contract }, { $$type: 'Deploy', // This because the contract inherits the Deployable trait. queryId: 0n, }, ); // Here is the test. In this case it tests that the contract is deployed correctly. expect(deployResult.transactions).toHaveTransaction({ from: deployer.address, to: simpleContract.address, deploy: true, success: true, }); }); it('should deploy', async () => { // the check is done inside beforeEach // blockchain and simpleContract are ready to use console.log('Deploying contract...'); const conttactId = await simpleContract.getId(); console.log(`Fetched ID during deployment: ${conttactId}`); }); it('should increase', async () => { console.log('Testing increase by 1 function...'); const counterBefore = await simpleContract.getCounter(); console.log('counterBefore - ', counterBefore); await simpleContract.send( deployer.getSender(), { value: toNano('0.02'), }, 'add 1', // The message the contract expects ); const counterAfter = await simpleContract.getCounter(); console.log('counterAfter - ', counterAfter); // Check it incremented the value expect(counterBefore).toBeLessThan(counterAfter); }); it('should save the amount', async () => { console.log('Testing increase by given value function...'); const numeberBefore = await simpleContract.getNumber(); const amount = 10n; console.log(`Value to save: ${amount}`); console.log(`Number saved before: ${numeberBefore}`); await simpleContract.send( deployer.getSender(), { value: toNano('0.02'), }, { $$type: 'Save', // This time it's an object and not just text amount: amount, }, ); const numberAfter = await simpleContract.getNumber(); console.log(`Number saved after: ${numberAfter}`); }); }); ``` ### Breakdown of the Test File Here is a quick breakdown, the test and interaction scripts are written in TypeScript. The idea is that the test file spins up a virtual chain to run the tests on with `let blockchain: Blockchain; // Init a virtual chain`. 1. **Imports** * Import necessary modules and utilities from the TON Sandbox, core libraries, and the `SimpleContract` wrapper. ```typescript TypeScript theme={"system"} import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox'; import { toNano } from '@ton/core'; import { SimpleContract } from '../wrappers/SimpleContract'; import '@ton/test-utils'; ``` 2. **Describe Block** * Define the test suite for the `SimpleContract`. Inside the `describe` block, we initialize variables for the blockchain, deployer, and contract instances. ```typescript TypeScript theme={"system"} describe('SimpleContract', () => { let blockchain: Blockchain; // Init a virtual chain let deployer: SandboxContract; let simpleContract: SandboxContract; // Init the smart contract instance const contractId = 1648n; // Id for deployment that will be passed in the constructor. Random value in this example ``` 3. **beforeEach Hook** * This hook runs before each test. It sets up the blockchain environment, initializes the contract, and deploys it using a deployer with 1M TON tokens available. The deployment is then verified to ensure the contract is deployed successfully. ```typescript TypeScript theme={"system"} beforeEach(async () => { blockchain = await Blockchain.create(); simpleContract = blockchain.openContract(await SimpleContract.fromInit(contractId)); // Init the deployer. It comes with 1M TON tokens deployer = await blockchain.treasury('deployer'); const deployResult = await simpleContract.send( deployer.getSender(), { value: toNano('0.05'), // Value to send to the contract }, { $$type: 'Deploy', // This because the contract inherits the Deployable trait. queryId: 0n, }, ); // Here is the test. In this case it tests that the contract is deployed correctly. expect(deployResult.transactions).toHaveTransaction({ from: deployer.address, to: simpleContract.address, deploy: true, success: true, }); }); ``` 4. **Test: should deploy** * This test checks if the contract is deployed correctly by fetching the contract ID. The actual deployment check is handled in the `beforeEach` hook. ```typescript TypeScript theme={"system"} it('should deploy', async () => { // the check is done inside beforeEach // blockchain and simpleContract are ready to use console.log('Deploying contract...'); const contractId = await simpleContract.getId(); console.log(`Fetched ID during deployment: ${contractId}`); }); ``` 5. **Test: should increase** * This test verifies the functionality of the `add 1` message. It retrieves the counter value before and after sending the message and checks if it has increased. ```typescript TypeScript theme={"system"} it('should increase', async () => { console.log('Testing increase by 1 function...'); const counterBefore = await simpleContract.getCounter(); console.log('counterBefore - ', counterBefore); await simpleContract.send( deployer.getSender(), { value: toNano('0.02'), }, 'add 1', // The message the contract expects ); const counterAfter = await simpleContract.getCounter(); console.log('counterAfter - ', counterAfter); // Check it incremented the value expect(counterBefore).toBeLessThan(counterAfter); }); ``` 6. **Test: should save the amount** * This test checks the functionality of saving a specified amount in the contract. It sends a `Save` message and verifies if the `savedNumber` variable is updated correctly. Run the test with the test command. ```bash Shell theme={"system"} npx blueprint test ``` ## Deploy to the TON chain The Blueprint SDK allows you to deploy contracts to the mainnet or testenet and it provides endpoints out of the box, but in this case we want to use the Chainstack endpoint we deployed since performs better and it's more reliable. We can add it in a configuration file, in the project's root create a new file named `blueprint.config.ts` and paste the code. ```typescript blueprint.config.ts theme={"system"} import { Config } from '@ton/blueprint'; export const config: Config = { network: { endpoint: 'YOUR_CHAINSTACK_ENDPOINT', type: 'testnet', version: 'v4', //key: 'YOUR_API_KEY', }, }; ``` Now that the custom endpoint is configured, edit the deploy script in`scripts` to include the contract ID; in this case, the ID is a random value, but you might change it based on the resulting address that will give you. ```ts deploySimpleContract.ts theme={"system"} import { toNano } from '@ton/core'; import { SimpleContract } from '../wrappers/SimpleContract'; import { NetworkProvider } from '@ton/blueprint'; export async function run(provider: NetworkProvider) { // Edit this ID const contractId = 1648n; const simpleContract = provider.open(await SimpleContract.fromInit(contractId)); await simpleContract.send( provider.sender(), { value: toNano('0.5'), }, { $$type: 'Deploy', queryId: 0n, }, ); // Deploy contract await provider.waitForDeploy(simpleContract.address); console.log(`Deployed at address ${simpleContract.address}`); // run methods on `simpleContract` } ``` Blueprint allows various deployment options; in this case, we'll use the CLI and the run command directly. > [!NOTE] > > Find more methods in the [Blueprint SDK docs](https://github.com/ton-org/blueprint?tab=readme-ov-file#deploy-one-of-the-contracts). First, add environment variables from the terminal if you want to use the mnemonic phrase to use your wallet; you can also use Tonkeeper and the app from your phone (more secure). ```bash Shell theme={"system"} export WALLET_MNEMONIC="" export WALLET_VERSION="v4" ``` Run the `run` command and follow the instructions, we used the mnemonic deployment in this case. ```bash Shell theme={"system"} npx blueprint run ``` Example result: ```bash Shell theme={"system"} Using file: deploySimpleContract ? Which network do you want to use? testnet ? Which wallet are you using? Mnemonic Connected to wallet at address: EQDrNXDLYKstXHj5xV6_md1nYvvrb6y6v4bFyTZReZ-vFYdx Sent transaction Contract deployed at address EQDVoYZ96Gtc-nQM0U4-rj0mporVOTlSpmB64Tn6HJax98VN You can view it at https://testnet.tonscan.org/address/EQDVoYZ96Gtc-nQM0U4-rj0mporVOTlSpmB64Tn6HJax98VN Deployed at address EQDVoYZ96Gtc-nQM0U4-rj0mporVOTlSpmB64Tn6HJax98VN ``` ## Interact with the contract Read data from the contract. Make a new file in the `scripts` direcotry named `getCounter.ts`: ```ts ts theme={"system"} import { SimpleContract } from '../wrappers/SimpleContract'; import { NetworkProvider } from '@ton/blueprint'; export async function run(provider: NetworkProvider) { const contractId = 1648n; // Random in this case const simpleContract = provider.open(await SimpleContract.fromInit(contractId)); const id = await simpleContract.getId(); const savedNumber = await simpleContract.getNumber(); const counter = await simpleContract.getCounter(); console.log(`Fethching smart contract data...`); console.log(`Contract ID: ${id}`); console.log(`Current saved number: ${savedNumber}`); console.log(`Current counter: ${counter}`); } ``` Run it with the same `run` command and follow the instructions: ```bash Shell theme={"system"} npx blueprint run ``` Result: ```bash Shell theme={"system"} ? Choose file to use ? Choose file to use getCounter ? Which network do you want to use? ? Which network do you want to use? testnet ? Which wallet are you using? ? Which wallet are you using? Mnemonic Connected to wallet at address: EQDrNXDLYKstXHj5xV6_md1nYvvrb6y6v4bFyTZReZ-vFYdx Fethching smart contract data... Contract ID: 1648 Current counter: 0 ``` Now use the wallet to send a transaction, including the message "Save" and some TON token to save the value. Make a new script in `scripts` named `addValue`: ```ts ts theme={"system"} import { toNano } from '@ton/core'; import { SimpleContract } from '../wrappers/SimpleContract'; import { NetworkProvider } from '@ton/blueprint'; export async function run(provider: NetworkProvider) { const contractId = 1648n; const simpleContract = provider.open(await SimpleContract.fromInit(contractId)); const id = await simpleContract.getId(); const counter = await simpleContract.getNumber(); console.log(`Sending increasing value...`); console.log(`Contract ID: ${id}`); console.log(`Current counter: ${counter}`); // Call the Add function and add 7 await simpleContract.send(provider.sender(), { value: toNano('0.02') }, { $$type: 'Save', amount: 7n }); } ``` Follow the same process with the `run` command, then once the transaction is validated, you can run the get script to fetch the updated value. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Global Node Source: https://docs.chainstack.com/docs/global-elastic-node Deploy Global Nodes with Chainstack for high-performance blockchain access. Get elastic scaling, global distribution, and optimized RPC endpoints worldwide. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). ## What is a Global Node? Global Node is a service providing a load-balanced node that routes requests to the closest available location for a specific protocol, based on the caller's location. This ensures efficient service access for users worldwide by routing requests in an optimized manner. The main advantages of Global Nodes are the following: * Enhanced load balancing — Global Nodes include a large load balancer that can switch nodes if one fails or falls behind the chain head, thus ensuring uninterrupted service. * Reduced latency — by distributing traffic to the nearest endpoint, Global Node reduces latency, leading to faster transactions and improved user experience. * Global reach — Global Nodes can be accessed by anyone from any location in the world. They direct users to the endpoints nearest to their location, which maximizes the availability and responsiveness of services. * High availability — Global Nodes are designed for high availability, ensuring that your DApp continues to run with minimal interruptions. * Instant deployment — unlike regional Trader Nodes, which take 3-6 minutes to deploy, Global Node is ready in under a second. > [!NOTE] > > Global Nodes support [Warp transactions](/docs/warp-transactions) for high-speed transaction propagation on Ethereum, Solana, and BNB Smart Chain networks. # Gnosis Chain Source: https://docs.chainstack.com/docs/gnosis-chain Complete reference of supported Gnosis Chain JSON-RPC methods on Chainstack, including eth, debug, trace, and net namespace availability details. See also [interactive Gnosis Chain API call examples](/reference/gnosis-getting-started). ## Execution layer | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blobBaseFee | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | txpool\_content | | | | txpool\_inspect | | | | txpool\_contentFrom | | | | txpool\_status | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_getTrieFlushInterval | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | trace\_block | | | | trace\_call | | | | trace\_callMany | | | | trace\_filter | | | | trace\_rawTransaction | | | | trace\_replayBlockTransactions | | | | trace\_replayTransaction | | | | trace\_transaction | | | | parity\_clearEngineSigner | | | | parity\_enode | | | | parity\_getBlockReceipts | | | | parity\_netPeers | | | | parity\_pendingTransactions | | | | parity\_setEngineSigner | | | | parity\_setEngineSignerSecret | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | ## Consensus layer (Beacon Chain) | Method | Availability | Comment | | - | - | - | | /eth/v1/beacon/blocks/\{block\_id}/attestations | | | | /eth/v1/beacon/blocks/\{block\_id}/root | | | | /eth/v1/beacon/blob\_sidecars/\{\{block\_id}} | | | | /eth/v1/beacon/genesis | | | | /eth/v1/beacon/headers | | | | /eth/v1/beacon/headers/\{block\_id} | | | | /eth/v1/beacon/states/\{state\_id}/committees | | | | /eth/v1/beacon/states/\{state\_id}/finality\_checkpoints | | | | /eth/v1/beacon/states/\{state\_id}/fork | | | | /eth/v1/beacon/states/\{state\_id}/root | | | | /eth/v1/beacon/states/\{state\_id}/sync\_committees | | | | /eth/v1/beacon/states/\{state\_id}/validator\_balances | | | | /eth/v1/beacon/states/\{state\_id}/validators | | | | /eth/v1/beacon/states/\{state\_id}/validators/\{validator\_id} | | | | /eth/v1/beacon/rewards/sync\_committee/\{block\_id} | | | | /eth/v1/beacon/rewards/blocks/\{block\_id} | | | | /eth/v1/beacon/rewards/attestations/\{epoch} | | | | /eth/v1/config/deposit\_contract | | | | /eth/v1/config/spec | | | | /eth/v1/events | | | | /eth/v1/node/peer\_count | | | | /eth/v1/node/peers | | | | /eth/v1/node/syncing | | | | /eth/v1/node/version | | | | /eth/v1/validator/aggregate\_attestation | | | | /eth/v1/validator/blinded\_blocks/\{slot} | | | | /eth/v1/validator/duties/attester/\{epoch} | | | | /eth/v1/validator/duties/proposer/\{epoch} | | | | /eth/v1/validator/sync\_committee\_contribution | | | | /eth/v2/beacon/blocks/\{block\_id} | | | | /eth/v2/debug/beacon/states/\{state\_id} | | | # Gnosis Chain tooling Source: https://docs.chainstack.com/docs/gnosis-tooling Set up MetaMask, Hardhat, Foundry, and development tools to build and deploy smart contracts on Gnosis Chain through your Chainstack RPC node endpoints. Get started with a [reliable Gnosis Chain RPC endpoint](https://chainstack.com/build-better-with-gnosis-chain/) to use the tools below. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Gnosis Chain nodes. **Step 1** Install [Hardhat](https://hardhat.org/) and create a project. **Step 2** Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract **Step 3** Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: **Step 1** Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](/docs/gnosis-tooling#metamask). **Step 2** In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Gnosis Chain nodes deployed with Chainstack. **Step 1** Install [web3.py](https://web3py.readthedocs.io/). **Step 2** Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Gnosis Chain nodes deployed with Chainstack. **Step 1** Install [web3.php](https://github.com/web3p/web3.php). **Step 2** Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password **Step 3** Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Php theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Gnosis Chain nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint * USERNAME — your node access username * PASSWORD — your node access password See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Gnosis Chain nodes deployed with Chainstack. **Step 1** Install [ethers.js](https://www.npmjs.com/package/ethers). **Step 2** Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Gnosis Chain network ID: * Mainnet: `100` * Testnet: `10200` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — Gnosis Chain network ID: * Mainnet: `100` * Testnet: `10200` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Brownie **Step 1** Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). **Step 2** Use the `brownie networks add` command with the node endpoint: ```shell Shell theme={"system"} brownie networks add Gnosis ID name="NETWORK_NAME" host= YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `chainstack-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password * NETWORK\_ID — Gnosis Chain network ID: * Mainnet: `100` * Testnet: `10200` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ## Foundry **Step 1** Install [Foundry](https://getfoundry.sh). **Step 2** Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Gnosis: Soulbound token with Remix and OpenZeppelin Source: https://docs.chainstack.com/docs/gnosis-tutorial-simple-soulbound-token-with-remix-and-openzeppelin Create and deploy a non-transferable soulbound ERC-721 token on Gnosis Chain Chiado testnet using Remix, OpenZeppelin, and a Chainstack RPC node. **TLDR:** * Soulbound tokens are non-transferable ERC-721s, enforced via a transfer override, so they can only be minted or burned. * This tutorial covers creating and deploying a soulbound token contract on the Gnosis Chain Chiado testnet through a Chainstack node. * You’ll use Remix for development, MetaMask for signing, and Blockscout to verify and interact with the contract. * Minting a token assigns it to an address, and that address alone can burn it, ensuring the token never changes ownership. ## Main article Soulbound tokens, [originally proposed by Vitalik Buterin](https://vitalik.ca/general/2022/01/26/soulbound.html), at their core are simply non-transferable NFTs. In this tutorial, you will: * Create an ERC-721 contract that has a transfer override to make the token soulbound. * Deploy the contract on the Gnosis Chain Chiado testnet through a node deployed with Chainstack. * Interact with the deployed contract. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Gnosis Chain RPC endpoint](https://chainstack.com/build-better-with-gnosis-chain/). * [Remix IDE](https://remix.ethereum.org/) to compile the contract and deploy through MetaMask. * [MetaMask](https://metamask.io/) to deploy the contract through your Chainstack node and interact with the contract. ## Overview To get from zero to a deployed soulbound token contract on the Gnosis Chain Chiado testnet, do the following: 1. With Chainstack, create a public chain project (A public chain project- a project to join public networks). 2. With Chainstack, join Gnosis Chain Chiado testnet. 3. With Chainstack, access your Gnosis Chain node credentials. 4. Set up your MetaMask to work through a Chainstack node. 5. With Remix IDE, create and compile the soulbound contract. 6. With Remix IDE, deploy the contract on the Gnosis Chain Chiado testnet. 7. Issue a soulbound token and burn it. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Gnosis Chiado testnet See [Join a public network](/docs/manage-your-networks). ### Get your Gnosis Chain node access and credentials See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Set up MetaMask See [Gnosis Chain tooling: MetaMask](/docs/gnosis-tooling#metamask). ### Create and compile the soulbound contract 1. Open [Remix IDE](https://remix.ethereum.org/). 2. On the home page, click **Environments** > **Solidity**. 3. On the left pane, click **File explorers** > **contracts** > **New File**. 4. In the modal, give any name to your contract. For example, `soulbound.sol`. 5. Put in the contract code: ```solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract SoulBoundToken is ERC721, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; constructor() ERC721("SoulBoundToken", "SBT") {} function safeMint(address to) public onlyOwner { uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); } function burn(uint256 tokenId) external { require(ownerOf(tokenId) == msg.sender, "Only the owner of the token can burn it."); _burn(tokenId); } function _beforeTokenTransfer(address from, address to, uint256) pure override internal { require(from == address(0) || to == address(0), "This a Soulbound token. It cannot be transferred. It can only be burned by the token owner."); } function _burn(uint256 tokenId) internal override(ERC721) { super._burn(tokenId); } } ``` This is your soulbound token contract: * It uses the audited OpenZeppelin libraries to make the contract of the ERC-721 standard, belonging to the deployer, and increments each issued token ID by 1. * The contract has a modification to prohibit the token transfer, which makes the issued tokens soulbound. * The contract also implements a burn function to allow the owner of the issued token to be able to burn it. 6. Compile the contract. On the left pane, click **Solidity compiler** > **Compile**. ### Fund your account Fund the account that you will use to deploy the contract with xDAI. Use the [xDAI testnet faucet](https://gnosisfaucet.com/). ### Set up Remix IDE to work through your Chainstack node On the left pane, click **Deploy** and switch to **Injected Provider - MetaMask**. ### Deploy the soulbound contract On the left pane: 1. Click **Deploy & run transactions**. 2. In contract, select **contracts/soulbound.sol**. 3. Click **Deploy**. This will engage your MetaMask to deploy the contract to the Gnosis Chain Chiado testnet through your currently selected MetaMask account. Click **Confirm** in the MetaMask modal. ### Interact with the contract Once your contract is deployed, you can view it online at [Blockscout Gnosis Chain Chiado testnet explorer](https://blockscout.com/gnosis/chiado). You are now going to verify the contract in the Blockscout explorer to be able to use the explorer as a web app and easily interact with the contract online. ### Flatten your contract code Since your soulbound contract uses imported OpenZeppelin libraries, you must put all the imports into one `.sol` file to make Blockscout be able to verify it. 1. In your Remix IDE, click **Plugin manager** > **Flattener** > **Activate**. 2. Click **Flattener** > **Flatten contracts/soulbound.sol**. The flattened contract is now in your clipboard. ### Verify the deployed contract on Blockscout explorer 1. Go to [Blockscout explorer](https://blockscout.com/gnosis/chiado). 2. Find your deployed contract. The address of your contract is on the left pane of Remix IDE under Deployed Contracts. 3. On the contract page on Blockscout, click **Code** > **Verify & Publish**. 4. Select **Via flattened source code**. 5. In **Contract Name**, provide the name of your contract. In our example, the name is `SoulBoundToken`. 6. In **Compiler**, select the same compiler version that was used in Remix IDE. 7. In **Optimization**, select **No**. 8. In **Enter the Solidity Contract Code**, paste the flattened contract code. 9. Click **Verify & publish**. Blockscout will take a few seconds to compile your contract, verify, and publish it. ### Issue a soulbound token Now that your soulbound contract is verified, you can check Blockscout to interact with it. 1. On Blockscout, on your contract, click **Write Contract**. 2. In your MetaMask, make sure you have the same address selected as the one that deployed the contract. 3. Click **Connect wallet**. This will connect your MetaMask instance with the contract owner as the active address. 4. In **safeMint**, provide an address that you own and to which you will issue a soulbound token. 5. Click **Write**. This will issue a soulbound token to the provided address. ### Burn the soulbound token Now that your other account has a soulbound token, you can burn it. In your MetaMask instance, switch to the account that has a soulbound token tied to it. 1. On Blockscout, on your contract, click **Write Contract**. 2. In your MetaMask, make sure you have the address selected that owns the issued soulbound token. 3. Click **Connect wallet**. This will connect your MetaMask instance with the token owner as the active address. 4. In **burn**, provide the token ID. If this is the first issued token, the ID is `0`. 5. Click **Write**. This will send the soulbound token from the current owner to the address `0x0000000000000000000000000000000000000000`. ## Conclusion This tutorial guided you through the basics of creating and deploying a simple soulbound contract on the Gnosis Chain Chiado testnet through your Chainstack-deployed node. You have also interacted with the contract, issued, and burned the token using Blockscout as a web app and MetaMask as your interaction tool that works through your Chainstack-deployed Gnosis Chain node. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Get the most out of Chainstack platform API Source: https://docs.chainstack.com/docs/guide-get-the-most-out-of-the-chainstack-platform-api Best practices for using the Chainstack platform API, including key management, error handling, rate limiting, and automation with code examples. **TLDR** * Generate and rotate API keys regularly, and safeguard them as environment variables to reduce unauthorized access risks. * Always consult the Chainstack API reference for endpoint details, request/response formats, and error codes. * Implement robust error handling (status codes, response parsing, retry logic) to deal with network or server issues gracefully. * Securely handle sensitive data (private keys, tokens) by encrypting storage, masking logs, and restricting access. * Thoroughly test API integrations and keep tabs on performance and error rates to maintain reliability in production. ## Introduction The Chainstack API offers you a comprehensive set of tools, making it easier to manage your organization, projects, networks, nodes, and identities programmatically. For a secure and efficient integration with the Chainstack platform, it's good to follow some best practices. This article will give you some top tips for using the Chainstack API effectively. ## Create and safeguard your API keys To use the Chainstack API, you'll need an API key to authenticate your requests. These keys are like access passes to your organization's resources, so they hold a lot of power. To keep your account and data secure, here's what you need to do: * Get into the habit of rotating your API keys. Just like changing your passwords regularly, it's important to update your API keys on a routine basis. This practice is a solid defense strategy that helps to lower the risk of unauthorized access. If your key accidentally leaks, it won't be of much use to anyone if it's already been changed. Learn how to [generate a new API key](/reference/platform-api-getting-started#create-api-key). * Treat your API keys like a secret treasure. Your API keys are precious, so handle them with care. Embedding them directly into your codebase or sharing them in plaintext is like leaving your house key under the doormat—not the best idea. Instead, consider using environment variables or a secure key management system. > [!NOTE] > > Read our [Guide to environment variables](/docs/how-to-store-your-web3-dapp-secrets-guide-to-environment-variables) to have more details. ## Take advantage of the API reference To get the most out of the Chainstack API, it's a good idea to keep the official API reference documentation handy. This guide is like your API encyclopedia, packed with details about each API operation, including the parameters you'll need, the responses you can expect, and some handy examples. Here's what you can do with the API reference: * **Understand endpoint functionality**. The API reference is your go-to guide for understanding what each endpoint does. It's like a map, showing you which API endpoints to use for different tasks, whether you're creating projects, managing networks, or chatting with blockchain nodes. * **Learn request and response structures**. The API reference is your textbook for learning about the structure and formatting of API requests and responses. It tells you what parameters you need, their data types, and any other options or constraints. Knowing how to construct valid requests and handle the data you get back is key to being an API pro. * **Explore available query parameters**. Sometimes, the Chainstack API lets you use query parameters to tweak the behavior of certain operations. The API reference is like your instruction manual, explaining what query parameters are available and what they do. This way, you can fine-tune your requests to get exactly what you want. * **Decode error codes and messages**. The API reference also has a section on the error codes and messages you might run into while using the Chainstack API. It's like your troubleshooting guide, helping you understand what went wrong and how to fix it or work around it. It's essential to regularly refer to the API reference, as it's consistently updated to mirror any modifications or advancements in the Chainstack API. By leaning on the official documentation, you can stay in the loop and current, guaranteeing your integration with the Chainstack API remains precise, streamlined, and in sync with the newest features and enhancements. Utilizing the insights from the API reference empowers you to make educated decisions, craft well-structured API requests, manage responses aptly, and troubleshoot any challenges that might surface during the integration journey. > [!NOTE] > > Here you can find the [Chainstack platform API reference](https://api.chainstack.com/reference/). ## Understand and leverage API endpoints The Chainstack API offers a range of endpoints to manage different aspects of your blockchain infrastructure. Familiarize yourself with these endpoints and understand their purpose to leverage the full potential of the Chainstack platform. Key areas to explore include: * **Organization** — API endpoints for managing your organization's settings, billing, and user roles. * **Projects** — APIs to create, configure, and interact with blockchain projects. * **Networks** — endpoints to manage the networks within your projects, such as creating, connecting, and modifying networks. * **Nodes** — APIs for managing blockchain nodes, including creation, configuration, and monitoring. * **Identities** — endpoints to manage cryptographic identities, including key pair generation and certificate issuance. ### Examples of API calls Here you will find a collection of examples in JavaScript and Python to familiarize yourself with the Chainstack platform API. #### API key authentication The Chainstack API relies on API keys to authenticate requests. To provide your API key, include it in the `Authorization` header. The header's value should comprise the `Bearer` prefix followed by the secret key generated through the platform's user interface. Here's an example using curl: ```bash cURL theme={"system"} curl -X GET 'https://api.chainstack.com/v1/organization/' \ --header 'Authorization: Bearer YOUR_API_KEY' ``` > [!WARNING] > > Note that all API requests must be made over HTTPS for security reasons. #### API calls using JavaScript This example demonstrates how you can engage with the Chainstack platform API using node.js and the Axios library. It's a practical illustration of how to communicate with the API using code. > [!NOTE] > > Note that this example employs the `dotenv` package to load sensitive data from environment variables. This is an effective strategy to ensure the security of your secret keys, for example: > > `BEARER_TOKEN=”YOUR_API_KEY"` Make sure to install `axios` and `dotenv` before running the code: ```shell Shell theme={"system"} npm i axios dotenv ``` ```js JavaScript theme={"system"} require('dotenv').config(); const axios = require('axios'); async function getOrganization() { try { const response = await axios.get('https://api.chainstack.com/v1/organization/', { headers: { 'Authorization': `Bearer ${process.env.BEARER_TOKEN}` } }); console.log(response.data); } catch (error) { console.error(error); } } getOrganization(); ``` This particular example uses the **Get Organization name and ID** endpoint to fetch information about the organization. It's a straightforward way to incorporate API calls into your routine tasks. #### API calls using Python This example demonstrates how you can interact with the Chainstack platform API using Python and the requests library. It's a practical illustration of how to communicate with the API using Python code. > [!NOTE] > > Note that this example uses the `python-dotenv` package to load sensitive data from environment variables. This is an effective strategy to ensure the security of your secret keys. Make sure to install `requests` and `python-dotenv` before running the code: ```shell Shell theme={"system"} pip install requests python-dotenv ``` ```python Python theme={"system"} import os from dotenv import load_dotenv import requests load_dotenv() def get_nodes(): try: response = requests.get( 'https://api.chainstack.com/v1/nodes/', headers={'Authorization': f'Bearer {os.getenv("BEARER_TOKEN")}'} ) response.raise_for_status() print(response.json()) except requests.exceptions.RequestException as err: print(f"An error occurred: {err}") get_nodes() ``` This specific example calls the **List all Nodes** endpoint to fetch data about the nodes deployed by your organization. This makes it easy to introduce API calls into your Python workflow. ## Best practices for error handling in API requests When interacting with APIs, it's crucial to have robust error-handling mechanisms in place. This allows you to manage any errors or exceptions that might crop up gracefully. Here are some practices to consider: ### 1: Handle HTTP status codes Always check for and handle HTTP status codes returned by the API. These codes give you a heads-up about the type of response you're dealing with. For instance, a 200 status code means your request was successful, a 400 indicates a client error, and 500 points to a server error. ### 2: Analyze error responses Make sure to parse and scrutinize error responses returned by the API. The Chainstack API typically dishes out informative error messages that can help you get to the bottom of the issue. ### 3: Implement retry logic Consider setting up retry logic for certain types of errors, like network timeouts or temporary server issues. But remember, the API has rate limits. So, ensure your retry mechanism is designed to handle these limits appropriately. ## Securely manage sensitive information In your interactions with the Chainstack API, you'll likely handle sensitive information such as private keys, certificates, or access tokens. It's crucial to handle this data with utmost care to uphold a high level of security. Here are some practices to consider: * **Be cautious with logs and error messages**. It's easy to inadvertently expose sensitive data in logs or error messages. Always double-check what you're logging and avoid including sensitive information. This is especially important in debug logs, which can often be overlooked. * **Encrypt and safeguard stored data**. Any sensitive data stored on disk or in databases should be encrypted and protected. This adds an extra layer of security and ensures that even if someone gains unauthorized access to the storage, they won't be able to read the data without the encryption key. * **Use secure connections**. When interacting with the Chainstack API, always use secure connections (HTTPS). This encrypts the data in transit, preventing anyone from intercepting and reading your data as it travels over the network. * **Regularly review access control policies**. Access control policies determine who can access your sensitive data. Regularly reviewing and updating these policies ensures that only authorized individuals have access. This is particularly important in dynamic environments where roles and responsibilities can change frequently. By following these practices, you can significantly reduce the risk of a security breach and ensure that your sensitive data remains secure. ## Test and monitor API integrations Before deploying your application to production, thoroughly test your integration with the Chainstack API. Test various scenarios and edge cases to validate the correctness and robustness of your implementation. Additionally, implement monitoring mechanisms to track API usage, performance metrics, and error rates. This allows you to proactively identify and address any issues that may arise. ## Conclusion By following these best practices, you can effectively leverage the Chainstack API to manage your organization's blockchain infrastructure securely and efficiently. Remember to create and secure your API keys, familiarize yourself with the available API endpoints, refer to the API reference documentation, implement proper error handling, securely manage sensitive information, and thoroughly test and monitor your API integrations. Following these guidelines will help you build reliable and robust applications that integrate seamlessly with the Chainstack platform. # Handle real-time data with WebSockets in JS & Python Source: https://docs.chainstack.com/docs/handle-real-time-data-using-websockets-with-javascript-and-python Connect to Chainstack WebSocket endpoints for real-time blockchain data streaming using web3.js, ethers.js, and Python with auto-reconnect logic. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR** * Demonstrates how to connect to the Chainstack API over WebSockets for real-time blockchain data using web3.js, ethers.js, and Python. * Explains the advantages of WebSocket vs. HTTP (bidirectional, persistent connection). * Includes examples of implementing auto-reconnect logic to handle unexpected disconnections or timeouts. * Highlights the typical connection limits and the importance of retry mechanisms to maintain an uninterrupted data stream. ## Main article The Chainstack API simplifies access to your EVM node through either HTTPS or WebSocket connections. This tutorial illustrates how to utilize a WebSocket connection to retrieve real-time data employing web3.js and ethers.js, as well as Python. It also explains how to incorporate WebSocket reconnect logic to ensure that the connection is automatically reestablished if it happens to drop for any reason. ## Difference between HTTP and WebSocket protocols **HTTP** (hypertext transfer protocol) operates on a unidirectional basis, whereas **WebSocket** offers bidirectional communication. In the traditional client-server model using HTTP, each request made by the client initiates a new connection. This connection is then closed once the client receives a response from the server. This cyclical process of establishing and terminating connections can be inefficient and resource-intensive, especially for applications requiring real-time updates or frequent data exchanges. > For example, how your browser requests data to the server to display this article. On the contrary, WebSocket enhances the client-server communication process by initiating a connection just once, which is then kept open and reused for as long as either the server or the client maintains it. This persistent connection supports a more efficient data exchange between the server and the client without constantly opening and closing connections. This key advantage of WebSocket, connection reusability, is a game-changer for real-time applications. Alongside this, it features a "keep-alive" mechanism, ensuring the connection stays active, reducing the chances of timeouts or connection drops. This constant open channel allows for seamless data transfer, promoting faster, more dynamic interactions. ## HTTP and WebSocket connection limitations ### HTTP * 60 seconds timeout for the idle connection, so the connection will be terminated if idle for 60 seconds. * Each connection supports a maximum of 1,000 requests. After serving these 1,000 requests over a single HTTP connection, the current connection is concluded, and a fresh connection is established for subsequent requests. ### WebSocket * 3,600 seconds (1 hour) timeout for the idle connection, so the connection will be terminated if idle for 3,600 seconds, equivalent to 1 hour. * A maximum of 500 concurrent connections can be maintained over WebSocket. ## Managing WebSocket reconnections While WebSockets maintain a persistent, two-way connection, preparing for scenarios where the connection might be interrupted is crucial. The examples in this guide all integrate a WebSocket reconnection logic designed to automatically reestablish the connection should it become unexpectedly terminated. ### Common WebSocket error codes Below are some of the common codes and keywords that indicate the WebSocket was disconnected and you need to implement a reconnect logic: * Code `1006` (abnormal closure) * `ECONNRESET` > [!NOTE] > > Implement WebSocket reconnect logic or implement a [dedicated gateway](/docs/trader-node#dedicated-gateways). ## Retrieve real-time data with WebSocket The Chainstack API allows you to use subscriptions to retrieve real-time data about blocks, pending transactions, and logs. Leveraging subscriptions, you can keep your application informed with recent developments, ensuring an accurate and timely representation of the blockchain state. Learn more about subscriptions in the [API documentation](/reference/ethereum-web3js-subscriptions-methods). ### Real-time data with WebSocket and web3.js The web3.js library integrates subscription functionalities, enabling you to get real-time data from the blockchain effortlessly. > [!NOTE] > > Learn how to set up the best [node.js environment](/docs/web3-nodejs-from-zero-to-a-full-fledged-project) for your JavaScript applications. First, install the web3.js library by running the following command: ```shell Shell theme={"system"} npm i web3 ``` The following example demonstrates using the web3.js library to receive real-time block headers, including WebSocket reconnect logic. ```javascript Javascript theme={"system"} const { Web3 } = require("web3"); const NODE_URL = "YOUR_CHAINSTACK_WSS_ENDPOINT"; // Reconnect options const reconnectOptions = { autoReconnect: true, // Automatically attempt to reconnect delay: 5000, // Reconnect after 5 seconds maxAttempts: 10, // Max number of retries }; const web3 = new Web3( new Web3.providers.WebsocketProvider(NODE_URL, undefined, reconnectOptions) ); async function subscribeToNewBlocks() { try { // Create a new subscription to the 'newBlockHeaders' event const event = "newBlockHeaders"; const subscription = await web3.eth.subscribe(event); // Changed to 'newHeads' console.log(`Connected to ${event}, Subscription ID: ${subscription.id}`); // Attach event listeners to the subscription object for 'data' and 'error' subscription.on("data", handleNewBlock); subscription.on("error", handleError); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } // Fallback functions to react to the different events // Event listener that logs the received block header data function handleNewBlock(blockHeader) { console.log("New block header:", blockHeader); } // Event listener that logs any errors that occur function handleError(error) { console.error("Error when subscribing to new block header:", error); } subscribeToNewBlocks(); ``` > [!NOTE] > > Learn more about the [web3.js `newBlockHeaders` subscription](/reference/ethereum-subscribenewblockheaders). ### Real-time data with WebSocket and ethers.js Install the ethers.js and ws libraries: ```shell Shell theme={"system"} npm i ethers ws ``` The following is an example of establishing a WebSocket connection using ethers.js, designed to subscribe to new block headers, which also incorporates a mechanism for automatic WebSocket reconnection. ```javascript Javascript theme={"system"} const ethers = require("ethers"); const WebSocket = require("ws"); const NODE_URL = "YOUR_CHAINSTACK_WSS_ENDPOINT"; function createWebSocket() { const ws = new WebSocket(NODE_URL); ws.on("close", () => { console.log("Disconnected. Reconnecting..."); setTimeout(() => { provider = new ethers.WebSocketProvider(createWebSocket()); startListening(); }, 3000); }); ws.on("error", (error) => { console.log("WebSocket error: ", error); }); return ws; } let provider = new ethers.WebSocketProvider(createWebSocket()); function startListening() { provider.on("block", async (blockNumber) => { console.log("New block number:", blockNumber); const block = await provider.getBlock(blockNumber); console.log("Block details:", block); }); } startListening(); ``` ### Real-time data with WebSocket and Python Install the websockets library: ```shell Shell theme={"system"} pip install websockets ``` The following is an example of establishing a WebSocket connection using Python, designed to subscribe to new block headers, incorporating an automatic WebSocket reconnection mechanism. ```python Python theme={"system"} # Import required libraries import asyncio import json import websockets # Replace with your own Ethereum node WebSocket URL eth_node_ws_url = 'YOUR_CHAINSTACK_WSS_ENDPOINT' async def subscribe_to_blocks(ws_url): # Continuously try to connect and subscribe while True: try: # Establish a WebSocket connection to the Ethereum node async with websockets.connect(ws_url) as websocket: # Send a subscription request for the Transfer event logs await websocket.send(json.dumps({ "id": 1, "method": "eth_subscribe", "params": [ "newHeads" ], "jsonrpc": "2.0" })) # Wait for the subscription response and print it subscription_response = await websocket.recv() print(f'Subscription response: {subscription_response}') # Continuously process incoming logs while True: # Receive a log entry and parse it as JSON log = await websocket.recv() log_data = json.loads(log) # Print the log data print(f'New log: {log_data}') print("#"*10) # If there's an exception (e.g., connection error), attempt to reconnect except Exception as e: print(f'Error: {e}') print('Reconnecting...') await asyncio.sleep(5) # Execute the subscription function asyncio.run(subscribe_to_blocks(eth_node_ws_url)) ``` ## Conclusion This guide has walked you through leveraging the power of WebSocket over traditional HTTP, utilizing JavaScript and Python, and interacting with the Chainstack API to retrieve real-time data. We also explored how to implement WebSocket reconnect logic. # Harmony methods Source: https://docs.chainstack.com/docs/harmony-methods Complete reference of supported Harmony JSON-RPC methods on Chainstack nodes, covering hmy, hmyv2, eth, net, and web3 namespace calls. Our pricing is simple: 1 RU per request. Harmony has no archive split — see [Request units](/docs/request-units#full-vs-archive-by-protocol). | Method | Availability | Comment | | - | - | - | | hmy\_getBalanceByBlockNumber | | | | hmy\_getTransactionCount | | | | hmy\_getBalance | | | | hmy\_getFilterLogs | | | | hmy\_newFilter | | | | hmy\_newPendingTransactionFilter | | | | hmy\_newBlockFilter | | | | hmy\_getFilterChanges | | | | hmy\_getLogs | | | | hmy\_getStakingTransactionByBlockHashAndIndex | | | | hmy\_getStakingTransactionByBlockNumberAndIndex | | | | hmy\_getStakingTransactionByHash | | | | hmy\_getCurrentTransactionErrorSink | | | | hmy\_getPendingCrossLinks | | | | hmy\_getPendingCXReceipts | | | | hmy\_getCXReceiptByHash | | | | hmy\_pendingTransactions | | | | hmy\_sendRawStakingTransaction | | | | hmy\_getTransactionsHistory | | | | hmy\_sendRawTransaction | | | | hmy\_getTransactionReceipt | | | | hmy\_getBlockTransactionCountByHash | | | | hmy\_getBlockTransactionCountByNumber | | | | hmy\_getTransactionByHash | | | | hmy\_getTransactionByBlockNumberAndIndex | | | | hmy\_getTransactionByBlockHashAndIndex | | | | hmy\_getBlockByNumber | | | | hmy\_getBlockByHash | | | | hmy\_getBlocks | | | | hmy\_estimateGas | | | | hmy\_getStorageAt | | | | hmy\_call | | | | hmy\_getCode | | | | hmy\_isLastBlock | | | | hmy\_epochLastBlock | | | | hmy\_latestHeader | | | | hmy\_getShardingStructure | | | | hmy\_blockNumber | | | | hmy\_syncing | | | | hmy\_gasPrice | | | | net\_peerCount | | | | hmy\_getEpoch | | | | hmy\_getLeader | | | | hmy\_getCirculatingSupply | | | | hmy\_getTotalSupply | | | | hmy\_getStakingNetworkInfo | | | | hmy\_getAllValidatorInformation | | | | hmy\_getAllValidatorInformationByBlockNumber | | | | hmy\_getCurrentUtilityMetrics | | | | hmy\_getDelegationsByValidator | | | | hmy\_getDelegationsByDelegatorAndValidator | | | | hmy\_getDelegationsByDelegator | | | | hmy\_getValidatorMetrics | | | | hmy\_getMedianRawStakeSnapshot | | | | hmy\_getElectedValidatorAddresses | | | | hmy\_getAllValidatorAddresses | | | | hmy\_getCurrentStakingErrorSink | | | | hmy\_getValidatorInformation | | | | hmy\_getValidators | | | | hmy\_getSignedBlocks | | | | hmy\_isBlockSigner | | | | hmy\_getBlockSigners | | | | trace\_block | | | | trace\_transaction | | | # Harmony tooling Source: https://docs.chainstack.com/docs/harmony-tooling Set up MetaMask, Hardhat, Foundry, and other development tools to build and deploy smart contracts on Harmony through Chainstack node endpoints. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Harmony nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Harmony nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Harmony nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php theme={"system"} where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password ``` 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Harmony nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint * USERNAME — your node access username * PASSWORD — your node access password See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Harmony nodes deployed with Chainstack. **Step 1** Install [ethers.js](https://www.npmjs.com/package/ethers). **Step 2** Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Harmony network ID: * Mainnet Shard 0: `1666600000` * Devnet Shard 0: `1666900000` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — Harmony network ID: * Mainnet Shard 0: `1666600000` * Devnet Shard 0: `1666900000` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Brownie **Step 1** Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). **Step 2** Use the `brownie networks add` command with the node endpoint: ```shell Shell theme={"system"} brownie networks add Harmony ID name="NETWORK_NAME" host= YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `chainstack-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password * NETWORK\_ID — Harmony network ID: * Mainnet Shard 0: `1666600000` * Devnet Shard 0: `1666900000` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ## Foundry **Step 1** Install [Foundry](https://getfoundry.sh/). **Step 2** Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Chainlink oracles: Fetch real-time crypto prices Source: https://docs.chainstack.com/docs/harnessing-chainlink-oracles-with-chainstack-fetching-real-time-crypto-prices-from-ethereum Fetch real-time cryptocurrency prices from Chainlink oracle contracts on Ethereum using web3.js and a Chainstack node, removing reliance on external APIs. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR:** * Set up a node.js script using web3.js to connect to Ethereum via Chainstack endpoints. * Fetch real-time crypto prices through Chainlink’s AggregatorV3Interface contracts, removing reliance on external APIs. * Conversion involves retrieving integer values from the contract and formatting them into human-readable outputs. * Call prices at intervals to keep data updated without single-point-of-failure risks. ## Main article In today's digital age, accessing real-time and reliable cryptocurrency data is crucial for many applications. While many resort to third-party APIs, these can sometimes introduce risks, such as downtime, potential manipulation, or sudden discontinuation of services. Chainlink, a decentralized oracle network, solves this dilemma as a dependable bridge between on-chain and off-chain data. Paired with the robust infrastructure of an Ethereum node hosted by Chainstack, we can further enhance the reliability of our data source. One advantage of this approach is using decentralized exchange (DeX) oracles, which largely eliminates the risk of relying on single-point-of-failure third-party APIs. This ensures uninterrupted access to critical crypto pricing data and fosters a decentralized ethos in our applications. In this guide, we'll demonstrate how to harness the power of Chainlink oracles by using a Chainstack Ethereum node to fetch real-time crypto prices from the network, giving you a resilient and dependable data source. ## Setting the scene For our purpose, we'll use node.js alongside the web3.js library, allowing us to communicate with the Ethereum blockchain. We aim to fetch prices for five cryptocurrencies: BTC, ETH, LINK, BNB, and LTC against USD. > [!NOTE] > > Check out [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project) to learn how to set up a node.js project. ### Prerequisites * [node.js](https://nodejs.org/en/download) library * web3.js library * Contract addresses of pairs you’re interested in * A Chainstack Ethereum node ### Get an Ethereum node Follow these steps to deploy an Ethereum node: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Install web3.js Create a new project in a directory and open a terminal, then run: ```shell Shell theme={"system"} npm i web3 ``` ### The code In the same directory, create a new file named `index.js` and paste the following code: ```javascript index.js theme={"system"} const { Web3 } = require("web3"); const web3 = new Web3( "YOUR_CHAINSTACK_ENDPOINT" ); // Object with the smart contracts for the pairs const pairs = { "BTC / USD": "0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c", "ETH / USD": "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419", "LINK / USD": "0x2c1d072e956AFFC0D435Cb7AC38EF18d24d9127c", "BNB / USD": "0x14e613AC84a31f709eadbdF89C6CC390fDc9540A", "LTC / USD": "0x6AF09DF7563C363B5763b9102712EbeD3b9e859B", }; // aggregatorV3Interface ABI const aggregatorV3InterfaceABI = [ { inputs: [], name: "decimals", outputs: [{ internalType: "uint8", name: "", type: "uint8" }], stateMutability: "view", type: "function", }, { inputs: [], name: "description", outputs: [{ internalType: "string", name: "", type: "string" }], stateMutability: "view", type: "function", }, { inputs: [{ internalType: "uint80", name: "_roundId", type: "uint80" }], name: "getRoundData", outputs: [ { internalType: "uint80", name: "roundId", type: "uint80" }, { internalType: "int256", name: "answer", type: "int256" }, { internalType: "uint256", name: "startedAt", type: "uint256" }, { internalType: "uint256", name: "updatedAt", type: "uint256" }, { internalType: "uint80", name: "answeredInRound", type: "uint80" }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "latestRoundData", outputs: [ { internalType: "uint80", name: "roundId", type: "uint80" }, { internalType: "int256", name: "answer", type: "int256" }, { internalType: "uint256", name: "startedAt", type: "uint256" }, { internalType: "uint256", name: "updatedAt", type: "uint256" }, { internalType: "uint80", name: "answeredInRound", type: "uint80" }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "version", outputs: [{ internalType: "uint256", name: "", type: "uint256" }], stateMutability: "view", type: "function", }, ]; let conversionRate = {}; async function fetchPrices() { try { for (let pair in pairs) { // Smart contract instance const priceFeed = new web3.eth.Contract( aggregatorV3InterfaceABI, pairs[pair] ); // use eth_call const roundData = await priceFeed.methods.latestRoundData().call(); // Chainlink returns price data with 8 decimal places for accuracy. // We divide by 1e8 to convert it to a human-readable format. const price = Number(roundData.answer) / 1e8; conversionRate[pair] = price.toFixed(2); } console.log("Prices fetched:", conversionRate); } catch (error) { console.error("Error fetching prices:", error); } } // Fetch prices initially and then at regular intervals fetchPrices(); setInterval(fetchPrices, 60 * 1000); // Fetch every minute ``` Add your Chainstack Ethereum endpoint to `web3`, save and run `node index` in the terminal; the console will display an object with the most up-to-date prices. ```shell Shell theme={"system"} Prices fetched: { 'BTC / USD': '29385.26', 'ETH / USD': '1848.97', 'LINK / USD': '7.51', 'BNB / USD': '240.95', 'LTC / USD': '83.65' } ``` ### Understanding the code The script starts by importing the `web3` library and creating an instance to an Ethereum node, then the bulk of the logic is as follows: 1. **Specify the Chainlink price feeds**. Ethereum smart contracts represent Chainlink's decentralized price feeds. Each cryptocurrency price feed has a unique contract address. Our script stores these addresses in the `pairs` dictionary with the cryptocurrency pair name as the key. You can find additional price feed contract addresses on Chainlink’s [Price Feed Contract Addresses](https://docs.chain.link/data-feeds/price-feeds/addresses). 2. **Specify the Chainlink price feed contract addresses**. Chainlink's decentralized price feeds are represented through smart contracts. Each price feed for a specific cryptocurrency pair is associated with a unique contract address. In our script, these addresses are stored in a dictionary named `pairs`, where the key is the name of the cryptocurrency pair. Refer to Chainlink's [Price Feed Contract Addresses](https://docs.chain.link/data-feeds/price-feeds/addresses) page for additional price feed contract addresses. 3. **The aggregatorV3Interface ABI**. We add the `aggregatorV3Interface` ABI required to interact with the smart contract. The ABI outlines the functions in the smart contract so the library knows how to use them. 4. **Interact with Chainlink price feeds**. Chainlink price feeds are implemented using the AggregatorV3Interface, which provides several functions for data interaction. Among these functions, `latestRoundData` is the one we use to fetch the latest price information. For a complete reference to the functions and capabilities of the AggregatorV3Interface, you can consult Chainlink's [API Reference](https://docs.chain.link/data-feeds/api-reference) page. 5. **Fetching the Prices**. In our `fetchPrices` function, we iterate through the specified pairs, and for each pair: * Create an instance of the Chainlink price feed contract using the web3.js library. * Call the `latestRoundData` function to retrieve the most recent price data. * Convert the obtained price to a readable format (divide by `1e8` and round to two decimal places) and store it in our `conversionRate` dictionary. * 6. **Continual data refresh**. To ensure we continually have up-to-date price information, we call our `fetchPrices` function every minute using JavaScript's `setInterval` function. ## Conclusion Following the above steps, you've successfully built a robust and lightweight script to fetch real-time cryptocurrency prices directly from the Ethereum blockchain, eliminating the need for third-party APIs or services. With this approach, you ensure data accuracy and gain unparalleled control over how you fetch and manage this data. Happy coding and trading! ### About the author > [card] Edin Drazevicanin > > ![Edin Drazevicanin](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/mudOE4f.jpeg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=3ba2d85603139a2e46ef0de230402b3f) > > Technical Support Engineer @ Chainstack > > [https://github.com/0x6564](https://github.com/0x6564) > > [https://twitter.com/edeenn22](https://twitter.com/edeenn22) > > [https://www.linkedin.com/in/edindr/](https://www.linkedin.com/in/edindr/) # Web3 dApp secrets: Environment variables guide Source: https://docs.chainstack.com/docs/how-to-store-your-web3-dapp-secrets-guide-to-environment-variables Protect private keys, API tokens, and RPC endpoints in Web3 dApps using environment variables, backend proxies, and secret management services. **TLDR:** * This article explores the importance of securely handling environment variables (e.g., private keys, access tokens, RPC endpoints) in Web3 DApps to prevent leaks and malicious attacks. * It demonstrates why storing secrets on the front end is unsafe and how a back-end proxy or specialized service (e.g., Dotenv Vault) adds a protective layer. * It covers common practices (like .env files, whitelisting, and rate limiting), as well as enterprise-level approaches (secret manager tools) for robust security. * By following these methods, developers can safeguard their DApp’s integrity and user trust across development, staging, and production environments. ## Main article As a DApp developer, it is essential to ensure that sensitive information such as private keys, access tokens, and node endpoint URLs are properly safeguarded to prevent unauthorized access. Typically, such information is called a “secret” or an “environment variable”, and there is an extensive list of possible approaches for its application. This guide will explore the various methods for storing environment variable secrets and the best practices for implementing them in your DApp. So, by the end of the guide, you will have a solid understanding of how to securely store environment variable secrets for your Web3 DApp, whether it is in development, staging, or production setting. And considering just how rampant poor security practices for environment variables are in the industry, this guide is not only nice to have, but an essential reading material for many Web3 BUIDLers out there, regardless of their level. That being said, let’s dig into the details of DApp secrets and environment variables. ## What are environment variables? Environment variables are values that can be passed to a computer's operating system or an application at runtime. These values can be used to configure the behavior of the operating system or application, and they are often used to store sensitive information such as passwords and private keys. Environment variables are typically set in a script or configuration file and can be accessed by the operating system or application using predefined methods. In a blockchain context, a Web3 DApp may use an environment variable to specify the account’s private key that it should use to sign transactions or the network that it should be deployed. This allows Web3 developers to change the behavior of the DApp without having to make changes to its code base. This prevents sensitive information from being hard-coded into the DApp's code, which could be potentially accessed by unauthorized parties. ## Avoid front-end storage for environment variables When running an app that sends requests to an API and includes a front end, it can be easy and convenient to store the secret keys directly in the front end, but they are also very easy to expose and exploit this way. This can create a multitude of issues for both you and your users, ranging from data leaks and unauthorized access all the way to the dreaded denial of service (DDoS) attack vector. ### Example of API keys stored in the front end Check out this [simple web app](https://unprotected-front-end.vercel.app/) we built and deployed to showcase such a scenario. The app uses the Etherscan API to retrieve the latest block number on the Ethereum network. Open the browser DevTools to inspect the Network tab, then click the **Latest block** button to fetch the latest block on the Ethereum chain, you will see the request in the **Network** tab, and you can easily find the API request. Don’t worry; there is no actual secret in this case, as the Etherscan API allows you to make a limited number of requests without an API key. > [!NOTE] > > ### Hint > > Right-click → Inspect → Network ### What are the consequences of storing API keys in the front end? For several reasons, it is important to avoid using exposed environment variables in a Web3 DApp. Firstly, exposing environment variables can potentially allow unauthorized parties to access sensitive information such as private keys and access tokens. This can lead to preventable security risks that compromise the overall security of your DApp and make it vulnerable to bad actors. Secondly, exposing environment variables can facilitate attackers in reverse engineering your DApp and potentially gain access to sensitive information or assets. Lastly, exposing environment variables can also help attackers to impersonate your DApp and carry out malicious actions. Overall, it is best to avoid using exposed environment variables. Instead, use secure methods to store and access sensitive information with your Web3 DApp. Let’s have a look at some of the most popular methods to do just that securely in the following paragraphs. ## How to store environment variables securely? There are several ways to store environment variables securely in a Web3 DApp. In this guide, we will explore a few methods based on use cases. 1. Development, testing, and learning environment via an environment or configuration file The first method involves storing environment variables in a separate configuration file or environment file, which can be accessed by the DApp at runtime. This file can be encrypted and password-protected to prevent unauthorized access. Typically this is done using the [dotenv](https://github.com/motdotla/dotenv) package and a `.env` file. This is a very good solution for testing or educational environments where you intend to push the app to a remote repository like GitHub, and no front end is involved. 2. Custom solution for storing secrets in a secure database via a backend server A way to store environment variables when using a front end is in a secure database via a backend server, which serves as a proxy. This method allows for fine-grained control over access to sensitive information. It can provide additional security measures such as encryption and access control, but it is also more complex to build and maintain. 3. Professional/enterprise level via a secret manager tool Lastly, there is an extensive list of tools available that can be used to manage and securely store environment variables for a Web3 DApp. Some of the more popular options are [Dotenv Vault](https://www.dotenv.org/), [Microsoft Azure Key Vault](https://azure.microsoft.com/en-in/products/key-vault/), [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/), [Google Cloud Secret Manager](https://cloud.google.com/secret-manager), [HashiCorp Vault](https://www.vaultproject.io/), and [Doppler](https://www.doppler.com/). These tools often provide features such as encryption and access control to ensure that sensitive information is properly safeguarded. Overall, the best approach for securely storing environment variables in a Web3 DApp will depend on the specific needs and requirements of your project. It is recommended to carefully evaluate the different options and choose the best approach for your use case’s security and compliance requirements. ## Local and development environments The dotenv package allows you to use `.env` files to store secrets. Before platform and SaaS alternatives made it into the spotlight, developers used a `.env` file that is not committed to a public repo to keep all their keys and secrets there. While the approach is still used today, it is primarily recommended for use in local and development environments only. This is so because exposing your secrets is just one “commit” away from being exposed, and anyone with access to the `.env` file basically has all the keys to your kingdom. Using a `.env` file helps you mitigate the risk of exposing your API keys, but you still need to be careful when pushing code to a remote repository. To have peace of mind, make sure to include a `.gitignore` file in your local repository. A `.gitignore` holds a list of directories and files that you want to “ignore” when pushing code to the version control platform, for example, GitHub. Use a [gitignore generator](https://mrkandreev.name/snippets/gitignore-generator/#Node) to make sure you cover all of the files and directories potentially holding sensitive data. Example of a `.gitignore` file: ```sh .gitignore theme={"system"} // Dependency directories node_modules/ jspm_packages/ // dotenv environment variables files .env .env.test .env.production ``` Typically, this approach is best suited for small teams since it gets difficult to keep members of a large team in sync with just a `.env` file. You can also use the `dotenv` package to work with `.env` files easily. All you have to do is add a `require` reference for its config method as early as possible in your code. Afterward, you can always fetch a particular secret with a `process.env` call of the key, whose value you want to get like so. ### How to use the dotenv package Here’s what you need to do to replicate this for your environment: 1. [Download and install node.js](https://nodejs.org/) if you don't have it already 2. Navigate to your project's root folder using the CLI 3. Install the dotenv package from your CLI via npm: ```bash theme={"system"} npm install dotenv ``` 4. Create a `.env` file and enter your secrets in the appropriate format: ```sh theme={"system"} CHAINSTACK_NODE_URL="https://ethereum-mainnet.core.chainstack.com/YOUR_AUTH_TOKEN" ``` 5. Enter this as early as possible in your DApp's JavaScript file to load the package once it runs: ```js theme={"system"} require('dotenv').config(); ``` 6. Fetch your DApp secret like so: ```js theme={"system"} const secret = process.env.CHAINSTACK_NODE_URL; ``` 7. Confirm your secret is loading correctly in your script by logging the result in the console: ```js theme={"system"} // If secret is declared console.log(secret); // If the secret is not declared console.log(process.env.YOUR_KEY); ``` 8. Run your script and check if the result is correct: ```bash theme={"system"} node index.js ``` ```bash theme={"system"} // Script response YOUR_VALUE ``` ### Full script example The best way to learn is through practice, so let's try to replicate the script in the video above step-by-step. Following is an example of what a `.env` file looks like, how you can create one and import it in your script, as well as use its content anywhere throughout your JavaScript code. For this particular case, we will use two secret key-value pairs that closely resemble those you would typically find in a real-world Web3 DApp context, even if it is a rather simple one. First, create a `.env` file in your project root and add your “secrets” to it: ```sh .env theme={"system"} ENDPOINT_URL='https://ethereum-mainnet.core.chainstack.com/YOUR_AUTH_TOKEN' ADDRESS='0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5' ``` Then use the environment variables in JavaScript: ```js index.js theme={"system"} require('dotenv').config(); const endpoint = process.env.ENDPOINT_URL; const address = process.env.ADDRESS; console.log(`Environment variables in use: \n ${endpoint} \n ${address}`); ``` [This repository](https://github.com/soos3d/Deploy-an-ERC-20-token-on-Scroll-using-Hardhat) is a good example of a simple project set up in a development environment that uses `.env` and `.gitignore` files to protect sensitive data. If you look into the `.gitignore` file, you will see a list of files and directories that are not pushed to the remote repository, and you will not find a `.env` file committed along with other files. To use this project, you need to create your own `.env` file after you clone the repository. ### Approach overview * Good for testing/small projects/educational content * Not ideal for big teams * Does not properly protect API keys When deploying an app, the API keys will need to be stored in the (public) repo as a `.env` file, for example, as is the case for [Vercel](https://vercel.com/) and [Heroku](https://vercel.com/). Alternatively, you can enter your production secrets within the platforms to give you a sense of security, but once they are pushed to the (public) repo, the keys will be visible to anyone. ## Secure front-end usage via custom proxy When deploying an app with a front end, an option to protect your [REST API](https://www.ibm.com/cloud/learn/rest-apis) keys or [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) endpoints is to handle the request using a back-end proxy server. A proxy server is a computer that sits between a device and the internet. In this case, when you access a website or online service, the request goes through the proxy server. The proxy server then sends the request to the website or service on your behalf and passes the response back to you. Such a concept is really useful for this use case because you can have your front end send a request to the back-end proxy, which then passes it to the actual API or endpoint. This way, the front end never communicates directly with the API and endpoints, so you don’t need to store the secrets there. There are several ways to do this, so in this section, you will learn how to build a simple proxy server to protect REST API keys and RPC endpoints. You will find two examples further: 1. A simple web app that uses the Etherscan API to retrieve the latest block number 2. Another simple web app that sends a request to an Ethereum endpoint to retrieve an account balance ### Build a proxy server to protect REST API keys For this example, we built a simple app that uses the Etherscan API to retrieve the latest block from the Ethereum network and then display it on the screen. You can find the source code and how to test it in its [GitHub repository here](https://github.com/chainstacklabs/express-rest-api-proxy-server). ### The REST API proxy server In this example, we use the [express.js framework](https://expressjs.com/) to build a simple server that we can use as a proxy to communicate with the Etherscan API. Express.js is a web application framework for node.js designed for building web applications and APIs. It provides a set of features and tools for building web applications, including routing, middleware, and template engines. In the repository, the `index.js` file in the root directory holds the server’s code, and as you can see, is very straightforward: ```js index.js theme={"system"} // This is the server file. const express = require('express') const cors = require('cors') const rateLimit = require('express-rate-limit') require('dotenv').config() const PORT = process.env.PORT || 3000 const app = express() // Rate limiting, limit the number of requests a user can send within a specific amount of time. // With this setup, the user can only make 100 request max every 10 minutes. const limiter = rateLimit({ WindowMs: 10 * 60 * 1000, // 10 minutes in ms. max: 100 // 100 request max. }) app.use(limiter) app.set('trust proxy', 1) // Set static folder; this allows our server to pick up the HTML file in the src folder. app.use(express.static('src')) // Routes // This route looks into the index.js file in the routes folder and picks up the '/' route. app.use('/api', require('./routes')) // Enable cors app.use(cors()) app.listen(PORT, () => console.log(`Server running on port ${PORT}`)) ``` This server runs on the port you specify in the `.env` file of the project or port 3000 if you don’t specify it explicitly. It then creates a route to the `index.js` file inside the routes directory, where the URL is built to then send a [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) request to the Etherscan API. Here's the route file: ```js index.js theme={"system"} const express = require('express') const router = express.Router() const needle = require('needle') // You could use 'node-fetch' too, but it might have some conflicts. // Env variables, taken from the .env file. const ETHERSCAN_API_BASE_URL = process.env.ETHERSCAN_API_BASE_URL const ETHERSCAN_API_KEY_NAME = process.env.ETHERSCAN_API_KEY_NAME const ETHERSCAN_API_KEY_VALUE = process.env.ETHERSCAN_API_KEY_VALUE // Route from the server file. router.get('/', async (req, res) => { try { // URLSearchParams allows us to build the URL const apiKey = new URLSearchParams({ [ETHERSCAN_API_KEY_NAME]: ETHERSCAN_API_KEY_VALUE, }) // Build the full API URL using URLSearchParams const fullUrl = `${ETHERSCAN_API_BASE_URL}&${apiKey}` // Send the request to the Etherscan API, and retrieve the JSON body response. const apiResponse = await needle('get', fullUrl) const data = apiResponse.body res.status(200).json(data) } catch (error) { res.status(500).json({ error }) } }) module.exports = router ``` This simple solution helps protect your API keys but is not bulletproof. A skilled bad actor could still find where the request is sent to, and although they could not extract your secret API keys, they could still flood the servers with requests. This could severely slow down the service you offer, drive up your costs in case of usage costs, and even prevent access to it entirely in a denial of service (DDoS) attack vector. To mitigate these issues, we included a rate limiter and enabled [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (CORS) in the server file. The rate limiter is customizable and set up only to allow 100 requests every 10 minutes. ```js index.js theme={"system"} const limiter = rateLimit({ WindowMs: 10 * 60 * 1000, // 10 minutes in ms. max: 100 // 100 request max. }) app.use(limiter) ``` CORS, instead, is a mechanism that allows a web server to serve resources to a web page from a different domain than the one it originated from. This allows web pages to access resources from APIs or other servers hosted on a different domain. It allows the server to specify which domains are allowed to request it. It is important for security because it helps prevent malicious websites from making unauthorized requests to servers on behalf of the user and helps to protect against [cross-site scripting](https://owasp.org/www-community/attacks/xss/) (XSS) attacks and other types of malicious activity. ## What is whitelisting Whitelisting is a relatively common practice to protect systems and networks, and it’s a security measure that maintains a list of approved applications or domains that are allowed to run on a system, network, or device while blocking all others. CORS, which we mentioned earlier, is an example of this; it provides a way to specify the domains that are allowed to access restricted resources on a web page from an external domain outside of your own. This approach provides a higher level of security than other security measures, such as blacklisting, which blocks known malicious applications or software. With whitelisting, even if an unknown or new threat arises, it cannot run on the system or network because it is not on the approved whitelist. Implementing whitelisting is highly recommended when designing your application, and many providers offer such options. ### Whitelisting limitations While whitelisting is a valuable practice that should be included in your DApp design, it's not foolproof and can still be exploited. In the Web3 world, whitelisting is commonly implemented by allowing only specific domains or IP addresses to send requests to a server or RPC endpoint. This can add a layer of protection, but it shouldn't be the only thing you rely on to secure your RPC endpoint—particularly if it's exposed on the front end. The main attacks whitelists are susceptible to are **distributed denial of service** (DDoS) and **spoofing**. #### DDoS attacks Distributed Denial of Service (DDoS) attacks overwhelm targeted systems or networks with massive amounts of traffic from numerous sources, leading to increased resource usage and potentially making them inaccessible to legitimate users. You might wonder how this issue arises when domains aren't on the whitelist. When a request is sent to an endpoint, the server receives and responds to it regardless of your authorization status. If you have permission, the server will process your request and provide the relevant information. If access is denied, the server will return an error message. Unfortunately, this process can be exploited to consume server resources during a DDoS attack. Consider the following response examples from whitelisted endpoints: ```Json JSON theme={"system"} {"jsonrpc":"2.0","error":{"code":-32002,"message":"rejected due to project ID settings"}} {"jsonrpc":"2.0","error":{"code":0,"message":"not authorized"},"id":null} ``` As you can see, the endpoint responds even though we are not allowed to use it, and during a DDoS attack, this might even cause the node to go out of sync and be unusable. #### Spoofing attacks In a spoofing attack, a bad actor pretends to be someone or something trustworthy to sneak into systems, steal private information, or mess up communication between people. The attacker changes or hides information like IP addresses, email addresses, or website links to trick users or systems into thinking they're interacting with a real, safe source. In our specific case, [IP spoofing](https://www.cloudflare.com/learning/ddos/glossary/ip-spoofing/) and bypassing CORS policies are possible exploitations. During an **IP spoofing attack**, the attacker changes the source IP address in the packets they send to make it seem like they are coming from a trusted source. This can be used to get around network security measures, launch DDoS attacks, or deceive users into giving away sensitive information. In this case, this could lead to getting around the IP whitelist, and the attacker could flood your endpoint with requests consuming your resources and driving up your costs; a severe DDoS attack could even bring your service down. **Cross-origin resource sharing** it’s a way to whitelist domains so that only the allowed ones can request restricted web resources. This approach is also vulnerable to attacks as the CORS policy might be misconfigured or taken advantage of. The following is a list of precautions that should be followed: 1. Properly configure the `Access-Control-Allow-Origin` header. Ensure that sensitive information is not exposed by specifying the origin in the `Access-Control-Allow-Origin` header. This should be set to a trusted domain rather than using a wildcard (`*`) or `null` value. 2. Only allow trusted sites. The origin specified in the `Access-Control-Allow-Origin` header should only be sites that are trusted. Avoid reflecting origins from cross-origin requests without proper validation, as this is easily exploitable. 3. Avoid using null. Avoid using the `Access-Control-Allow-Origin: null` header, as cross-origin resource calls from internal documents and sandboxed requests can specify the null origin. CORS headers should be properly defined in respect of trusted origins for private and public servers. 4. Avoid wildcards in internal networks. Avoid using wildcards in internal networks, as trusting network configuration alone to protect internal resources is not sufficient when internal browsers can access untrusted external domains. 5. Use proper server-side security policies. Remember that CORS is not a substitute for server-side protection of sensitive data. An attacker can directly forge a request from any trusted origin. Therefore, web servers should continue to apply protections over sensitive data, such as authentication and session management, in addition to properly configured CORS. By taking these steps, web developers and administrators can prevent common CORS-based attacks and better protect sensitive data. > [!NOTE] > > To learn more, see [Exploiting CORS – How to Pentest Cross-Origin Resource Sharing Vulnerabilities](https://www.freecodecamp.org/news/exploiting-cors-guide-to-pentesting/#exploitable-cors-cases) from freeCodeCamp. ## Implement security measures Designing a truly secure DApp is not easy, and you should take a comprehensive approach to security from the very beginning as it’s difficult to implement major changes once the project scales. This is why you will often see even big and famous projects with this kind of vulnerability. To avoid this, you need to take a comprehensive approach to security, using the strategies we talked about and integrating with third-party tools like [Cloudflare](https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/) and [NGINX](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) reverse proxy services. These tools are especially helpful for big companies who want to make their DApps as safe as possible. > [!NOTE] > > In addition to third-party tools, consider following the [Secure Software Development Lifecycle](https://owasp.org/www-project-integration-standards/writeups/owasp_in_sdlc/) and the [OWASP Top 10 web application security risks](https://owasp.org/www-project-top-ten/). ### Connect front end and proxy server Now that we understand how the back end works let’s see how it connects to the front end. In our small project, this is done by the `script.js` file in the `src` directory. ```js script.js theme={"system"} async function fetchBlock() { let displayBlock = document.getElementById("block-number") // Fetch the latest block number querying the Etherscan API const response = await fetch("/api") // Place the JSON response in a constant and log it const blockHex = await response.json() console.log(`The latest block in HEX is: ${blockHex.result}`) // Extract the HEX block value from the JSON const blockString = JSON.stringify(blockHex.result) //console.log(blockString) // Slice the HEX value to remove 0x const slicedBlock = blockString.slice(3, -1) //console.log(slicedBlock) // Convert and log the decimal block number const blockDecimal = parseInt(slicedBlock, 16) console.log(`The latest block number is: ${blockDecimal}`) displayBlock.innerHTML = blockDecimal } ``` As you can see, this script handles the action coming from the HTML. In this case, when the user presses the button, the script sends a request to our backend API, parses the response, and displays it in the HTML. Check out the [repository](https://github.com/chainstacklabs/express-proxy-server)—you can clone it and run the app locally. This will give you a very good idea of how the logic works. If you inspect the source code, you will only see that the request is sent to the API, but you won’t find any secret. To use this concept on a deployed app, you’ll have to deploy your back end separately and modify the `script.js` file to send the request to that address. ### Build a proxy server to protect an RPC endpoint The principle is the same for this scenario; the substantial difference is that, in this case, we have to build the body of the POST request to send to the endpoint. The following function handles this part: ```js index.js theme={"system"} async function fetchBalance() { // Proxy server URL // localhost when ran locally, you will need to add your server URL once deployed. const url = 'http://localhost:4000/balance'; // Initialize the HTML element where the balance will be displayed in the front end let displayBalance = document.getElementById("balance") // The Ethereum address to query picked up from the front end const address = document.getElementById("address").value console.log(`Address to query: ${address}`) // Send a POST request to the proxy server to query the balance fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ address }) }) .then(response => response.json()) .then(data => { // Handle the response data console.log(`The balance is: ${data.balance} ETH`); // Slice the response to only show the first 6 numbers const slicedBalance = data.balance.slice(0, 7) // Display the balance in the front end displayBalance.innerHTML = `Balance: ${slicedBalance} ETH` }) .catch(error => { // Handle any errors console.error(error); }); } ``` Note that we specify the URL to the proxy server in `const url = 'http://localhost:4000/balance';`. In this case, the server is running on the same machine, but you’ll have to add the address where you’ll deploy the server in case. Then we build the POST request’s body. The response is then cleaned up a bit to display only the first six digits. The same side effects apply to this concept, so implementing a rate limiter and CORS is again a good solution to avoid abuses. You can find the source code and how to test it in its [GitHub repository here](https://github.com/chainstacklabs/express-proxy-server). ### How to connect the front end with the back end So far, we've discussed building the back end, but let's now focus on integrating it with the front end. In the [`script.js`](https://github.com/soos3d/node-proxy-server-to-protect-your-rpc-url-endpoint/blob/main/src/script.js) file from the example showing how to protect an RPC endpoint, you'll notice a connection to the back end via a URL like this: `http://localhost:4000/YOUR_ROUTE`. This is because the server was initially tested in a local environment. To make this work on the internet, follow these steps: 1. Create a separate repository for the server. 2. Add the server files to the new repository. 3. Deploy the server using a platform like Digital Ocean. 4. Set the environment variables in the deployment platform. 5. Replace `http://localhost:4000/` in the front-end files with the URL of your deployed server. By completing these steps, you'll successfully connect the front end to the back end, allowing your application to work online. ## Production, CI/CD, and enterprise application Now that we have explored some custom ways, let’s see how you could protect your secrets at an enterprise level. For a cloud-based deployment in production, it is recommended to use a secure service like an online password/secret manager to safely handle your API keys and endpoints. They also allow you to separate your environment variables by permission levels, thus allowing your whole team to access its credentials from a single source. For this guide, we will take a closer look at the [Dotenv Vault secret manager](https://www.dotenv.org/), which is incredibly user-friendly. It is a perfect way for you to make the transition from development to production, both swiftly and easily, while still offering the exceptional security features you would need for a Web3 DApp in production. ### Dotenv Vault sync anywhere Dotenv Vault effectively utilizes the same approach as `.git` and GitHub when handling your DApp secrets. This means you can use it as a private repo for everything you normally store in a `.env` file. Once you have set everything up in the Dotenv Vault interface, you can use the pull function to instantly sync it locally to any device you want via your CLI: ```sh CLI theme={"system"} // CLI // Connect to your Dotenv Vault project npx dotenv-vault new INSERT_YOUR_VAULT_ID_HERE_OR_LEAVE_BLANK_TO_CREATE_NEW // Log into your Dotenv Vault project npx dotenv-vault login // Open your Dotenv Vault project to introduce changes npx dotenv-vault open // Pull your Dotenv Vault project .env file to local storage npx dotenv-vault pull ``` Even if you are just using Dotenv Vault for local file sync, you can also leverage its powerful feature set for your DApp. One of these is the ability to have different values for each key according to the environmental context you are currently using. Thanks to this, you can quickly and easily move from `development` to `production` environment variables—all you need to do is pull the file for the appropriate environment: ```sh CLI theme={"system"} // CLI // Pull the development .env file npx dotenv-vault pull development // Pull the staging .env file npx dotenv-vault pull staging // Pull the ci .env file npx dotenv-vault pull ci // Pull the production .env file npx dotenv-vault pull production ``` While Dotenv Vault’s sync anywhere feature is great already, it offers exceptionally easy integration with pretty much any platform. This means you can use it natively within your CI/CD workflow without any critical information hitting your HDD. And the best thing about it? Dotenv Vault uses first-party storage for all relevant data, so there is less risk of external partner leaks, as is the case for all other alternatives. With [Dotenv Vault’s Integrate Everywhere™](https://www.dotenv.org/docs/tutorials/integrations) approach, you can do encrypted fetching of your environment variables completely in memory. Thanks to this priceless feature, you can rest easy knowing that exposed hardcoded variables and any security risks that originate from them are pretty much a thing of the past. To use this method, you follow the same procedure as you would during a remote-to-local `.env` file sync. Once you have the `.env` file locally, you will need to build it and then fetch its decryption key. With the key safely in your possession, it can be entered into [GitHub Actions](https://docs.github.com/en/actions) ⇒ Secrets, as in this example, or any other CI/CD platform like [Heroku](https://www.heroku.com/), [CircleCI](https://circleci.com/), [Netlify](https://www.netlify.com/), and [Vercel](https://vercel.com/), among many others. The same goes for other cloud build platforms like [AWS](https://aws.amazon.com/), [Google Cloud Platform (GCP)](https://cloud.google.com/), [Gatsby](https://gatsbyjs.com/), and even those supporting the containerized application process like [Docker](https://www.docker.com/) and [Kubernetes](https://kubernetes.io/). And once you have entered your decryption key in the appropriate platform’s settings, all you need to do is make a `require` reference to the `[dotenv-vault-core package](https://github.com/dotenv-org/dotenv-vault-core)` as early in your code as possible: ```js JavaScript theme={"system"} // index.js require('dotenv-vault-core').config(); const endpoint = process.env.ENDPOINT_URL; const address = process.env.PUBLIC_KEY; console.log('Environment variables in use:\n' + endpoint + '...\n' + addresss); ``` Overall the workflow is quite accessible, even for the less tech-savvy, making it a perfect tool for you to make a swift and adequate transition from development to production. Here’s how it works in a [GitHub Actions CI/CD workflow](https://github.com/petarsrepo/web3-github-dotenv): Figure 8: Dotenv Vault integration example - [GitHub Actions CI/CD workflow for Web3 DApp](https://github.com/petarsrepo/web3-github-dotenv) ## Other alternatives Now that you know how the Dotenv Vault workflow plays out, it is important to note there are other secret manager alternatives too. Some of them are built into the corresponding platform, while others are available for external use just as well. Overall, there are plenty of options to choose from, available on the market, whether you're looking for a free(mium) tool to do the basics, or a paid one that can offer you more advanced capabilities. Among the popular examples of alternative online secret managers are: > [card] [Microsoft Azure Key Vault](https://azure.microsoft.com/en-in/products/key-vault/) > [card] [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) > [card] [Google Cloud Secret Manager](https://cloud.google.com/secret-manager) > [card] [HashiCorp Vault](https://www.vaultproject.io/) > [card] [Doppler](https://www.doppler.com/) ## Conclusion Now that you have made it this far, it should be quite clear that securely storing environment variables is essential for ensuring the seamless operation and integrity of any Web3 DApp. Overall, the most suitable approach for securely storing environment variables will depend on the specific requirements of your project. By using secure methods such as configuration/environment files, secure databases via a backend server, or secret manager tools, you, as Web3 BUIDLers, can protect sensitive information and prevent unauthorized access. Additionally, following best practices such as encrypting and password-protecting environment variable files and implementing access control measures can further enhance the security of your DApp. And by taking the time to store environment variables properly, you are effectively taking an important step towards building a secure and reliable Web3 landscape not just for yourself and your project but for everyone involved end-to-end. Isn’t that what we all want in the end?… > [!NOTE] > > ### See also > > > [card] [HTTP batch request VS multicall contract](/docs/http-batch-request-vs-multicall-contract) ### About the authors > [card] Petar Stoykov > > ![Petar Stoykov](https://pbs.twimg.com/profile_images/1565444249784565761/J6viLLoe_400x400.jpg) > > Senior Copywriter @ Chainstack > \ > Writes on Ethereum, NFTs, and underlying technology > > [https://github.com/petarsrepo](https://github.com/petarsrepo) > > [https://twitter.com/petarcopyrock](https://twitter.com/petarcopyrock) > > [https://www.linkedin.com/in/pstoykov/](https://www.linkedin.com/in/pstoykov/) > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # HTTP batch request VS multicall contract Source: https://docs.chainstack.com/docs/http-batch-request-vs-multicall-contract Compare HTTP batch requests and Multicall contracts for efficient blockchain data fetching, with performance benchmarks and code examples on Chainstack. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR:** * Both HTTP batch requests and multicall contracts can bundle multiple calls to reduce client-server overhead and improve response times. * In the tests, HTTP batch requests often slightly outperformed multicall contracts, but both outperformed sending multiple single requests by a significant margin. * Batch requests still count each call against your RPC usage, whereas multicall typically counts as a single request. However, multicall requires extra contract deployment and can lead to “request timeout” or “response size too big” errors for more complex calls. * The best choice depends on your specific use cases, desired simplicity, and performance requirements. ## Main article This article is about two approaches many developers believe can help them save on RPC consumptions: batch request and multicall smart contract. In this article, we will explore how to use them and compare their performances. ## HTTP batch request HTTP batch request is a feature most Ethereum clients support, for example, [Geth](https://geth.ethereum.org/docs/interacting-with-geth/rpc/batch). With batch requests enabled, multiple HTTP requests can be packaged into one single request and sent to the server. Server process this bulk request and returns a bulk result. All of these are done in a single round trip. This feature can be useful for reducing the load on the server and improving the performance to a certain extent. ## How to implement To implement an HTTP batch request, just send a request with a payload containing multiple request objects in an array like below: ```Json JSON theme={"system"} [ {"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}, {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":2}, {"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":3}, {"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":4} ] ``` The server sends back the results in one response. For example: > [!WARNING] > > **Important: Response order is not guaranteed** > > According to the [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification#batch), the server may return batch responses in any order. Always use the `id` field to match requests with responses, not the array position. > > This is especially important when using ethers.js v5, which had an issue that incorrectly relied on response ordering. If you're using ethers.js v5 and experiencing ordering issues, upgrade to ethers.js v6 where this has been fixed. See [ethers.js issue #3431](https://github.com/ethers-io/ethers.js/issues/3431) for details. ```Json JSON theme={"system"} [ { "jsonrpc": "2.0", "id": 1, "result": "Geth/v1.10.26-stable-e5eb32ac/linux-amd64/go1.18.8" }, { "jsonrpc": "2.0", "id": 2, "result": "0x10058f8" }, { "jsonrpc": "2.0", "id": 3, "result": false }, { "jsonrpc": "2.0", "id": 4, "result": "0x1" } ] ``` To run it in curl: ```bash cURL theme={"system"} curl 'YOUR_CHAINSTACK_ENDPOINT' \ --header 'Content-Type: application/json' \ --data '[ {"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}, {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":2}, {"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":3}, {"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":4} ]' ``` Popular Web3 libraries like [web3.js](https://web3js.readthedocs.io/en/v1.2.11/web3-eth-personal.html?highlight=batch) and [ethers.js](https://docs.ethers.org/v5/api/providers/other/#JsonRpcBatchProvider) support this feature too. Below is an example of getting ether's balance from multiple accounts using web3.js. > [!NOTE] > > ### Web3.js install instructions > > Run `npm i web3` to install web3.js. The code in this guide is compatible with `web3.js V4`. ```js JavaScript theme={"system"} const { Web3 } = require("web3"); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const web3 = new Web3(NODE_URL); const addresses = [ "0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326", "0x2bB42C655DdDe64EE508a0Bf29f9D1c6150Bee5F", ]; async function getBalances() { const startTime = Date.now(); // Create a batch request object const batch = new web3.BatchRequest(); // Array to hold promises for each request const promises = []; // Loop through each address and add a getBalance request to the batch addresses.forEach((address, index) => { const request = { jsonrpc: "2.0", id: index + 1, method: "eth_getBalance", params: [address, "latest"], }; // Add request to the batch and store the promise const requestPromise = batch.add(request); promises.push(requestPromise); }); // Send the batch request and wait for all responses const responses = await batch.execute(); // Process responses responses.forEach((response, index) => { if (response.error) { console.error(response.error); } else { const balance = response.result; const timeFromStart = Date.now() - startTime; console.log( `${addresses[index]} has a balance of ${Number( web3.utils.fromWei(balance, "ether") ).toFixed(3)} ETH retrieved in: ${timeFromStart / 1000} seconds.` ); } }); } getBalances(); ``` The `getBalances` function creates a new `BatchRequest` object using `web3.BatchRequest()`. The function then loops through each address in the `addresses` array and creates a new request to get the balance of that address using `web3.eth.getBalance.request()`. It adds each request to the batch using `batch.add()`. Finally, the function executes the batch request using `batch.execute()`. When executed, the requests in the batch are sent to the Ethereum network simultaneously, and the callback functions are executed when the responses are received. ## Multicall contract A multicall contract is a smart contract that takes in the function call objects as parameters and executes them together. A developer can use the multicall contract as a proxy to call other contracts on Ethereum. The implementation of a multicall contract is, in fact, very simple: it leverages Solidity’s [call function](https://solidity-by-example.org/call/) to broadcast contract calls. This is a sample implementation of multicall’s aggregate function: ```solidity solidity theme={"system"} function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) { blockNumber = block.number; returnData = new bytes[](calls.length); for(uint256 i = 0; i < calls.length; i++) { (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); require(success); returnData[i] = ret; } } ``` In summary, this function takes an array of `Call`, calls each one, and returns an array of the results along with the block number in which the function was called. It is designed to be used as a general-purpose aggregator for calling other contracts on the Ethereum blockchain. ## How to implement Anyone can deploy their own multicall contract. In this article, we leverage [MakerDAO’s multicall contract](https://github.com/makerdao/multicall) on the Ethereum mainnet; which is deployed at [0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441](https://etherscan.io/address/0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441). Below is an example calling the smart contract with MakerDAO’s helper library [multicall.js](https://github.com/makerdao/multicall.js?files=1); it essentially does the same thing as the previous example: ```js JavaScript theme={"system"} const multicall = require("@makerdao/multicall") const config = { rpcUrl: "YOUR_CHAINSTACK_ENDPOINT", multicallAddress: "0xeefba1e63905ef1d7acba5a8513c70307c1ce441" }; const addressArr = [ "0x2B6ee955a98cE90114BeaF8762819d94C107aCc7", "0x2bB42C655DdDe64EE508a0Bf29f9D1c6150Bee5F" ]; async function main() { const startTime = Date.now(); console.log("Started..."); const calls = []; // Retrieve the Ether balance of each Ethereum address in addressArr using the multicall library. for (let i = 0; i < addressArr.length; i++) { const callObj = { call: [ 'getEthBalance(address)(uint256)', addressArr[i] ], returns: [ [`ETH_BALANCE ${addressArr[i]}`, val => val / 10 ** 18] ] }; calls.push(callObj); } const result = await multicall.aggregate(calls, config); console.log(result); const timeFromStart = Date.now() - startTime; console.log(`Result received in ${timeFromStart / 1000} seconds`); } main(); ``` The `main` function iterates through each address in the `addressArr` array and creates a call object for each address. These call objects use the multicall library to retrieve the ether balance for each address. Once all of the call objects have been created and pushed to the `calls` array, the multicall library's `aggregate` function is called with the array of call objects and the configuration object. This function aggregates the results of all of the calls into a single object, which is stored in the `result` variable. Finally, the code logs the "result" to the console and calculates the time it took to receive the "result", which is also logged to the console. You will need to [install the multicall.js library](https://github.com/makerdao/multicall.js?files=1#installation) to run this code. ## Performance comparison In this section, we compare the performance of 3 different approaches: * Sending multiple HTTP requests in parallel * Sending a batch HTTP request * Using a multicall contract We will test based on two common use cases: 1. Getting account balance 2. Calling a smart contract ## Getting account balance for 30 distinct addresses The testing script for batch requests and multicall contract is already included in the previous sections. Below is the code for sending multiple HTTP requests in parallel: ```js JavaScript theme={"system"} const Web3 = require('web3'); const web3 = new Web3(new Web3.providers.HttpProvider('YOUR_CHAINSTACK_ENDPOINT')); var addressArr = [ "0x2B6ee955a98cE90114BeaF8762819d94C107aCc7", "0x2bB42C655DdDe64EE508a0Bf29f9D1c6150Bee5F" ] async function main() { var startTime = Date.now() console.log("started") for (i = 0; i < addressArr.length; i++) { web3.eth.getBalance(addressArr[i]).then(function(result) { var timeFromStart = Date.now() - startTime console.log("Result received in:" + timeFromStart / 1000 + " seconds") }) } } main(); ``` ### Result | | Parallel single requests | Batch request | Multicall | | - | - | - | - | | Round 1 | 1.789 | 1.49 | 1.447 | | Round 2 | 1.896 | 1.159 | 1.54 | | Round 3 | 2.337 | 1.113 | 2.132 | | Round 4 | 2.942 | 1.224 | 1.609 | | Round 5 | 1.638 | 1.602 | 2.012 | The test was conducted between a server in Europe and a client in Singapore. A total of 15 measurements were averaged, which shows the performance of batch request > Multicall > normal request. Compared with sending single requests in parallel, batch request reduces 38% of the total request time, and multicall reduces 18% of the total request time. ## Getting the owners of BAYC tokens Below are the testing scripts using web3.js for making smart contract calls. The tests are based on an ERC-721 standard method [`ownerOf`](https://docs.openzeppelin.com/contracts/2.x/api/token/erc721) from BAYC’s smart contract. Sending multiple HTTP requests in parallel: ```js JavaScript theme={"system"} const Web3 = require('web3'); const web3 = new Web3(new Web3.providers.HttpProvider('YOUR_CHAINSTACK_ENDPOINT')); const abi = [{ "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], "name": "ownerOf", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }] const contract = new web3.eth.Contract(abi, "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"); async function main() { const startTime = Date.now() console.log("started") for (i = 0; i < 30; i++) { contract.methods.ownerOf(i).call().then(function(result) { console.log(result) var timeFromStart = Date.now() - startTime console.log("result received in: " + timeFromStart / 1000 + " seconds") }) } } main(); ``` Sending batch request: ```js JavaScript theme={"system"} const { Web3 } = require('web3'); const web3 = new Web3('YOUR_CHAINSTACK_ENDPOINT); const abi = [ { inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], name: "ownerOf", outputs: [{ internalType: "address", name: "", type: "address" }], stateMutability: "view", type: "function", }, ]; const contract = new web3.eth.Contract( abi, "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D" ); async function main() { const startTime = Date.now(); const batch = new web3.BatchRequest(); console.log("started"); // Array to hold promises for each request const promises = []; for (let i = 0; i < 30; i++) { const request = { jsonrpc: "2.0", id: i + 1, method: "eth_call", params: [ { to: contract.options.address, data: contract.methods.ownerOf(i).encodeABI(), }, "latest", ], }; // Add request to the batch and store the promise const requestPromise = batch.add(request); promises.push(requestPromise); } // Send the batch request and wait for all responses const responses = await batch.execute(); // Process responses responses.forEach((response, index) => { if (response.error) { console.error(response.error); } else { const ownerAddress = web3.eth.abi.decodeParameter( "address", response.result ); const timeFromStart = Date.now() - startTime; console.log( `${index} token owner is ${ownerAddress} received in: ${ timeFromStart / 1000 } seconds` ); } }); } main(); ``` Multicall contract: ```js JavaScript theme={"system"} const multicall = require("@makerdao/multicall") const config = { rpcUrl: "YOUR_CHAINSTACK_ENDPOINT", multicallAddress: "0xeefba1e63905ef1d7acba5a8513c70307c1ce441" }; async function main() { var startTime = Date.now() console.log("started") var calls = [] for (i = 0; i < 30; i++) { var callObj = { target: "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", call: ['ownerOf(uint256)(address)', i], returns: [ ['OWNER_ADDR ' + i] ] } calls.push(callObj) } const result = await multicall.aggregate(calls, config); console.log(result.results); var timeFromStart = Date.now() - startTime console.log("Result received in: " + timeFromStart / 1000 + " seconds") } main(); ``` ### Result | | Parallel single requests | Batch request | Multicall | | - | - | - | - | | Round 1 | 1.693 | 1.931 | 1.878 | | Round 2 | 1.717 | 1.592 | 1.195 | | Round 3 | 1.712 | 1.617 | 2.183 | | Round 4 | 2.103 | 1.589 | 1.3 | | Round 5 | 2.785 | 1.416 | 1.429 | The same test was conducted for read contract calls. The result shows that both batch requests and multicall contracts save around 20% of total request time compared with sending single requests. ## Common questions > [accordion] If I package 100 requests into a single batch request, does that count as 1 request or 100 requests on Chainstack? > > As an RPC provider, Chainstack counts “request” as RPC calls. After a server receives an HTTP batch request, it “unpacks” the request and processes the calls separately. So from the server’s point of view, 1 batch request of 100 calls consumes 100 requests instead of 1. > [!NOTE] > > Check the [Understanding your request consumption](https://support.chainstack.com/hc/en-us/articles/4412534652313-Understanding-your-request-consumption-over-HTTP-and-WebSocket) page on Chainstack support docs for more details. > [accordion] If I package 100 calls into a single multicall request, does that count as 1 request or 100 requests? > > In this case, even though it is a very heavy call, it counts as a single request. > [accordion] Is there any hard limit for the number of calls to multicall contracts? > > The BAYC testing script stops working with 1,040 calls. ## Which approach works better for me Even though tests show that batch request and multicall contract improves performance significantly, they do have their own limitations. Requests in a batch request are executed in order, which means if a new block is received during execution, the subsequent results are likely to be different. If you want to use a multicall contract, you should probably deploy your own contract for production just to ensure its availability. Both batch request and multicall contract return multiple results in a single response. Both of them require much more computational resources. They can easily trigger “request timeout” errors and “response size too big” errors, which makes them not suitable for complex calls. > [!NOTE] > > ### See also > > > [card] [HTTP batch request VS multicall contract](/docs/http-batch-request-vs-multicall-contract) > > > [card] [How to store your Web3 DApp secrets: Guide to environment variables](/docs/how-to-store-your-web3-dapp-secrets-guide-to-environment-variables) > [card] Wuzhong Zhu > > ![Wuzhong Zhu](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1548860905064017921/xCKUgveS_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=075e8dc4db13d9384c63bf4904eb520d) > > Developer Advocate @ Chainstack > \ > BUIDLs on Ethereum, zkEVMs, and The Graph protocol > > [https://twitter.com/wuzhongzhu](https://twitter.com/wuzhongzhu) > > [https://www.linkedin.com/in/wuzhong-zhu-44563589/](https://www.linkedin.com/in/wuzhong-zhu-44563589/) > > [https://github.com/wuzhong-zhu](https://github.com/wuzhong-zhu) # Hybrid hosting Source: https://docs.chainstack.com/docs/hybrid-hosting Chainstack can be used as a solution 100% hosted in your data center or with any selected components deployed in the location of your choice. Ideal for clients prioritizing full control over their nodes, keys, and infrastructure, while seeking to avoid the complexity of developing proprietary orchestration tools. ### Example A software company utilizes Chainstack's interface (either through a graphical user interface or application programming interface) to coordinate and oversee blockchain nodes, networks, and services that operate exclusively within their custom-built infrastructure. > [!NOTE] > > ### Contact us for hybrid hosting > > Start today by sending [us a message](https://support.chainstack.com/). ## Chainstack-managed hosting vs hybrid hosting With Chainstack-managed hosting, you get the complete managed blockchain services experience: monitoring, logging, backups, deployment of fully synced nodes, and request metrics. With hybrid hosting, you get the nodes running in your own infrastructure. | Features | Chainstack-managed hosting | Hybrid hosting | | - | - | - | | Monitoring | Complete node monitoring: node availability and node health. | Partial node monitoring: node availability is monitored. | | Analytics and logging | Full node logging and resource consumption logging. For Chainstack-managed hosting nodes, request metrics are collected: number of requests, a breakdown of method calls, and server response data. For dedicated Chainstack-managed hosting nodes, request metrics, CPU, memory, and storage usage data are collected. | Analytics solution setup on request. | | Backups | Daily automated backups that enable the provision of fully synced nodes in a matter of hours | You can use your own backup solution available on the market. | | Bolt | Dedicated Nodes are deployed as synced and near-instantly available for use. | A node must be synced from scratch, which takes weeks or months. To reduce the sync time to hours, a manual snapshot transfer is also available on the [Enterprise subscription](https://chainstack.com/pricing/). | | Billing | You pay for the subscription and the node usage. | You pay for the subscription and node management fee. | | Cloud providers and regions | It is possible to use the default Chainstack cloud providers to run a node on. | You are not limited to the Chainstack supported cloud providers. Use your own cloud infrastructure. | | Benefits | Easy to deploy and manage nodes. Low operation costs. Near-instant node availability. | Blockchain is completely running in your cloud, which is useful in various scenarios, including compliance related concerns. For any application running on top of blockchain, the latency will benefit from the choice to colocate the application on the same server as the node. | # Hyperliquid: Authentication via signatures overview Source: https://docs.chainstack.com/docs/hyperliquid-authentication-guide Configure Hyperliquid API authentication with wallet private keys and API wallet delegation for secure programmatic trading through Chainstack endpoints. ## Overview Hyperliquid uses two distinct signing mechanisms with different chain IDs and purposes: | Type | Chain ID | Domain | Purpose | | - | - | - | - | | **L1 Actions** | 1337 | "Exchange" | Trading operations: order, cancel, cancelByCloid, modify, batchModify, scheduleCancel, updateLeverage, updateIsolatedMargin, vaultTransfer, subAccountTransfer | | **User-Signed** | 0x66eee | "HyperliquidSignTransaction" | Administrative operations: approveAgent, usdSend, spotSend, usdClassTransfer, withdraw, approveBuilderFee, tokenDelegate | Unlike info endpoints that can be queried without authentication, exchange endpoints require cryptographic signatures to ensure security and prevent unauthorized access. > [!NOTE] > > See also the [Hyperliquid API reference](/reference/hyperliquid-getting-started). > [!WARNING] > > Exchange endpoints will not work without proper signatures. The Hyperliquid Python SDK v0.18.0+ handles signing complexity internally. ## Types of signatures ### 1. L1 Action signatures (Trading operations) L1 actions use a **phantom agent** construction - a temporary signing identity created from your action's hash: * **Chain ID**: 1337 (NOT Arbitrum's 42161) * **Domain**: "Exchange" * **Serialization**: Msgpack binary format * **Implementation guide**: [L1 Action signing guide](/docs/hyperliquid-l1-action-signing) ### 2. User-Signed actions (Administrative operations) User-signed actions use direct EIP-712 signing without phantom agent abstraction: * **Chain ID**: 0x66eee (421614 in decimal) * **Domain**: "HyperliquidSignTransaction" * **Serialization**: Direct JSON structure * **Implementation guide**: [User-signed actions guide](/docs/hyperliquid-user-signed-actions) ## Key concepts ### Phantom Agent A cryptographic construct (not a real wallet) used for L1 actions: 1. Action serialized with msgpack 2. Nonce and vault address appended 3. Data hashed with keccak256 4. Temporary "agent" object created with hash as connectionId 5. This phantom agent is signed via EIP-712 ### Agent Wallet A separate keypair authorized to sign L1 actions on behalf of your account: * **Stateless**: No funds or positions * **Nonce isolation**: Independent nonce tracking * **Limited scope**: Can only sign L1 actions, not transfers * **Approved via**: User-signed action ([`approveAgent`](/reference/hyperliquid-exchange-approve-agent)) ### Signature components Every signature consists of three components: ```json theme={"system"} { "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "s": "0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321", "v": 27 } ``` * **r** — First 32 bytes of the ECDSA signature * **s** — Second 32 bytes of the ECDSA signature * **v** — Recovery ID (27 or 28) used to recover the public key ## Architecture ``` L1 Actions → Phantom Agent → ChainId 1337 ↑ Agent Wallet (approved via user-signed) ↑ User-Signed → Direct EIP-712 → ChainId 0x66eee ``` ## Authentication flow **1. Choose signing mechanism** Determine if you need L1 actions (trading) or user-signed actions (admin/transfers) **2. Prepare the action** Create the action object with operation details **3. Add timestamp** Use current timestamp in milliseconds as nonce **4. Sign appropriately** L1 actions: Use phantom agent construction User-signed: Use direct EIP-712 signing **5. Send the request** Include signature, action, and nonce in request body ## Complete list of supported actions ### L1 Actions (Chain ID: 1337) These actions use phantom agent construction with msgpack serialization. | Action | Description | Category | | - | - | - | | `order` | Place new orders | Trading | | `cancel` | Cancel orders by order ID | Trading | | `cancelByCloid` | Cancel orders by client order ID | Trading | | `modify` | Modify existing orders | Trading | | `batchModify` | Modify multiple orders at once | Trading | | `scheduleCancel` | Schedule order cancellation | Trading | | `updateLeverage` | Adjust leverage for positions | Position Management | | `updateIsolatedMargin` | Manage isolated margin | Position Management | | `vaultTransfer` | Transfer between vault accounts | Internal Transfers | | `subAccountTransfer` | Transfer between sub-accounts | Internal Transfers | | `noop` | No operation (for testing signatures) | Utility | > [!NOTE] > > For implementation details and code examples, see the [L1 Action signing guide](/docs/hyperliquid-l1-action-signing). ### User-Signed Actions (Chain ID: 0x66eee) These actions use direct EIP-712 signing with JSON structure. | Action | Description | Category | | - | - | - | | `approveAgent` | Authorize an agent wallet to trade on your behalf | Agent Management | | `usdSend` | Transfer USDC between accounts | Fund Transfers | | `spotSend` | Transfer spot tokens | Fund Transfers | | `usdClassTransfer` | Transfer between USD classes | Fund Transfers | | `withdraw` | Withdraw funds to Layer 1 | Withdrawals | | `approveBuilderFee` | Approve builder fee structures | Advanced | | `tokenDelegate` | Delegate token voting rights | Advanced | > [!NOTE] > > For implementation details and code examples, see the [User-signed actions guide](/docs/hyperliquid-user-signed-actions). ## Common issues and solutions ### Wrong chain ID **Error**: Signature verification failed\ **Cause**: Using incorrect chain ID (especially using Arbitrum's 42161)\ **Solution**: * L1 actions: Use Chain ID **1337** (NOT 42161) * User-signed: Use Chain ID **0x66eee** (421614 in decimal) ### Invalid nonce **Error**: Invalid or expired nonce\ **Cause**: Nonce is too old, in the future, or not in milliseconds\ **Solution**: Use current timestamp in milliseconds: `get_timestamp_ms()` or `Date.now()` ### Signature type mismatch **Error**: Unauthorized or invalid signature\ **Cause**: Mixing up signing mechanisms or serialization\ **Solution**: * L1 actions: Use phantom agent with msgpack serialization * User-signed: Use direct EIP-712 with JSON structure ### Agent issues **Error**: Agent not approved / Agent already exists\ **Cause**: Using unapproved agent or duplicate agent name\ **Solution**: * Approve agent first with `exchange.approve_agent()` * Use unique agent names for each bot/integration ### Invalid action format **Error**: Failed to deserialize / Invalid action\ **Cause**: Missing required fields or wrong structure\ **Solution**: Ensure action payloads include all required fields (e.g., `grouping` for orders) ## Best practices > [!TIP] > > **DO** > > * Use the official SDK when possible for automatic signature handling > * Keep your private keys secure and never expose them in code > * Use environment variables or secure key management systems > * Implement proper error handling for signature failures > * Test with small amounts on mainnet or use testnet first > [!WARNING] > > **DON'T** > > * Share your private keys or commit them to version control > * Reuse nonces across different requests > * Use placeholder signatures in production > * Skip signature validation in your implementation ## Official SDKs > [!NOTE] > > The official SDKs handle all signing complexity internally, including phantom agent construction and EIP-712 formatting. ### Python SDK * **Repository**: [hyperliquid-python-sdk](https://github.com/hyperliquid-dex/hyperliquid-python-sdk) * **Version**: v0.18.0+ required * **Features**: * Automatic phantom agent construction for L1 actions * Built-in wrapper functions for all user-signed actions * Support for both mainnet and testnet ### Rust SDK * **Repository**: [hyperliquid-rust-sdk](https://github.com/hyperliquid-dex/hyperliquid-rust-sdk) * **Features**: * Full L1 action support * User-signed action support * High-performance implementation ### Installation **Python:** ```bash theme={"system"} pip install hyperliquid-python-sdk ``` **Rust:** ```toml theme={"system"} [dependencies] hyperliquid-rust-sdk = "0.3" ``` ## Testing your implementation 1. **Start with testnet**: Use `https://api.hyperliquid-testnet.xyz` for testing or connect to a [reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) 2. **Verify signatures locally**: Ensure your signatures are properly formatted before sending 3. **Use small amounts**: Test with minimal amounts when moving to mainnet 4. **Monitor rate limits**: Respect API rate limits to avoid being blocked ## Implementation guides > [card] [Quick Reference](/docs/hyperliquid-signing-overview) > > Side-by-side comparison of both signing mechanisms > [card] [L1 Action Signing](/docs/hyperliquid-l1-action-signing) > > Complete guide with code examples for trading operations > [card] [User-Signed Actions](/docs/hyperliquid-user-signed-actions) > > Detailed guide for administrative operations and agent management > [card] [API Reference](/reference/hyperliquid-exchange-place-order) > > Full API documentation for all exchange endpoints ## Key points to remember * **Info endpoints** don't require signatures and can be accessed directly * **Exchange endpoints** require appropriate signing based on the action type * **L1 actions** use phantom agent construction with Chain ID 1337 * **User-signed actions** use direct EIP-712 signing with Chain ID 0x66eee * **Agent wallets** can only perform L1 actions, not transfers or withdrawals # Hyperliquid: Bridging USDC between HyperCore and HyperEVM Source: https://docs.chainstack.com/docs/hyperliquid-bridging-usdc Bridge USDC between Arbitrum and Hyperliquid L1 using the Python SDK and smart contract deposits for funding your Hyperliquid trading account. ## TLDR | Direction | Method | | - | - | | HyperCore → HyperEVM | `spot_transfer()` to system address `0x2000000000000000000000000000000000000000` | | HyperEVM → HyperCore | Approve native USDC to CoreDepositWallet, then call `deposit(amount, 4294967295)` for spot | ## Overview Hyperliquid operates two interconnected layers: * **HyperCore** — the high-performance L1 for trading (perps and spot) * **HyperEVM** — the EVM-compatible layer for smart contracts Moving USDC between these layers requires understanding the bridging mechanism, system addresses, and token linking. This guide provides complete code examples for both directions. > [!NOTE] > > Connect to a [reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) to follow along with the examples. ## System addresses Every token on Hyperliquid has a designated **system address** that serves as the bridge between HyperCore and HyperEVM. The format is: ``` 0x20 + zeros + token_index (big-endian) ``` | Token | Index | System address | | - | - | - | | USDC | 0 | `0x2000000000000000000000000000000000000000` | | HYPE | N/A | `0x2222222222222222222222222222222222222222` | | Token 200 | 200 | `0x20000000000000000000000000000000000000c8` | > [!WARNING] > > HYPE uses a special system address (`0x2222222222222222222222222222222222222222`) and is received as the native gas token on HyperEVM, not as an ERC20. ## Contract addresses ### Mainnet | Contract | Address | Purpose | | - | - | - | | Native USDC (Circle) | `0xb88339CB7199b77E23DB6E890353E22632Ba630f` | Standard ERC20 USDC | | CoreDepositWallet | `0x6b9e773128f453f5c2c60935ee2de2cbc5390a24` | Bridge contract (from `spotMeta`) | | System address (USDC) | `0x2000000000000000000000000000000000000000` | HyperCore → HyperEVM destination | | System address (HYPE) | `0x2222222222222222222222222222222222222222` | HYPE bridge | | CoreWriter | `0x3333333333333333333333333333333333333333` | HyperCore actions from EVM | ### Testnet | Contract | Address | Purpose | | - | - | - | | Native USDC (Circle) | `0x2B3370eE501B4a559b57D449569354196457D8Ab` | Standard ERC20 USDC | | CoreDepositWallet | `0x0b80659a4076e9e93c7dbe0f10675a16a3e5c206` | Bridge contract (from `spotMeta`) | | System address (USDC) | `0x2000000000000000000000000000000000000000` | HyperCore → HyperEVM destination | > [!WARNING] > > **Important distinction between the two USDC-related contracts:** > > * **Native USDC** — Circle's standard ERC20 USDC token. Use this for DeFi apps, DEXs, and as the source for bridging. > * **CoreDepositWallet** — Circle's bridge contract (shown in `spotMeta` as `evmContract.address`). Call `deposit()` on this contract to bridge USDC to HyperCore. > > The CoreDepositWallet is NOT a standard ERC20—do not call `transfer()` on it directly. > [!NOTE] > > You can retrieve the correct linked USDC contract address programmatically using the [`spotMeta`](/reference/hyperliquid-info-spotmeta) API endpoint. The contract address is returned in the `evmContract.address` field. ## Endpoint requirements Different bridging operations require different endpoints: | Operation | Endpoint | Provider | | - | - | - | | HyperCore → HyperEVM (`spotSend`) | Info HTTP API | Hyperliquid public RPC only | | HyperEVM → HyperCore (`transfer`) | EVM JSON-RPC | Chainstack or Hyperliquid | | Check balances (`spotMeta`, `userState`) | Info HTTP API | Chainstack or Hyperliquid | | Read EVM state (`eth_call`, `eth_getBalance`) | EVM JSON-RPC | Chainstack or Hyperliquid | > [!WARNING] > > Exchange actions like `spotSend` are only available on Hyperliquid's public RPC (`https://api.hyperliquid.xyz` for mainnet). Chainstack endpoints support Info API queries and EVM JSON-RPC but not exchange actions. See [Hyperliquid methods](/docs/hyperliquid-methods) for complete endpoint capabilities. ## Prerequisites **1. Install dependencies** Install the required Python packages: ```bash theme={"system"} pip install hyperliquid-python-sdk eth-account web3 ``` **2. Set up configuration** Create a `config.json` file with your credentials: ```json theme={"system"} { "private_key": "0x...", "rpc_endpoint": "YOUR_CHAINSTACK_EVM_ENDPOINT" } ``` **3. Verify token linking** USDC must be linked between HyperCore and HyperEVM. On mainnet, USDC is already linked. On testnet, check [token linking status](#token-linking) before bridging. ## HyperCore to HyperEVM Transfer USDC from your HyperCore spot account to your HyperEVM wallet using the `spotSend` action directed at the **system address**. > [!WARNING] > > Exchange actions like `spotSend` require the Hyperliquid public RPC endpoint (`https://api.hyperliquid.xyz` for mainnet). Chainstack endpoints support Info API and EVM JSON-RPC but not exchange actions. See [Hyperliquid methods](/docs/hyperliquid-methods) for endpoint capabilities. ### Using the Python SDK ```python theme={"system"} #!/usr/bin/env python3 """ Bridge USDC from HyperCore to HyperEVM using spotSend to system address """ from hyperliquid.exchange import Exchange from hyperliquid.info import Info from hyperliquid.utils import constants from eth_account import Account import json # USDC system address (token index 0) USDC_SYSTEM_ADDRESS = "0x2000000000000000000000000000000000000000" def bridge_to_evm(amount: float): """ Bridge USDC from HyperCore Spot to HyperEVM. The tokens are sent to the system address, and Hyperliquid credits them to your HyperEVM address automatically. Args: amount: Amount of USDC to bridge """ # Load configuration with open('config.json') as f: config = json.load(f) wallet = Account.from_key(config['private_key']) print(f"Bridging {amount} USDC from HyperCore to HyperEVM") print(f"Wallet: {wallet.address}") print(f"Sending to system address: {USDC_SYSTEM_ADDRESS}") # Initialize exchange - must use Hyperliquid public endpoint exchange = Exchange( wallet=wallet, base_url=constants.MAINNET_API_URL # Exchange actions require HL public RPC ) # Check current spot balance info = Info(constants.MAINNET_API_URL, skip_ws=True) spot_state = info.spot_user_state(wallet.address) print(f"\nCurrent spot balances:") for b in spot_state.get('balances', []): print(f" {b['coin']}: {b['total']}") # spotSend to the USDC system address triggers the bridge # Tokens are credited to YOUR address on HyperEVM (the sender) result = exchange.spot_transfer( amount=amount, destination=USDC_SYSTEM_ADDRESS, # Send to system address token="USDC" ) print(f"\nResult: {result}") if result.get('status') == 'ok': print("✓ Bridge transaction submitted") print(f" {amount} USDC will appear on HyperEVM at {wallet.address}") else: print(f"✗ Error: {result}") return result if __name__ == "__main__": # Bridge 10 USDC to HyperEVM bridge_to_evm(amount=10.0) ``` ### How it works 1. You send a `spotSend` action with the **system address** (`0x2000000000000000000000000000000000000000`) as the destination 2. Hyperliquid detects this as a bridge request and triggers a system transaction 3. The system transaction calls `transfer(sender_address, amount)` on the linked ERC20 contract 4. USDC appears in **your** HyperEVM wallet (the sender's address) > [!NOTE] > > The bridge is nearly instant—funds typically appear within 2 seconds. The HyperCore → HyperEVM transfer costs approximately 200K gas at the base gas price. ## HyperEVM to HyperCore Transfer USDC from HyperEVM back to HyperCore for trading using the CoreDepositWallet contract. > [!NOTE] > > The linked USDC contract shown in `spotMeta` is a **CoreDepositWallet** contract developed by Circle—not a standard ERC20. To bridge USDC from HyperEVM to HyperCore, you must use the `deposit()` function on this contract after approving the native USDC token. ### Understanding the two USDC contracts Bridging from HyperEVM to HyperCore involves two contracts: | Contract | Mainnet | Testnet | Purpose | | - | - | - | - | | Native USDC (Circle) | `0xb88339CB7199b77E23DB6E890353E22632Ba630f` | `0x2B3370eE501B4a559b57D449569354196457D8Ab` | Standard ERC20 USDC | | CoreDepositWallet | `0x6b9e773128f453f5c2c60935ee2de2cbc5390a24` | `0x0b80659a4076e9e93c7dbe0f10675a16a3e5c206` | Bridge contract (from `spotMeta`) | The CoreDepositWallet address is the `evmContract.address` returned by the `spotMeta` API endpoint. ### Using the CoreDepositWallet The `deposit()` function signature: ```solidity theme={"system"} function deposit(uint256 amount, uint32 destinationDex) external ``` Parameters: * `amount` — amount in 6 decimals (native USDC decimals) * `destinationDex` — destination on HyperCore: * `0` = perps balance * `4294967295` (`type(uint32).max`) = spot balance ```python theme={"system"} #!/usr/bin/env python3 """ Bridge USDC from HyperEVM to HyperCore using CoreDepositWallet """ from web3 import Web3 from eth_account import Account import json # Contract addresses (mainnet) NATIVE_USDC = Web3.to_checksum_address("0xb88339CB7199b77E23DB6E890353E22632Ba630f") CORE_DEPOSIT_WALLET = Web3.to_checksum_address("0x6b9e773128f453f5c2c60935ee2de2cbc5390a24") # Destination dex values PERPS_DEX = 0 SPOT_DEX = 4294967295 # type(uint32).max # ABIs ERC20_ABI = [ { "inputs": [ {"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"} ], "name": "approve", "outputs": [{"name": "", "type": "bool"}], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ {"name": "owner", "type": "address"}, {"name": "spender", "type": "address"} ], "name": "allowance", "outputs": [{"name": "", "type": "uint256"}], "stateMutability": "view", "type": "function" }, { "inputs": [{"name": "account", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "", "type": "uint256"}], "stateMutability": "view", "type": "function" } ] DEPOSIT_ABI = [ { "inputs": [ {"name": "amount", "type": "uint256"}, {"name": "destinationDex", "type": "uint32"} ], "name": "deposit", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] def bridge_to_core(amount: float, to_spot: bool = True): """ Bridge USDC from HyperEVM to HyperCore. Args: amount: Amount of USDC to bridge (human-readable, e.g., 10.0) to_spot: If True, deposit to Spot balance; otherwise to Perps """ with open('config.json') as f: config = json.load(f) w3 = Web3(Web3.HTTPProvider(config['rpc_endpoint'])) wallet = Account.from_key(config['private_key']) wallet_address = Web3.to_checksum_address(wallet.address) destination = "Spot" if to_spot else "Perps" print(f"Bridging {amount} USDC from HyperEVM to HyperCore {destination}") # USDC has 6 decimals on HyperEVM amount_wei = int(amount * 10**6) native_usdc = w3.eth.contract(address=NATIVE_USDC, abi=ERC20_ABI) core_deposit = w3.eth.contract(address=CORE_DEPOSIT_WALLET, abi=DEPOSIT_ABI) # Check native USDC balance balance = native_usdc.functions.balanceOf(wallet_address).call() print(f"Native USDC balance: {balance / 10**6}") if balance < amount_wei: print("Insufficient USDC balance") return None # Check HYPE for gas hype_balance = w3.eth.get_balance(wallet_address) if hype_balance == 0: print("No HYPE for gas fees") return None nonce = w3.eth.get_transaction_count(wallet_address) # Step 1: Approve if needed allowance = native_usdc.functions.allowance(wallet_address, CORE_DEPOSIT_WALLET).call() if allowance < amount_wei: print(f"Approving {amount} USDC...") approve_tx = native_usdc.functions.approve( CORE_DEPOSIT_WALLET, amount_wei ).build_transaction({ 'from': wallet_address, 'nonce': nonce, 'gas': 100000, 'maxFeePerGas': w3.to_wei(0.1, 'gwei'), 'maxPriorityFeePerGas': w3.to_wei(0.05, 'gwei'), }) signed = w3.eth.account.sign_transaction(approve_tx, config['private_key']) tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction) w3.eth.wait_for_transaction_receipt(tx_hash) nonce += 1 print("Approval confirmed") # Step 2: Deposit to HyperCore destination_dex = SPOT_DEX if to_spot else PERPS_DEX print(f"Depositing to HyperCore {destination}...") deposit_tx = core_deposit.functions.deposit( amount_wei, destination_dex ).build_transaction({ 'from': wallet_address, 'nonce': nonce, 'gas': 200000, 'maxFeePerGas': w3.to_wei(0.1, 'gwei'), 'maxPriorityFeePerGas': w3.to_wei(0.05, 'gwei'), }) signed = w3.eth.account.sign_transaction(deposit_tx, config['private_key']) tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction) receipt = w3.eth.wait_for_transaction_receipt(tx_hash) if receipt['status'] == 1: print(f"Bridge successful! Gas used: {receipt['gasUsed']}") print(f"Funds credited to HyperCore {destination}") else: print("Transaction failed") return receipt if __name__ == "__main__": # Bridge 10 USDC to HyperCore Spot bridge_to_core(amount=10.0, to_spot=True) ``` > [!NOTE] > > The CoreDepositWallet source code is available at [circlefin/hyperevm-circle-contracts](https://github.com/circlefin/hyperevm-circle-contracts/blob/master/src/CoreDepositWallet.sol). ### How it works 1. **Approve** the native USDC contract to allow CoreDepositWallet to spend your tokens 2. **Call `deposit()`** on the CoreDepositWallet with: * Amount in 6 decimals * Destination dex (`0` for perps, `4294967295` for spot) 3. The CoreDepositWallet transfers USDC from your wallet and credits your HyperCore account > [!WARNING] > > For new HyperCore accounts, a small account creation fee may be deducted from your first deposit. ### Using CoreWriter for spot/perps transfers Once USDC is on HyperCore (via the direct transfer method above), you can use the **CoreWriter** system contract to transfer between spot and perps accounts: ```python theme={"system"} #!/usr/bin/env python3 """ Transfer USDC between Spot and Perps on HyperCore using CoreWriter Note: This requires USDC already on HyperCore, not HyperEVM """ from web3 import Web3 from eth_account import Account from eth_abi import encode import json # CoreWriter system contract address CORE_WRITER = Web3.to_checksum_address("0x3333333333333333333333333333333333333333") # CoreWriter ABI (minimal - for usdClassTransfer) CORE_WRITER_ABI = [ { "inputs": [{"name": "data", "type": "bytes"}], "name": "sendRawAction", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] def transfer_between_spot_perps(amount: float, to_perp: bool = True): """ Transfer USDC between Spot and Perps accounts on HyperCore. This uses the CoreWriter contract which emits events processed by HyperCore. Args: amount: Amount of USDC to transfer to_perp: If True, transfer Spot -> Perps; otherwise Perps -> Spot """ with open('config.json') as f: config = json.load(f) w3 = Web3(Web3.HTTPProvider(config['rpc_endpoint'])) wallet = Account.from_key(config['private_key']) wallet_address = Web3.to_checksum_address(wallet.address) direction = "Spot -> Perps" if to_perp else "Perps -> Spot" print(f"Transferring {amount} USDC ({direction})") # Amount in raw units (USDC has 6 decimals on HyperCore for this action) amount_raw = int(amount * 1e6) # Encode the usdClassTransfer action (action ID 7). # Header: version byte (0x01) + 3-byte big-endian action ID -> 0x01000007 # Payload: the raw ABI encoding of the action's Solidity types, abi.encode(uint64 ntl, bool toPerp). header = bytes([1]) + (7).to_bytes(3, "big") payload = encode(["uint64", "bool"], [amount_raw, to_perp]) data = header + payload core_writer = w3.eth.contract(address=CORE_WRITER, abi=CORE_WRITER_ABI) tx = core_writer.functions.sendRawAction(data).build_transaction({ 'from': wallet_address, 'nonce': w3.eth.get_transaction_count(wallet_address), 'gas': 100000, 'maxFeePerGas': w3.to_wei(0.1, 'gwei'), 'maxPriorityFeePerGas': w3.to_wei(0.05, 'gwei'), }) signed_tx = w3.eth.account.sign_transaction(tx, config['private_key']) tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) print(f"Transaction submitted: {tx_hash.hex()}") receipt = w3.eth.wait_for_transaction_receipt(tx_hash) if receipt['status'] == 1: print(f"✓ Transfer submitted to HyperCore") print(" Note: CoreWriter does not revert on HyperCore failures") else: print("✗ EVM transaction failed") return receipt if __name__ == "__main__": # Transfer 10 USDC from Spot to Perps transfer_between_spot_perps(amount=10.0, to_perp=True) ``` > [!WARNING] > > The CoreWriter contract (`0x3333333333333333333333333333333333333333`) is an event emitter only. It does not revert if the corresponding HyperCore action fails—the EVM transaction will succeed even if the HyperCore action silently fails. Always verify balances after transfers. > [!NOTE] > > For simple spot/perps transfers, you can also use the Python SDK's `usd_class_transfer()` method which handles the encoding automatically. The CoreWriter approach is useful when building smart contracts that need to interact with HyperCore—see [Write to HyperCore from a contract with CoreWriter](/docs/hyperliquid-corewriter) for the full contract pattern, the complete action table, and the prerequisites that keep actions from being silently dropped. ## Token linking For custom tokens (not USDC), you must link the HyperEVM ERC20 contract to the HyperCore spot token before bridging works. ### Check if a token is linked You can use Chainstack's Info HTTP API endpoint for this: ```python theme={"system"} from hyperliquid.info import Info import requests # Using Chainstack Info HTTP API endpoint CHAINSTACK_INFO_ENDPOINT = "YOUR_CHAINSTACK_INFO_ENDPOINT" # Option 1: Using the SDK info = Info(CHAINSTACK_INFO_ENDPOINT, skip_ws=True) spot_meta = info.spot_meta() # Option 2: Direct API call spot_meta = requests.post( f"{CHAINSTACK_INFO_ENDPOINT}/info", json={"type": "spotMeta"} ).json() # Check token linking status for token in spot_meta.get('tokens', []): evm_contract = token.get('evmContract') if evm_contract: address = evm_contract.get('address') if isinstance(evm_contract, dict) else evm_contract print(f"{token['name']}: linked to {address}") else: print(f"{token['name']}: NOT linked") ``` > [!NOTE] > > The `spotMeta` response includes the `evmContract` field for linked tokens. For USDC, you'll see the contract address and `evm_extra_wei_decimals` (typically -2, meaning 6 decimals on EVM vs 8 on HyperCore). ### Linking process (for token deployers) Token linking is a two-step process: **1. Request EVM contract** Send a `RequestEvmContract` action specifying the ERC20 contract address and decimal difference. **2. Finalize linking** Send a `FinalizeEvmContract` action with nonce verification or storage slot validation. > [!WARNING] > > Only the spot token deployer can link a token. USDC is already linked on mainnet. ## Testnet considerations > [!WARNING] > > Testnet bridging has specific restrictions that differ from mainnet. ### Net-transfer restriction On testnet, the net transfer from HyperCore to HyperEVM is capped. If you try to bridge more USDC to HyperEVM than you've previously bridged to HyperCore, the transaction will fail silently. **Workaround:** 1. First bridge USDC from an external chain (like Arbitrum Sepolia) to HyperCore 2. Then you can bridge that amount to HyperEVM ### New account fees New HyperCore accounts incur a 1 USDC fee on first deposit: * Minimum first deposit: >1 USDC + any forwarding fees * Deposits ≤1 USDC to new accounts fail silently * Multiple deposits to new accounts in the same block each incur the fee ## Common issues ### "Cannot self-transfer" error **Cause:** You tried to send tokens to your own address using `spot_transfer()`. **Solution:** For bridging, send to the **system address** (`0x2000000000000000000000000000000000000000`), not to your own address. Self-transfers to the same user are not allowed. ### "Token not linked" error **Cause:** The token hasn't been linked between HyperCore and HyperEVM. **Solution:** For USDC, this shouldn't happen on mainnet. For custom tokens, the token deployer must complete the linking process. ### balanceOf() reverts on CoreDepositWallet **Cause:** The CoreDepositWallet contract (from `spotMeta`) is not a standard ERC20—it's a bridge contract that doesn't support `balanceOf()` or other ERC20 read functions. **Solution:** This is expected behavior. To check your USDC balance on HyperEVM, call `balanceOf()` on the **Native USDC** contract (`0xb88339CB7199b77E23DB6E890353E22632Ba630f` on mainnet). ### "Caller is not the system address" error **Cause:** You tried to call `transfer()` directly on the CoreDepositWallet contract. This contract has access control and does not support standard ERC20 transfers. **Solution:** Use the `deposit(amount, destinationDex)` function on the CoreDepositWallet instead: 1. First approve your native USDC to the CoreDepositWallet 2. Call `deposit(amount, 4294967295)` for spot balance or `deposit(amount, 0)` for perps See the [HyperEVM to HyperCore](#hyperevm-to-hypercore) section for complete code examples. ### Silent failure on testnet **Cause:** Net-transfer restriction or insufficient amount for new account. **Solution:** * Ensure you've deposited to HyperCore before trying to bridge to HyperEVM * For new accounts, deposit >1 USDC ### Transaction succeeds but balance unchanged **Cause:** Transfer to unlinked token system address or incorrect destination. **Solution:** Verify the system address matches the token you're bridging. ### "No HYPE for gas" on HyperEVM **Cause:** HyperEVM uses HYPE as the native gas token, and your wallet has no HYPE. **Solution:** Bridge some HYPE from HyperCore to HyperEVM first. HYPE uses the special system address `0x2222222222222222222222222222222222222222`. ## Gas costs | Direction | Approximate cost | | - | - | | HyperCore → HyperEVM | \~200K gas at base price | | HyperEVM → HyperCore | \~100K gas (ERC20 transfer) | ## Best practices > [!TIP] > > **DO** > > * Use the Python SDK `spot_transfer()` for HyperCore → HyperEVM bridging > * Use the `CoreDepositWallet.deposit()` function for HyperEVM → HyperCore bridging > * Approve native USDC to the CoreDepositWallet before calling deposit > * Verify token linking status before bridging custom tokens > * Test with small amounts first > * Check balances on both sides after bridging > [!WARNING] > > **DON'T** > > * Call `transfer()` on the CoreDepositWallet contract (will fail with "Caller is not the system address") > * Confuse native USDC with the CoreDepositWallet address > * Bridge amounts ≤1 USDC to new HyperCore accounts > * Assume testnet behaves identically to mainnet > * Bridge unlinked tokens (they'll be lost) ## Related resources * [Hyperliquid methods](/docs/hyperliquid-methods) — Complete list of supported methods and endpoint capabilities * [Hyperliquid tooling](/docs/hyperliquid-tooling) — SDKs and API reference * [HyperCore to HyperEVM transfers](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm/hypercore-less-than-greater-than-hyperevm-transfers) — Official Hyperliquid documentation * [Circle CCTP documentation](https://developers.circle.com/cctp/cctp-on-hypercore) — Cross-chain USDC transfers * [Circle HyperEVM contracts](https://github.com/circlefin/hyperevm-circle-contracts) — CoreDepositWallet source code # Building a copy trading bot with spot order mirroring Source: https://docs.chainstack.com/docs/hyperliquid-copy-trading-websocket Build a copy trading bot that mirrors Hyperliquid spot trades in real time using WebSocket subscriptions and the Hyperliquid Python SDK on Chainstack. Copy trading on Hyperliquid requires more than just watching orders—you need proper state management, event sequencing, and dynamic order sizing. This guide shows you how to build a copy trading bot that mirrors spot trades in real-time. > [!NOTE] > > **Prerequisites** > > * Python 3.8 or higher > * `hyperliquid-python-sdk` installed (`pip install hyperliquid-python-sdk`) > * [Reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) ([sign up for free](https://console.chainstack.com/)) ## Understanding Hyperliquid's WebSocket architecture Hyperliquid provides two essential WebSocket channels for monitoring trading activity. These channels serve different purposes: **`orderUpdates`** delivers `WsOrder[]` events containing order status changes—when orders are placed, modified, or canceled. This is your primary signal for mirroring trading decisions. **`userEvents`** provides `WsUserEvent` data including fills, funding payments, and liquidations. For this tutorial, the most important part is that the channel provides TWAP order updates not available in `orderUpdates`. Together, these channels give you a complete picture of a trader's activity: ```python theme={"system"} order_subscription = { "method": "subscribe", "subscription": {"type": "orderUpdates", "user": LEADER_ADDRESS} } events_subscription = { "method": "subscribe", "subscription": {"type": "userEvents", "user": LEADER_ADDRESS} } await websocket.send(json.dumps(order_subscription)) await websocket.send(json.dumps(events_subscription)) ``` ## How spot markets work on Hyperliquid Before diving into implementation, you need to understand how Hyperliquid distinguishes spot from perpetual assets. The `coin` field format tells you the market type: **Perpetual contracts** use simple asset names returned from the `meta` endpoint: * `BTC` — Bitcoin perpetual * `ETH` — Ethereum perpetual * `SOL` — Solana perpetual **Spot markets** use two formats: * `@{index}` — numeric spot pair index (for example, `@0` for BTC/USDC, `@107` for HYPE/USDC) * `{TOKEN}/USDC` — human-readable pair names (for example, `PURR/USDC`) Why the `@index` format? It's Hyperliquid's internal representation—more efficient for the order book engine. When you receive WebSocket events, the `coin` field typically contains the `@index` format for spot trades. This matters for copy trading because: 1. You receive `@123` in WebSocket events 2. You need to query metadata using that index 3. You place orders using the same `@index` format The index maps to token metadata through the [`spotMeta`](/reference/hyperliquid-info-spotmeta) endpoint, which provides crucial information like price precision and size decimals. ## Detecting spot vs perpetual orders Your bot monitors a trader who trades both spot and perps. Spot orders are cash trades (you own the asset), perps involve leverage and funding payments. For simplicity, you might want to mirror only spot trades initially. The `coin` field in WebSocket events tells you the market type: ```python theme={"system"} def detect_market_type(coin_field): if coin_field.startswith("@"): return "SPOT" # "@123" = spot index format elif "/" in coin_field: return "SPOT" # "PURR/USDC" = spot pair name else: return "PERP" # "BTC", "ETH" = perpetual contracts ``` This logic follows Hyperliquid's naming convention: if the coin starts with `@` or contains `/`, it's a spot market. Otherwise, it's a perpetual contract. Filter to spot-only mirroring: ```python theme={"system"} def is_spot_order(coin_field): return detect_market_type(coin_field) == "SPOT" # In your event handler if not is_spot_order(coin_field): continue # Skip perp orders ``` ## Dynamic order sizing for spot assets A leader might trade \$10,000 of BTC, but your bot has a \$1,000 budget. You need proportional sizing based on your capital allocation. But it's not just about scaling down—Hyperliquid enforces strict precision rules. According to the [official documentation](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#order-limits): **Size precision** — Round to the asset's `szDecimals` (found in `meta` response): * `szDecimals = 3`: `1.001` valid, `1.0001` invalid * `szDecimals = 1`: `0.1` valid, `0.01` invalid **Price precision** — Max 5 significant figures, no more than `MAX_DECIMALS - szDecimals` decimal places (spot: `MAX_DECIMALS = 8`, perps: `MAX_DECIMALS = 6`): * `szDecimals = 0`: `0.0001234` valid (8 decimals) * `szDecimals = 2`: `0.0001234` invalid (exceeds 8-2=6 decimals) * `1234.5` valid, `1234.56` invalid (too many sig figs) Query metadata and calculate valid sizes: ```python theme={"system"} async def get_spot_asset_info(info: Info, coin_field: str) -> Optional[dict]: """Get real-time price and size decimals for proper order sizing""" spot_data = info.spot_meta_and_asset_ctxs() spot_meta = spot_data[0] asset_ctxs = spot_data[1] index = int(coin_field[1:]) # "@123" -> 123 ctx = asset_ctxs[index] price = float(ctx.get("midPx", ctx.get("markPx", 0))) pair_info = next(p for p in spot_meta["universe"] if p["index"] == index) token_info = spot_meta["tokens"][pair_info["tokens"][0]] return { "price": price, "szDecimals": token_info.get("szDecimals", 6), "coin": coin_field } # Calculate and round order size asset_info = await get_spot_asset_info(info, "@0") follower_size = round( FIXED_ORDER_VALUE_USDC / asset_info["price"], asset_info["szDecimals"] ) ``` **Example**: \$15 / \$65,000 BTC with `szDecimals = 5` → `0.00023076923...` rounds to `0.00023`. ## Understanding order lifecycle and state transitions Orders on Hyperliquid go through a lifecycle with specific state transitions. When you subscribe to `orderUpdates`, you receive events for each state change. ### Order state events explained **`open` status** — A new order has been placed on the book by the leader. **`canceled` status** — Order removed from the book, either by: * Trader manually canceling * Part of a modification (old order canceled, new order created with different ID) * System cancellation (insufficient balance, delisting, etc.) **`filled` status** — Order fully executed. You'll also see detailed fill information in the `userEvents` channel. ### How order modifications work When a trader modifies an order on Hyperliquid (changes price or size), the exchange handles this atomically as: 1. Cancel the existing order (you receive `"canceled"` event) 2. Create a new order with the new parameters (you receive `"open"` event with **new order ID**) This means you don't need special modification logic—simply handle cancellations and new orders: ```python theme={"system"} async def handle_leader_order_events(data: dict, exchange: Exchange, info: Info): """Process order lifecycle events from leader""" for order_update in data.get("data", []): order = order_update.get("order", {}) status = order_update.get("status") leader_order_id = order.get("oid") coin_field = order.get("coin", "") # Skip non-spot orders if not is_spot_order(coin_field): continue if status == "open": # New order placed - mirror it follower_order_id = await place_follower_order(exchange, info, order) if follower_order_id: # Store mapping for future cancellations order_mappings[leader_order_id] = follower_order_id elif status == "canceled" and leader_order_id in order_mappings: # Leader canceled an order - cancel our follower order follower_order_id = order_mappings[leader_order_id] await cancel_follower_order(exchange, follower_order_id, coin_field) # Remove from mapping - this order is no longer active del order_mappings[leader_order_id] ``` The `order_mappings` dictionary tracks active orders by mapping each leader order ID to your corresponding follower order ID. This allows you to cancel the correct follower order when the leader cancels ## Placing orders with the Hyperliquid SDK The [hyperliquid-python-sdk](https://github.com/hyperliquid-dex/hyperliquid-python-sdk) provides the `exchange.order()` method for placing trades. Let's break down each parameter and why it matters: ```python theme={"system"} result = exchange.order( name=coin_field, # "@0" for BTC/USDC - must match WebSocket coin format is_buy=True, # True for buy, False for sell sz=order_size, # Calculated with proper precision from metadata limit_px=price, # Limit price - protects against slippage order_type=HLOrderType({"limit": {"tif": "Gtc"}}), # Good-til-canceled reduce_only=False # False = can open new positions ) ``` The response structure tells you whether your order was accepted: ```python theme={"system"} if result.get("status") == "ok": statuses = result["response"]["data"]["statuses"] status_info = statuses[0] if "resting" in status_info: # Order placed on the book, waiting to fill follower_order_id = status_info["resting"]["oid"] return follower_order_id elif "filled" in status_info: # Order executed immediately (market was liquid enough) follower_order_id = status_info["filled"]["oid"] return follower_order_id ``` Why check both `resting` and `filled`? If you place a limit buy at \$100 and the best ask is \$99, your order fills immediately. You still need the order ID for tracking fills and state management. > [!TIP] > > Order successfully placed and order ID received for tracking ## Testing with same wallet (development only) > [!WARNING] > > This section describes a **development shortcut** for testing. In production, you'll use separate leader and follower wallets, and this infinite loop problem won't exist. When building your bot, you probably don't want to manage two separate wallets and fund both with testnet USDC. For testing purposes, you can use the same wallet as both leader and follower—but this creates a challenge. **The infinite loop problem**: **1. Leader places order** The leader wallet (your test wallet) initiates a new order **2. Bot mirrors it as follower** Your bot detects the order and places a mirror order using the same wallet **3. Bot sees its own follower order** WebSocket receives the follower order as a "new leader order" (because you're watching the same wallet) **4. Bot tries to mirror again** Bot attempts to mirror the follower order, creating another order **5. Infinite loop** Process repeats indefinitely, placing unlimited orders **The solution**: Track which orders are follower-placed and skip them: ```python theme={"system"} order_mappings: Dict[int, int] = {} # leader_order_id -> follower_order_id # In event handler if leader_order_id in order_mappings.values(): # This order ID is a follower order we placed - skip it print(f"Skipping follower order {leader_order_id}") continue ``` When you mirror a leader order, you store the mapping `{leader_order_id: follower_order_id}`. Later, when that follower order appears in the WebSocket feed (because you're watching your own wallet), you check: "Is this order ID in my follower list?" If yes, ignore it. **Important**: In production with separate wallets, leader and follower order IDs never overlap—you don't need this check. This is purely for development convenience. ## Sequential processing (simplified for testing) > [!NOTE] > > This section describes a **simplified architecture for testing**. In production with high-frequency trading, you'd process events in parallel with proper concurrency controls. When WebSocket messages arrive, you have two architectural choices: **Concurrent processing** (production): * Process multiple order events simultaneously * Requires locks/semaphores to prevent race conditions * Optimal for high-frequency traders placing hundreds of orders per minute * More complex to implement and debug **Sequential processing** (testing/development): * Process one order event at a time * No race conditions by design * Simple to implement and reason about * Sufficient for mirroring human traders (10-100 orders per hour) For testing and learning, sequential processing is simpler: ```python theme={"system"} message_queue = asyncio.Queue() async def message_receiver(): """Receive messages and queue them""" async for message in websocket: await message_queue.put(message) async def message_processor(): """Process messages one at a time""" while running: message = await asyncio.wait_for(message_queue.get(), timeout=1.0) data = json.loads(message) await handle_leader_order_events(data, exchange, info) message_queue.task_done() # Run both tasks concurrently await asyncio.gather(message_receiver(), message_processor()) ``` The receiver task puts messages in the queue as fast as they arrive. The processor task pulls one message, processes it completely (place order, update mapping, etc.), marks it done, then moves to the next. **When to upgrade to parallel**: When you're mirroring algorithmic traders or high-frequency strategies that place 10+ orders per second. At that scale, you need concurrent processing with proper state locking. ## Summary Copy trading on Hyperliquid requires tracking order state through WebSocket subscriptions, mapping spot assets via `@index` format, and calculating properly rounded order sizes using metadata. Order modifications are handled as cancel + create events, eliminating the need for special modification logic. For production, use separate leader and follower wallets with concurrent event processing. > [!NOTE] > > **Complete implementation**: See the [Chainstack Hyperliquid trading bot repository](https://github.com/chainstacklabs/hyperliquid-trading-bot/tree/main/learning_examples) for the full working code. ## Related resources * [Authentication guide](/docs/hyperliquid-authentication-guide) — Authenticate with Hyperliquid exchange API * [L1 action signing](/docs/hyperliquid-l1-action-signing) — Understand L1 action signing for trading operations * [API reference](/reference/hyperliquid-getting-started) — Explore the complete Hyperliquid API reference * [Node configuration](/docs/hyperliquid-node-configuration) — Configure your Hyperliquid node endpoint # Write to HyperCore from a contract with CoreWriter Source: https://docs.chainstack.com/docs/hyperliquid-corewriter Use the CoreWriter system contract to send HyperCore actions—orders, transfers, staking—from a HyperEVM smart contract, with a complete Solidity example and the prerequisites that stop actions from being silently dropped. CoreWriter is the bridge that lets a HyperEVM smart contract act on HyperCore. Your contract places orders, moves funds between accounts, stakes, and delegates—the same actions a trader sends to the `/exchange` endpoint—directly from Solidity. This guide explains how CoreWriter encodes those actions, walks through a contract you can deploy on a Chainstack HyperEVM endpoint, and covers the account prerequisites that decide whether your actions actually land. > [!NOTE] > > **Prerequisites** > > * [Foundry](https://book.getfoundry.sh/getting-started/installation) installed (`forge` and `cast`) > * A [reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) from Chainstack ([sign up for free](https://console.chainstack.com/)) > * A HyperEVM account funded with HYPE for gas > * For the production path: [`hyper-evm-lib`](https://github.com/hyperliquid-dev/hyper-evm-lib) (`forge install hyperliquid-dev/hyper-evm-lib`) > * Basic Solidity and Foundry knowledge ## Two directions: read precompiles and CoreWriter HyperEVM and HyperCore are two execution environments on the same chain, and contracts move data between them in two directions: * **Read** — HyperCore exposes read precompiles starting at `0x0000000000000000000000000000000000000800`. A contract calls them to query perp positions, spot balances, oracle prices, staking delegations, and the L1 block number. The returned values match HyperCore state at the time the EVM block is built. * **Write** — the CoreWriter system contract at `0x3333333333333333333333333333333333333333` sends actions *to* HyperCore. This guide covers the write direction. This split mirrors the API: read precompiles are the on-chain equivalent of `/info` queries, and CoreWriter is the on-chain equivalent of signed `/exchange` actions. The difference is that CoreWriter actions are authored *by the calling contract's own address*—the contract is the HyperCore actor, so no off-chain signature is involved. > [!NOTE] > > CoreWriter actions are submitted as ordinary HyperEVM transactions, so deploying the contract and calling CoreWriter both run against your Chainstack HyperEVM (`/evm`) endpoint—a contract drives HyperCore orders and transfers without the signed `/exchange` API. Chainstack also serves the `/info` reads used to verify results ([`clearinghouseState`](/reference/hyperliquid-info-clearinghousestate), [`spotClearinghouseState`](/reference/hyperliquid-info-spotclearinghousestate), [`openOrders`](/reference/hyperliquid-info-openorders)). The one step that needs Hyperliquid's public `/exchange` endpoint is the big-block toggle below. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full endpoint matrix. ## How CoreWriter works CoreWriter exposes a single function: ```solidity theme={"system"} interface ICoreWriter { function sendRawAction(bytes calldata data) external; } ``` When you call `sendRawAction`, the contract burns roughly 25,000 gas and emits a log that HyperCore picks up and processes as an action. A basic call costs around 47,000 gas in total. The action runs as the caller's HyperCore user—the contract address that invoked `sendRawAction`. ### Action encoding The `data` argument is a byte string with a fixed 4-byte header followed by an ABI-encoded payload: | Bytes | Meaning | | - | - | | Byte 1 | Encoding version. Only version `1` is supported today; the version byte keeps the format upgradeable. | | Bytes 2–4 | Action ID, as a big-endian unsigned integer. | | Remaining bytes | The action payload—the raw ABI encoding of a sequence of Solidity types specific to the action. | In Solidity this is exactly `abi.encodePacked(uint8(1), uint24(actionId), abi.encode(...fields))`: the `uint8` is the version byte, the `uint24` is the 3-byte big-endian action ID, and `abi.encode` produces the payload. ### Supported actions CoreWriter supports the following actions. The fields are ABI-encoded in the order shown. Action ID `14` is intentionally absent. | ID | Action | Fields | Solidity types | | - | - | - | - | | 1 | Limit order | (asset, isBuy, limitPx, sz, reduceOnly, encodedTif, cloid) | (uint32, bool, uint64, uint64, bool, uint8, uint128) | | 2 | Vault transfer | (vault, isDeposit, usd) | (address, bool, uint64) | | 3 | Token delegate | (validator, wei, isUndelegate) | (address, uint64, bool) | | 4 | Staking deposit | wei | uint64 | | 5 | Staking withdraw | wei | uint64 | | 6 | Spot send | (destination, token, wei) | (address, uint64, uint64) | | 7 | USD class transfer | (ntl, toPerp) | (uint64, bool) | | 8 | Finalize EVM contract | (token, encodedFinalizeEvmContractVariant, createNonce) | (uint64, uint8, uint64) | | 9 | Add API wallet | (apiWalletAddress, apiWalletName) | (address, string) | | 10 | Cancel order by oid | (asset, oid) | (uint32, uint64) | | 11 | Cancel order by cloid | (asset, cloid) | (uint32, uint128) | | 12 | Approve builder fee | (maxFeeRate, builder) | (uint64, address) | | 13 | Send asset | (destination, subAccount, sourceDex, destinationDex, token, wei) | (address, address, uint32, uint32, uint64, uint64) | | 15 | Borrow lend operation | (encodedOperation, token, wei) | (uint8, uint64, uint64) | | 16 | Set abstraction | (user, abstraction) | (address, uint8) | A few encoding conventions are easy to get wrong: * **Limit order** — `encodedTif` is `1` for Alo, `2` for Gtc, `3` for Ioc. `cloid` of `0` means no client order ID; any other value is used as the cloid. `limitPx` and `sz` are sent as 10^8 times the human-readable value, and `sz` must respect the asset's `szDecimals` (see [Make sure your action lands](#make-sure-your-action-lands)). * **Finalize EVM contract** — `encodedFinalizeEvmContractVariant` is `1` for Create, `2` for FirstStorageSlot, `3` for CustomStorageSlot. `createNonce` is only used with the Create variant. * **Add API wallet** — an empty `apiWalletName` makes this the main API wallet (agent). * **Approve builder fee** — `maxFeeRate` is in decibps. To approve a 0.01% fee, pass `10`. * **Send asset** — if `subAccount` is not the zero address, the transfer comes from that sub-account. Use `uint32` max for `sourceDex` or `destinationDex` to mean spot. * **Borrow lend operation** — `encodedOperation` is `0` for Supply, `1` for Withdraw. A `wei` of `0` applies the operation maximally (for example, withdraw the full reserve balance). * **Set abstraction** — `abstraction` is `1` for disabled (standard), `2` for unifiedAccount, `3` for portfolioMargin. `user` can be the master user or a sub-account. ## Encode an action yourself The contract below wraps two actions—a USD class transfer (move USDC between the perp and spot wallets) and a limit order—and exposes a generic `sendRawAction` passthrough for any action you encode yourself. It depends on nothing but the CoreWriter interface. ```solidity CoreWriterCaller.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; interface ICoreWriter { function sendRawAction(bytes calldata data) external; } contract CoreWriterCaller { ICoreWriter constant CORE_WRITER = ICoreWriter(0x3333333333333333333333333333333333333333); // Action 7: move USDC between this contract's spot and perp balances. // ntl is the amount in perp units (USDC has 6 decimals on the perp side). function usdClassTransfer(uint64 ntl, bool toPerp) external { bytes memory action = abi.encodePacked(uint8(1), uint24(7), abi.encode(ntl, toPerp)); CORE_WRITER.sendRawAction(action); } // Action 1: place a limit order. // limitPx and sz are 10^8 * the human-readable value; sz must respect the // asset's szDecimals. encodedTif: 1 = Alo, 2 = Gtc, 3 = Ioc. cloid 0 = none. function limitOrder( uint32 asset, bool isBuy, uint64 limitPx, uint64 sz, bool reduceOnly, uint8 encodedTif, uint128 cloid ) external { bytes memory action = abi.encodePacked( uint8(1), uint24(1), abi.encode(asset, isBuy, limitPx, sz, reduceOnly, encodedTif, cloid) ); CORE_WRITER.sendRawAction(action); } // Escape hatch: send any action by ID with a pre-encoded payload. function sendRawAction(uint24 actionId, bytes calldata payload) external { CORE_WRITER.sendRawAction(abi.encodePacked(uint8(1), actionId, payload)); } } ``` `abi.encodePacked(uint8(1), uint24(actionId), ...)` lays the header out exactly as the encoding table describes: the `uint8` occupies one byte, the `uint24` occupies three big-endian bytes, and the ABI-encoded payload follows. Building the action this way keeps the contract self-contained, so callers pass plain arguments instead of pre-formatted byte strings. ## Use hyper-evm-lib in production Encoding actions by hand is useful for understanding the wire format, but most builders use [`hyper-evm-lib`](https://github.com/hyperliquid-dev/hyper-evm-lib)—an MIT-licensed, actively maintained Solidity library (under the `hyperliquid-dev` org) that wraps every CoreWriter action and read precompile with typed helpers, handles the EVM↔Core decimal conversions, and ships a Foundry test engine that simulates HyperCore locally so you can test contracts without deploying to testnet. Install it with Foundry: ```bash theme={"system"} forge install hyperliquid-dev/hyper-evm-lib ``` The same two actions, plus an on-chain readiness check, with the library: ```solidity TraderVault.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {CoreWriterLib} from "hyper-evm-lib/src/CoreWriterLib.sol"; import {PrecompileLib} from "hyper-evm-lib/src/PrecompileLib.sol"; contract TraderVault { using CoreWriterLib for *; // Move USDC from spot to perp, then place a GTC limit order. // Call only after this contract's HyperCore account exists and holds USDC. function openPosition( uint32 asset, bool isBuy, uint64 limitPx, uint64 sz, uint64 perpUsdc ) external { CoreWriterLib.transferUsdClass(perpUsdc, true); // spot -> perp CoreWriterLib.placeLimitOrder(asset, isBuy, limitPx, sz, false, 2, 0); } // Confirm on-chain that this contract is a HyperCore user before acting. function isReady() external view returns (bool) { return PrecompileLib.coreUserExists(address(this)); } } ``` `CoreWriterLib` exposes named helpers for the full action set—`placeLimitOrder`, `transferUsdClass`, `spotSend`, `delegateToken`, `vaultTransfer`, `setAbstraction`, `bridgeToCore`, and more—each of which encodes the action exactly as the table above describes. `PrecompileLib` mirrors the read precompiles, including `coreUserExists` and `perpAssetInfo` (which returns `szDecimals`). ## Make sure your action lands CoreWriter actions are fire-and-forget. `sendRawAction` emits its log and the EVM transaction succeeds **whether or not HyperCore accepts the action**. If the action is malformed or the account is not set up, HyperCore drops it silently—no EVM revert, no error event, and the order simply never appears in `openOrders`. This is the single most common source of confusion, so check the following before concluding that CoreWriter is broken. ### The account must exist on HyperCore first A CoreWriter action runs as the *contract's* HyperCore user, and that user must already exist on HyperCore **before the EVM block is built**. An address becomes a HyperCore user once it receives a Core asset such as USDC. > [!WARNING] > > Initializing the account with a HyperEVM-to-HyperCore transfer *in the same block* as the action does not work—the action is still rejected. Fund the contract's HyperCore account in an earlier block, then send actions. Send a small amount of USDC to the contract's address on HyperCore in a separate, earlier transaction. You can confirm the account exists on-chain by calling the core-user-exists precompile at `0x0000000000000000000000000000000000000810` (`PrecompileLib.coreUserExists(address)` in `hyper-evm-lib`) before sending the action. ### Perp orders need USDC on the perp side Bridging USDC from HyperEVM lands it in the contract's **spot** balance. A perp limit order draws on the **perp** balance, so a freshly funded contract can hold plenty of USDC and still have its orders dropped. Move funds to the perp side first with a USD class transfer (action 7, `toPerp: true`)—that is why the `TraderVault` example calls `transferUsdClass(perpUsdc, true)` before `placeLimitOrder`. > [!NOTE] > > Setting the account abstraction mode (action 16) is **not** required to place an order—a new account places orders fine in the default mode. You only need `setAbstraction` to change how margin works, or to put a builder-fee address into standard mode, which is required for builder-fee accrual. ### Respect tick and lot size `limitPx` and `sz` are scaled by 10^8, but they must also conform to the asset's tick and lot size. Sizes must be rounded to the asset's `szDecimals`: if `szDecimals` is `3`, then `1.001` is valid but `1.0001` is not. A size or price with too many decimals is rejected by HyperCore—again, silently. Read `szDecimals` from the perp-asset-info precompile at `0x000000000000000000000000000000000000080a` (`PrecompileLib.perpAssetInfo(asset).szDecimals`). ### Wait for the action to process Order actions are delayed on-chain by a few seconds (see [Action timing](#action-timing)). Do not check `openOrders` in the same block and conclude the order was dropped—give it at least one block confirmation first. ## Deploy and call the contract ### Compile Save the contract in a Foundry project and compile it: ```bash theme={"system"} forge build ``` ### Deploy through a Chainstack endpoint HyperEVM uses a [dual-block architecture](/docs/hyperliquid-development): fast 1-second small blocks with a 3M gas limit, and slow 1-minute big blocks with a 30M gas limit. The example contracts here deploy comfortably in small blocks. Larger contracts need big blocks, which you opt into with the HyperCore action `{"type": "evmUserModify", "usingBigBlocks": true}`. That is an `/exchange` action, so—unlike the deploy and contract calls, which use your Chainstack endpoint—it goes to Hyperliquid's public endpoint (`https://api.hyperliquid.xyz/exchange`); Chainstack does not serve `/exchange` (see [Hyperliquid methods](/docs/hyperliquid-methods)). Deploy with `forge create`, pointing at your Chainstack HyperEVM endpoint: ```bash theme={"system"} forge create src/CoreWriterCaller.sol:CoreWriterCaller \ --rpc-url YOUR_CHAINSTACK_HYPERLIQUID_ENDPOINT \ --private-key 0xYOUR_PRIVATE_KEY \ --broadcast ``` > [!NOTE] > > Replace `YOUR_CHAINSTACK_HYPERLIQUID_ENDPOINT` with your Chainstack HyperEVM endpoint. The Hyperliquid HyperEVM mainnet uses chain ID 999. ### Send an action Once the contract is deployed and its HyperCore account exists (see [Make sure your action lands](#make-sure-your-action-lands)), call an action with `cast`. This moves 5 USDC (5,000,000 in perp units) from the contract's spot wallet to its perp wallet: ```bash theme={"system"} cast send YOUR_CONTRACT_ADDRESS \ "usdClassTransfer(uint64,bool)" 5000000 true \ --rpc-url YOUR_CHAINSTACK_HYPERLIQUID_ENDPOINT \ --private-key 0xYOUR_PRIVATE_KEY ``` To place a limit buy for `0.1` units of asset `0` at a price of `1000`, with Gtc time-in-force and no client order ID: ```bash theme={"system"} cast send YOUR_CONTRACT_ADDRESS \ "limitOrder(uint32,bool,uint64,uint64,bool,uint8,uint128)" \ 0 true 100000000000 10000000 false 2 0 \ --rpc-url YOUR_CHAINSTACK_HYPERLIQUID_ENDPOINT \ --private-key 0xYOUR_PRIVATE_KEY ``` Here `100000000000` is `1000 * 10^8` (the price) and `10000000` is `0.1 * 10^8` (the size). ## Action timing CoreWriter actions are not applied the instant the EVM transaction lands. On an L1 block that produces a HyperEVM block, the order of operations is: 1. The L1 block is built. 2. The EVM block is built. 3. EVM-to-Core transfers are processed. 4. CoreWriter actions are processed. To prevent latency advantages from bypassing the L1 mempool, order actions and vault transfers sent through CoreWriter are delayed on-chain for a few seconds. This has no practical effect on user experience because the user already waits for at least one block confirmation. These delayed actions appear twice in the L1 explorer: first as an enqueuing, then as the HyperCore execution. ## Verify the result CoreWriter does not return a value to the caller—`sendRawAction` only emits a log. To confirm an action took effect, read HyperCore state: * Query the API `/info` endpoint (for example, [`clearinghouseState`](/reference/hyperliquid-info-clearinghousestate) for perp balances or [`spotClearinghouseState`](/reference/hyperliquid-info-spotclearinghousestate) for spot balances) for the contract address. * Read it on-chain through the HyperCore read precompiles at `0x0800` and above. * Watch the L1 explorer for the enqueuing and execution entries described above. If an action does not appear, work through [Make sure your action lands](#make-sure-your-action-lands): an uninitialized HyperCore account, funds on the wrong (spot vs perp) side, a size that violates `szDecimals`, or checking before the action delay elapses. ## Next steps * Read the [Hyperliquid methods reference](/docs/hyperliquid-methods) for the full set of HyperCore actions and queries. * [Fork Hyperliquid EVM with Foundry](/docs/hyperliquid-forking-evm-foundry) to test CoreWriter calls against live state locally. * Explore [Hyperliquid smart contract development](/docs/hyperliquid-development) for the dual-block model and HyperEVM tooling. # Hyperliquid: Debugging signature errors Source: https://docs.chainstack.com/docs/hyperliquid-debugging-signature-errors Diagnose and fix common Hyperliquid L1 action signature errors including chain ID mismatches, nonce issues, and vault address validation problems. ## Overview Signature errors on Hyperliquid often produce cryptic messages that don't point to the actual cause. This guide covers the most common issues developers encounter, based on real problems reported in the Hyperliquid developer community. > [!NOTE] > > For foundational signing concepts, see the [authentication guide](/docs/hyperliquid-authentication-guide) and [signing overview](/docs/hyperliquid-signing-overview). ## "User or API Wallet does not exist" with changing addresses This error is one of the most confusing because the wallet address in the error message changes with every request: ``` L1 error: User or API Wallet 0xABC123... does not exist. L1 error: User or API Wallet 0xDEF456... does not exist. // Different address! ``` ### Why the address changes The address isn't random—it's the **recovered address** from your signature. Hyperliquid uses ECDSA signature recovery to determine who signed the message. When your signature is malformed, the recovery produces a different (invalid) address each time. ### Root causes > [accordion] Key ordering in action objects > > The action hash depends on msgpack serialization, which is **order-sensitive**. Different key orders produce different hashes, leading to different recovered addresses. > > ```python theme={"system"} > # WRONG - unpredictable key order in Python dict > action = {"sz": "0.001", "coin": "BTC", "is_buy": True} > > # CORRECT - use SDK helper functions that ensure consistent ordering > from hyperliquid.utils.signing import order_request_to_order_wire > action = order_request_to_order_wire(order_request) > ``` > > ```typescript theme={"system"} > // WRONG - object property order can vary > const action = { sz: "0.001", coin: "BTC", isBuy: true }; > > // CORRECT - use SDK methods that handle ordering > const result = await client.order({ > orders: [{ a: 0, b: true, p: "50000", s: "0.001", r: false, t: { limit: { tif: "Gtc" } } }], > grouping: "na" > }); > ``` > [accordion] Address case sensitivity > > Hyperliquid requires lowercase addresses for signature verification: > > ```python theme={"system"} > # WRONG - mixed case > agent_address = "0xAbCdEf1234567890AbCdEf1234567890AbCdEf12" > > # CORRECT - lowercase > agent_address = "0xabcdef1234567890abcdef1234567890abcdef12" > ``` > > > [!WARNING] > > > > Always lowercase addresses before signing. The SDK does this automatically, but manual implementations must handle it explicitly. > [accordion] Agent wallet not approved > > If you're using an agent wallet that hasn't been approved by the master account, signature recovery fails: > > ```python theme={"system"} > from hyperliquid.info import Info > from hyperliquid.utils import constants > > info = Info(constants.MAINNET_API_URL, skip_ws=True) > state = info.user_state("0x_YOUR_MASTER_ADDRESS") > > # Check approved agents > print(state.get("agentAddresses", [])) > ``` > > If your agent isn't listed, approve it first: > > ```python theme={"system"} > result, agent_key = exchange.approve_agent("my_bot") > ``` > [accordion] Account has no funds > > Even with a valid signature, the error appears if the master account has zero balance: > > ```python theme={"system"} > # Verify account has collateral > state = info.user_state("0x_YOUR_ADDRESS") > print(f"Margin summary: {state.get('marginSummary', {})}") > ``` > > Deposit USDC before attempting trades. ### Debugging checklist **1. Log the exact action object** Print the action just before signing to verify structure and key order **2. Verify address formatting** Ensure all addresses are lowercase with `0x` prefix **3. Check agent approval** Query `user_state` to confirm agent is in `agentAddresses` list **4. Verify account balance** Confirm the master account has collateral deposited **5. Compare with SDK output** Run the equivalent SDK method and compare the generated signature ## chainId mismatch in browser wallets When integrating with viem or wagmi, you may encounter: ``` ChainMismatchError: Provided chainId '1337' must match the active chainId '42161' ``` Or from MetaMask: ``` Provided chainId "1337" must match the active chainId "10" ``` ### Why this happens Hyperliquid L1 actions require signing with **chainId 1337**, but your wallet is connected to Arbitrum (42161), Optimism (10), or another chain. Browser wallets enforce that the signing chain matches the connected chain. ### Solutions > [tab] Use agent wallets (recommended) > > Create an agent wallet for trading operations. The agent signs server-side with chainId 1337, avoiding the browser mismatch entirely: > > ```typescript theme={"system"} > import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; > import { privateKeyToAccount } from "viem/accounts"; > > // Agent wallet signs with chainId 1337 automatically > const agentAccount = privateKeyToAccount(AGENT_PRIVATE_KEY); > const client = new ExchangeClient({ > transport: new HttpTransport(), > wallet: agentAccount > }); > > // User's browser wallet only signs approveAgent (chainId 0x66eee) > // which matches Arbitrum, so no mismatch > ``` > [tab] Custom chain definition > > Define a custom chain for Hyperliquid signing: > > ```typescript theme={"system"} > import { createWalletClient, custom } from "viem"; > > const hyperliquidL1 = { > id: 1337, > name: "Hyperliquid L1", > nativeCurrency: { name: "USD", symbol: "USD", decimals: 18 }, > rpcUrls: { > default: { http: ["https://api.hyperliquid.xyz"] } > } > } as const; > > const walletClient = createWalletClient({ > chain: hyperliquidL1, > transport: custom(window.ethereum) > }); > ``` > > > [!WARNING] > > > > This approach shows users unreadable hex data when signing. Not recommended for production frontends where user trust matters. > [tab] Sign with local account > > Use a local viem account that doesn't enforce chain matching: > > ```typescript theme={"system"} > import { privateKeyToAccount } from "viem/accounts"; > import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; > > // Local accounts don't enforce chainId matching > const account = privateKeyToAccount(PRIVATE_KEY); > const client = new ExchangeClient({ transport: new HttpTransport(), wallet: account }); > ``` ### Best practice for frontends For production applications, use this two-wallet pattern: 1. **Browser wallet** (user's MetaMask/WalletConnect) — signs [`approveAgent`](/reference/hyperliquid-exchange-approve-agent) (chainId 0x66eee matches Arbitrum) 2. **Agent wallet** (generated keypair stored locally) — signs all L1 actions (chainId 1337) ```typescript theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // First: user approves agent via browser wallet (one-time) const agentAddress = await approveAgentWithBrowserWallet(); // Then: agent handles all trading const agentClient = new ExchangeClient({ transport: new HttpTransport(), wallet: privateKeyToAccount(agentPrivateKey) }); // No chainId conflicts because browser wallet never signs L1 actions ``` ## TypeScript SDK options Hyperliquid has two community TypeScript SDKs: | SDK | Maintainer | Features | | - | - | - | | [@nktkas/hyperliquid](https://github.com/nktkas/hyperliquid) | nktkas | Full API coverage, viem/ethers wallet support | | [nomeida/hyperliquid](https://github.com/nomeida/hyperliquid) | nomeida | Simpler API, good for quick integrations | ### @nktkas/hyperliquid examples Install: ```bash theme={"system"} npm install @nktkas/hyperliquid viem ``` ```typescript With viem theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const account = privateKeyToAccount("0x..."); // your private key const client = new ExchangeClient({ transport: new HttpTransport(), wallet: account }); const result = await client.order({ orders: [{ a: 0, // Asset index (0 = BTC) b: true, // is_buy p: "50000", // price s: "0.001", // size r: false, // reduce_only t: { limit: { tif: "Gtc" } } }], grouping: "na" }); ``` ```typescript With an ethers account theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { Wallet } from "ethers"; const wallet = new Wallet("0x..."); // your private key const client = new ExchangeClient({ transport: new HttpTransport(), wallet }); // Same API as with viem const result = await client.order({...}); ``` ```typescript Manual signing for custom wallets theme={"system"} import { signL1Action } from "@nktkas/hyperliquid/signing"; // For hardware wallets, MPC, or other custom signers const signature = await signL1Action({ wallet: myCustomWallet, // Must implement signTypedData action: orderAction, nonce: Date.now(), isTestnet: false }); // Submit manually const response = await fetch("https://api.hyperliquid.xyz/exchange", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: orderAction, nonce: signature.nonce, signature: signature.signature, vaultAddress: null }) }); ``` ## reserveRequestWeight for HIP-3 builders If you're building a HIP-3 DEX and need to push oracle prices, you'll encounter [`reserveRequestWeight`](/reference/hyperliquid-exchange-reserve-request-weight)—an action that uses a unique hybrid signing pattern. ### The edge case Most actions follow clear rules: * L1 actions → phantom agent schema, signed by agent wallet * User-signed actions → direct EIP-712, signed by user wallet `reserveRequestWeight` breaks this pattern: it uses the **agent-style schema** but must be signed by the **user wallet**. > [!NOTE] > > This hybrid exists because oracle updates need the authority of the main account but use the same submission path as L1 actions. ### Implementation ```python theme={"system"} # The SDK handles this internally for HIP-3 operations # If implementing manually, use phantom agent construction # but sign with the user's wallet (not an agent wallet) action = { "type": "reserveRequestWeight", "asset": 110000, # HIP-3 asset index "weight": 1000 } # Uses phantom agent hash BUT requires user wallet signature signature = sign_l1_action( user_wallet, # NOT agent_wallet action, None, timestamp, None, True ) ``` ## Error reference | Error message | Likely cause | Solution | | - | - | - | | "User or API Wallet does not exist" | Signature recovery failed | Check key ordering, address case, agent approval | | "Invalid signature" | Wrong signing scheme | L1 actions: chainId 1337; user-signed: chainId 0x66eee | | "Provided chainId must match" | Browser wallet chain mismatch | Use agent wallet for L1 actions | | "Invalid or expired nonce" | Nonce not in milliseconds | Use `get_timestamp_ms()` or `Date.now()` | | "Failed to deserialize" | Missing required fields | Ensure all required fields present (e.g., `grouping` for orders) | | "Agent not approved" | Agent wallet not authorized | Call `approve_agent()` first | | "Agent already exists" | Duplicate agent name | Use unique names per agent | ## Summary Most Hyperliquid signature errors fall into three categories: 1. **Serialization issues** — Key ordering, address casing, missing fields 2. **Chain ID confusion** — Using wrong chainId for L1 vs user-signed actions 3. **Wallet configuration** — Agent not approved, no funds, wrong wallet type When debugging, always start by comparing your implementation against the SDK's output—it handles all the edge cases correctly. ## Related resources * [Authentication guide](/docs/hyperliquid-authentication-guide) — Overview of both signing mechanisms * [L1 action signing](/docs/hyperliquid-l1-action-signing) — Phantom agent construction details * [User-signed actions](/docs/hyperliquid-user-signed-actions) — Agent approval and transfers * [Hyperliquid API reference](/reference/hyperliquid-getting-started) — Complete endpoint documentation # Mastering Hyperliquid Source: https://docs.chainstack.com/docs/hyperliquid-development Get started with Hyperliquid development on Chainstack, covering node setup, Python SDK configuration, API authentication, and trading integration. Welcome to the Mastering Hyperliquid series. These guides will help you build powerful applications on Hyperliquid's L1 blockchain and leverage its onchain order book for trading. ## What you'll learn > [card] [Authentication & Signatures](/docs/hyperliquid-authentication-guide) > > Master EIP-712 signature generation for Hyperliquid exchange API authentication > [card] [Trading Bot Development](/docs/hyperliquid-copy-trading-websocket) > > Build automated trading systems using Hyperliquid's perpetual and spot markets > [card] Order Management > > Implement advanced order types, modifications, and risk management > [card] HyperEVM Integration > > Deploy and interact with smart contracts on the HyperEVM > [card] [Node Configuration](/docs/hyperliquid-node-configuration) > > Configure nodes for system transactions and cross-layer transfers ## Key topics covered * **EIP-712 signature authentication** — Learn how to properly sign and authenticate requests to the Hyperliquid exchange API * **Order lifecycle management** — Place, modify, and cancel orders programmatically * **Risk management** — Implement leverage controls, isolated margin, and position management * **Asset transfers** — Move USDC and spot tokens between accounts and wallets * **Market data streaming** — Connect to WebSocket feeds for real-time order book and trade data * **Smart contract development** — Build DeFi applications on the HyperEVM ## Prerequisites Before diving into these guides, you should have: * Basic understanding of blockchain and DeFi concepts * Familiarity with JavaScript/TypeScript or Python * Access to a [reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) (available through Chainstack) * Understanding of cryptographic signatures (helpful but not required) ## Getting started Start with the [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) to understand how to properly authenticate with the Hyperliquid exchange API. This is essential for all trading operations. Check the guides on the left pane for detailed tutorials on specific topics. # Fork Hyperliquid EVM with Foundry Source: https://docs.chainstack.com/docs/hyperliquid-forking-evm-foundry Fork the Hyperliquid EVM chain locally using Foundry Anvil for offline smart contract development, testing, and debugging with a Chainstack endpoint. This guide demonstrates how to fork the Hyperliquid EVM chain using Foundry's Anvil to create a local development environment for testing smart contracts and dApps. ## Prerequisites Before starting, ensure you have: * [Foundry](https://book.getfoundry.sh/getting-started/installation) installed * A [reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) from Chainstack * Basic knowledge of Foundry and smart contract development ## Why fork Hyperliquid EVM? Forking allows you to: * Test smart contracts against the actual Hyperliquid EVM state * Simulate transactions without spending real funds * Debug and develop dApps in a controlled environment * Test interactions with deployed contracts on Hyperliquid ## Start a forked instance Use Anvil to fork the Hyperliquid EVM mainnet: ```bash theme={"system"} anvil --fork-url YOUR_CHAINSTACK_HYPERLIQUID_ENDPOINT ``` Replace `YOUR_CHAINSTACK_HYPERLIQUID_ENDPOINT` with your actual Chainstack Hyperliquid EVM endpoint. > [!NOTE] > > The Hyperliquid HyperEVM mainnet uses chain ID 999. Anvil will automatically inherit this when forking. ## Configure fork options ### Fork from a specific block To fork from a specific block height: ```bash theme={"system"} anvil --fork-url YOUR_CHAINSTACK_HYPERLIQUID_ENDPOINT --fork-block-number 13276400 ``` ### Set custom chain ID Override the chain ID if needed: ```bash theme={"system"} anvil --fork-url YOUR_CHAINSTACK_HYPERLIQUID_ENDPOINT --chain-id 31337 ``` ### Adjust block time Set custom block time (default is instant mining): ```bash theme={"system"} anvil --fork-url YOUR_CHAINSTACK_HYPERLIQUID_ENDPOINT --block-time 1 ``` ## Test the fork ### Connect with cast Verify your fork is running: ```bash theme={"system"} cast block-number --rpc-url http://localhost:8545 ``` Or use curl to retrieve the HYPE token total supply off the local fork: ```bash theme={"system"} curl --request POST \ --url 127.0.0.1:8545 \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "method": "eth_call", "params": [ { "to": "0x5555555555555555555555555555555555555555", "data": "0x18160ddd" }, "latest" ], "id": 1 }' ``` ### Check account balances View the balance of any Hyperliquid address: ```bash theme={"system"} cast balance 0xYourAddress --rpc-url http://localhost:8545 ``` ### Impersonate accounts Impersonate any account for testing: ```bash theme={"system"} cast rpc anvil_impersonateAccount 0xAccountToImpersonate --rpc-url http://localhost:8545 ``` > [!WARNING] > > Remember that forked state is ephemeral. Any transactions or state changes exist only in your local fork and don't affect the actual Hyperliquid network. ## Best practices **Step 1** Always fork from a recent block to ensure state relevance **Step 2** Use deterministic testing by forking from specific blocks **Step 3** Save fork state when testing complex scenarios **Step 4** Monitor your Chainstack endpoint usage to avoid rate limits ## Conclusion Forking Hyperliquid EVM with Foundry provides a powerful development environment for testing smart contracts and dApps. This approach allows you to interact with actual on-chain state without risking real funds or affecting the live network. ## Next steps * Explore [Hyperliquid smart contract development](/docs/hyperliquid-development) * Learn about [Hyperliquid authentication](/docs/hyperliquid-authentication-guide) * [Foundry docs](https://book.getfoundry.sh/) # Implementing spot-perp funding rate arbitrage Source: https://docs.chainstack.com/docs/hyperliquid-funding-rate-arbitrage Implement a funding rate arbitrage strategy between Hyperliquid spot and perpetual markets using Python, calculating entry sizes and expected returns. Funding rate arbitrage captures periodic payments between perpetual and spot markets. When funding is positive, you profit by longing spot and shorting perps—collecting funding payments while your positions hedge each other's price risk. > [!NOTE] > > **Prerequisites** > > * Python 3.8 or higher > * `hyperliquid-python-sdk` installed (`pip install hyperliquid-python-sdk`) > * [Reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) ([sign up for free](https://console.chainstack.com/)) > [!NOTE] > > **Educational purpose** > > This article explores the mechanics, math, and risks of funding arbitrage as a learning exercise. The strategies discussed require careful risk management and may not be profitable under all market conditions. > [!NOTE] > > **Note on cross-exchange arbitrage** > > For simplicity, this article focuses on executing both spot and perp positions on Hyperliquid. However, developers can implement funding arbitrage across different venues—for example, holding spot on one DEX/CEX while shorting perps on another. We'll examine a tactical approach: opening shorts minutes before funding payments to minimize exposure. ## How funding arbitrage works Perpetual contracts on Hyperliquid pay [funding every hour](https://hyperliquid.gitbook.io/hyperliquid-docs/trading/perpetuals#funding). When the rate is positive (perp price > spot price), shorts receive payments from longs. **The strategy**: 1. Buy spot asset (for example, 1 BTC) 2. Short equal amount on perp (for example, 1 BTC perp) 3. Collect hourly funding payments 4. Exit both positions when opportunity ends **Hedged exposure**: Spot long offsets perp short price risk. If BTC rises to \$70,000, your spot gains \$5,000 while perp loses \$5,000—net zero. Profit comes from funding, not directional price movement. ## Finding arbitrage opportunities Not all assets exist in both spot and perp markets on Hyperliquid. BTC, ETH, SOL are available in both, but many altcoins only trade as perps. Check eligibility before attempting arbitrage: ```python theme={"system"} async def find_arbitrage_eligible_assets(): response = await client.post( f"{BASE_URL}/info", json={"type": "spotMeta"} ) spot_meta = response.json() spot_assets = {token["name"] for token in spot_meta["tokens"]} info = Info(BASE_URL, skip_ws=True) meta = info.meta() perp_assets = {asset["name"] for asset in meta["universe"]} eligible = spot_assets.intersection(perp_assets) return eligible ``` The [`spotMeta`](/reference/hyperliquid-info-spotmeta) endpoint returns all spot-tradeable tokens. The `meta()` SDK method returns perp universe data. Their intersection shows which assets support the strategy. ## Calculating real profit after fees Funding rates look attractive, but trading fees create a profitability threshold. [Hyperliquid's fee structure](https://hyperliquid.gitbook.io/hyperliquid-docs/trading/fees) varies by maker/taker and volume tier: **Base tier fees (0-30 day rolling volume)**: * **Spot** — 0.070% taker / 0.040% maker * **Perpetuals** — 0.045% taker / 0.015% maker **Why use maker orders?** Maker fees are significantly lower (0.040% vs 0.070% for spot, 0.015% vs 0.045% for perps). For tactical arbitrage where you enter positions minutes before funding, using limit orders as a maker can save substantial fees—especially when repeated multiple times daily. ```python theme={"system"} def calculate_profit_potential(funding_rate: float, position_value: float, hours_held: int = 1): gross_profit = funding_rate * position_value * hours_held entry_fees = position_value * 0.00055 # 0.040% spot maker + 0.015% perp maker exit_fees = position_value * 0.00055 # 0.040% spot maker + 0.015% perp maker total_fees = entry_fees + exit_fees net_profit = gross_profit - total_fees return { "gross_profit": gross_profit, "total_fees": total_fees, "net_profit": net_profit, "break_even_hours": total_fees / (funding_rate * position_value) if funding_rate > 0 else 0 } ``` **Complete cycle fees (using maker orders)**: spot buy (0.040%) + perp short (0.015%) + spot sell (0.040%) + perp close (0.015%) = **0.11% total**. > [!NOTE] > > This calculation assumes maker fees. If using taker orders (market orders), total fees increase to 0.23% (spot 0.070% × 2 + perps 0.045% × 2), requiring much higher funding rates to be profitable. ### Minimum profitable funding rate To make arbitrage worthwhile, you need funding to exceed fees. Let's calculate the minimum: **For 1-hour position**: * Fees — 0.11% of position value * Required funding rate — **>0.11%/hour** (just to break even) * Realistic minimum — **>0.15%/hour** for meaningful profit **Example**: \$10,000 position, 0.05% funding rate: * Gross profit — \$5.00/hour * Total fees — \$11.00 * Break-even — 2.2 hours This is why arbitrageurs target rates well above 0.10%/hour to make meaningful profit on single-period positions. ## Getting current funding rates The [`metaAndAssetCtxs`](/reference/hyperliquid-info-meta-and-asset-ctxs) endpoint returns market metadata and current asset contexts, including live funding rates: ```python theme={"system"} async def get_funding_rates(): info = Info(BASE_URL, skip_ws=True) meta_and_contexts = info.meta_and_asset_ctxs() meta = meta_and_contexts[0] asset_ctxs = meta_and_contexts[1] opportunities = [] for i, asset_ctx in enumerate(asset_ctxs): asset_name = meta["universe"][i]["name"] funding_rate = float(asset_ctx.get("funding", "0")) mark_price = float(asset_ctx.get("markPx", "0")) if funding_rate > 0.001: # 0.1% minimum for profitability opportunities.append({ "asset": asset_name, "funding_rate": funding_rate, "funding_rate_pct": funding_rate * 100, "annual_rate_pct": funding_rate * 100 * 365 * 24, "mark_price": mark_price }) opportunities.sort(key=lambda x: x["funding_rate"], reverse=True) return opportunities ``` The 0.001 (0.1%) threshold filters rates near the break-even point. Below this, you'd need multiple hours to overcome the 0.11% fee burden. ## Checking market liquidity High funding rates mean nothing if slippage destroys your profit margin. The [`l2Book`](/reference/hyperliquid-info-l2-book) endpoint returns level-2 order book data for spread and depth analysis: ```python theme={"system"} async def check_liquidity(asset: str): response = await client.post( f"{BASE_URL}/info", json={"type": "l2Book", "coin": asset} ) book_data = response.json() levels = book_data["levels"] bids = levels[0] asks = levels[1] best_bid = float(bids[0]["px"]) best_ask = float(asks[0]["px"]) spread_pct = ((best_ask - best_bid) / best_bid) * 100 bid_depth = sum(float(level["sz"]) for level in bids[:5]) ask_depth = sum(float(level["sz"]) for level in asks[:5]) return { "spread_pct": spread_pct, "bid_depth": bid_depth, "ask_depth": ask_depth } ``` **Example**: SOL showing 0.05% funding rate: * Spread — 0.02% → acceptable, minimal slippage impact * Top 5 bid depth — 500 SOL → can enter 50 SOL position cleanly * Spread — 0.15% → avoid, slippage exceeds funding gain Check both spot and perp books. Illiquid spot markets often have wider spreads than their perp counterparts. ## Predicted funding rates The [`predictedFundings`](/reference/hyperliquid-info-predicted-fundings) endpoint shows estimated rates for the next funding period, calculated based on current spot-perp price divergence: ```python theme={"system"} async def get_predicted_fundings(): response = await client.post( f"{BASE_URL}/info", json={"type": "predictedFundings"} ) predicted = response.json() hl_fundings = [] for item in predicted: asset_name = item[0] exchange_data = item[1] for exchange_info in exchange_data: if exchange_info[0] == 'HlPerp': details = exchange_info[1] if 'fundingRate' in details: funding_rate = float(details['fundingRate']) hl_fundings.append((asset_name, funding_rate)) return sorted(hl_fundings, key=lambda x: x[1], reverse=True) ``` Why predicted rates matter: If current funding is 0.15% but predicted is 0.05%, arbitrageurs are already compressing the opportunity. Entering now means lower future returns. Look for stable or increasing predictions. ## Execution strategy: tactical pre-funding short **The idea**: Open positions 5-10 minutes before funding payment, collect the payment, then immediately close both positions. This minimizes price exposure while capturing funding. **Advantages**: * Minimal directional risk (short time window) * Lower capital requirements (high position turnover) * Reduced basis risk from spot-perp divergence **Challenges**: * Must overcome 0.11% fees with a single funding payment * Requires funding rate > 0.11% to be profitable * Timing is critical—miss the funding window and you pay fees for nothing **Execution flow**: **1. Set timing alarm** Set alarm for 10 minutes before next funding period (funding occurs at top of each hour) **2. Verify funding rate** Check current funding rate is > 0.11% (minimum to overcome fees) **3. Enter positions** Simultaneously enter spot long + perp short **4. Wait for funding** Wait for funding payment notification via WebSocket **5. Close positions** Immediately close both positions **6. Verify payment** Verify funding payment received via [`userFunding`](/reference/hyperliquid-info-user-funding) endpoint > [!TIP] > > Funding payment received and both positions closed successfully > [!WARNING] > > If spot-perp spread widens during your 15-minute window, slippage can exceed your funding profit. Only use this when spreads are tight (\<0.05%). ## Summary Funding rate arbitrage requires rates above 0.11%/hour (with maker orders) to overcome fees. The tactical approach—entering 10-15 minutes before funding—minimizes exposure but demands tight spreads and high rates (0.15%+). Only BTC, ETH, SOL and few other assets support spot-perp arbitrage on Hyperliquid. Monitor predicted funding rates to avoid compressed opportunities and test with small positions first. The complete implementation is available in the [Chainstack Hyperliquid trading bot repository](https://github.com/chainstacklabs/hyperliquid-trading-bot/tree/main/learning_examples). ## Related resources * [Copy trading with spot orders](/docs/hyperliquid-copy-trading-websocket) — Build a copy trading bot that mirrors spot trades * [Order precision and metadata](/docs/hyperliquid-order-precision) — Handle order precision dynamically across assets * [Authentication guide](/docs/hyperliquid-authentication-guide) — Authenticate with Hyperliquid exchange API * [API reference](/reference/hyperliquid-getting-started) — Explore the complete Hyperliquid API reference # Trading HIP-4 outcome markets on Hyperliquid Source: https://docs.chainstack.com/docs/hyperliquid-hip4-outcome-markets-trading Build Python code for trading HIP-4 outcome and prediction markets on Hyperliquid: asset encoding, settlement formula, fees, and SDK patching. HIP-4 introduces outcome markets to Hyperliquid — fully collateralized binary contracts that settle within a fixed range. They are a general-purpose primitive for prediction markets and bounded options-like instruments. HIP-4 launched on mainnet on May 2, 2026 with a recurring BTC daily binary, and additional markets are rolling out in stages. > [!NOTE] > > **Prerequisites** > > * Python 3.8 or higher > * `hyperliquid-python-sdk` installed (`pip install hyperliquid-python-sdk`) > * [Reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) ([sign up for free](https://console.chainstack.com/)) > * A funded testnet or mainnet wallet with **USDH** — HIP-4 settles in USDH, not USDC. The standard testnet faucet pays USDC; you swap USDC for USDH on the `@1338` spot pair (\~1:1). The reference repo includes a one-line script for this — see [Where to go next](#where-to-go-next). ## What HIP-4 outcome markets are An outcome market is a fully collateralized binary contract priced as a probability between 0 and 1. At expiry, the contract settles to either YES (1) or NO (0) based on a deterministic rule. Each side is held as a native HyperCore asset — there are no ERC-20 wrappers, no Gnosis Conditional Tokens, and no separate matching layer. Outcomes share the matching engine, account model, and API surface with spot and perpetuals. Once you handle the asset encoding correctly, every existing tool works — order placement, cancels, modifies, fills, the websocket. There is no leverage and no liquidation. Positions are fully collateralized at open. The first mainnet market is a recurring BTC daily binary that settles at 06:00 UTC against the BTC mark price on HyperCore. Markets are validator-curated for now; multi-outcome categorical "questions" exist on testnet and will roll out on mainnet in stages. ## Why the standard Python SDK won't work The official `hyperliquid-python-sdk` (v0.23.0 at the time of writing) does not know about HIP-4. The `Info` client builds its `coin_to_asset` map from `spot_meta` only — outcome encodings starting at `100_000_000` are unknown to the resolver. A naive call fails: ```python theme={"system"} from hyperliquid.info import Info info = Info("https://api.hyperliquid.xyz", skip_ws=True) info.l2_snapshot("#10") # KeyError: '#10' — coin not registered ``` The fix is small but necessary: fetch `outcomeMeta`, compute the asset id and coin string for every live outcome, and inject those entries into the SDK's resolver maps. After that, `Exchange.order(coin="#10", ...)` and `Info.l2_snapshot("#10")` work as if HIP-4 were a native concept. ## Asset ID encoding Outcomes share most implementation details with spot, but the API representation is different. The encoding is the single biggest source of bugs for new HIP-4 integrators. For an outcome with id `outcome` and side `side` (only `0` and `1` are valid): ``` encoding = 10 * outcome + side ``` By convention, side `0` is YES and side `1` is NO. The same `encoding` integer is used in **three different string forms**, each in a different context: | Form | Used in | Mainnet BTC daily YES (outcome 1) | | - | - | - | | `#` (outcome spot coin) | `/info` `l2Book`, `Exchange.order(...)`, websocket subscriptions | `#10` | | `+` (outcome token name) | `spotClearinghouseState` balances | `+10` | | `100_000_000 + encoding` (outcome asset id) | Internal asset references where an integer is needed | `100_000_010` | > [!WARNING] > > **Two coin strings exist for the same side asset.** The `#` form is what `/info` [`l2Book`](/reference/hyperliquid-info-l2-book) and `Exchange.order(...)` expect. The `+` form is what [`spotClearinghouseState`](/reference/hyperliquid-info-spotclearinghousestate) returns under your account balances. Mixing them up will silently fail to find your position — `l2Book` with `+10` returns nothing, balance lookup with `#10` returns nothing. A small encoding helper: ```python theme={"system"} def encode_coin(outcome_id: int, side: int) -> str: """Coin string for l2Book and order placement.""" if side not in (0, 1): raise ValueError(f"side must be 0 or 1, got {side}") return f"#{10 * outcome_id + side}" def encode_balance_coin(outcome_id: int, side: int) -> str: """Coin string for spotClearinghouseState balances.""" if side not in (0, 1): raise ValueError(f"side must be 0 or 1, got {side}") return f"+{10 * outcome_id + side}" def encode_asset_id(outcome_id: int, side: int) -> int: if side not in (0, 1): raise ValueError(f"side must be 0 or 1, got {side}") return 100_000_000 + 10 * outcome_id + side ``` ## Discovering markets with `outcomeMeta` The `outcomeMeta` info request returns all live outcomes and any categorical questions. Note that this method, like `l2Book` and the `/exchange` endpoints, is served only by the official Hyperliquid public API — it is not part of the open-source node software, so it is not available on Chainstack-hosted endpoints. Point HIP-4 trading code at `https://api.hyperliquid.xyz` (mainnet) or `https://api.hyperliquid-testnet.xyz` (testnet). For HyperEVM operations and the supported `/info` static metadata methods, continue to use your Chainstack endpoint. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability matrix. ```python theme={"system"} import httpx HYPERLIQUID_PUBLIC_URL = "https://api.hyperliquid.xyz" def fetch_outcome_meta(base_url: str = HYPERLIQUID_PUBLIC_URL) -> dict: r = httpx.post( f"{base_url}/info", json={"type": "outcomeMeta"}, timeout=10.0, ) r.raise_for_status() return r.json() data = fetch_outcome_meta() for o in data["outcomes"]: print(f"outcome={o['outcome']} name={o['name']} desc={o['description']}") for q in data.get("questions", []): print(f"question={q['question']} name={q['name']} legs={q['namedOutcomes']}") ``` A typical response on mainnet: ```json theme={"system"} { "outcomes": [ { "outcome": 1, "name": "Recurring", "description": "class:priceBinary|underlying:BTC|expiry:20260504-0600|targetPrice:78213|period:1d", "sideSpecs": [{"name": "Yes"}, {"name": "No"}] } ], "questions": [] } ``` The `description` field is a pipe-separated list of `key:value` pairs: * `class` — currently `priceBinary` (binary outcome on a price target). Future classes will follow. * `underlying` — asset ticker (`BTC`, `HYPE`, etc.). * `expiry` — `YYYYMMDD-HHMM` UTC. * `targetPrice` — strike for binary price markets. * `period` — for recurring markets (`1d`, `15m`, `3m`, etc.). > [!NOTE] > > `outcomeMeta` does not return the per-side coin strings or asset ids. You compute them client-side from `outcome` and `side` using the encoding formula above. A short parser for the recurring-binary description format: ```python theme={"system"} from dataclasses import dataclass @dataclass class RecurringSpec: cls: str underlying: str expiry: str target_price: float period: str def parse_recurring_description(desc: str) -> RecurringSpec | None: if not desc.startswith("class:"): return None parts = dict(p.split(":", 1) for p in desc.split("|") if ":" in p) try: return RecurringSpec( cls=parts["class"], underlying=parts["underlying"], expiry=parts["expiry"], target_price=float(parts["targetPrice"]), period=parts["period"], ) except KeyError: return None ``` ## Patching the SDK Once you have the live outcomes, inject them into the SDK's resolver maps so the existing client methods accept HIP-4 coin strings: ```python theme={"system"} from eth_account import Account from hyperliquid.exchange import Exchange from hyperliquid.info import Info def make_clients( private_key: str, address: str, base_url: str = HYPERLIQUID_PUBLIC_URL, ) -> tuple[Info, Exchange]: account = Account.from_key(private_key) info = Info(base_url, skip_ws=True) exchange = Exchange(account, base_url, account_address=address) data = fetch_outcome_meta(base_url) for o in data.get("outcomes", []): for side in (0, 1): coin = encode_coin(o["outcome"], side) asset_id = encode_asset_id(o["outcome"], side) # Skip if the SDK ever ships native HIP-4 support and these are present. if coin in info.coin_to_asset: continue info.coin_to_asset[coin] = asset_id info.name_to_coin[coin] = coin exchange.info.coin_to_asset[coin] = asset_id exchange.info.name_to_coin[coin] = coin return info, exchange ``` The `if coin in info.coin_to_asset: continue` guard is defensive: if a future SDK release adds native HIP-4 support, the patch becomes a no-op rather than producing duplicate entries. Pin a tested SDK version in your `requirements.txt` regardless. ## Settlement mechanics Recurring outcomes are auto-deployed and auto-settled by the protocol on a fixed cadence. The settlement rule for binary price-based outcomes: ``` Contract settles to YES if and only if markPrice0 + (settlementTime - t0) / (t1 - t0) * (markPrice1 - markPrice0) >= targetPrice ``` Where `markPrice0` and `markPrice1` are the two HyperCore mark-price updates immediately before and after `settlementTime`, and `t0`, `t1` are their timestamps. The protocol takes those two flanking samples, linearly interpolates between them at the exact settlement timestamp, and compares the interpolated value to the target. > [!WARNING] > > The samples are mark-price updates, not candle OHLCV closes, not oracle snapshots, and not a TWAP. If you implement pre-expiry position management against candle data, your settlement estimate will diverge from the actual settled value, especially in fast-moving markets where mark updates are dense around the settlement timestamp. For very fast-moving markets the choice of `t0` and `t1` updates can move the settled price by several ticks compared to the visible mark price at the exact timestamp. ## Holding to settlement Settlement is automatic. There is no `claim`, `redeem`, or `settle` call. At expiry, USDH credits land in the account in proportion to the side balances held, and the outcome is removed from the next `outcomeMeta` response. To watch a position through settlement, poll `spotClearinghouseState` for the `+` balance until it disappears, or compare your USDH balance before and after the expiry timestamp: ```python theme={"system"} import time def wait_for_settlement( info: Info, address: str, outcome_id: int, poll_interval: float = 5.0, ): while True: state = info.spot_user_state(address) balances = {b["coin"]: b for b in state.get("balances", [])} held = any( encode_balance_coin(outcome_id, side) in balances for side in (0, 1) ) if not held: return time.sleep(poll_interval) ``` If a reader expects a Polymarket or Gnosis CTF flow with a `redeemPositions` call, they will wait for a transaction that never comes — settlement happens inside the matching engine. ## Reading the order book Each outcome side is a separate book. To get a unified market view of a binary outcome, fetch both YES (`side=0`) and NO (`side=1`) and compose them client-side: ```python theme={"system"} def fetch_book(base_url: str, coin: str) -> tuple[list, list]: r = httpx.post( f"{base_url}/info", json={"type": "l2Book", "coin": coin}, timeout=10.0, ) r.raise_for_status() levels = r.json().get("levels", [[], []]) return levels[0], levels[1] # Mainnet BTC daily, both sides btc_yes_bids, btc_yes_asks = fetch_book(HYPERLIQUID_PUBLIC_URL, "#10") # see encoding table btc_no_bids, btc_no_asks = fetch_book(HYPERLIQUID_PUBLIC_URL, "#11") ``` The mid prices on the two sides should sum to approximately 1 in a tight market — if they don't, there's an arbitrage. Use the `#` form for `l2Book` and websocket subscriptions, never the `+` balance form. ## Placing an order Once the SDK is patched, order placement is a normal `Exchange.order(...)` call: ```python theme={"system"} info, exchange = make_clients(private_key, address) result = exchange.order( "#10", # outcome 1, side 0 (YES) — see encoding table is_buy=True, sz=10, # integer size limit_px=0.42, # 0.001 .. 0.999 order_type={"limit": {"tif": "Gtc"}}, ) print(result) ``` The matching engine enforces these limits — the server will reject orders that violate any of them: | Constraint | Value | | - | - | | Price range | `[0.001, 0.999]` (probabilities, not raw prices) | | Price tick | `0.0001` on the BTC binary; varies per market — see [Order precision](/docs/hyperliquid-order-precision) for the general rules | | Order size | Integer (testnet observed: `27.0`, `1024.0`, etc.) | | Min notional | \$10 USDH | | Leverage | None — fully collateralized at open | Validate price range and integer size client-side before signing the order — the round-trip to discover an invalid value via server rejection is wasteful in latency-sensitive code. ## Fees, mint, normal trade, and burn Outcome trading **only charges fees when closing or settling, not when opening**. The matching engine classifies every fill into one of six cases: | Case | What happened | Fee | | - | - | - | | Mint | Both counterparties had no prior position (both opening) | 0 | | Normal trade, fee-paying side | One side opens, the other closes | Fee on the closing side; volume = `fee_paying_px * sz` | | Normal trade, no-fee | Edge case where neither side pays | No volume counted | | Burn, both pay | Both counterparties hold opposite sides and unwind together | Both pay; volume = `1 * sz` | | Burn, taker only | Same as above but maker is rebate-eligible | Taker pays; volume = `taker_px * sz` | | Settlement | Auto-settlement at expiry | Each user gets `settle_fraction * sz` | Fee rates are taken from the same per-user tier returned by [`userFees`](/reference/hyperliquid-info-userfees) (`makerRate` / `takerRate`). There are no maker rebates for outcome trading — users who would otherwise receive rebates pay zero on maker orders instead. There is no `splitPosition` or `mergePositions` API. To get YES exposure, place a buy on the YES book. To get short-YES exposure, you have two equivalent paths: place a sell on the YES book at price `p` (synthetic short, opens a new short position via mint classification when the counterparty is also flat), or place a buy on the NO book at price `1 - p`. Both routes are economically equivalent in a fair market but show up differently in your inventory and route through different books — which matters for execution and fee classification. Mint and burn happen implicitly inside the matching engine as a side-effect of the fill — you cannot directly request them. As of mainnet launch, `splitPosition` and `mergePositions` exist in the protocol but are not enabled. The closest comparison points for fee structure: Polymarket charges around 200 basis points on winnings every trade, and Kalshi roughly 700 basis points. HIP-4 charges only on close, burn, or settlement, at the perp-tier rate. ## Where to go next Worked-out reference implementation with 12 progressive examples and a market-maker bot: * [chainstacklabs/hyperliquid-hip-4](https://github.com/chainstacklabs/hyperliquid-hip-4) — Python scripts for every primitive operation: USDH funding, market discovery, order book snapshot, websocket feed, limit order, cancel, modify, close, categorical trade, mint/burn explainer, settlement watcher, and a passive market-maker. Related reference and guides on Chainstack: * [Hyperliquid methods](/docs/hyperliquid-methods) — full method availability matrix; HIP-4 methods are flagged as Hyperliquid public RPC. * [outcomeMeta API reference](/reference/hyperliquid-info-outcomemeta) — request and response schema. * [Hyperliquid order precision](/docs/hyperliquid-order-precision) — `szDecimals` and price tick rules that apply to outcome orders too. * [Hyperliquid signing overview](/docs/hyperliquid-signing-overview) — required for every `Exchange.*` call. Upstream documentation: * [HIP-4 specification](https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-4-outcome-markets) * [Asset IDs (encoding spec)](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/asset-ids) * [Contract specifications (settlement formula)](https://hyperliquid.gitbook.io/hyperliquid-docs/trading/contract-specifications) * [Fees (the six cases)](https://hyperliquid.gitbook.io/hyperliquid-docs/trading/fees) # Understanding Hyperliquid infrastructure and RPC nodes Source: https://docs.chainstack.com/docs/hyperliquid-infrastructure-faq Answers to common Hyperliquid infrastructure questions covering node types, RPC endpoints, EVM compatibility, and Chainstack deployment configuration. This guide addresses common questions about [Hyperliquid's infrastructure](https://chainstack.com/what-is-hyperliquid/), particularly regarding [Hyperliquid RPC nodes](https://chainstack.com/build-better-with-hyperliquid/) and the platform's dual-layer architecture. Understanding these technical foundations is essential for developers building on Hyperliquid. ## Understanding Hyperliquid terminology Hyperliquid's architecture uses several interchangeable terms that can cause confusion. The platform consists of two distinct execution layers running on a shared consensus mechanism. The native trading layer—variously called **HyperCore**, Hyperliquid L1, Core, or RustVM—handles all order book operations, perpetuals trading, and margin calculations. Official documentation predominantly uses "HyperCore," while community discussions often reference "Hyperliquid L1." The term "RustVM" emphasizes that this is a custom Rust-based execution environment different from the Ethereum Virtual Machine. **HyperEVM** refers to the Ethereum-compatible smart contract layer where developers deploy DeFi protocols and other decentralized applications. > [!NOTE] > > A critical distinction: "Hyperliquid L1" carries ambiguity. In some contexts, it refers specifically to HyperCore; in others, it encompasses the entire blockchain system including both HyperCore and HyperEVM. Both layers share HyperBFT consensus and produce interleaved blocks. Rather than operating as separate chains, they function as two distinct virtual machines—one Rust-based, one EVM-compatible—unified under a single consensus layer. ## HyperCore vs HyperEVM ### Architecture and purpose **HyperCore** serves as the foundation for all trading operations, managing perpetual and spot order books with sub-second latency. This Rust-based layer handles order matching, margin calculations, and position management entirely on-chain without requiring smart contract deployments. **HyperEVM** operates as an Ethereum-compatible smart contract layer for applications where standard Web3 tooling and composability outweigh the need for microsecond-level performance. Developers can deploy Solidity contracts using familiar tools like Hardhat, Foundry, and Remix. ### API structure The platform exposes distinct endpoints for each layer. HyperCore operations route through `/info` for queries and `/exchange` for trading actions, while HyperEVM uses the standard JSON-RPC endpoint with Chain ID 999. Chainstack exposes HyperEVM on two paths: `/evm` (default, hl-node-compliant — system transactions stripped from block-shaped responses) and `/nanoreth` (with system transactions included). Both exist by default on every node, on mainnet and testnet, and you append the path you need to your endpoint URL — see [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) for when to use which. ### Cross-layer integration HyperEVM contracts access HyperCore data directly through **precompiles**, eliminating the need for external oracles. Smart contracts can read order book prices, user positions, and account balances with zero latency, enabling novel DeFi primitives such as lending protocols that trigger liquidations through HyperCore's native order books rather than external automated market makers. ### When to use each layer **Trading infrastructure**—including [Hyperliquid trading bots](https://chainstack.com/how-to-build-a-hyperliquid-trading-bot/), market-making systems, and real-time analytics—belongs on HyperCore. The layer's zero gas fees and sub-second execution make it optimal for high-frequency operations requiring direct order book access. **Smart contract development** naturally fits HyperEVM. Protocols requiring composability with other DeFi applications, complex state management, or compatibility with existing Ethereum tooling should deploy here. The most sophisticated applications leverage both layers simultaneously. By bridging assets between HyperCore and HyperEVM, developers enable tokens to trade on the order book while maintaining full composability with the broader DeFi ecosystem. ## Why proprietary methods require official Hyperliquid RPC Third-party RPC providers run non-validator nodes using the official open-source node software from the [hyperliquid-dex/node](https://github.com/hyperliquid-dex/node) GitHub repository. These nodes synchronize blockchain state but do not participate in consensus or order matching. [Chainstack fully supports HyperEVM and a subset of HyperCore query methods](https://chainstack.com/build-better-with-hyperliquid/). > [!WARNING] > > The `/exchange` endpoint is completely unavailable on non-validator nodes. You must route trading operations through `api.hyperliquid.xyz`, which forwards signed actions to validators for consensus processing. While the node software includes a `--serve-info` flag to expose local info queries, it provides only a subset of capabilities. The official documentation notes that "historical time series queries and websockets are not currently supported" on self-hosted info servers. Queries requiring real-time order book state like [`l2Book`](/reference/hyperliquid-info-l2-book) or live trading feeds like [`recentTrades`](/reference/hyperliquid-info-recent-trades) depend on the active matching engine, not replicated blockchain data. Third-party providers fully support HyperEVM through the `--serve-eth-rpc` flag, offering standard JSON-RPC methods ([`eth_call`](/reference/hyperliquid-evm-eth-call), [`eth_getLogs`](/reference/hyperliquid-evm-eth-get-logs), [`eth_blockNumber`](/reference/hyperliquid-evm-eth-block-number)) with enhanced infrastructure—better uptime, archive access, and geographic distribution. This creates a complementary model: official APIs handle trading operations exclusively, while third-party providers like [Chainstack's Hyperliquid RPC endpoint](https://chainstack.com/hyperliquid-rpc-node/) optimize smart contract access. ## Public infrastructure rate limiting Hyperliquid's public infrastructure implements multiple overlapping rate limit systems affecting different operation types. > [!NOTE] > > This section summarizes information from the official documentation. In case of discrepancies, refer to the [official Hyperliquid API documentation](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/rate-limits-and-user-limits). ### IP-based limits Weight is a cost value that represents the computational complexity of each API request. Requests consume weight from your 1200 weight/minute quota. | Endpoint Type | Limit | Notes | | - | - | - | | REST API (`/info`, `/exchange`) | 1200 weight/min | See weight table below | | HyperEVM (`/evm`) | 100 req/min | Basic rate limiting | | WebSocket connections | 100 connections | Maximum simultaneous connections | | WebSocket subscriptions | 1000 subscriptions | Across all connections | | WebSocket unique users | 10 users | Across user-specific subscriptions | | WebSocket messages | 2000 messages/min | Messages sent to Hyperliquid | | WebSocket inflight posts | 100 messages | Simultaneous post messages | #### REST API weight calculation Batching combines multiple operations (e.g., placing 50 orders) into a single API request. | Request Type | Base Weight | Additional Weight | | - | - | - | | Exchange actions (unbatched) | 1 | `+floor(batch_length / 40)` for batched requests | | Info: `l2Book`, `allMids`, `clearinghouseState`, `orderStatus`, `spotClearinghouseState`, `exchangeStatus` | 2 | — | | Info: `userRole` | 60 | — | | Info: most other endpoints | 20 | — | | Info: `recentTrades`, `historicalOrders`, `userFills`, `userFillsByTime`, `fundingHistory`, `userFunding`, `nonUserFundingUpdates`, `twapHistory`, `userTwapSliceFills`, `userTwapSliceFillsByTime`, `delegatorHistory`, `delegatorRewards`, `validatorStats` | 20 | +1 per 20 items returned | | Info: `candleSnapshot` | 20 | +1 per 60 items returned | | Explorer API: blockchain data queries | 40 | `blockList`: +1 per block; older blocks weighted more heavily | ### Address-based limits Address-based limits apply per user, with sub-accounts treated separately. These limits affect only actions, not info requests. | Limit Type | Default | Growth Mechanism | Maximum | | - | - | - | - | | Request quota | 10,000 initial buffer | +1 per \$1 USDC traded cumulatively | — | | Throttled rate | 1 req/10 seconds | When quota exhausted | — | | Cancel quota | Higher than regular | `min(request_quota + 100000, request_quota * 2)` | — | | Open orders | 1,000 orders | +1 per \$5M volume | 5,000 orders | | High congestion throttle | 2x maker percentage | During high traffic, the system limits users to 2x their maker order proportion | — | **Key clarifications:** * **Batched requests** — count as 1 for IP limits, but `n` for address limits (where `n` = order/cancel count) * **Reduce-only orders** — orders that only close existing positions * **Trigger orders** — orders that execute when price conditions are met * The system rejects orders placed at 1000+ open orders if they are reduce-only or trigger types ### When to upgrade infrastructure Private RPC becomes necessary for applications requiring over 60 HyperEVM requests per minute, SLA guarantees, multi-user platforms, event indexing, or high-frequency trading. Public infrastructure suits prototyping and educational projects under 100 requests per minute. For testing, you can obtain test tokens through the [Hyperliquid faucet](https://chainstack.com/hyperliquid-faucet/). > [!TIP] > > Optimization strategies: > > * Use WebSocket subscriptions for real-time data > * Batch exchange actions to reduce IP weight consumption > * Implement local caching > * Design strategies accounting for address-based limits > * Avoid resending cancels during high traffic once results are returned > > Private RPC eliminates the 100 requests per minute ceiling while providing dedicated bandwidth and geographic routing. ## Related resources * [Hyperliquid methods](/docs/hyperliquid-methods) — Complete RPC methods reference showing availability on Chainstack vs public infrastructure * [Hyperliquid API reference](/reference/hyperliquid-getting-started) — Chainstack API reference for Hyperliquid nodes * [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) — Understanding system addresses and node flags * [Hyperliquid tooling](/docs/hyperliquid-tooling) — SDKs and tools for building on Hyperliquid * [Authentication guide](/docs/hyperliquid-authentication-guide) — Authenticate with the exchange API using signatures # Hyperliquid: L1 action signing on HyperCore with Python SDK Source: https://docs.chainstack.com/docs/hyperliquid-l1-action-signing Implement Hyperliquid L1 action signing with EIP-712 typed data using Python, covering order placement, cancellation, and transfer authorization. ## Technical overview L1 actions use a **phantom agent** construction - a temporary signing identity created from your action's hash. This provides privacy by hashing the actual data before signing. > [!NOTE] > > The Hyperliquid Python SDK v0.18.0+ handles phantom agent construction internally. You don't need to implement this complexity yourself. ## Key characteristics | Property | Value | | - | - | | **Chain ID** | 1337 (NOT Arbitrum's 42161) | | **Domain name** | "Exchange" | | **Version** | "1" | | **Serialization** | Msgpack binary format | | **Agent type** | Phantom agent from action hash | | **Source** | "a" for mainnet, "b" for testnet | ## What is a phantom agent? A phantom agent is a cryptographic construct that provides privacy for trading operations: **1. Serialize action** Action is serialized using msgpack binary format **2. Append metadata** Nonce and vault address (if applicable) are appended **3. Hash the data** Complete data is hashed with keccak256 **4. Create agent object** Temporary "agent" object created with hash as connectionId **5. Sign via EIP-712** This phantom agent is signed using EIP-712 typed data ## Supported L1 actions All L1 actions use the **same signing method** `sign_l1_action()` - only the action payload changes: ### Trading actions * `order` — Place new orders * `cancel` — Cancel orders by order ID * `cancelByCloid` — Cancel orders by client order ID * `modify` — Modify existing orders * `batchModify` — Modify multiple orders ### Position management * `updateLeverage` — Adjust leverage for positions * `updateIsolatedMargin` — Manage isolated margin ### Transfers * `vaultTransfer` — Transfer between vault accounts * `subAccountTransfer` — Transfer between sub-accounts ### Utility * `scheduleCancel` — Schedule order cancellation * `noop` — No operation (for testing) ## Complete implementation example ```python theme={"system"} #!/usr/bin/env python3 """ L1 Action Signing Example - Demonstrates phantom agent construction SDK v0.18.0+ """ from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms from eth_account import Account import json def main(): # Load configuration with open('config.json') as f: config = json.load(f) wallet = Account.from_key(config['private_key']) print(f"Account: {wallet.address}") # Initialize exchange for sending signed actions exchange = Exchange( wallet=wallet, base_url=constants.MAINNET_API_URL ) # Example 1: Place an order (SDK handles signing internally) order_result = exchange.order( coin="BTC", is_buy=True, sz=0.01, limit_px=50000, order_type={"limit": {"tif": "Gtc"}}, reduce_only=False ) print(f"Order result: {order_result}") # Example 2: Manual signing for understanding timestamp = get_timestamp_ms() # Create a minimal L1 action action = {"type": "noop"} # Sign with phantom agent construction signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after True # is_mainnet ) print(f"\nPhantom agent signature generated:") print(f" r: {signature['r']}") print(f" s: {signature['s']}") print(f" v: {signature['v']}") # Send the signed action to Hyperliquid result = exchange._post_action(action, signature, timestamp) print(f"\nResult: {result}") if __name__ == "__main__": main() ``` ## Action payload examples ### Place order ```python theme={"system"} action = { "type": "order", "orders": [{ "a": 0, # Asset index (0 = BTC) "b": True, # Buy = True, Sell = False "p": "50000", # Price "s": "0.01", # Size "r": False, # Reduce-only "t": {"limit": {"tif": "Gtc"}} # Order type }], "grouping": "na" } ``` ### Cancel order ```python theme={"system"} action = { "type": "cancel", "cancels": [{ "a": 0, # Asset index "o": 123456 # Order ID }] } ``` ### Modify order ```python theme={"system"} action = { "type": "batchModify", "modifies": [{ "oid": 123456, # Order ID "order": { "a": 0, "b": True, "p": "51000", # New price "s": "0.01", "r": False, "t": {"limit": {"tif": "Gtc"}} } }] } ``` ### Update leverage ```python theme={"system"} action = { "type": "updateLeverage", "asset": 0, "isCross": True, "leverage": 10 } ``` ## Technical implementation details ### Phantom agent construction ```python theme={"system"} def construct_phantom_agent(hash, is_mainnet): return { "source": "a" if is_mainnet else "b", "connectionId": hash } ``` ### EIP-712 domain ```python theme={"system"} domain = { "name": "Exchange", "version": "1", "chainId": 1337, "verifyingContract": "0x0000000000000000000000000000000000000000" } ``` ### Type definition ```python theme={"system"} types = { "Agent": [ {"name": "source", "type": "string"}, {"name": "connectionId", "type": "bytes32"} ] } ``` ## TypeScript implementation ```typescript theme={"system"} import { ethers } from 'ethers'; import msgpack from 'msgpack-lite'; import { keccak256 } from 'ethers/lib/utils'; async function signL1Action( privateKey: string, action: any, vaultAddress: string | null, nonce: number, isMainnet: boolean ): Promise<{ r: string; s: string; v: number }> { // Serialize action with msgpack const actionBytes = msgpack.encode(action); // Append vault address and nonce const data = Buffer.concat([ actionBytes, vaultAddress ? Buffer.from(vaultAddress.slice(2), 'hex') : Buffer.alloc(20), Buffer.from(nonce.toString(16).padStart(16, '0'), 'hex') ]); // Hash to create connectionId const hash = keccak256(data); // EIP-712 domain const domain = { name: 'Exchange', version: '1', chainId: 1337, // L1 actions always use 1337 verifyingContract: '0x0000000000000000000000000000000000000000' }; const types = { Agent: [ { name: 'source', type: 'string' }, { name: 'connectionId', type: 'bytes32' } ] }; const value = { source: isMainnet ? 'a' : 'b', connectionId: hash }; const wallet = new ethers.Wallet(privateKey); const signature = await wallet._signTypedData(domain, types, value); return ethers.utils.splitSignature(signature); } ``` ## Configuration file Create a `config.json` file: ```json theme={"system"} { "private_key": "0x...", "account_address": "0x..." } ``` ## Common errors and solutions ### Wrong chain ID > [!WARNING] > > Never use Arbitrum's chain ID (42161) for L1 actions. Always use chain ID 1337. ```python theme={"system"} # CORRECT domain = {"chainId": 1337, ...} # INCORRECT domain = {"chainId": 42161, ...} # This will fail! ``` ### Invalid action format Ensure action payloads match the expected structure: ```python theme={"system"} # CORRECT - proper structure action = { "type": "order", "orders": [...], "grouping": "na" } # INCORRECT - missing required fields action = { "type": "order", "orders": [...] # Missing "grouping" field } ``` ### Nonce issues Always use current timestamp in milliseconds: ```python theme={"system"} from hyperliquid.utils.signing import get_timestamp_ms # CORRECT nonce = get_timestamp_ms() # Also correct import time nonce = int(time.time() * 1000) # INCORRECT nonce = int(time.time()) # Missing milliseconds ``` ## Testing your implementation **1. Start with noop** Test with a `noop` action first - it validates signing without any side effects **2. Use testnet** Test on testnet before mainnet: `constants.TESTNET_API_URL` or connect to a [reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) **3. Verify signatures** Check that signatures have proper r, s, v components **4. Monitor responses** Successful actions return `{"status": "ok"}` or order details ## Best practices > [!TIP] > > **DO** > > * Use the official SDK when possible > * Keep private keys secure (use environment variables) > * Test thoroughly on testnet first > * Handle errors gracefully > * Use proper nonce management > [!WARNING] > > **DON'T** > > * Hardcode private keys in code > * Mix up chain IDs (1337 vs 0x66eee) > * Reuse nonces across requests > * Skip error handling > * Use outdated SDK versions ## Summary L1 actions use phantom agent construction for privacy - the actual data is hashed before signing. The SDK's `sign_l1_action` handles all complexity internally, making it easy to implement trading operations securely. ## Related resources * [Hyperliquid signing overview](/docs/hyperliquid-signing-overview) — Quick reference for both signing types * [User-signed actions guide](/docs/hyperliquid-user-signed-actions) — For administrative operations * [Exchange API reference](/reference/hyperliquid-exchange-place-order) — Complete API documentation # Hyperliquid methods Source: https://docs.chainstack.com/docs/hyperliquid-methods Complete reference of supported Hyperliquid EVM JSON-RPC methods on Chainstack, including eth, net, and web3 namespace method availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > Methods marked as "Hyperliquid public RPC" in the table below are proprietary to Hyperliquid DEX and have not been open sourced. These methods are currently only available on the public Hyperliquid infrastructure. > > If you receive a response similar to `Failed to deserialize the JSON body into the target type`, you need to switch to the Hyperliquid Public RPC to use these methods. > > HyperEVM methods listed as `/evm` are also reachable on `/nanoreth`. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) for the per-method differences. | Method | Endpoint | Engine | Availability | | - | - | - | - | | meta | /info | HyperCore | | | spotMeta | /info | HyperCore | | | clearinghouseState | /info | HyperCore | | | spotClearinghouseState | /info | HyperCore | | | openOrders | /info | HyperCore | | | exchangeStatus | /info | HyperCore | | | frontendOpenOrders | /info | HyperCore | | | liquidatable | /info | HyperCore | | | activeAssetData | /info | HyperCore | | | maxMarketOrderNtls | /info | HyperCore | | | vaultSummaries | /info | HyperCore | | | userVaultEquities | /info | HyperCore | | | leadingVaults | /info | HyperCore | | | extraAgents | /info | HyperCore | | | subAccounts | /info | HyperCore | | | userFees | /info | HyperCore | | | userRateLimit | /info | HyperCore | | | spotDeployState | /info | HyperCore | | | perpDeployAuctionStatus | /info | HyperCore | | | delegations | /info | HyperCore | | | perpDexs | /info | HyperCore | | | delegatorSummary | /info | HyperCore | | | maxBuilderFee | /info | HyperCore | | | userToMultiSigSigners | /info | HyperCore | | | userRole | /info | HyperCore | | | perpsAtOpenInterestCap | /info | HyperCore | | | validatorL1Votes | /info | HyperCore | | | webData2 | /info | HyperCore | | | webData3 | /info | HyperCore | | | userDexAbstraction | /info | HyperCore | | | userAbstraction | /info | HyperCore | | | borrowLendUserState | /info | HyperCore | | | borrowLendReserveState | /info | HyperCore | | | allBorrowLendReserveStates | /info | HyperCore | | | allPerpMetas | /info | HyperCore | | | perpAnnotation | /info | HyperCore | | | perpCategories | /info | HyperCore | | | perpDexLimits | /info | HyperCore | | | perpDexStatus | /info | HyperCore | | | spotPairDeployAuctionStatus | /info | HyperCore | | | marginTable | /info | HyperCore | | | gossipPriorityAuctionStatus | /info | HyperCore | | | subAccounts2 | /info | HyperCore | | | approvedBuilders | /info | HyperCore | | | perpConciseAnnotations | /info | HyperCore | | | settledOutcome | /info | HyperCore | | | allMids | /info | HyperCore | Hyperliquid public RPC | | userFills | /info | HyperCore | Hyperliquid public RPC | | userFillsByTime | /info | HyperCore | Hyperliquid public RPC | | orderStatus | /info | HyperCore | Hyperliquid public RPC | | l2Book | /info | HyperCore | Hyperliquid public RPC | | candleSnapshot | /info | HyperCore | Hyperliquid public RPC | | historicalOrders | /info | HyperCore | Hyperliquid public RPC | | userTwapSliceFills | /info | HyperCore | Hyperliquid public RPC | | recentTrades | /info | HyperCore | Hyperliquid public RPC | | vaultDetails | /info | HyperCore | Hyperliquid public RPC | | portfolio | /info | HyperCore | Hyperliquid public RPC | | referral | /info | HyperCore | Hyperliquid public RPC | | delegatorHistory | /info | HyperCore | Hyperliquid public RPC | | delegatorRewards | /info | HyperCore | Hyperliquid public RPC | | metaAndAssetCtxs | /info | HyperCore | Hyperliquid public RPC | | userFunding | /info | HyperCore | Hyperliquid public RPC | | userNonFundingLedgerUpdates | /info | HyperCore | Hyperliquid public RPC | | fundingHistory | /info | HyperCore | Hyperliquid public RPC | | gossipRootIps | /info | HyperCore | Hyperliquid public RPC | | predictedFundings | /info | HyperCore | Hyperliquid public RPC | | spotMetaAndAssetCtxs | /info | HyperCore | Hyperliquid public RPC | | outcomeMeta | /info | HyperCore | Hyperliquid public RPC | | tokenDetails | /info | HyperCore | Hyperliquid public RPC | | isVip | /info | HyperCore | Hyperliquid public RPC | | legalCheck | /info | HyperCore | Hyperliquid public RPC | | preTransferCheck | /info | HyperCore | Hyperliquid public RPC | | twapHistory | /info | HyperCore | Hyperliquid public RPC | | userBorrowLendInterest | /info | HyperCore | Hyperliquid public RPC | | userTwapSliceFillsByTime | /info | HyperCore | Hyperliquid public RPC | | validatorSummaries | /info | HyperCore | Hyperliquid public RPC | | order | /exchange | HyperCore | Hyperliquid public RPC | | cancel | /exchange | HyperCore | Hyperliquid public RPC | | cancelByCloid | /exchange | HyperCore | Hyperliquid public RPC | | scheduleCancel | /exchange | HyperCore | Hyperliquid public RPC | | modify | /exchange | HyperCore | Hyperliquid public RPC | | batchModify | /exchange | HyperCore | Hyperliquid public RPC | | updateLeverage | /exchange | HyperCore | Hyperliquid public RPC | | updateIsolatedMargin | /exchange | HyperCore | Hyperliquid public RPC | | usdSend | /exchange | HyperCore | Hyperliquid public RPC | | spotSend | /exchange | HyperCore | Hyperliquid public RPC | | withdraw3 | /exchange | HyperCore | Hyperliquid public RPC | | usdClassTransfer | /exchange | HyperCore | Hyperliquid public RPC | | twapOrder | /exchange | HyperCore | Hyperliquid public RPC | | twapCancel | /exchange | HyperCore | Hyperliquid public RPC | | approveAgent | /exchange | HyperCore | Hyperliquid public RPC | | approveBuilderFee | /exchange | HyperCore | Hyperliquid public RPC | | setReferrer | /exchange | HyperCore | Hyperliquid public RPC | | createSubAccount | /exchange | HyperCore | Hyperliquid public RPC | | subAccountTransfer | /exchange | HyperCore | Hyperliquid public RPC | | subAccountSpotTransfer | /exchange | HyperCore | Hyperliquid public RPC | | convertToMultiSigUser | /exchange | HyperCore | Hyperliquid public RPC | | multiSig | /exchange | HyperCore | Hyperliquid public RPC | | userSetAbstraction | /exchange | HyperCore | Hyperliquid public RPC | | agentSetAbstraction | /exchange | HyperCore | Hyperliquid public RPC | | userDexAbstraction | /exchange | HyperCore | Hyperliquid public RPC | | agentEnableDexAbstraction | /exchange | HyperCore | Hyperliquid public RPC | | evmUserModify | /exchange | HyperCore | Hyperliquid public RPC | | sendAsset | /exchange | HyperCore | Hyperliquid public RPC | | sendToEvmWithData | /exchange | HyperCore | Hyperliquid public RPC | | vaultTransfer | /exchange | HyperCore | Hyperliquid public RPC | | tokenDelegate | /exchange | HyperCore | Hyperliquid public RPC | | cDeposit | /exchange | HyperCore | Hyperliquid public RPC | | cWithdraw | /exchange | HyperCore | Hyperliquid public RPC | | spotDeploy | /exchange | HyperCore | Hyperliquid public RPC | | perpDeploy | /exchange | HyperCore | Hyperliquid public RPC | | CSignerAction | /exchange | HyperCore | Hyperliquid public RPC | | CValidatorAction | /exchange | HyperCore | Hyperliquid public RPC | | validatorL1Stream | /exchange | HyperCore | Hyperliquid public RPC | | noop | /exchange | HyperCore | Hyperliquid public RPC | | reserveRequestWeight | /exchange | HyperCore | Hyperliquid public RPC | | userOutcome | /exchange | HyperCore | Hyperliquid public RPC | | borrowLend | /exchange | HyperCore | Hyperliquid public RPC | | createVault | /exchange | HyperCore | Hyperliquid public RPC | | vaultModify | /exchange | HyperCore | Hyperliquid public RPC | | vaultDistribute | /exchange | HyperCore | Hyperliquid public RPC | | subAccountModify | /exchange | HyperCore | Hyperliquid public RPC | | setDisplayName | /exchange | HyperCore | Hyperliquid public RPC | | registerReferrer | /exchange | HyperCore | Hyperliquid public RPC | | finalizeEvmContract | /exchange | HyperCore | Hyperliquid public RPC | | gossipPriorityBid | /exchange | HyperCore | Hyperliquid public RPC | | linkStakingUser | /exchange | HyperCore | Hyperliquid public RPC | | stakingLinkDisableTradingUser | /exchange | HyperCore | Hyperliquid public RPC | | userPortfolioMargin | /exchange | HyperCore | Hyperliquid public RPC | | net\_version | /evm | HyperEVM | | | web3\_clientVersion | /evm | HyperEVM | | | eth\_blockNumber | /evm | HyperEVM | | | eth\_blobBaseFee | /evm | HyperEVM | | | eth\_call | /evm | HyperEVM | | | eth\_callMany | /evm | HyperEVM | | | eth\_chainId | /evm | HyperEVM | | | eth\_createAccessList | /evm | HyperEVM | | | eth\_estimateGas | /evm | HyperEVM | | | eth\_feeHistory | /evm | HyperEVM | | | eth\_gasPrice | /evm | HyperEVM | | | eth\_getBalance | /evm | HyperEVM | | | eth\_getBlockByHash | /evm | HyperEVM | | | eth\_getBlockByNumber | /evm | HyperEVM | | | eth\_getBlockReceipts | /evm | HyperEVM | | | eth\_getBlockTransactionCountByHash | /evm | HyperEVM | | | eth\_getBlockTransactionCountByNumber | /evm | HyperEVM | | | eth\_getCode | /evm | HyperEVM | | | eth\_getFilterChanges | /evm | HyperEVM | | | eth\_getFilterLogs | /evm | HyperEVM | | | eth\_getHeaderByHash | /evm | HyperEVM | | | eth\_getHeaderByNumber | /evm | HyperEVM | | | eth\_getLogs | /evm | HyperEVM | | | eth\_getProof | /evm | HyperEVM | | | eth\_getRawTransactionByBlockHashAndIndex | /evm | HyperEVM | | | eth\_getRawTransactionByBlockNumberAndIndex | /evm | HyperEVM | | | eth\_getRawTransactionByHash | /evm | HyperEVM | | | eth\_getStorageAt | /evm | HyperEVM | | | eth\_getTransactionByBlockHashAndIndex | /evm | HyperEVM | | | eth\_getTransactionByBlockNumberAndIndex | /evm | HyperEVM | | | eth\_getTransactionByHash | /evm | HyperEVM | | | eth\_getTransactionBySenderAndNonce | /evm | HyperEVM | | | eth\_getTransactionCount | /evm | HyperEVM | | | eth\_getTransactionReceipt | /evm | HyperEVM | | | eth\_getUncleByBlockHashAndIndex | /evm | HyperEVM | | | eth\_getUncleByBlockNumberAndIndex | /evm | HyperEVM | | | eth\_getUncleCountByBlockHash | /evm | HyperEVM | | | eth\_getUncleCountByBlockNumber | /evm | HyperEVM | | | eth\_maxPriorityFeePerGas | /evm | HyperEVM | | | eth\_newBlockFilter | /evm | HyperEVM | | | eth\_newFilter | /evm | HyperEVM | | | eth\_protocolVersion | /evm | HyperEVM | | | eth\_sendRawTransaction | /evm | HyperEVM | | | eth\_simulateV1 | /evm | HyperEVM | | | eth\_syncing | /evm | HyperEVM | | | eth\_uninstallFilter | /evm | HyperEVM | | | eth\_bigBlockGasPrice | /evm | HyperEVM | | | eth\_usingBigBlocks | /evm | HyperEVM | | | eth\_getSystemTxsByBlockNumber | /evm | HyperEVM | | | eth\_getSystemTxsByBlockHash | /evm | HyperEVM | | | net\_listening | /evm | HyperEVM | | | net\_peerCount | /evm | HyperEVM | | | eth\_subscribe (logs) | /evm (WSS) | HyperEVM | | | eth\_subscribe (newHeads) | /evm (WSS) | HyperEVM | | | eth\_subscribe (syncing) | /evm (WSS) | HyperEVM | | | eth\_unsubscribe | /evm (WSS) | HyperEVM | | | debug\_getRawBlock | /evm | HyperEVM | | | debug\_getRawHeader | /evm | HyperEVM | | | debug\_getRawReceipts | /evm | HyperEVM | | | debug\_getRawTransaction | /evm | HyperEVM | | | debug\_traceBlockByHash | /evm | HyperEVM | | | debug\_traceBlockByNumber | /evm | HyperEVM | | | debug\_traceCall | /evm | HyperEVM | | | debug\_traceTransaction | /evm | HyperEVM | | | trace\_block | /evm | HyperEVM | | | trace\_call | /evm | HyperEVM | | | trace\_callMany | /evm | HyperEVM | | | trace\_filter | /evm | HyperEVM | | | trace\_rawTransaction | /evm | HyperEVM | | | trace\_replayBlockTransactions | /evm | HyperEVM | | | trace\_replayTransaction | /evm | HyperEVM | | | erigon\_getHeaderByNumber | /evm | HyperEVM | | | ots\_getApiLevel | /evm | HyperEVM | | | ots\_getBlockDetails | /evm | HyperEVM | | | ots\_getBlockDetailsByHash | /evm | HyperEVM | | | ots\_getBlockTransactions | /evm | HyperEVM | | | ots\_getContractCreator | /evm | HyperEVM | | | ots\_getHeaderByNumber | /evm | HyperEVM | | | ots\_getInternalOperations | /evm | HyperEVM | | | ots\_getTransactionBySenderAndNonce | /evm | HyperEVM | | | ots\_getTransactionError | /evm | HyperEVM | | | ots\_hasCode | /evm | HyperEVM | | | ots\_traceTransaction | /evm | HyperEVM | | # Hyperliquid node configuration and system transactions Source: https://docs.chainstack.com/docs/hyperliquid-node-configuration Chainstack exposes two HyperEVM JSON-RPC paths on every Hyperliquid node, mainnet and testnet: a default path that strips system transactions, and /nanoreth which includes them. Every Chainstack Hyperliquid node, on both mainnet and testnet, exposes two HyperEVM JSON-RPC paths on the same endpoint: | Console label | Path | What it returns | | - | - | - | | **JSON-RPC API (default)** | `/evm` | hl-node-compliant output; system transactions stripped from block-shaped responses | | **JSON-RPC API (with system tx)** | `/nanoreth` | Full nanoreth output; system transactions included in blocks and receipts | You don't pick one at deploy time and there's nothing to enable — both paths exist by default on every node. Append the path you need to your endpoint URL. ## What system transactions are System transactions are protocol-generated pseudo-transactions that record HyperCore state changes on the HyperEVM ledger. They have no signing key — the protocol creates them to bridge state between HyperCore and HyperEVM (HYPE deposits and withdrawals, spot token transfers, and similar). They always carry `gasPrice: 0x0` and originate from system addresses: * `0x2222222222222222222222222222222222222222` — HYPE native token bridge * `0x20000000000000000000000000000000000000XX` — per-token bridge contract, where `XX` is the big-endian token index (token 200 → `…00c8`) For background, see Hyperliquid docs: [HyperCore ↔ HyperEVM transfers](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm/hypercore-less-than-greater-than-hyperevm-transfers) and the [nanoreth README](https://github.com/hl-archive-node/nanoreth). ## How `/evm` and `/nanoreth` differ in practice All methods are present on both paths; the difference is whether system transactions appear in the response. To compare the two paths yourself, use a block that contains a system transaction, such as `0x202e371` (one HYPE bridge system transaction): | Method | `/evm` | `/nanoreth` | | - | - | - | | `eth_getBlockByNumber` / `eth_getBlockByHash` | transactions array excludes system tx | transactions array includes system tx | | `eth_getBlockTransactionCountByNumber` / `ByHash` | count excludes system tx | count includes system tx | | `eth_getBlockReceipts` | receipts array excludes system tx receipt | receipts array includes system tx receipt | | `eth_getTransactionReceipt` for a system tx hash | returns `null` | returns the receipt | | `eth_getTransactionByHash` for a system tx hash | returns the transaction | returns the transaction | | `eth_getSystemTxsByBlockNumber` / `ByHash` | returns system txs | returns system txs | | `debug_*` / `trace_*` / `ots_*` methods | unchanged | unchanged | | All non-system-tx methods (regular blocks, txs, receipts, traces) | unchanged | unchanged | In short: `/evm` only hides system transactions inside **block-shaped responses** — the transactions array, the transaction count, the receipts array, and `eth_getTransactionReceipt` keyed by a system tx hash. Everything else, including direct system tx lookup by hash via `eth_getTransactionByHash` and the dedicated `eth_getSystemTxsBy*` methods, behaves identically on both paths. ## Endpoint format ```text theme={"system"} # JSON-RPC API (default), hl-node-compliant https://hyperliquid-mainnet.core.chainstack.com//evm # JSON-RPC API (with system tx), nanoreth full output https://hyperliquid-mainnet.core.chainstack.com//nanoreth # WebSocket (HyperEVM) wss://hyperliquid-mainnet.core.chainstack.com//evm # Info HTTP API (HyperCore) https://hyperliquid-mainnet.core.chainstack.com//info ``` The examples above use the mainnet host. For testnet, use `hyperliquid-testnet.core.chainstack.com` with the same `/evm` and `/nanoreth` paths. Find both paths in the [console](https://console.chainstack.com/) under **Access and credentials** on any Hyperliquid node, mainnet or testnet. Username/password access is provided alongside. ## Related resources * [Hyperliquid methods](/docs/hyperliquid-methods) — full method-by-method availability and which path each runs on * [Hyperliquid infrastructure FAQ](/docs/hyperliquid-infrastructure-faq) — node types, rate limits, and the dual-layer architecture * [`eth_getSystemTxsByBlockNumber`](/reference/hyperliquid-evm-eth-get-system-txs-by-block-number) — dedicated method for system transactions * [`eth_getSystemTxsByBlockHash`](/reference/hyperliquid-evm-eth-get-system-txs-by-block-hash) — same, keyed by block hash * [Build better with Hyperliquid](https://chainstack.com/build-better-with-hyperliquid/) — Chainstack's Hyperliquid RPC overview # Handling order precision and market metadata Source: https://docs.chainstack.com/docs/hyperliquid-order-precision Query Hyperliquid metadata to dynamically calculate valid order sizes, tick precision, and lot sizes across different trading assets using the SDK. Hyperliquid rejects orders with wrong precision. BTC requires 5 decimal places for size, PURR needs 0 (whole integers only), and others vary from 0 to 8. If you hardcode precision, your bot breaks when trading new assets. This guide shows you how to query metadata and calculate valid order sizes dynamically. > [!NOTE] > > **Prerequisites** > > * Python 3.8 or higher > * `hyperliquid-python-sdk` installed (`pip install hyperliquid-python-sdk`) > * [Reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) ([sign up for free](https://console.chainstack.com/)) ## The precision problem This order gets rejected: ```python theme={"system"} # Wrong: BTC spot order with 3 decimals exchange.order( name="@0", # BTC spot is_buy=True, sz=0.001, # Rejected: BTC spot needs exactly 5 decimals limit_px=65000.0, order_type={"limit": {"tif": "Gtc"}} ) ``` Error: `"Tick size violation"` or `"Invalid order size"` This works: ```python theme={"system"} # Correct: BTC spot with 5 decimals exchange.order( name="@0", is_buy=True, sz=0.00100, # Valid: 5 decimal places limit_px=65000.0, order_type={"limit": {"tif": "Gtc"}} ) ``` Hardcoding `0.00100` breaks when you switch to another asset. You need dynamic precision. ## Tick size vs lot size Hyperliquid enforces precision on both price and quantity: **Tick size** — price precision: * Prices limited to **5 significant figures** * Max decimal places: `MAX_DECIMALS - szDecimals`, where `MAX_DECIMALS` is 6 for perps and 8 for spot * Integer prices are always valid regardless of significant figures (for example, `123456` is valid for BTC) * Examples for BTC perp (`szDecimals` = 5, so max 1 decimal place): `97000.5` ✓, `97000.55` ✗ **Lot size** — order quantity precision: * Determined by `szDecimals` in asset metadata, fixed per asset at deployment * Ranges from 0 (whole integers only, for example, PURR) to 5 (for example, BTC) * Must query from API—cannot hardcode Both are enforced at the API level. Violating either causes immediate order rejection. ## Querying asset metadata ### Understanding Hyperliquid asset formats Hyperliquid uses multiple formats to identify assets: **Spot assets**: * `@{index}` — direct index reference (for example, `@0` = BTC/USDC) * `{TOKEN}/USDC` — pair name (for example, `PURR/USDC`) **Perpetual assets**: * `{SYMBOL}` — direct symbol (for example, `BTC`, `ETH`, `SOL`) You need to handle all three formats when querying metadata. ### Getting spot asset metadata Query [`spotMetaAndAssetCtxs`](/reference/hyperliquid-info-spot-meta-and-asset-ctxs) for comprehensive spot data including precision requirements: ```python theme={"system"} from hyperliquid.info import Info def get_spot_asset_info(info: Info, coin_field: str) -> dict: """Get price and size decimals for spot assets""" # Get both metadata and live contexts spot_data = info.spot_meta_and_asset_ctxs() spot_meta = spot_data[0] # Metadata (universe, tokens) asset_ctxs = spot_data[1] # Live data (prices, volumes) if coin_field.startswith("@"): # Handle @index format index = int(coin_field[1:]) if index >= len(asset_ctxs): raise ValueError(f"Invalid spot index: {coin_field}") # Get current price (try midPx first, fallback to markPx) ctx = asset_ctxs[index] price = float(ctx.get("midPx", ctx.get("markPx", 0))) if price <= 0: raise ValueError(f"No price available for {coin_field}") # Find pair metadata pair_info = next( (p for p in spot_meta["universe"] if p["index"] == index), None ) if not pair_info: raise ValueError(f"No metadata for {coin_field}") # Get base token size decimals token_indices = pair_info["tokens"] base_token_index = token_indices[0] token_info = spot_meta["tokens"][base_token_index] size_decimals = token_info.get("szDecimals", 6) return { "coin": coin_field, "price": price, "szDecimals": size_decimals, "name": pair_info.get("name", coin_field) } elif "/" in coin_field: # Handle PAIR/USDC format - convert to @index pair = next( (p for p in spot_meta["universe"] if p["name"] == coin_field), None ) if not pair: raise ValueError(f"Pair not found: {coin_field}") # Get info using @index format return get_spot_asset_info(info, f"@{pair['index']}") else: raise ValueError(f"Unsupported spot format: {coin_field}") ``` ### Getting perpetual asset metadata Perps use a simpler structure via [`metaAndAssetCtxs`](/reference/hyperliquid-info-meta-and-asset-ctxs): ```python theme={"system"} def get_perp_asset_info(info: Info, symbol: str) -> dict: """Get price and size decimals for perpetual contracts""" meta_and_contexts = info.meta_and_asset_ctxs() meta = meta_and_contexts[0] asset_ctxs = meta_and_contexts[1] # Find asset in universe asset_index = None for i, asset in enumerate(meta["universe"]): if asset["name"] == symbol: asset_index = i break if asset_index is None: raise ValueError(f"Perp asset not found: {symbol}") # Get metadata asset_meta = meta["universe"][asset_index] size_decimals = asset_meta.get("szDecimals", 8) # Get current price ctx = asset_ctxs[asset_index] price = float(ctx.get("markPx", "0")) if price <= 0: raise ValueError(f"No price for {symbol}") return { "coin": symbol, "price": price, "szDecimals": size_decimals, "name": asset_meta["name"] } ``` ## Working with order precision ### Calculating valid order sizes (lot size) Order sizes must be rounded to the `szDecimals` specified in asset metadata. This is also known as the lot size—the minimum increment for order quantities. ```python theme={"system"} import math def calculate_order_size(target_value_usdc: float, asset_info: dict) -> float: """Calculate order size with correct precision (lot size)""" price = asset_info["price"] size_decimals = asset_info["szDecimals"] # Calculate raw size raw_size = target_value_usdc / price # Floor-truncate to valid precision (never round up beyond balance) valid_size = math.floor(raw_size * 10**size_decimals) / (10**size_decimals) return valid_size ``` > [!WARNING] > > Use `math.floor`, not `round`, for size truncation. Rounding up can produce a size larger than your balance, causing order rejection. This matches the [official SDK rounding example](https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/master/examples/rounding.py). **Example** (from the copy trading implementation): * Target value: \$15 USDC * BTC spot price: \$65,000 * Size decimals (`szDecimals`): 5 * Raw size: 15 / 65000 = 0.000230769... * Valid size: `floor(0.000230769 * 100000) / 100000` = `0.00023` (5 decimals) The `szDecimals` field in metadata determines lot size precision. Query this value from `spot_meta_and_asset_ctxs()` for spot or `meta_and_asset_ctxs()` for perps. ### Minimum notional value Hyperliquid enforces [minimum order values](https://hyperliquid.gitbook.io/hyperliquid-docs/trading/orders) of \$10 (or 10 USDC for spot). The only exception is exactly closing a position with a reduce-only order. Check before placing: ```python theme={"system"} def validate_order_size(size: float, price: float, min_notional: float = 10.0) -> bool: """Verify order meets minimum notional value""" notional_value = size * price if notional_value < min_notional: raise ValueError( f"Order value \${notional_value:.2f} below minimum \${min_notional}" ) return True ``` ### Price precision (tick size) Price precision on Hyperliquid follows [specific rules](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/tick-and-lot-size): **Significant figures rule** — prices can have up to **5 significant figures** **Integer exception** — integer prices are always valid regardless of significant figures (for example, `123456` is valid for BTC even though it has 6 significant figures) **Maximum decimal places** — determined by the formula `MAX_DECIMALS - szDecimals`: * **Perpetuals** — `MAX_DECIMALS` = 6 * **Spot** — `MAX_DECIMALS` = 8 This means the allowed price decimal places depend on the asset. Examples for perpetuals: | Asset | `szDecimals` | Max price decimals (`6 - szDecimals`) | Example valid price | | - | - | - | - | | BTC | 5 | 1 | `97000.5` | | ETH | 4 | 2 | `2567.35` | | BNB | 3 | 3 | `625.123` | | ATOM | 2 | 4 | `9.1234` | | DYDX | 1 | 5 | `1.01234` | | TRX | 0 | 6 | `0.123456` | **Examples of valid and invalid perpetual prices** (BTC, `szDecimals` = 5): * `97000.5` ✓ (5 sig figs, 1 decimal) * `97000.55` ✗ (exceeds `6 - 5 = 1` max decimal places) * `123456` ✓ (integer exception—always valid) **Examples of valid and invalid perpetual prices** (general): * `1234.5` ✓ (5 significant figures) * `1234.56` ✗ (6 significant figures—too many) * `0.001234` ✓ (4 significant figures, 6 decimals) **Price formatting for API**: ```python theme={"system"} def format_price(price: float, sz_decimals: int, is_spot: bool = False) -> float: """Format price matching the official SDK rounding logic. Uses 5 significant figures with MAX_DECIMALS - szDecimals decimal places. Integer prices above 100,000 are always valid. """ max_decimals = 8 if is_spot else 6 if price > 100_000: # Integer prices always valid regardless of sig figs return round(price) # 5 significant figures, then cap decimal places return round(float(f"{price:.5g}"), max_decimals - sz_decimals) # Examples format_price(97123.456, sz_decimals=5) # 97123.0 (BTC perp, max 1 decimal) format_price(2567.891, sz_decimals=4) # 2567.9 (ETH perp, max 2 decimals) format_price(0.12345678, sz_decimals=2, is_spot=True) # 0.12346 (spot, max 6 decimals) ``` > [!WARNING] > > When signing orders, trailing zeros in price and size strings cause the signature hash to change, producing the misleading error `"User or API Wallet 0x1a2B3c4D5e6F7a8B9c0D1e2F3a4B5c6D7e8F9a0b does not exist."` The SDK handles this automatically via `float_to_wire()`, which uses Python's `Decimal.normalize()` to strip trailing zeros. If you build order wire messages manually, strip trailing zeros before signing. ## Implementation examples ### Complete order placement flow Putting it together for dynamic multi-asset trading: ```python theme={"system"} def place_dynamic_order( exchange: Exchange, info: Info, coin_field: str, is_buy: bool, target_value_usdc: float, price_offset_pct: float = 0 ): """Place order with automatic precision handling""" # Determine asset type is_spot = coin_field.startswith("@") or "/" in coin_field # Get asset info if is_spot: asset_info = get_spot_asset_info(info, coin_field) else: asset_info = get_perp_asset_info(info, coin_field) # Calculate order size with proper precision order_size = calculate_order_size(target_value_usdc, asset_info) # Validate minimum notional validate_order_size(order_size, asset_info["price"]) # Calculate limit price with offset market_price = asset_info["price"] offset_multiplier = 1 + (price_offset_pct / 100) raw_price = market_price * offset_multiplier # Round price to tick size using the correct formula limit_price = format_price(raw_price, asset_info["szDecimals"], is_spot) print(f"Placing order: {order_size} {coin_field} @ \${limit_price}") # Place order result = exchange.order( name=coin_field, is_buy=is_buy, sz=order_size, limit_px=limit_price, order_type={"limit": {"tif": "Gtc"}}, reduce_only=False ) return result ``` ### Handling unknown assets When trading new or obscure assets: ```python theme={"system"} from typing import Optional def get_asset_info_safe(info: Info, coin_field: str) -> Optional[dict]: """Get asset info with graceful fallback""" try: if coin_field.startswith("@") or "/" in coin_field: return get_spot_asset_info(info, coin_field) else: return get_perp_asset_info(info, coin_field) except Exception as e: print(f"Warning: failed to get metadata for {coin_field}: {e}") # Fallback to safe defaults return { "coin": coin_field, "price": None, "szDecimals": 8, # Conservative default (most restrictive) "name": coin_field } ``` Use conservative defaults when metadata is unavailable, but always warn users. A higher `szDecimals` default is safer because it produces a more precisely truncated (smaller) size via `math.floor`. ### Caching metadata Query metadata once, reuse for multiple orders: ```python theme={"system"} class AssetMetadataCache: def __init__(self, info: Info): self.info = info self.cache = {} def get_asset_info(self, coin_field: str) -> dict: if coin_field not in self.cache: if coin_field.startswith("@") or "/" in coin_field: self.cache[coin_field] = get_spot_asset_info( self.info, coin_field ) else: self.cache[coin_field] = get_perp_asset_info( self.info, coin_field ) return self.cache[coin_field] def invalidate(self, coin_field: str = None): """Clear cache for asset or all assets""" if coin_field: self.cache.pop(coin_field, None) else: self.cache.clear() ``` Refresh cache periodically or when orders get rejected. ## Troubleshooting ### Common rejection reasons **"Order has invalid size"** (lot size violation) — size is not a multiple of `10^(-szDecimals)`. Query `szDecimals` from metadata and floor-truncate order size accordingly. **"Price must be divisible by tick size"** (price precision) — price has too many significant figures (>5) or exceeds `MAX_DECIMALS - szDecimals` decimal places. Use the `format_price` function above. **"Order must have minimum value of 10 USDC"** — below minimum notional value. Increase order size. Exception: exactly closing a position with reduce-only bypasses this minimum. **"User or API Wallet 0x1a2B3c4D5e6F7a8B9c0D1e2F3a4B5c6D7e8F9a0b does not exist"** — often caused by trailing zeros in price or size strings during signing. For example, `"0.2300"` fails where `"0.23"` succeeds. If you build order wire messages manually, use `Decimal.normalize()` to strip trailing zeros before signing. The SDK's `float_to_wire()` handles this automatically. **"Asset not found"** — wrong coin field format (`@index` for spot, `SYMBOL` for perps) or delisted asset. **"Insufficient balance"** — not a precision issue, but verify wallet has enough collateral before placing orders. ## Summary Query `szDecimals` from asset metadata to calculate valid order sizes dynamically—never hardcode precision. Floor-truncate sizes to `szDecimals` decimal places. Prices must have ≤5 significant figures with max `MAX_DECIMALS - szDecimals` decimal places (where `MAX_DECIMALS` is 6 for perps, 8 for spot); integer prices are always valid. Support all asset formats (`@index`, `PAIR/USDC`, `SYMBOL`), validate minimum notional (\$10 USDC), and cache metadata to reduce API calls. The complete implementation is available in the [Chainstack Hyperliquid trading bot repository](https://github.com/chainstacklabs/hyperliquid-trading-bot/tree/main/learning_examples). ## Related resources * [Copy trading with spot orders](/docs/hyperliquid-copy-trading-websocket) — Build a copy trading bot that mirrors spot trades * [TWAP order execution](/docs/hyperliquid-twap-orders) — Place and monitor TWAP orders on Hyperliquid * [Authentication guide](/docs/hyperliquid-authentication-guide) — Authenticate with Hyperliquid exchange API * [API reference](/reference/hyperliquid-getting-started) — Explore the complete Hyperliquid API reference # Hyperliquid peering Source: https://docs.chainstack.com/docs/hyperliquid-peering Reserve a gossip slot for your own Hyperliquid non-validator node on Chainstack peers. $449 per month per node IP, open access, 99.9% uptime SLA. Hyperliquid peering gives the Hyperliquid node you run yourself a *peering slot* on Chainstack peers, so your node has a dedicated upstream instead of competing for public root peers. One peering slot costs \$449 per month and covers one node IP address. You keep operating your own node. Chainstack holds a peering slot for your node IP address on Chainstack peers and tells you which Chainstack peers to connect to. > [!NOTE] > > Hyperliquid peering is for teams that run their own Hyperliquid non-validator node. If you want Chainstack to run the node for you, see [Dedicated Nodes](/docs/dedicated-node). ## Why Hyperliquid peering matters A Hyperliquid node typically streams data from one upstream peer at a time, so the node is only as fast and as reliable as that peer. When the peer is slow, the node falls behind. When the peer drops, the node stops advancing until it finds a new one. The public root peers returned by the `gossipRootIps` request to the Hyperliquid info endpoint are shared by every node on the network. Their slots fill up and their addresses change, so the peer your node gets is a matter of chance. A peering slot on Chainstack peers removes that chance: your node's IP address is on the allowlist of every Chainstack peer, and Chainstack holds the peering slot for your node whether or not the network is busy. ## What Hyperliquid peering includes Each peering slot includes the following: | Included | Detail | | - | - | | Peering slot | One peering slot covers one node IP address. Chainstack allowlists that IP address on every Chainstack peer. | | Chainstack peers | Two Chainstack peers in Tokyo. Point your node at both, so your node can fail over if one peer is unavailable. | | Full gossip | Blocks, and mempool transactions when `split_client_blocks` is enabled on your node. | | Uptime service level agreement (SLA) | 99.9% per calendar month. | | Price | \$449 per month per peering slot, billed monthly through Stripe. No bandwidth or egress charges. | ## Pricing and open access Hyperliquid peering costs \$449 per month per peering slot, billed monthly through Stripe, with no usage, bandwidth, or egress charges. Every customer pays the same price, connects to the same set of Chainstack peers, and gets the same priority. Chainstack does not give any customer, such as a market maker, preferential treatment. To peer more than one node, request a peering slot for each node IP address. ## Requirements for your node Before you request Hyperliquid peering, confirm the following about your node: * Ports `4001` and `4002` are open to the public internet. Hyperliquid uses these ports for peer-to-peer (P2P) gossip, and peers deprioritize a node that does not accept inbound connections on ports `4001` and `4002`. * The node has a static public IP address. Chainstack allowlists that exact IP address, so dynamic IP addresses and NAT pools with changing IP addresses do not work. * The node is a Hyperliquid non-validator node on Hyperliquid mainnet. ## How to set up Hyperliquid peering Setting up Hyperliquid peering takes three steps: you send your node's IP address, Chainstack allowlists that IP address and returns the Chainstack peer IP addresses, and you point your node at those peers. **1. Request a peering slot** Email [support@chainstack.com](mailto:support@chainstack.com) with the subject "Hyperliquid peering" and include the public IP address of each node you want to peer, one IP address per peering slot. Chainstack support allowlists your node IP address and replies with the Chainstack peer IP addresses. **2. Point your node at Chainstack peers** Write the Chainstack peer IP addresses into `~/override_gossip_config.json` on your node: ```json override_gossip_config.json theme={"system"} { "root_node_ips": [ { "Ip": "" }, { "Ip": "" } ], "try_new_peers": false, "chain": "Mainnet", "reserved_peer_ips": [] } ``` Replace `` and `` with the IP addresses from Chainstack support. With `try_new_peers` set to `false`, your node pulls data only from Chainstack peers. With `try_new_peers` set to `true`, your node can also connect to public peers it discovers. Your node then keeps syncing if both Chainstack peers become unreachable, but your node may stream from a public peer instead of a Chainstack peer. To receive mempool transactions, also set `split_client_blocks` to `true` in the same file. Mempool transactions reach your node only when every peer between your node and the validators has `split_client_blocks` enabled, and Chainstack peers have `split_client_blocks` enabled. **3. Restart and verify** Restart your Hyperliquid node. Your node is receiving data when your node keeps writing new files to `~/hl/data/replica_cmds`. The following command lists files written in the last minute: ```bash theme={"system"} find ~/hl/data/replica_cmds -type f -newermt '-1 minute' | tail -5 ``` If the command returns no files, or your Hyperliquid node logs show repeated peer disconnects, send the logs to [support@chainstack.com](mailto:support@chainstack.com). ## What Hyperliquid peering does not include Hyperliquid peering gives your own node a reliable upstream. Hyperliquid peering does not include RPC access, the Info API, or data services. To query Hyperliquid without running a node, use a Hyperliquid Global Node or Dedicated Node on Chainstack: * [Hyperliquid tooling](/docs/hyperliquid-tooling) — set up the Hyperliquid Python SDK and connect to Hyperliquid through Chainstack RPC nodes. * [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) — the `/evm` and `/nanoreth` paths on every Hyperliquid node on Chainstack. * [Global Nodes](/docs/global-elastic-node) — globally distributed Hyperliquid nodes on Chainstack. * [Dedicated Nodes](/docs/dedicated-node) — a Dedicated Hyperliquid mainnet node that Chainstack runs for your workload only. ## Hyperliquid peering FAQ > [accordion] How much traffic should my node expect? > > Based on observed mempool volume, full gossip with mempool transactions is roughly 1 TB per day and grows during volatile markets. Size your server's bandwidth, disk, and retention for that volume. Chainstack does not charge for this traffic. > [accordion] What happens if my node's IP address changes? > > Email the new IP address to [support@chainstack.com](mailto:support@chainstack.com) and keep the old IP address running until Chainstack confirms the change. With `try_new_peers` set to `false`, a node whose IP address is not on the allowlist has no peer to fall back to and stops syncing. > [accordion] Can I peer a testnet node? > > No. Hyperliquid peering is available for Hyperliquid mainnet nodes only. # Hyperliquid signing overview Source: https://docs.chainstack.com/docs/hyperliquid-signing-overview Understand Hyperliquid's dual signing architecture for L1 actions and EVM transactions, including EIP-712 typed data and standard Ethereum signatures. ## Quick reference Hyperliquid uses two distinct signing mechanisms for different purposes: | Type | Chain ID | Domain | Purpose | | - | - | - | - | | **L1 Actions** | 1337 | "Exchange" | Trading operations | | **User-Signed** | 0x66eee | "HyperliquidSignTransaction" | Admin & transfers | ## Architecture overview ``` L1 Actions → Phantom Agent → ChainId 1337 ↑ Agent Wallet (approved via user-signed) ↑ User-Signed → Direct EIP-712 → ChainId 0x66eee ``` ## L1 Actions L1 actions use a **phantom agent** construction for trading operations. ### Characteristics * **Chain ID**: 1337 (NOT Arbitrum's 42161) * **Domain name**: "Exchange" * **Serialization**: Msgpack binary format before hashing * **Agent construction**: Phantom agent from action hash ### Supported actions All L1 actions use the same `sign_l1_action()` method: * `order`, `cancel`, `cancelByCloid` * `modify`, `batchModify` * `scheduleCancel` * `updateLeverage`, `updateIsolatedMargin` * `vaultTransfer`, `subAccountTransfer` ### Example ```python theme={"system"} from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms timestamp = get_timestamp_ms() action = {"type": "order", "orders": [...]} signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after True # is_mainnet ) ``` ## User-Signed actions User-signed actions use direct EIP-712 signing for administrative operations. ### Characteristics * **Chain ID**: 0x66eee (421614 in decimal) * **Domain name**: "HyperliquidSignTransaction" * **Serialization**: Direct JSON structure * **No phantom agent**: Direct signing of typed data ### Supported actions Each action has its own signing wrapper: * `approveAgent` → `sign_agent()` * `usdSend` → `sign_usd_transfer_action()` * `spotSend` → `sign_spot_transfer_action()` * `withdraw` → `sign_withdraw_from_bridge_action()` * `approveBuilderFee` → `sign_approve_builder_fee()` * `usdClassTransfer` → `sign_usd_class_transfer_action()` * `tokenDelegate` → `sign_token_delegate_action()` ### Example ```python theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants exchange = Exchange( wallet=master_wallet, base_url=constants.MAINNET_API_URL ) # Approve an agent wallet result, agent_key = exchange.approve_agent("bot_v1") ``` ## Key concepts ### Phantom Agent A cryptographic construct (not a real wallet) for L1 actions: 1. Action serialized with msgpack 2. Nonce and vault address appended 3. Data hashed with keccak256 4. Temporary "agent" object created with hash as connectionId 5. This phantom agent is signed via EIP-712 ### Agent Wallet A separate keypair authorized to sign L1 actions: * **Stateless**: No funds or positions * **Nonce isolation**: Independent nonce tracking * **Limited scope**: Can only sign L1 actions, not transfers * **Created via**: User-signed [`approveAgent`](/reference/hyperliquid-exchange-approve-agent) action ## SDK support > [!NOTE] > > The Hyperliquid Python SDK v0.18.0+ handles all signing complexity internally. You don't need to implement the low-level signing logic manually. Connect to a [reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) to get started. ### Installation ```bash theme={"system"} pip install hyperliquid-python-sdk ``` ### Configuration Create a `config.json` file: ```json theme={"system"} { "private_key": "0x...", "account_address": "0x..." } ``` ## Common pitfalls > [!WARNING] > > The most common error is using the wrong chain ID. Always use: > > * Chain ID **1337** for L1 actions (trading) > * Chain ID **0x66eee** for user-signed actions (admin) > Never use Arbitrum's chain ID (42161) for Hyperliquid signatures. ## Decision flowchart ```mermaid theme={"system"} graph TD A[What operation?] --> B{Trading?} B -->|Yes| C[L1 Action] B -->|No| D{Admin/Transfer?} D -->|Yes| E[User-Signed] C --> F[Chain ID: 1337] E --> G[Chain ID: 0x66eee] F --> H[Use sign_l1_action] G --> I[Use specific wrapper] ``` ## Related guides * [L1 Action signing guide](/docs/hyperliquid-l1-action-signing) — Detailed guide for trading operations * [User-signed actions guide](/docs/hyperliquid-user-signed-actions) — Complete guide for administrative operations * [Authentication guide](/docs/hyperliquid-authentication-guide) — Overview of authentication mechanisms # Hyperliquid tooling Source: https://docs.chainstack.com/docs/hyperliquid-tooling Set up the Hyperliquid Python SDK, configure API credentials, and connect your development environment to Hyperliquid through Chainstack RPC nodes. Get started with a [reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) to use the tools below. ## Python SDK Build Hyperliquid trading applications with the official Python SDK. The [Hyperliquid Python SDK](https://github.com/hyperliquid-dex/hyperliquid-python-sdk) provides comprehensive access to trading functionality, market data, and account management. Install the SDK: ```bash theme={"system"} pip install hyperliquid-python-sdk ``` Use the SDK to interact with your Chainstack Hyperliquid node: ```python Python theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants # Use your Chainstack endpoint info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) user_state = info.user_state("0xcd5051944f780a621ee62e39e493c489668acf4d") print(user_state) ``` ## TypeScript SDK For TypeScript/JavaScript developers, the community-maintained [@nktkas/hyperliquid](https://github.com/nktkas/hyperliquid) SDK provides comprehensive API coverage with full type safety. Install the SDK: ```bash theme={"system"} npm install @nktkas/hyperliquid ``` Use the SDK: ```typescript TypeScript theme={"system"} import { InfoClient, ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Query market data (no authentication required) const info = new InfoClient({ transport: new HttpTransport() }); const meta = await info.meta(); console.log(meta); // Trading operations (requires authentication) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const exchange = new ExchangeClient({ transport: new HttpTransport(), wallet }); const result = await exchange.order({ orders: [{ a: 0, // Asset index (0 = BTC) b: true, // is_buy p: "50000", // price s: "0.001", // size r: false, // reduce_only t: { limit: { tif: "Gtc" } } }], grouping: "na" }); ``` The SDK also supports viem and ethers wallet integrations. See the [debugging signature errors guide](/docs/hyperliquid-debugging-signature-errors) for advanced integration patterns. ## Rust SDK For Rust developers, use the [Hyperliquid Rust SDK](https://github.com/hyperliquid-dex/hyperliquid-rust-sdk) to build high-performance trading applications. Install the SDK: ```bash theme={"system"} cargo add hyperliquid_rust_sdk ``` ## REST API Interact with the Hyperliquid DEX through your Hyperliquid node using the REST API. See the complete [Hyperliquid API reference](/reference/hyperliquid-getting-started) for all available endpoints. Use your Chainstack Hyperliquid REST endpoint to access market data and execute trades through HTTP requests. ## JSON-RPC API Interact with the HyperEVM of Hyperliquid through your Hyperliquid node using the JSON-RPC API. See the complete [Hyperliquid API reference](/reference/hyperliquid-getting-started) for all available endpoints. Use your Chainstack Hyperliquid JSON-RPC endpoint to retrieve the EVM chain state of Hyperliquid. ### Debug & Trace APIs Hyperliquid HyperEVM supports debug and trace APIs on both mainnet and testnet for smart contract debugging and transaction tracing. To enable these APIs, deploy a [Global Node](/docs/global-elastic-node) with debug & trace APIs enabled. See [Debug & Trace APIs](/docs/debug-and-trace-apis#hyperliquid) for more information. ## Hyperliquid system transactions The default `/evm` path strips HyperCore ↔ HyperEVM transfers (system transactions) from block-shaped responses; the `/nanoreth` path includes them. Both paths exist by default on every Hyperliquid node, on mainnet and testnet. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) for the per-method differences. # TWAP order execution and monitoring Source: https://docs.chainstack.com/docs/hyperliquid-twap-orders Place, monitor, and mirror TWAP orders on Hyperliquid using raw L1 action API calls and WebSocket subscriptions for real-time order tracking. TWAP orders split large trades into smaller chunks over time to reduce market impact. Instead of consuming the entire order book with one trade, you execute incrementally—minimizing slippage and front-running risk. This guide shows you how to place TWAP orders on Hyperliquid and monitor execution via WebSocket. > [!NOTE] > > **Prerequisites** > > * Python 3.8 or higher > * `hyperliquid-python-sdk` installed (`pip install hyperliquid-python-sdk`) > * [Reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) ([sign up for free](https://console.chainstack.com/)) ## TWAP order structure TWAP orders aren't yet available in the Python SDK's high-level methods. According to the [Hyperliquid API documentation](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#twap-order), you need to use raw API calls with the `twapOrder` action type: ```python theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils.signing import get_timestamp_ms, sign_l1_action, float_to_wire from eth_account import Account wallet = Account.from_key(private_key) exchange = Exchange(wallet, BASE_URL) # For spot asset with index 5, asset ID = 10000 + 5 = 10005 twap_action = { "type": "twapOrder", "twap": { "a": 10005, # Asset ID (10000 + spot index) "b": True, # Buy (True) or Sell (False) "s": float_to_wire(100.0), # Total size "r": False, # Reduce-only "m": 30, # Duration in minutes "t": False # Randomize timing } } timestamp = get_timestamp_ms() signature = sign_l1_action(wallet, twap_action, exchange.vault_address, timestamp, exchange.expires_after, False) result = exchange._post_action(twap_action, signature, timestamp) ``` ### TWAP parameters explained * `a` (asset ID) — for spot: `10000 + index` from spot metadata. For perps: use the perp index directly. Get index from `info.spot_meta_and_asset_ctxs()` or `info.meta()`. * `b` (buy/sell) — `True` for buy, `False` for sell. Boolean, not string. * `s` (size) — total size to execute. Use `float_to_wire()` to avoid trailing zeros that cause API rejections. * `m` (duration in minutes) — how long to spread execution. Range: 1–1440 (24 hours). Longer duration = lower market impact but more price exposure. * `t` (randomize) — `False` = evenly spaced intervals (30-min TWAP → child orders every \~30 seconds). `True` = randomized intervals, harder for algorithms to detect. * `r` (reduce-only) — `True` prevents opening new positions, only closes existing ones. ## Understanding TWAP execution When you submit a TWAP order, Hyperliquid's exchange backend calculates child order sizing and timing, then executes incrementally. You receive WebSocket updates for each fill. **Example with real numbers** — 30-minute TWAP for 100 units, `randomize=False`: * Duration — 1800 seconds * Child order interval — \~30 seconds (1800 / 60 ≈ 30) * Child order size — \~1.67 units (100 / 60 ≈ 1.67) * Total child orders — \~60 If liquidity is thin and some child orders only partially fill, the TWAP adjusts remaining size across future child orders. Why `randomize=True` matters: In competitive markets, other algorithms detect regular 30-second intervals and front-run your orders. Randomization spreads child orders across the same total duration but at unpredictable intervals. Average still \~30 seconds, but actual intervals vary from 10–50 seconds. ## Placing TWAP orders with validation Hyperliquid enforces precision and minimum size rules. Each asset has `szDecimals` (size precision) and a \$10 minimum notional value requirement: ```python theme={"system"} from hyperliquid.utils.signing import get_timestamp_ms, sign_l1_action, float_to_wire async def place_twap_order( exchange: Exchange, info: Info, symbol: str, is_buy: bool, total_size: float, duration_minutes: int, randomize: bool = False, reduce_only: bool = False ): # Get spot metadata and find asset spot_data = info.spot_meta_and_asset_ctxs() spot_meta = spot_data[0] target_pair = None for pair in spot_meta.get("universe", []): if pair.get("name") == symbol: target_pair = pair break if not target_pair: raise ValueError(f"Asset {symbol} not found") asset_index = target_pair.get("index") asset_id = 10000 + asset_index # Spot asset ID # Validate size precision sz_decimals = target_pair.get("szDecimals", 8) valid_size = round(total_size, sz_decimals) # Build TWAP action twap_action = { "type": "twapOrder", "twap": { "a": asset_id, "b": is_buy, "s": float_to_wire(valid_size), "r": reduce_only, "m": duration_minutes, "t": randomize } } # Sign and send timestamp = get_timestamp_ms() signature = sign_l1_action(exchange.wallet, twap_action, exchange.vault_address, timestamp, exchange.expires_after, False) result = exchange._post_action(twap_action, signature, timestamp) if result.get("status") == "ok": response_data = result.get("response", {}).get("data", {}) status_info = response_data.get("status", {}) if "running" in status_info: return status_info["running"]["twapId"] return None ``` > [!TIP] > > TWAP order placed successfully and TWAP ID received for tracking > [!NOTE] > > Why `float_to_wire()`? Prevents trailing zeros that cause API rejections. `100.0` becomes `"100"` string format. > [!WARNING] > > Each child order must meet the \$10 minimum. A 30-minute TWAP creates \~60 child orders. If your TWAP is \$500 total, each child = \$500 / 60 ≈ \$8.33 (rejected). Minimum safe TWAP: \$600+ for 60 child orders. ## Monitoring TWAP execution via WebSocket According to the [WebSocket API documentation](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket), the `userEvents` channel delivers TWAP updates in the `twapHistory` array: ```python theme={"system"} import websockets import json async def monitor_twap_execution(leader_address: str): async with websockets.connect(WS_URL) as websocket: subscription = { "method": "subscribe", "subscription": {"type": "userEvents", "user": leader_address} } await websocket.send(json.dumps(subscription)) async for message in websocket: data = json.loads(message) if data.get("channel") == "user": for twap_event in data.get("data", {}).get("twapHistory", []): await handle_twap_event(twap_event) ``` Each TWAP event contains `state` (order details) and `status` (execution phase): ```python theme={"system"} async def handle_twap_event(twap_event: dict): state = twap_event.get("state", {}) status = twap_event.get("status", {}).get("status", "unknown") coin = state.get("coin", "N/A") side = "BUY" if state.get("side") == "B" else "SELL" total_size = float(state.get("sz", "0")) executed_size = float(state.get("executedSz", "0")) executed_notional = state.get("executedNtl", "0") progress_pct = (executed_size / total_size * 100) if total_size > 0 else 0 if status == "activated": print(f"TWAP ACTIVATED: {side} {total_size} {coin}") elif status == "completed": avg_price = float(executed_notional) / executed_size if executed_size > 0 else 0 print(f"TWAP COMPLETED: {executed_size} {coin} @ avg \${avg_price:.2f}") elif status == "terminated": print(f"TWAP TERMINATED: {executed_size}/{total_size} ({progress_pct:.1f}%)") elif status == "canceled": print(f"TWAP CANCELED: Partial fill {executed_size}/{total_size}") return {"coin": coin, "status": status, "progress_pct": progress_pct} ``` ### TWAP status lifecycle * `activated` — TWAP accepted by exchange, child order execution begins. * `completed` — all size executed successfully. * `terminated` — exchange stopped execution early: * Insufficient balance — account ran out of funds mid-execution * Market conditions — extreme volatility or order book issues * Execution failure — child orders consistently rejected * `canceled` — you manually stopped the TWAP using the `twapCancel` API action. During execution, you'll also receive individual fill events in the `userEvents.fills` array for each child order. Correlate these with the active TWAP to track real-time progress. ## Canceling TWAP orders Cancel active TWAPs using raw API with [`twapCancel`](/reference/hyperliquid-exchange-twap-cancel) action: ```python theme={"system"} from hyperliquid.utils.signing import get_timestamp_ms, sign_l1_action def cancel_twap_order(exchange: Exchange, asset_id: int, twap_id: int): twap_cancel_action = { "type": "twapCancel", "a": asset_id, # Same asset ID used when placing (10000 + index for spot) "t": twap_id # TWAP ID from placement response } timestamp = get_timestamp_ms() signature = sign_l1_action(exchange.wallet, twap_cancel_action, exchange.vault_address, timestamp, exchange.expires_after, False) result = exchange._post_action(twap_cancel_action, signature, timestamp) if result.get("status") == "ok": response_data = result.get("response", {}).get("data", {}) return response_data.get("status") == "success" return False ``` > [!NOTE] > > You can only cancel TWAPs still executing (status `activated` in WebSocket events). Completed or terminated TWAPs return an error. ## Tracking child order fills Individual child order fills appear in `userEvents.fills` alongside regular orders. Correlate with active TWAPs: ```python theme={"system"} async def handle_user_events(data: dict, active_twaps: dict): for fill in data.get("data", {}).get("fills", []): coin = fill.get("coin") if coin in active_twaps: size = float(fill.get("sz")) price = float(fill.get("px")) # Track for VWAP calculation active_twaps[coin]["fills"].append({"size": size, "price": price}) # Calculate running VWAP total_notional = sum(f["size"] * f["price"] for f in active_twaps[coin]["fills"]) total_size = sum(f["size"] for f in active_twaps[coin]["fills"]) vwap = total_notional / total_size if total_size > 0 else 0 print(f"TWAP child fill: {size} {coin} @ \${price} (VWAP: \${vwap:.2f})") ``` ## Reduce-only TWAPs for position exits Set `"r": True` in the TWAP action to prevent accidentally increasing positions when closing: ```python theme={"system"} twap_action = { "type": "twapOrder", "twap": { "a": 0, # BTC perp index "b": False, # Sell to close long "s": float_to_wire(10.0), "r": True, # Reduce-only "m": 60, "t": False } } ``` You hold a 10 BTC long position. You place a reduce-only TWAP sell for 15 BTC. The TWAP stops after selling 10 BTC—it won't open a 5 BTC short position. Without `"r": True`, you'd flip from 10 BTC long to 5 BTC short. ## Mirroring TWAP orders in copy trading Copy trading with TWAP orders presents unique challenges compared to regular limit orders. The fundamental issue: TWAP IDs are asymmetric. When you place a TWAP, the API response includes the TWAP ID: ```python theme={"system"} result = exchange._post_action(twap_action, signature, timestamp) response_data = result.get("response", {}).get("data", {}) status_info = response_data.get("status", {}) if "running" in status_info: twap_id = status_info["running"]["twapId"] # You get the ID here ``` But when monitoring via WebSocket, the `twapHistory` events don't include the TWAP ID: ```python theme={"system"} for twap_event in data.get("data", {}).get("twapHistory", []): state = twap_event.get("state", {}) # state contains: coin, side, sz, minutes, randomize # state does NOT contain: twapId ``` According to the [WebSocket API documentation](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket), `twapHistory` events contain a `state` object with TWAP properties but no `twapId` field for tracking. This asymmetry creates problems for copy trading: * You can't use TWAP IDs to deduplicate events (no ID in WebSocket) * You can't map leader TWAP IDs to follower TWAP IDs (you never see leader's ID) * You can cancel your own TWAPs (you have the follower TWAP ID from placement), but you need a different strategy to know when to cancel based on leader activity The solution: Use TWAP properties as composite keys instead of relying on IDs. ### The combination-based tracking strategy According to the [WebSocket API documentation](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket), `twapHistory` events in the `userEvents` channel contain a `state` object with TWAP properties but no unique identifier for tracking. The solution: Create combination keys from TWAP properties to detect duplicates: ```python theme={"system"} def create_leader_twap_combination(state: dict) -> str: """Create leader TWAP combination ID with original size""" coin_field = state.get("coin", "") side = state.get("side") # "B" for buy, "A" for sell minutes = state.get("minutes", 1) randomize = state.get("randomize", False) size = state.get("sz", "0") return f"{coin_field}_{side}_{minutes}_{randomize}_{size}" leader_twap_combinations: set = set() # Track processed leader TWAPs # In event handler leader_combination = create_leader_twap_combination(state) if leader_combination in leader_twap_combinations: # Already processed this TWAP - skip continue leader_twap_combinations.add(leader_combination) ``` > [!NOTE] > > A leader might place two identical 30-minute BTC TWAPs with different sizes (for example, 0.5 BTC and 1.0 BTC). Without size in the combination, they'd have the same key and you'd only mirror one. ### Handling same-wallet testing scenarios > [!WARNING] > > This section describes a development shortcut for testing. In production with separate leader and follower wallets, this infinite loop problem won't exist. When testing with the same wallet as both leader and follower, you need to prevent mirroring your own follower TWAPs. But since WebSocket events don't include TWAP IDs, you can't simply check "is this TWAP ID in my follower list?" The solution: Track follower TWAP combinations separately with adjusted sizes: ```python theme={"system"} follower_twap_combinations: set = set() # Track our placed follower TWAPs def create_follower_twap_combination(coin_field: str, side: str, minutes: int, randomize: bool, follower_size: float) -> str: """Create follower TWAP combination ID with adjusted size""" return f"{coin_field}_{side}_{minutes}_{randomize}_{follower_size}" # After placing follower TWAP follower_combination = create_follower_twap_combination( coin_field, side, minutes, randomize, follower_total_size ) follower_twap_combinations.add(follower_combination) ``` Leader combination uses leader's size, follower combination uses follower's (potentially different) size. When a WebSocket event arrives, you check both: **1. Check if processed as leader** Is this combination in `leader_twap_combinations`? If yes, already processed as leader TWAP. **2. Check if own follower order** Is this combination in `follower_twap_combinations`? If yes, this is our own follower TWAP—skip it. ```python theme={"system"} async def handle_leader_twap_events(data: dict, exchange: Exchange, info: Info): for twap_event in data.get("data", {}).get("twapHistory", []): state = twap_event.get("state", {}) twap_status = twap_event.get("status", {}).get("status", "unknown") # Check if this is our own follower TWAP try: side = state.get("side") minutes = state.get("minutes", 1) randomize = state.get("randomize", False) current_size = float(state.get("sz", "0")) coin_field = state.get("coin", "") potential_follower_combination = create_follower_twap_combination( coin_field, side, minutes, randomize, current_size ) if potential_follower_combination in follower_twap_combinations: # Skip our own follower TWAP continue except (ValueError, TypeError): pass leader_combination = create_leader_twap_combination(state) if twap_status == "activated": if leader_combination in leader_twap_combinations: # Already processed continue leader_twap_combinations.add(leader_combination) # Mirror the TWAP with adjusted sizing follower_twap_id = await place_follower_twap_order( exchange, info, twap_event, leader_combination ) ``` > [!NOTE] > > In production with separate wallets, leader and follower sizes don't overlap in the same WebSocket feed—you don't need this follower combination check. This is purely for development convenience. ### Placing follower TWAPs with adjusted sizing When mirroring a leader's TWAP, you need to maintain the same time parameters (duration, randomization) while adjusting size based on your capital allocation: ```python theme={"system"} async def place_follower_twap_order( exchange: Exchange, info: Info, leader_twap_data: dict, leader_combination: str ) -> Optional[int]: """Place corresponding follower TWAP order with adjusted sizing""" state = leader_twap_data.get("state", {}) coin_field = state.get("coin", "") side = state.get("side") # "B" or "A" minutes = state.get("minutes", 1) randomize = state.get("randomize", False) reduce_only = state.get("reduceOnly", False) # Get current price for sizing calculation asset_info = await get_spot_asset_info(info, coin_field) if not asset_info: return None # Calculate follower size based on fixed USDC allocation follower_total_size = round( FIXED_ORDER_VALUE_USDC / asset_info["price"], asset_info["szDecimals"] ) # Get asset index for API call if coin_field.startswith("@"): asset_index = int(coin_field[1:]) else: # Lookup index for TOKEN/USDC format spot_meta = info.spot_meta() pair_info = next(p for p in spot_meta["universe"] if p["name"] == coin_field) asset_index = pair_info["index"] # Place TWAP with same time parameters, adjusted size twap_action = { "type": "twapOrder", "twap": { "a": 10000 + asset_index, # Spot asset ID "b": side == "B", # Buy/sell "s": float_to_wire(follower_total_size), "r": reduce_only, "m": minutes, # Same duration as leader "t": randomize # Same randomization as leader } } timestamp = get_timestamp_ms() signature = sign_l1_action(exchange.wallet, twap_action, exchange.vault_address, timestamp, exchange.expires_after, False) result = exchange._post_action(twap_action, signature, timestamp) if result and result.get("status") == "ok": response_data = result.get("response", {}).get("data", {}) status_info = response_data.get("status", {}) if "running" in status_info: follower_twap_id = status_info["running"]["twapId"] # Track follower combination to prevent re-mirroring follower_combination = create_follower_twap_combination( coin_field, side, minutes, randomize, follower_total_size ) follower_twap_combinations.add(follower_combination) return follower_twap_id return None ``` > [!TIP] > > The leader chose those parameters based on market conditions and their execution strategy. If the leader uses 30 minutes with randomization, they're likely trading in a market where that timing avoids front-running. Your follower TWAP benefits from the same strategy. ### Canceling follower TWAPs when leader cancels When a leader cancels or terminates a TWAP, you need to cancel the corresponding follower TWAP. What you have: * Follower TWAP ID (from placement response — stored when you placed it) * Leader TWAP combination (from WebSocket event properties) What you need: * A way to map: "when this leader combination cancels → cancel this follower TWAP ID" Use combination-based mappings to bridge the gap: ```python theme={"system"} twap_mappings: Dict[str, int] = {} # leader_combination -> follower_twap_id # After placing follower TWAP twap_mappings[leader_combination] = follower_twap_id # Store the ID you got from placement # When leader TWAP is canceled/terminated if twap_status in ["canceled", "terminated"]: if leader_combination in twap_mappings: follower_twap_id = twap_mappings[leader_combination] # Retrieve the stored ID # Now you can cancel using the TWAP ID you saved earlier await cancel_follower_twap_order(exchange, info, follower_twap_id, coin_field) # Clean up tracking del twap_mappings[leader_combination] leader_twap_combinations.discard(leader_combination) # Remove follower combination (find matching entry) for follower_combo in list(follower_twap_combinations): if follower_combo.startswith(f"{coin_field}_{side}_{minutes}_{randomize}_"): follower_twap_combinations.discard(follower_combo) break ``` > [!NOTE] > > If the leader places the same TWAP combination later (same asset, same parameters, same size), you want to mirror it again. Removing it from `leader_twap_combinations` allows reprocessing. ### When to mirror vs when to skip Not all leader TWAPs should be mirrored. Consider these scenarios: **Mirror when:** * TWAP status is `activated` (new TWAP just started) * Asset is spot (unless you specifically want to mirror perp TWAPs) * Combination hasn't been processed yet * You have sufficient balance for the follower TWAP **Skip when:** * TWAP status is `completed` (already finished, no action needed) * Combination is in `leader_twap_combinations` (already mirrored) * Combination is in `follower_twap_combinations` (this is your own follower TWAP) * Asset is perpetual and you only mirror spot trades * TWAP size is too small to meet minimum notional after adjusting for your allocation **Cancel follower TWAP when:** * Leader TWAP status changes to `canceled` or `terminated` * You have a corresponding follower TWAP tracked in `twap_mappings` This logic ensures you mirror leader strategy changes (canceling TWAPs early) while avoiding duplicate mirrors and infinite loops. ## Summary TWAP orders split large trades into smaller chunks over time to reduce market impact. Use raw API calls with [`twapOrder`](/reference/hyperliquid-exchange-twap-order) action (asset ID: `10000 + index` for spot), monitor execution via `userEvents` WebSocket subscription, and ensure each child order meets the \$10 minimum notional. For copy trading, track TWAPs using combination keys (`coin_side_minutes_randomize_size`) since TWAP IDs aren't available in WebSocket events. > [!NOTE] > > The complete implementation is available in the [Chainstack Hyperliquid trading bot repository](https://github.com/chainstacklabs/hyperliquid-trading-bot/tree/main/learning_examples). ## Related resources * [Copy trading with spot orders](/docs/hyperliquid-copy-trading-websocket) — Build a copy trading bot that mirrors spot trades * [Authentication guide](/docs/hyperliquid-authentication-guide) — Authenticate with Hyperliquid exchange API * [L1 action signing](/docs/hyperliquid-l1-action-signing) — Understand L1 action signing for trading operations * [API reference](/reference/hyperliquid-getting-started) — Explore the complete Hyperliquid API reference # Building on Hyperliquid with the nktkas TypeScript SDK Source: https://docs.chainstack.com/docs/hyperliquid-typescript-sdk-nktkas Build Hyperliquid trading and market-data apps in TypeScript with the nktkas SDK and Chainstack — clients, transports, wallets, trading, and real-time subscriptions. [@nktkas/hyperliquid](https://github.com/nktkas/hyperliquid) is the community-favorite TypeScript SDK for Hyperliquid: full HyperCore API coverage, end-to-end type safety, and support for every major JS runtime. This guide walks through building with it against a Chainstack Hyperliquid endpoint — reading market data, signing and placing trades, and streaming real-time updates. > [!NOTE] > > **Prerequisites** > > * Node.js 18 or higher (for the built-in `fetch` and `WebSocket`) > * `@nktkas/hyperliquid` and `viem` installed (`npm install @nktkas/hyperliquid viem`) > * [Reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) ([sign up for free](https://console.chainstack.com/)) ## The mental model: three clients, two transports The SDK splits along two axes — **what** you do and **how** you connect. The clients (the "what"): * **`InfoClient`** — read-only market and account data (no wallet needed). * **`ExchangeClient`** — write actions: place and cancel orders, transfers, leverage, and more (needs a wallet to sign). * **`SubscriptionClient`** — real-time streams over WebSocket. The transports (the "how"): * **`HttpTransport`** — request/response over HTTP. Used by `InfoClient` and `ExchangeClient`. * **`WebSocketTransport`** — persistent connection for `SubscriptionClient`. You compose them: pick a client, give it a transport. Everything below follows that pattern. ## Reading market data Start with the `InfoClient`. No wallet, no signing — just data: ```typescript theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); // Mid prices for every coin const mids = await info.allMids(); // Perpetuals metadata (asset names, size decimals, max leverage) const meta = await info.meta(); console.log(`${meta.universe.length} perpetual markets`); // L2 order book snapshot for a coin const book = await info.l2Book({ coin: "BTC" }); // A user's perpetuals account state const state = await info.clearinghouseState({ user: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }); ``` Every method is fully typed, so your editor autocompletes parameters and response fields. See the [Hyperliquid API reference](/reference/hyperliquid-getting-started) for the complete method list. ## Wallets and signing Write actions must be signed. nktkas does not ship its own key handling — you pass a [viem](https://viem.sh/) or [ethers](https://docs.ethers.org/) account as the `wallet`, and the SDK signs each action for you. ```typescript viem theme={"system"} import { privateKeyToAccount } from "viem/accounts"; const wallet = privateKeyToAccount("0x..."); // your private key ``` ```typescript ethers theme={"system"} import { Wallet } from "ethers"; const wallet = new Wallet("0x..."); // your private key ``` Hyperliquid has two signing schemes — **L1 actions** (orders, cancels) and **user-signed actions** (transfers, withdrawals). nktkas picks the right one per method automatically, so you rarely think about it. When you do need the details, see [Signing overview](/docs/hyperliquid-signing-overview), [L1 action signing](/docs/hyperliquid-l1-action-signing), and [user-signed actions](/docs/hyperliquid-user-signed-actions). > [!TIP] > > For production frontends, use an **agent wallet** (API wallet): the user approves it once from their browser wallet, then your app signs all trades with the agent key — no chain-mismatch prompts. See [debugging signature errors](/docs/hyperliquid-debugging-signature-errors) for the pattern. ## Placing and managing trades Give an `ExchangeClient` a transport and a wallet, then call action methods: ```typescript theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Place a limit buy: 0.01 BTC at 95000, good-til-canceled const result = await exchange.order({ orders: [{ a: 0, // asset index (0 = BTC; look it up in info.meta()) b: true, // isBuy p: "95000", // price s: "0.01", // size r: false, // reduceOnly t: { limit: { tif: "Gtc" } }, }], grouping: "na", }); console.log(result.response.data.statuses); // Cancel by asset index + order id await exchange.cancel({ cancels: [{ a: 0, o: 123456 }] }); // Adjust leverage await exchange.updateLeverage({ asset: 0, isCross: true, leverage: 5 }); ``` The asset index (`a`) comes from the `info.meta()` universe — its position in the array. Prices and sizes are **strings**, and Hyperliquid enforces strict tick and lot precision; rounding wrong gets the order rejected. See [order precision](/docs/hyperliquid-order-precision) for the rules. ## Real-time data For live data, swap to the `WebSocketTransport` and a `SubscriptionClient`. Each subscription takes a callback that fires on every update: ```typescript theme={"system"} import { SubscriptionClient, WebSocketTransport } from "@nktkas/hyperliquid"; const transport = new WebSocketTransport(); const subs = new SubscriptionClient({ transport }); // Stream mid prices for all coins await subs.allMids((data) => { console.log(data.mids); }); // Stream the BTC order book await subs.l2Book({ coin: "BTC" }, (book) => { console.log(book.levels); }); // Stream a user's order updates — the basis for copy-trading bots await subs.orderUpdates({ user: "0x..." }, (orders) => { console.log(orders); }); ``` `SubscriptionClient` also exposes `trades`, `userFills`, `userEvents`, `candle`, `bbo`, and `webData2`. For a full real-time application built on these streams, see [building a copy trading bot](/docs/hyperliquid-copy-trading-websocket). ## Using your Chainstack node By default, every transport points at the public Hyperliquid API. To route reads through your own Chainstack Hyperliquid node, pass its endpoint as `apiUrl`: ```typescript theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); // Served by your Chainstack node const meta = await info.meta(); const state = await info.clearinghouseState({ user: "0x..." }); ``` Two things to know, both driven by what the open-source Hyperliquid node implements: * **Trading stays on the public API.** `apiUrl` routes both `/info` and `/exchange`, but nodes do not serve `/exchange` actions — so keep your `ExchangeClient` on the default (public) transport. Trades are signed locally and submitted to Hyperliquid either way. * **Some reads are public-only.** Your node serves a large subset of HyperCore `info` methods (`meta`, `clearinghouseState`, `spotMeta`, `openOrders`, and more); others (`allMids`, `l2Book`, `metaAndAssetCtxs`, `candleSnapshot`, `userFills`, …) are only on the public API. The [Hyperliquid methods](/docs/hyperliquid-methods) table marks exactly which is which — a public-only method against your node returns `Failed to deserialize the JSON body into the target type`. Where your Chainstack node shines is **HyperEVM**: the full `eth_*` JSON-RPC surface (plus WebSocket subscriptions and debug/trace) runs on your dedicated endpoint. That side is standard EVM tooling — use [viem](https://viem.sh/) or [ethers](https://docs.ethers.org/) pointed at your endpoint rather than the HyperCore SDK above. See [Hyperliquid tooling](/docs/hyperliquid-tooling) and the [API reference](/reference/hyperliquid-getting-started). > [!NOTE] > > Pass `isTestnet: true` to a transport (`new HttpTransport({ isTestnet: true })`) to target Hyperliquid testnet instead of mainnet. ## Error handling The SDK throws on transport failures and on unsuccessful API responses, so wrap actions in `try/catch`. Successful actions still carry a status you should check: ```typescript theme={"system"} try { const result = await exchange.order({ orders: [{ a: 0, b: true, p: "95000", s: "0.01", r: false, t: { limit: { tif: "Gtc" } } }], grouping: "na", }); const status = result.response.data.statuses[0]; if ("error" in status) { console.error("Order rejected:", status.error); } else if ("resting" in status) { console.log("Resting order id:", status.resting.oid); } else if ("filled" in status) { console.log("Filled at:", status.filled.avgPx); } } catch (err) { // Network/timeout error, or the API returned an error response console.error("Request failed:", err); } ``` A common error is `Provided chainId ... must match the active chainId ...` when signing from a browser wallet — see [debugging signature errors](/docs/hyperliquid-debugging-signature-errors). ## Summary The nktkas SDK gives you three composable clients — `InfoClient`, `ExchangeClient`, and `SubscriptionClient` — over HTTP and WebSocket transports, with viem/ethers wallets for signing. Point reads at your Chainstack node for the HyperCore methods it serves and for all of HyperEVM, keep trading on the public API, and stream live data over WebSocket. From here, the copy-trading, TWAP, and funding-rate guides build complete strategies on these same primitives. ## Related resources * [Hyperliquid tooling](/docs/hyperliquid-tooling) — SDKs and APIs at a glance * [Hyperliquid methods](/docs/hyperliquid-methods) — which methods run on your node vs the public API * [Authentication guide](/docs/hyperliquid-authentication-guide) — sign and authenticate exchange actions * [Debugging signature errors](/docs/hyperliquid-debugging-signature-errors) — agent wallets and chain-mismatch fixes * [Order precision](/docs/hyperliquid-order-precision) — tick and lot sizing rules * [Building a copy trading bot](/docs/hyperliquid-copy-trading-websocket) — a full WebSocket application * [API reference](/reference/hyperliquid-getting-started) — the complete Hyperliquid API # Hyperliquid: User-signed actions with Python SDK Source: https://docs.chainstack.com/docs/hyperliquid-user-signed-actions Execute user-signed L1 actions on Hyperliquid HyperCore using the Python SDK for order placement, transfers, and withdrawals through Chainstack nodes. ## Technical overview User-signed actions use **direct EIP-712 signing** without phantom agent abstraction. These are used for administrative operations like approving agents, transferring funds, and managing withdrawals. > [!NOTE] > > The Hyperliquid Python SDK v0.18.0+ provides wrapper functions for all user-signed actions, handling the EIP-712 complexity internally. Connect to a [reliable Hyperliquid RPC endpoint](https://chainstack.com/build-better-with-hyperliquid/) to get started with user-signed actions. ## Key characteristics | Property | Value | | - | - | | **Chain ID** | 0x66eee (421614 in decimal) | | **Domain name** | "HyperliquidSignTransaction" | | **Version** | "1" | | **Serialization** | Direct JSON structure | | **Agent type** | No phantom agent - direct signing | ## Agent wallets explained Agent wallets are separate keypairs authorized to sign L1 actions on behalf of your account: ### Properties * **Stateless** — No funds or positions held * **Nonce isolation** — Independent nonce tracking from master account * **Limited scope** — Can only sign L1 actions, not transfers or withdrawals * **Revocable** — Can be revoked by the master account ### Use cases * **Trading bots** — Automated trading without exposing master key * **API integrations** — Third-party services can trade for you * **Security isolation** — Limit exposure if agent key is compromised ## Supported user-signed actions Each action has its own signing wrapper function, but all use `sign_user_signed_action()` internally: | Action | SDK Method | Purpose | | - | - | - | | `approveAgent` | `sign_agent()` | Authorize agent wallet | | `usdSend` | `sign_usd_transfer_action()` | Transfer USDC | | `spotSend` | `sign_spot_transfer_action()` | Transfer spot tokens | | `withdraw` | `sign_withdraw_from_bridge_action()` | Withdraw to L1 | | `approveBuilderFee` | `sign_approve_builder_fee()` | Approve builder fees | | `usdClassTransfer` | `sign_usd_class_transfer_action()` | Transfer between classes | | `tokenDelegate` | `sign_token_delegate_action()` | Delegate tokens | ## Complete agent approval example This example shows how to create and approve an agent wallet: ```python theme={"system"} #!/usr/bin/env python3 """ User-Signed Action Example - Agent Approval using SDK SDK v0.18.0+ """ from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from eth_account import Account import json def main(): # Load configuration with open('config.json') as f: config = json.load(f) master_wallet = Account.from_key(config['private_key']) print(f"Master account: {master_wallet.address}") # Initialize exchange exchange = Exchange( wallet=master_wallet, base_url=constants.MAINNET_API_URL ) # Create and approve agent using SDK's built-in method # This handles all the user-signed action complexity internally print("\nCreating and approving new agent...") result, agent_key = exchange.approve_agent("bot_v1") print(f"\nApproval result: {result}") if result.get('status') == 'ok': agent_wallet = Account.from_key(agent_key) print(f"✓ Agent approved successfully!") print(f" Agent address: {agent_wallet.address}") print(f" Agent key: {agent_key}") # Save agent credentials with open('agent.json', 'w') as f: json.dump({ "agent_address": agent_wallet.address, "agent_private_key": agent_key, "master_address": master_wallet.address }, f, indent=2) # Now agent can sign L1 actions for master agent_exchange = Exchange( wallet=agent_wallet, base_url=constants.MAINNET_API_URL, account_address=master_wallet.address # Target master's account ) print(f"\nAgent ready to sign L1 actions for {master_wallet.address}") if __name__ == "__main__": main() ``` ## Using an approved agent Once an agent is approved, it can perform L1 actions on behalf of the master: ```python theme={"system"} # Load agent credentials with open('agent.json') as f: agent_config = json.load(f) agent_wallet = Account.from_key(agent_config['agent_private_key']) # Create exchange instance for agent # Note: account_address points to master's account agent_exchange = Exchange( wallet=agent_wallet, base_url=constants.MAINNET_API_URL, account_address=agent_config['master_address'] ) # Agent can now place orders for master account order_result = agent_exchange.order( coin="BTC", is_buy=True, sz=0.01, limit_px=50000, order_type={"limit": {"tif": "Gtc"}}, reduce_only=False ) ``` ## USDC transfer example Transfer USDC between accounts using user-signed action: ```python theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from eth_account import Account # Initialize exchange exchange = Exchange( wallet=wallet, base_url=constants.MAINNET_API_URL ) # Transfer 100 USDC to another address result = exchange.usd_transfer( destination="0x...", # Recipient address amount=100.0 ) print(f"Transfer result: {result}") ``` ## Withdrawal example Withdraw funds to Layer 1: ```python theme={"system"} # Withdraw 50 USDC to Arbitrum result = exchange.withdraw( amount=50.0, destination="0x..." # L1 address ) print(f"Withdrawal initiated: {result}") ``` ## Technical implementation ### EIP-712 structure for agent approval ```python theme={"system"} # Types definition (handled internally by SDK) AGENT_SIGN_TYPES = [ {"name": "hyperliquidChain", "type": "string"}, {"name": "agentAddress", "type": "address"}, {"name": "agentName", "type": "string"}, {"name": "nonce", "type": "uint64"} ] # Example message message = { "hyperliquidChain": "Mainnet", "agentAddress": "0x...", "agentName": "bot_v1", "nonce": 1234567890123 } ``` ### EIP-712 structure for USDC transfer ```python theme={"system"} # Types definition USD_SEND_TYPES = [ {"name": "hyperliquidChain", "type": "string"}, {"name": "destination", "type": "string"}, {"name": "amount", "type": "string"}, {"name": "time", "type": "uint64"} ] # Example message message = { "hyperliquidChain": "Mainnet", "destination": "0x...", "amount": "100.5", "time": 1234567890123 } ``` ### Domain configuration All user-signed actions use this domain: ```python theme={"system"} domain = { "name": "HyperliquidSignTransaction", "version": "1", "chainId": 0x66eee, # 421614 in decimal "verifyingContract": "0x0000000000000000000000000000000000000000" } ``` ## TypeScript implementation ```typescript theme={"system"} import { ethers } from 'ethers'; async function signUserAction( privateKey: string, primaryType: string, types: any, message: any ): Promise<{ r: string; s: string; v: number }> { // Domain for user-signed actions const domain = { name: 'HyperliquidSignTransaction', version: '1', chainId: 0x66eee, // User-signed always use this verifyingContract: '0x0000000000000000000000000000000000000000' }; const wallet = new ethers.Wallet(privateKey); const signature = await wallet._signTypedData(domain, types, message); return ethers.utils.splitSignature(signature); } // Example: Approve agent async function approveAgent( privateKey: string, agentAddress: string, agentName: string ) { const types = { 'HyperliquidTransaction:ApproveAgent': [ { name: 'hyperliquidChain', type: 'string' }, { name: 'agentAddress', type: 'address' }, { name: 'agentName', type: 'string' }, { name: 'nonce', type: 'uint64' } ] }; const message = { hyperliquidChain: 'Mainnet', agentAddress: agentAddress, agentName: agentName, nonce: Date.now() }; return await signUserAction( privateKey, 'HyperliquidTransaction:ApproveAgent', types, message ); } ``` ## Agent architecture diagram ```mermaid theme={"system"} graph TD A[Master Account] -->|approveAgent| B[Agent Wallet] A -->|holds funds| C[Positions & Balances] B -->|signs L1 actions| D[Trading Operations] D -->|affects| C B -.->|cannot sign| E[Transfers/Withdrawals] A -->|can sign| E ``` ## Security best practices > [!TIP] > > **Agent wallet security** > > * Generate unique agent keys for each integration > * Store agent keys separately from master keys > * Regularly rotate agent keys > * Monitor agent activity > * Revoke unused agents promptly > [!WARNING] > > **Critical limitations** > > * Agents CANNOT transfer funds > * Agents CANNOT withdraw to L1 > * Agents CANNOT approve other agents > * Agents CAN place and cancel orders > * Agents CAN modify leverage ## Common use cases ### Trading bot deployment **1. Create agent** Generate a new agent wallet for your bot **2. Approve agent** Use master account to approve the agent **3. Deploy bot** Bot uses agent key to sign L1 actions **4. Monitor activity** Track orders and positions created by agent **5. Revoke if needed** Master can revoke agent access anytime ### Multi-account management ```python theme={"system"} # Approve different agents for different strategies strategies = [ {"name": "market_maker", "description": "MM bot"}, {"name": "arbitrage", "description": "Arb bot"}, {"name": "momentum", "description": "Trend follower"} ] agents = {} for strategy in strategies: result, agent_key = exchange.approve_agent(strategy["name"]) if result.get('status') == 'ok': agents[strategy["name"]] = agent_key print(f"✓ {strategy['name']} agent approved") ``` ## Error handling ### Common errors | Error | Cause | Solution | | - | - | - | | "Invalid chain ID" | Using wrong chain ID | Use 0x66eee for user-signed | | "Agent already exists" | Duplicate agent name | Use unique agent names | | "Insufficient funds" | Not enough balance | Check account balance | | "Invalid signature" | Wrong signing method | Use proper EIP-712 structure | ### Example error handling ```python theme={"system"} try: result, agent_key = exchange.approve_agent("bot_v1") if result.get('status') == 'ok': print("Agent approved successfully") else: print(f"Error: {result.get('response', {}).get('error', 'Unknown error')}") except Exception as e: print(f"Failed to approve agent: {e}") ``` ## Summary User-signed actions handle administrative operations on Hyperliquid. They use direct EIP-712 signing with chain ID 0x66eee. The SDK's wrapper functions make it easy to approve agents, transfer funds, and manage your account securely. ## Related resources * [Hyperliquid signing overview](/docs/hyperliquid-signing-overview) — Quick reference for both signing types * [L1 action signing guide](/docs/hyperliquid-l1-action-signing) — For trading operations * [Authentication guide](/docs/hyperliquid-authentication-guide) — Overview of authentication mechanisms # JWT validation in Go for Chainstack Marketplace Source: https://docs.chainstack.com/docs/implementing-jwt-validation-in-golang-for-chainstack-marketplace-integration Implement JWT token validation in Go for Chainstack Marketplace integration, covering RSA key retrieval, claims verification, and middleware setup. **TLDR:** * Demonstrates how to validate JWTs in Golang using Ed25519 public keys in PEM format – required for Chainstack Marketplace apps. * Explains parsing of EdDSA-signed JWT headers/payload with required audience matching and expiration checks. * Walks through environment variable setup with `.env`, retrieving the public key, decoding the token, and returning the claims for a successful validation. * Ensures robust security by verifying the token’s legitimacy and ensuring it is intended specifically for your application. # Introduction This guide is designed for developers who are integrating applications into the Chainstack Marketplace using Golang. The tutorial offers practical insights into working with JWTs that you'll obtain from Chainstack's validation endpoint. Within the context of a Golang application, you'll learn how to validate these tokens using public keys in PEM format. Notably, Chainstack employs the EdDSA (Edwards-curve Digital Signature Algorithm) for cryptographic operations, and this guide will get into how to validate JWTs to grant access to your users securely. ## What is JWT? JSON web token (JWT) is a compact, URL-safe means of representing claims between two parties. These claims are encoded as a JSON object and can be digitally signed or integrity-protected with a message authentication code (MAC) and/or encrypted. ## Why validate JWT? Validating a JWT is crucial for ensuring its integrity and authenticity. Failure to do so can lead to security risks like unauthorized access and data breaches. In the Chainstack Marketplace, users will use their Chainstack API key to obtain a JWT that is valid for one hour and includes an "audience" claim, which specifies the intended recipient of the token. ## What will you learn? This tutorial will guide you through the process of validating JWTs in a Golang application. We'll use the `golang-jwt/jwt` library for parsing and validating JWTs and the `golang.org/x/crypto/ed25519` library for cryptographic operations. By the end of this tutorial, you'll be able to: * Parse and decode Ed25519 public keys in PEM format. * Validate JWTs using Ed25519 public keys. * Verify the "audience" claim to ensure the JWT is intended for your application. Whether you're developing a new Golang application requiring JWT validation or integrating this feature into an existing project, this tutorial has you covered. ## Prerequisites Before diving into the code, make sure you have the following installed: * Go 1.16 or higher * A text editor or IDE of your choice (e.g., Visual Studio Code) * Terminal or command prompt for running shell commands You should also have a basic understanding of: * Go programming language * JSON web tokens (JWT) * Public key infrastructure (PKI) ## Setting up the project 1. Initialize a new Go project Open your terminal and run the following command to create a new directory for your project and navigate into it: ```bash theme={"system"} mkdir go-jwt-validation && cd go-jwt-validation ``` 2. Initialize Go module Initialize a new Go module by running: ```bash theme={"system"} go mod init jwt-validation ``` 3. Install required libraries Install the necessary Go libraries by running: ```bash theme={"system"} go get github.com/golang-jwt/jwt go get golang.org/x/crypto/ed25519 go get github.com/joho/godotenv ``` 4. Create a `.env` file Create a `.env` file in the root directory of your project and add the public key you received from Chainstack and other environment variables if needed. > [!NOTE] > > ### Ensure the public key is in the privacy-enhanced mail (PEM) format > > PEM is a widely used encoding format for cryptographic objects such as keys and certificates. It is a base64 encoding of the binary distinguished encoding rules (DER) format with additional header and footer lines. In the code, the public key is stored in PEM format between the lines `-----BEGIN PUBLIC KEY-----` and `-----END PUBLIC KEY-----`. ```bash theme={"system"} JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----PUBLIC_KEY_HERE-----END PUBLIC KEY-----" ``` ### Why use a `.env` file? Using a `.env` file lets you separate your configuration variables from your code. This is beneficial for several reasons: * **Security** — sensitive information like keys should not be hard-coded into your application. * **Portability** — it's easier to manage and change configuration when it's separated from the code. * **Environment specificity** — you can have different `.env` files for different environments like development, testing, and production. ## The Chainstack Marketplace authentication flow Before diving into the code, it's important to understand the authentication flow in the Chainstack Marketplace. The user who has purchased the integration with your application will authorize requests using their Chainstack API key. Note that this API key is not the JWT token you need to validate. To validate the API key, you must call the [Retrieve Application Token](https://api.chainstack.com/reference/#operation/retrieveToken) API. Here's an example API call to retrieve the validated JWT: ```bash cURL theme={"system"} curl --location --request POST '' \\ --header 'Authorization: Bearer CHAINSTACK_API_KEY' ``` This API call will return a validated JWT with a validity period of 60 minutes. ## Claims in a Chainstack Marketplace JWT The JWT from Chainstack will contain several claims that provide information about the token and its intended usage. Below is a table detailing these claims: | Column | Data type | Description | | - | - | - | | sub | string | Unique identifier of the Chainstack account that has installed the application. | | iss | string | The issuer of the token. In this case, it will be "chainstack". | | exp | number | Expiration time of the token in UTC timestamp seconds. Calculated as current time + 1 hour. | | nbf | number | The "not-before" time of the token in UTC timestamp seconds. Calculated as current time - 1 minute. | | iat | number | Issued-at time of the token in UTC timestamp seconds. | | aud | string | The agreed slug of the application. | | scope | string\[] | One or more scopes that should be included in the token payload. | The code we’ll show, for example, validates the JWT based on the timestamp and the expected audience. ## The code Where you initialized the project, create a new file named `validation.go` and paste the following code: ```go validation.go theme={"system"} package main import ( "crypto/x509" "encoding/pem" "fmt" "os" "strings" // Added this package for string manipulation "github.com/joho/godotenv" "github.com/golang-jwt/jwt" "golang.org/x/crypto/ed25519" ) // loadEnvVars loads environment variables from .env file func loadEnvVars() error { err := godotenv.Load() if err != nil { return fmt.Errorf("error loading .env file: %w", err) } return nil } // getPublicKey retrieves the public key from an environment variable func getPublicKey() (ed25519.PublicKey, error) { publicPEM := os.Getenv("JWT_PUBLIC_KEY") if publicPEM == "" { return nil, fmt.Errorf("Environment variable for public key is not set") } // Convert single-line PEM to multi-line PEM if needed publicPEM = strings.Replace(publicPEM, "-----BEGIN PUBLIC KEY-----", "-----BEGIN PUBLIC KEY-----\n", 1) publicPEM = strings.Replace(publicPEM, "-----END PUBLIC KEY-----", "\n-----END PUBLIC KEY-----", 1) block, _ := pem.Decode([]byte(publicPEM)) if block == nil { return nil, fmt.Errorf("Failed to parse PEM block containing the public key") } pubKey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, fmt.Errorf("Failed to parse public key: %w", err) } ed25519PubKey, ok := pubKey.(ed25519.PublicKey) if !ok { return nil, fmt.Errorf("Failed to convert public key to Ed25519 public key") } return ed25519PubKey, nil } // validateJWT validates the JWT token func validateJWT(ed25519PubKey ed25519.PublicKey, jwtToken, expectedAudience string) error { token, err := jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) { return ed25519PubKey, nil }) if err != nil { return fmt.Errorf("Failed to parse JWT token: %w", err) } // Print the JWT headers fmt.Println("JWT Headers:", token.Header) if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { audience, audienceOk := claims["aud"].(string) if audienceOk && audience == expectedAudience { fmt.Println("Decoded Payload:", claims) fmt.Println("Signature is valid.") return nil } else { return fmt.Errorf("Invalid Token: Invalid audience") } } else { return fmt.Errorf("Invalid Token: Token is not valid") } } func main() { err := loadEnvVars() if err != nil { fmt.Printf("Error occurred: %v\n", err) return } ed25519PubKey, err := getPublicKey() if err != nil { fmt.Printf("Error occurred: %v\n", err) return } // Add the JWT to validate jwtToken := "JWT_TO_VALIDATE" expectedAudience := "YOUR_COOL_APP" err = validateJWT(ed25519PubKey, jwtToken, expectedAudience) if err != nil { fmt.Printf("Error occurred: %v\n", err) return } } ``` ## Understanding the code ### Import statements The code starts by importing the necessary packages: ```go validation.go theme={"system"} import ( "crypto/x509" "encoding/pem" "fmt" "os" "strings" "github.com/joho/godotenv" "github.com/golang-jwt/jwt" "golang.org/x/crypto/ed25519" ) ``` * `crypto/x509` and `encoding/pem` — these are standard Go libraries used for parsing the PEM-encoded public key. * `fmt` — standard Go package for formatted I/O operations. * `os` — standard Go package for interacting with the operating system, used here for reading environment variables. * `strings` — standard Go package for string manipulation. * `github.com/joho/godotenv`— this library loads environment variables from a `.env` file. * `github.com/golang-jwt/jwt` — this library is used for parsing and validating JWTs. * `golang.org/x/crypto/ed25519` — this library provides the cryptographic operations for the Ed25519 algorithm. ### Functions and their roles #### `loadEnvVars()` This function loads environment variables from a `.env` file into the program. It uses the `godotenv.Load()` method to read the `.env` file and populate the environment variables. If an error occurs, it returns an error wrapped with additional context. #### `getPublicKey()` This function retrieves the public key stored in the `.env` file as an environment variable. It performs several steps: 1. Reads the `JWT_PUBLIC_KEY` environment variable. 2. Checks if the variable is empty and returns an error if it is. 3. Converts the single-line PEM to multi-line PEM format if needed. 4. Decodes the PEM block to get the public key bytes. 5. Parses the public key bytes to get an `ed25519.PublicKey` object. > [!NOTE] > > When validating the JWT, we are using the Ed25519 public key to validate a signature that was generated using the EdDSA algorithm with the Ed25519 parameters. #### `validateJWT()` This function validates the JWT token. It takes the Ed25519 public key, the JWT token string, and the expected audience as parameters. It performs the following steps: 1. Parse the JWT. Utilizes the `jwt.Parse` function from the `github.com/golang-jwt/jwt` library to parse the JWT. This function not only decodes the token but also validates it against a series of standard claims: * `exp` — expiration time of the token. If the token is expired, the function will return an error. * `nbf` — not-before time, indicating the earliest time the token is valid. * `iat` — issued-at time, indicating when the token was created. The function uses the provided Ed25519 public key for cryptographic validation of the token's signature. 2. Check audience. Explicitly checks the `aud` claim in the token to ensure it matches the expected audience. This is an additional validation layer on top of the default checks performed by `jwt.Parse`. This claim ensures the user is allowed to call your app specifically. 3. Output. If the token is valid, it prints the decoded payload and a validation success message. If the token is invalid for any reason (e.g., expired, wrong audience, etc.), an error message is returned. #### `main()` This is the entry point of the program. It orchestrates the other functions: 1. Calls `loadEnvVars()` to load the environment variables. 2. Calls `getPublicKey()` to retrieve the public key. 3. Calls `validateJWT()` to validate the JWT. ### How to run the code 1. **Validate a JWT**. To validate a JWT, you'll need an authorized API key. Use the [appropriate endpoint](https://api.chainstack.com/reference/#operation/retrieveToken) to validate the API key and obtain a JWT. Note that in a production setting, you'll either need to require users to validate and send the JWT first or implement a flow in your app that validates the API key dynamically. 2. **Add JWT and audience to the script**. Once you've validated the JWT, add it to the corresponding variable inside the `main` function, along with your expected audience. ```go go theme={"system"} jwtToken := "VALIDATED_JWT" expectedAudience := "YOUR_EXPECTED_AUDIENCE" ``` 3. **Run the program directly**. Open your terminal, navigate to the directory containing `validation.go`, and run: ```shell Shell theme={"system"} go run validation.go ``` This will compile and run your code in one step. If everything is set up correctly, you should see the decoded payload and a validation success message. ### Typical responses 1. **Success**. If the JWT is valid and the audience matches, you'll see output similar to the following: ``` JWT Headers: map[alg:EdDSA typ:JWT] Decoded Payload: map[aud:EXPECTED_AUDIENCE exp:TIMESTAMP iat:TIMESTAMP iss:ISSUER nbf:TIMESTAMP scope:[com.APP.api.Paid] sub:SUBJECT] Signature is valid. ``` > [!NOTE] > > ### The actual values will vary based on the JWT and your application At this point, you have a flow to validate JWTs and give access to users! 2. **Failure**. If the JWT is expired, you'll see an error message like: ``` Error occurred: Failed to parse JWT token: Token is expired ``` ## Conclusion In this comprehensive guide, we've explored the essential steps for integrating applications into the Chainstack Marketplace using Golang. We got into the intricacies of JWT validation, a critical aspect of ensuring secure and authorized access to your application. From setting up your Go project and installing the necessary libraries to understanding the Chainstack Marketplace's authentication flow and claims, we've covered it all. We also explored using the Ed25519 public key in conjunction with the EdDSA algorithm for cryptographic validation. This is particularly important given Chainstack's use of the EdDSA algorithm for its JWTs. By following this guide, you should now be equipped with the knowledge and code snippets needed to validate JWTs effectively in your Golang applications. Remember, security is not just a feature but a necessity. Validating JWTs is a fundamental step in safeguarding your application and its users. So, go ahead and implement these best practices in your application to ensure it's as secure as it can be. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Ink tooling Source: https://docs.chainstack.com/docs/ink-tooling Connect MetaMask, ethers.js, viem, web3.py, Foundry, and Hardhat to an Ink Mainnet node on Chainstack. Chain configuration, L1 data fees, contract verification on Blockscout, and the Ink behaviors that affect tooling. Ink is an [OP Stack](https://docs.optimism.io/) layer 2 that settles to Ethereum and uses ETH for gas, so the standard Ethereum libraries and frameworks work against an Ink node without modification. Chainstack serves Ink Mainnet on Global Nodes with archive data and both the `debug_*` and `trace_*` namespaces. See [Debug and trace APIs](/docs/debug-and-trace-apis#ink). ## Chain configuration | Property | Value | | - | - | | Network | Ink Mainnet | | Chain ID | `57073` | | Native gas currency | ETH | | Decimals | `18` | | Block time | About 1 second | | RPC URL | your Chainstack [Ink endpoint](/docs/manage-your-node#view-node-access-and-credentials) | | Multicall3 | `0xcA11bde05977b3631167028862bE2a173976CA11` | | Block explorer | [explorer.inkonchain.com](https://explorer.inkonchain.com) | | Contract verification API | `https://explorer.inkonchain.com/api` (Blockscout) | ## MetaMask Add Ink as a custom network. On Chainstack, get your [Ink endpoint](/docs/manage-your-node#view-node-access-and-credentials), then in MetaMask select **Add a custom network** and fill in: * Network name — Ink * Default RPC URL — your Chainstack Ink endpoint * Chain ID — `57073` * Currency symbol — ETH * Block explorer URL — `https://explorer.inkonchain.com` ## ethers.js Install [ethers.js](https://docs.ethers.org/): ```shell Shell theme={"system"} npm install ethers ``` ```javascript index.js theme={"system"} const { JsonRpcProvider, formatEther } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function main() { const network = await provider.getNetwork(); console.log("Chain ID:", network.chainId.toString()); const block = await provider.getBlockNumber(); console.log("Block:", block); const balance = await provider.getBalance("0x4200000000000000000000000000000000000016"); console.log("Balance:", formatEther(balance), "ETH"); } main(); ``` To receive new blocks as Ink produces them, connect to the WSS endpoint and subscribe: ```javascript index.js theme={"system"} const { WebSocketProvider } = require("ethers"); const provider = new WebSocketProvider("CHAINSTACK_WSS_URL"); provider.on("block", (blockNumber) => { console.log("New block:", blockNumber); }); ``` ## viem viem ships an `ink` chain definition in `viem/chains`. Its default RPC URLs are Ink's public endpoints, so pass your Chainstack endpoint to `http()`. The `publicActionsL2` extension from `viem/op-stack` adds OP Stack actions such as L1 data fee estimation. ```shell Shell theme={"system"} npm install viem ``` ```javascript index.mjs theme={"system"} import { createPublicClient, http, formatEther, parseEther } from "viem"; import { ink } from "viem/chains"; import { publicActionsL2 } from "viem/op-stack"; const client = createPublicClient({ chain: ink, transport: http("CHAINSTACK_NODE_URL"), }).extend(publicActionsL2()); console.log("Chain ID:", await client.getChainId()); console.log("Block:", await client.getBlockNumber()); const balance = await client.getBalance({ address: "0x4200000000000000000000000000000000000016", }); console.log("Balance:", formatEther(balance), "ETH"); const transfer = { account: "0x4200000000000000000000000000000000000016", to: "0x4200000000000000000000000000000000000016", value: parseEther("0.001"), }; console.log("L1 data fee:", formatEther(await client.estimateL1Fee(transfer)), "ETH"); console.log("Total fee:", formatEther(await client.estimateTotalFee(transfer)), "ETH"); ``` `estimateL1Fee` and `estimateTotalFee` estimate without sending anything, but the `account` must hold enough ETH for the `value`. Replace it with your own address. See [L1 data fees](#l1-data-fees-are-charged-on-top-of-gas). ## web3.py Install [web3.py](https://web3py.readthedocs.io/): ```shell Shell theme={"system"} pip install web3 ``` ```python main.py theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider("CHAINSTACK_NODE_URL")) print("Connected:", web3.is_connected()) print("Chain ID:", web3.eth.chain_id) print("Block:", web3.eth.block_number) address = Web3.to_checksum_address("0x4200000000000000000000000000000000000016") balance = web3.eth.get_balance(address) print("Balance:", web3.from_wei(balance, "ether"), "ETH") ``` ## Foundry Install [Foundry](https://getfoundry.sh/) and pass your Chainstack endpoint with `--rpc-url`. Read chain data with `cast`: ```shell Shell theme={"system"} cast chain-id --rpc-url CHAINSTACK_NODE_URL cast block-number --rpc-url CHAINSTACK_NODE_URL cast balance 0x4200000000000000000000000000000000000016 --ether --rpc-url CHAINSTACK_NODE_URL ``` Deploy a contract with `forge create`. Without `--broadcast`, `forge create` only simulates the deployment. ```shell Shell theme={"system"} forge create src/Counter.sol:Counter \ --rpc-url CHAINSTACK_NODE_URL \ --private-key YOUR_PRIVATE_KEY \ --broadcast ``` Verify the deployed contract on the Ink Blockscout. The API does not need a key. ```shell Shell theme={"system"} forge verify-contract DEPLOYED_CONTRACT_ADDRESS src/Counter.sol:Counter \ --chain 57073 \ --verifier blockscout \ --verifier-url https://explorer.inkonchain.com/api/ \ --watch ``` ## Hardhat In a [Hardhat 3](https://hardhat.org/) project that uses `@nomicfoundation/hardhat-toolbox-viem`, add Ink to `hardhat.config.ts`. Hardhat already includes the Ink Blockscout explorer for chain `57073`, so `hardhat verify` needs no extra configuration. ```typescript hardhat.config.ts theme={"system"} import { configVariable, defineConfig } from "hardhat/config"; import hardhatToolboxViem from "@nomicfoundation/hardhat-toolbox-viem"; export default defineConfig({ plugins: [hardhatToolboxViem], solidity: "0.8.28", networks: { ink: { type: "http", url: configVariable("INK_RPC_URL"), accounts: [configVariable("INK_PRIVATE_KEY")], }, }, }); ``` `configVariable` reads `INK_RPC_URL` and `INK_PRIVATE_KEY` from environment variables or the Hardhat keystore, which keeps the endpoint and the key out of the config file. Deploy with an Ignition module, for example `ignition/modules/Counter.ts`: ```typescript ignition/modules/Counter.ts theme={"system"} import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; export default buildModule("CounterModule", (m) => { const counter = m.contract("Counter"); return { counter }; }); ``` ```shell Shell theme={"system"} npx hardhat ignition deploy ignition/modules/Counter.ts --network ink npx hardhat verify blockscout --network ink DEPLOYED_CONTRACT_ADDRESS ``` ## Ink behaviors that affect tooling ### L1 data fees are charged on top of gas Every Ink transaction pays an L1 data fee for posting its data to Ethereum, in addition to the L2 execution fee. `eth_estimateGas` covers only L2 execution, and the total cost of a transaction is `gasUsed × effectiveGasPrice + l1Fee`. Transaction receipts carry the L1 portion in the `l1Fee`, `l1GasUsed`, `l1GasPrice`, and `l1BlobBaseFee` fields. To estimate the L1 data fee before sending, call `getL1Fee` on the `GasPriceOracle` predeploy at `0x420000000000000000000000000000000000000F`, or use viem's `estimateL1Fee` and `estimateTotalFee`, as in the [viem](#viem) example. ### Pending transactions are never delivered Ink is an OP Stack chain, so pending transactions stay with the sequencer. `eth_subscribe("newPendingTransactions")` and `eth_newPendingTransactionFilter` return an ID but deliver nothing, and `txpool_status` always reads empty. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ### Filters are node-local A [Global Node](/docs/global-elastic-node) endpoint is load balanced across backends, and a filter created by `eth_newFilter` or `eth_newBlockFilter` lives on the backend that created it. A follow-up `eth_getFilterChanges` that lands elsewhere returns `-32602 filter not found`. Poll `eth_getLogs` over an explicit block range instead, or hold a WSS subscription, which keeps one connection to one backend for its lifetime. ### Block-count limits and the proof window Ink produces a block about every second, so a 10,000-block `eth_getLogs` request covers under 3 hours of chain time. `eth_getProof` serves the latest block only. See [EVM range limits](/docs/limits#evm-range-limits) and [EVM `eth_getProof` history limits](/docs/limits#evm-eth_getproof-history-limits). # Introducing Bun: The future of JavaScript runtimes Source: https://docs.chainstack.com/docs/introducing-bun-the-future-of-javascript-runtimes Discover Bun JavaScript runtime with Chainstack. Learn fast server setup, built-in fetch API, TypeScript support, and zero dependencies for Web3 development. **TLDR:** * Bun is a fast, all-in-one JavaScript runtime with native server and hot reload features. * You can fetch Ethereum data using Bun’s built-in fetch API and a Chainstack node – no extra packages needed. * Quickly set up a server with Bun.serve() and handle requests to retrieve an address’s ETH balance. * Bun’s .env support, TypeScript/JSX readiness, and zero dependencies make it ideal for a streamlined dev workflow. ## Main article JavaScript's landscape just got more exciting with Bun's debut. Hitting the scene with version 1.0, Bun is a speedy toolkit that simplifies running, building, testing, and debugging JavaScript and TypeScript projects. It's a fresh take on the cluttered world of JS tooling since node.js came around. If you're a dev seeking a snappier tool or a sleek node.js alternative, Bun's got you covered. With its wide file support, built-in web APIs, and impressive speed, Bun is set to shake up JavaScript runtimes. Dive in as we explore Bun and craft a DApp using its APIs, all without any extra dependencies. Dive into this guide, and we'll kickstart your journey with Bun. We're crafting a simple DApp using its APIs. And here's the kicker: we'll spin up a server to get blockchain data with zero dependencies. ## Bun: A comprehensive JavaScript toolkit Before starting the project, let’s briefly review what makes Bun such an interesting tool. * All-in-one toolkit. Bun is a unified toolkit designed for running, building, testing, and debugging JavaScript and TypeScript applications, eliminating the need for multiple tools and dependencies. * Server capabilities: * `Bun.serve()`. Easily spin up an HTTP or WebSocket server using familiar web-standard APIs like `Request` and `Response`. * Performance. Bun can serve up to 4x more requests per second than node.js and 5x more messages per second than ws on node.js, as per Bun documentation. * TLS configuration. Secure your server with built-in TLS options. * Native .env support. Bun natively reads `.env` files, removing the need for third-party packages like `dotenv` and `cross-env`. * Hot reloading: * Built-in watch mode. Use `bun --hot` to enable hot reloading, which automatically reloads your application when files change. * Efficient reloading. Unlike tools like `nodemon`, Bun reloads your code without terminating the old process, preserving HTTP and WebSocket connections and maintaining state. * Additional features: * Native web APIs. Built-in support for web standard APIs available in browsers, such as `fetch`, `Request`, `Response`, `WebSocket`, and `ReadableStream`. * TypeScript and JSX support. Run JavaScript, TypeScript, and JSX/TSX files directly without any dependencies. * Module compatibility. Supports both CommonJS and ES modules without any configuration hassles. * Plugins. Highly customizable with a plugin API inspired by `esbuild`, allowing for custom loading logic and support for additional file types. * Optimized APIs. Bun offers highly optimized, standard-library APIs designed for speed and ease of use, outperforming many node.js counterparts. ## Project overview: Ethereum balance checker with Bun At its core, the project aims to provide a creative example of how you can use the new Bun JavaScript runtime to work with DApps, highlighting the fact that you can make a functioning tool with zero extra dependencies. In this guide, we'll walk you through creating a DApp designed to fetch the balance of an Ethereum address. Leveraging Bun, we'll set up a server that awaits user requests containing an Ethereum address. The application will then use a Chainstack Ethereum RPC node to get the balance. The retrieved balance will then be decoded and presented to the user as a structured JSON object looking like the following: ```Json JSON theme={"system"} { "address": "0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13", "balance": "162.140649034802429952", "unit": "Ether" } ``` ## Prerequisites * Bun installed on your machine. Bun is available on [macOS, Linux, and Windows](https://bun.sh/docs/installation). * A Chainstack account to deploy an Ethereum node. 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Getting started with the project Once you've installed Bun and secured an Ethereum endpoint from Chainstack, it's time to set up your project. Create a new directory and initiate it with the following: ```shell Shell theme={"system"} bun init ``` This process is straightforward, especially if you're familiar with `npm init`. Here's how I configured mine: ```shell Shell theme={"system"} package name (address_check): entry point (index.ts): server.js Your package.json is now created in the directory. You'll also find: + server.js + .gitignore + jsconfig.json (helpful for editor auto-complete) + README.md To start your project, simply run: bun run server.js ``` With that, your Bun project is set up, complete with a `.gitignore`, README, and main file, which in this case is named `server.js`. ## Setting up the `.env` file Next up, we're going to set up a `.env` file. This is where we'll store our Chainstack endpoint. Just create a new `.env` file and add this inside: ```bash Bash theme={"system"} CHAINSTACK_NODE_URL='YOUR_CHAINSTACK_ENDPOINT' ``` This way, our Chainstack endpoint stays safe. With Bun, we can directly get environment variables from its environment; no extra packages are needed. ## The code Now it’s time for the code; in the `server.js` file already created for us by Bun, paste the following: ```javascript server.js theme={"system"} const PORT = 5555; const ETHEREUM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/; const CHAINSTACK_NODE_URL = Bun.env.CHAINSTACK_NODE_URL; const JSON_HEADERS = { "Content-Type": "application/json" }; function isValidEthereumAddress(address) { return ETHEREUM_ADDRESS_REGEX.test(address); } async function fetchFromEthereumNode(address) { console.log(`Calling Ethereum node with address: ${address}\n`); const response = await fetch(CHAINSTACK_NODE_URL, { method: "POST", headers: { "accept": "application/json", "content-type": "application/json", }, body: JSON.stringify({ id: 1, jsonrpc: "2.0", method: "eth_getBalance", params: [address, "latest"], }), }); const responseBody = await response.json(); console.log( `Received response from Ethereum node: ${JSON.stringify(responseBody)}\n` ); if (!response.ok) { throw new Error("Error fetching balance"); } if (responseBody.error) { throw new Error( responseBody.error.message || "Error in Ethereum node response" ); } return responseBody; } function convertWeiToEther(weiValue) { const divisor = BigInt("1000000000000000000"); const wholeEthers = weiValue / divisor; const remainderWei = weiValue % divisor; const remainderEther = remainderWei.toString().padStart(18, "0"); return `${wholeEthers}.${remainderEther}`; } async function getEthereumBalance(address) { const responseBody = await fetchFromEthereumNode(address); const decimalValue = parseInt(responseBody.result.substring(2), 16); const weiValue = BigInt(decimalValue); return convertWeiToEther(weiValue); } function logAndReturnResponse(status, content) { console.log( `Sending response back to user: ${status} ${JSON.stringify(content)} \n` ); return new Response(JSON.stringify(content), { status: status, headers: JSON_HEADERS, }); } Bun.serve({ port: PORT, async fetch(request) { console.log(`Received request: ${request.method} ${request.url}\n`); const urlObject = new URL(request.url); const pathname = urlObject.pathname; try { if (request.method === "GET" && pathname.startsWith("/getBalance/")) { const address = pathname.split("/getBalance/")[1]; if (!isValidEthereumAddress(address)) { return logAndReturnResponse(400, { error: "Invalid Ethereum address format", }); } const balance = await getEthereumBalance(address); return logAndReturnResponse(200, { address: address, balance: balance, unit: "Ether", }); } return logAndReturnResponse(404, { error: "Endpoint does not exist" }); } catch (error) { console.error(`Error occurred: ${error.message}`); return logAndReturnResponse(500, { error: error.message }); } }, }); console.log(`Bun server running on port ${PORT}...`); ``` Let us further explain the code. ### 1. Setting up constants The beginning sets up some constants, like the port number, which you can adapt to your use case, and the Chainstack RPC URL picked up from the environment variables. ```jsx server.js theme={"system"} const PORT = 5555; const ETHEREUM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/; const CHAINSTACK_NODE_URL = Bun.env.CHAINSTACK_NODE_URL; const JSON_HEADERS = { "Content-Type": "application/json" }; ``` * `PORT` — the port number on which our server will listen. * `ETHEREUM_ADDRESS_REGEX` — a regular expression to validate Ethereum addresses. * `CHAINSTACK_NODE_URL` — the URL of the Ethereum node we'll be querying. This is fetched from an environment variable. > [!NOTE] > > Note that the environment variable is taken directly from the Bun environment using `Bun.env`. * `JSON_HEADERS` — standard JSON headers used in HTTP responses. ### 2. Utility functions #### `isValidEthereumAddress(address)` This function checks if a given string matches the Ethereum address format, and the server will return an error if the pattern doesn’t match; it is good practice and enhances UX. #### `convertWeiToEther(weiValue)` The smallest unit in Ethereum is named `Wei`. This function converts a value in wei to its equivalent in ether, considering that 1 ether = 10^18 wei, and it is used to return a human-readable value to the user. ### 3. Fetching data from Ethereum node #### `fetchFromEthereumNode(address)` This function does the following: * Logs the Ethereum address it's about to query. * Sends a POST request to the Ethereum node to fetch the balance of the address using [eth\_getBalance](/reference/ethereum-getbalance). * Checks and logs the response from the Ethereum node. * Throws errors if the response is not successful or if there's an error in the Ethereum node's response. * Returns the response body if everything is okay. ### 4. Getting Ethereum balance: #### `getEthereumBalance(address)` This function orchestrates the process of fetching the balance: * Calls `fetchFromEthereumNode(address)` to get the balance in wei. * Converts the wei value to ether. * Returns the balance in ether. ### 5. Logging and sending responses: #### `logAndReturnResponse(status, content)` This utility function logs the response status and content and returns the response to the client. ### 6. Server logic The `Bun.serve` function sets up the server: * It first logs any incoming request. * If the request is a GET request to the `"/getBalance/"` endpoint, it processes it the following way: * Validates the Ethereum address * Fetches the balance using `getEthereumBalance(address)` * Sends back the balance in ether * If the endpoint doesn't match, it sends a 404 error. * Any errors during the process are caught, and a 500 error is returned. Finally, a log statement indicates that the server is running and listening on the specified port. So, as you can see, we run everything within Bun. `Bun.serve` spins up a server with no extra dependencies. ## Running the server with hot reloading Your server setup is complete, and it's time to get it up and running. For an enhanced development experience, start the server with the `--hot` flag. This activates Bun's hot reloading feature, ensuring that any modifications you make to your modules or files are instantly reflected on the server without manual restarts. > [!NOTE] > > When using `Bun.serve()` for HTTP server tasks, Bun got you covered. It smartly identifies any changes and refreshes your fetch handler, all without rebooting the entire Bun process. This results in almost instantaneous hot reloads, optimizing your development flow. Start the server with the following command: ```shell Shell theme={"system"} bun --hot server.js ``` Upon execution, you should see: ```shell Shell theme={"system"} [0.59ms] ".env" Bun server running on port 5555... ``` With your server active, you're all set to test your DApp. You can use tools like [Postman](https://www.postman.com/); you can use the following curl request: ```bash cURL theme={"system"} curl --location 'localhost:5555/getBalance/0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13' ``` This prompts the server to retrieve the balance from the Ethereum node, returning: ```Json JSON theme={"system"} { "address": "0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13", "balance": "148.991988684657950720", "unit": "Ether" } ``` While the console will log the following: ```shell Shell theme={"system"} Received request: GET http://localhost:5555/getBalance/0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13 Calling Ethereum node with address: 0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13 Received response from Ethereum node: {"jsonrpc":"2.0","id":1,"result":"0x813ade050b80ebb48"} Sending response back to user: 200 {"address":"0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13","balance":"148.991988684657950720","unit":"Ether"} ``` You've just crafted a streamlined API to fetch Ethereum balances using Bun without additional dependencies. ## Conclusion In the ever-evolving landscape of JavaScript, Bun emerges as a promising toolkit that simplifies and streamlines the development process. Through this guide, we've witnessed the power and efficiency of Bun, crafting a DApp to fetch Ethereum balances with minimal fuss and zero extra dependencies. The ease with which we can set up servers, integrate with blockchain nodes, and benefit from hot reloading showcases Bun's potential to become a staple in the developer's toolkit. As we move forward, we must keep an eye on tools like Bun that prioritize developer experience and performance. Whether you're a seasoned developer or just starting, embracing such tools can significantly enhance your productivity and the quality of your projects. # Introduction to smart contract auditing using Foundry Source: https://docs.chainstack.com/docs/introduction-to-smart-contract-manual-auditing-with-foundry-and-slither Audit smart contracts manually using Foundry for testing and Slither for static analysis to identify vulnerabilities before deploying to production. **TLDR:** * Smart contract auditing is vital due to the immutable nature of on-chain code – once deployed, bugs and vulnerabilities can’t be easily patched. * Manual auditing (plus tools like Slither and Foundry) helps discover issues such as reentrancy, overflow/underflow, or weak pseudo-randomness before they cause real damage. * Following best practices—like checks-effects-interactions, mutex locks, proper random number generation, and robust testing—strengthens contract security. * Thoroughly documenting discovered vulnerabilities and their fixes fosters trust and ongoing improvement in the auditing process. ## Main article Smart contract auditing is the process of reviewing and evaluating the code of a smart contract to identify potential security vulnerabilities, bugs, and other issues that may impact the contract's functionality. There are two main types of auditing: manual auditing and automated auditing. Manual auditing involves reviewing the code line-by-line and using tools like Slither to identify potential issues. Automated auditing involves using software tools to scan the code and identify potential vulnerabilities. The need to audit smart contracts is critical, as smart contracts are immutable and can cause significant harm if they contain security vulnerabilities. In this project, we will provide an overview of smart contract auditing, with a focus on manual auditing techniques. We will also cover common attack vectors, such as reentrancy, replay attacks, and overflows, and provide code examples and snippets to demonstrate how to use tools like Slither in auditing contracts, as well as how to test and report on identified issues. The purpose of smart contract auditing is to guarantee that the contract works as it should and that the code is safe from malicious attacks and unforeseen outcomes. ### Why audit smart contracts? Auditing smart contracts is critical, as smart contracts are self-executing and immutable once deployed on the blockchain. This means that once a smart contract is deployed, its code and behavior cannot be changed, making it important to identify and resolve any security vulnerabilities before deployment. Smart contracts are often used to manage and transfer valuable assets, such as cryptocurrencies, on the blockchain. If a smart contract contains security vulnerabilities, it can be exploited by malicious actors, leading to the loss of these assets. For example, a smart contract that has a vulnerability that allows someone to steal funds from the contract can result in a significant financial loss for the contract's users. Additionally, smart contracts are designed to be autonomous and self-executing, meaning they can perform actions automatically without the need for human intervention. This is both a strength and a weakness of smart contracts, as a vulnerability in the code can cause the contract to behave in unintended ways, such as sending funds to the wrong address or executing code in an infinite loop. In short, auditing smart contracts is important because it helps identify and resolve potential security vulnerabilities and bugs, ensuring that the contract behaves as intended and provides the necessary security for users and their assets. ## Auditing smart contracts manually with Slither and Foundry ### What is Slither? Slither is a tool for making smart contracts more secure. It was created by **Trail Of Bits** and was first released to the public in 2018. Slither is a type of software that helps check the security of smart contracts. It's written in Python and looks for potential problems in the code of a smart contract. Slither has different types of checks built into it to help find different kinds of security issues. It also provides information about the details of the smart contract and has an API that makes it easy to add custom checks. This tool helps developers find and fix problems, understand the code better, and create custom checks as needed. Slither works by analyzing the code of a smart contract and looking for specific patterns and code snippets that are known to be vulnerable. Once Slither has identified potential vulnerabilities in the code, it generates a report that highlights the issues and provides recommendations for how to fix them. ### Install Slither Slither requires [solc](https://docs.soliditylang.org/en/latest/installing-solidity.html), the Solidity compiler, and [Python 3.8+](https://www.python.org/downloads/). I also recommend creating a new Python virtual environment for this project: ```shell Shell theme={"system"} python3 -m venv audits ``` Install Slither using pip by running the following command: ```shell Shell theme={"system"} pip3 install slither-analyzer ``` > [!NOTE] > > To learn more about other installation methods, check out the [Slither repository](https://github.com/crytic/slither#how-to-install). Next, install the [Slither extension](https://marketplace.visualstudio.com/items?itemName=trailofbits.slither-vscode) in VS Code. ### What is Foundry? Foundry is a toolkit for building applications on the Ethereum blockchain. It's written in a programming language called Rust and is designed to be fast, flexible, and easy to use. Foundry is made up of several different tools that work together to make building and testing Ethereum applications easier. Some of the tools include: * Forge — a testing framework for Ethereum applications, similar to other testing frameworks like Truffle, Hardhat, and DappTools. * Cast — a tool that helps you interact with smart contracts on the Ethereum blockchain. You can use it to send transactions and get information about the blockchain. * Anvil — a local Ethereum node, similar to Ganache and Hardhat Network, that you can use to test your applications. * Chisel — a Solidity REPL (Read-Eval-Print-Loop) that lets you test and run Solidity code in a fast, efficient, and verbose way. Each of these tools is designed to make different parts of the Ethereum development process easier and more efficient, and when used together, they provide a comprehensive toolkit for building and testing Ethereum applications. > [!NOTE] > > Learn more about Foundry from our [workshop with Learn Web3 DAO](https://github.com/chainstacklabs/learnweb3dao-foundry-workshop). ### Install Foundry To [get started with Foundry](/docs/getting-started-with-foundry), you need to install a tool called `foundryup`. Here's how: 1. Open your terminal and run this command (for Linux and macOS): ```bash cURL theme={"system"} curl -L https://foundry.paradigm.xyz | bash ``` 2. Once you've got the installation script, open a new terminal or make sure your PATH is up-to-date. Then, run the following command: ```shell Shell theme={"system"} foundryup ``` That's it! You're now ready to start using Foundry. If you require additional information or assistance, please visit the Foundry [repository](https://github.com/foundry-rs/foundry#installation) or check out the [Foundry book](https://book.getfoundry.sh/getting-started/installation). ## Auditing smart contracts Before we dive into the smart contract auditing process, let's take a moment to familiarize ourselves with the steps involved. The auditing journey typically unfolds as follows: 1. **Test execution**. We kick off the process by running a series of tests on the smart contract code. This helps us spot any potential hiccups that might be lurking in the code. 2. **Specification and documentation review**. Next, we immerse ourselves in the specifications and documentation of the smart contract. This deep dive gives us a comprehensive understanding of the contract's inner workings. 3. **Fast tool utilization**. Here, we employ rapid-fire tools like Slither to swiftly pinpoint potential vulnerabilities and security issues in the code. 4. **Manual analysis**. Post the automated tests and tool usage, we conduct a meticulous manual analysis of the code. This step helps us catch any issues that might have slipped through the automated checks. 5. **Discussion**. We then engage in a detailed discussion about the identified issues. This dialogue ensures a thorough understanding of the problem and helps us chart out the most effective course of action. 6. **Report compilation**. The final step involves crafting a comprehensive report that documents all the identified issues, along with recommendations for rectifying the problems. This report serves as a valuable reference for future updates or audits of the smart contract. Let's jump into auditing a code base! Remember, we'll be working on the project using Foundry and Slither. [Find the repository on GitHub](https://github.com/chainstacklabs/smart-contracts-audit-foundry-slither). First, clone the repository ```shell Shell theme={"system"} $ git clone https://github.com/chainstacklabs/smart-contracts-audit-foundry-slither.git $ cd audit-practice $ forge install $ forge build ``` In the `audit/src` directory, there are three contracts written in Solidity. We will be using these contracts for this tutorial. You can do static analysis with Slither in two ways. You can either use it in the terminal or as an [extension](https://marketplace.visualstudio.com/items?itemName=trailofbits.slither-vscode) in VS Code. We'll explain both options so it's easy to understand. In your terminal, run the `slither` command: ```shell Shell theme={"system"} slither . ``` > [!NOTE] > > The command `slither .` analyzes all Solidity files within the current directory, as denoted by the `.` symbol. Slither scans these files, generating a comprehensive report that outlines potential vulnerabilities, bugs, and areas of concern regarding code quality within the smart contracts. You will find the results categorized into three groups: high vulnerabilities (red), medium vulnerabilities (yellow), and low vulnerabilities (green). You can conveniently get the same outcome by using the Slither extension in VS Code. Simply click the Slither icon and proceed to run it. ![Example of Slither report](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/109627e-slither-extension-report.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=79e3c29dbe0b94e1246557237207470d) Slither found two high-level risks: * `Lottery.endLottery() (src/OverUnderFlowVul.sol#25-33) uses a weak PRNG` * `Reentrancy in ReentrancyExample.withdraw(uint256) (src/ReentrancyExample.sol#11-16)` ![High-level risks example](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/9b4e5fc-results.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1aaa3b751f088f1900a19d8c8d91852f) If you examine closely, you'll see that Slither missed a major problem in the `OverUnderVul.sol` contract and `ReplayVul.sol`. That's why it's important to also manually go through the code one line at a time. > [!NOTE] > > Please note that the following examples are for educational purposes only, and modern versions of Solidity might already include checks to avoid some of the issues explained here. ### Overflow and underflow vulnerabilities Let’s go over `OverUnderVul.sol`, which is a lottery contract, first. ```solidity solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Lottery { address public owner; uint public jackpot; mapping(address => uint) public balances; address[] public players; bool public isEnded; constructor() { owner = msg.sender; jackpot = 0; isEnded = false; } function buyTicket() public payable { require(!isEnded, "Lottery has ended"); require(msg.value == 1 ether, "Please send 1 ether to buy a ticket"); players.push(msg.sender); balances[msg.sender] += msg.value; jackpot += msg.value; } function endLottery() public { require(msg.sender == owner, "Only owner can end the lottery"); require(!isEnded, "Lottery has already ended"); isEnded = true; uint winnerIndex = uint(blockhash(block.number - 1)) % players.length; address winner = players[winnerIndex]; balances[winner] += jackpot; jackpot = 0; } } ``` After thoroughly reviewing the contract, it has been discovered that there are potential vulnerabilities with both overflow and underflow. * An overflow vulnerability exists in the jackpot variable. If the sum of all ticket purchases exceeds the maximum value that can be stored in a `uint` variable, which is 2^256-1, the jackpot variable will reset to zero. This can result in the winner receiving an incomplete prize and cause unexpected behavior in other areas of the contract. 2^256-1 is a very big number, but it’s better to cover every possibility. * In the `balances[msg.sender] += msg.value;` line, the balance is also susceptible to overflow. * An underflow vulnerability exists in the balance mapping. If a player withdraws more funds than they have in their balance, the balance will underflow and wrap around to the maximum value of a `uint` variable. This allows them to withdraw a large amount of funds they do not own and could be exploited by malicious actors to steal funds from the contract. *To prevent these vulnerabilities, the contract could add additional checks to ensure that the jackpot and balance variables do not overflow or underflow. For example, the contract could limit the maximum value of the jackpot. The contract could also check that a player has sufficient funds before allowing them to withdraw any amount.* ### Modifying and writing PoC for the `OverUnderVul.sol` contract Here is a modified version of the lottery contract in the `OverUnderFlowVul.sol` file that includes checks to prevent overflow and underflow vulnerabilities: ```solidity solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity 0.8.0; contract Lottery { address public owner; uint256 public jackpot; mapping(address => uint256) public balances; address[] public players; bool public isEnded; constructor() { owner = msg.sender; jackpot = 0; isEnded = false; } function buyTicket() public payable { require(!isEnded, "Lottery has ended"); require(msg.value == 1 ether, "Please send 1 ether to buy a ticket"); players.push(msg.sender); require(balances[msg.sender] + msg.value > balances[msg.sender], "Balance overflow"); balances[msg.sender] += msg.value; require(jackpot + msg.value > jackpot, "Jackpot overflow"); jackpot += msg.value; } function endLottery() public { require(msg.sender == owner, "Only owner can end the lottery"); require(!isEnded, "Lottery has already ended"); isEnded = true; uint256 winnerIndex = uint256(blockhash(block.number - 1)) % players.length; address winner = players[winnerIndex]; require(jackpot > 0, "No jackpot to award"); balances[winner] += jackpot; require(balances[winner] >= jackpot, "Balance underflow"); jackpot = 0; } } ``` The following are the contract modifications: * Two checks for the jackpot and balance overflow have been added to the `buyTicket` function to ensure that the jackpot and balance do not exceed the maximum value of a `uint256` variable. The line `require(jackpot + msg.value > jackpot, "Jackpot overflow");` ensures that the new value of `jackpot` (after adding `msg.value`) is indeed greater than the current `jackpot` value. If it's not, this means an overflow has occurred, and the function will revert due to the `require` statement. The balance check follows the same principle. * A check for balance underflow has been added to the endLottery function to ensure the winner's balance does not go below zero when adding the jackpot amount. The line `require(balances[winner] >= jackpot, "Balance underflow");` ensures that the new balance of the `winner` (after adding the `jackpot`) is indeed greater than or equal to the `jackpot` value. If it's not, this means an underflow has occurred, and the function will revert due to the `require` statement. ### Weak pseudo-random number generator warning Now that we covered those vulnerabilities let’s talk about the big one. The use of a weak pseudo-random number generator (PRNG) in your `endLottery()` function as highlighted by Slither. The line `uint256 winnerIndex = uint256(blockhash(block.number - 1)) % players.length;` is trying to generate a pseudo-random number to select a winner from the `players` array. However, Slither is warning you that this method of generating random numbers is not secure. The `blockhash` function returns the hash of the given block number, and in this case, it's the hash of the previous block (`block.number - 1`). The issue is that block hash, block number, and other similar variables are public on the blockchain. This means that validators (or anyone else) who can see these variables could potentially manipulate the outcome to their advantage. In the context of a lottery, this could mean that a validator might be able to influence the result to ensure they win, which would be a major security vulnerability. Therefore, it's generally recommended to use a more secure method for generating random numbers in a smart contract, which generally includes a service like [Chainlink VRF](/docs/smart-contracts-glossary#chainlink-vrf). > [!NOTE] > > Learn more about Chainlink VRF from our [Chainlink VRF Tutorial with Foundry](https://chainstack.com/using-chainlinks-vrf-with-foundry/). The following is a **PoC** contract factory that deploys and interacts with the modified contract: ```solidity solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./OverUnderFlowVul.sol"; contract LotteryPOC { OverUnderFlowVul public lottery; constructor() { lottery = new OverUnderFlowVul(); } function buyTickets(uint256 numTickets) public payable { for (uint256 i = 0; i < numTickets; i++) { lottery.buyTicket{value: 1 ether}(); } } function endLottery() public { lottery.endLottery(); } function withdraw() public { uint256 balance = lottery.balances(msg.sender); require(balance > 0, "No funds to withdraw"); lottery.balances(msg.sender) = 0; payable(msg.sender).transfer(balance); } } ``` The contract is a proof of concept that deploys an instance of the lottery contract. The contract provides three methods to the user: `buyTickets`, `endLottery`, and `withdraw`. The `buyTickets` method allows the user to buy a specified number of tickets, and the `endLottery` method allows the owner of the lottery contract to end the lottery and select a winner. Finally, the `withdraw` method allows players to withdraw their winnings. These changes were made to prevent vulnerabilities related to overflow and underflow in the lottery contract. By making these changes, the lottery contract is now more secure and fair for all players. ### Almost-replay vulnerability Now, let's go over `ReplayVul.sol` which is a simple NFT marketplace contract. ```solidity solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract SimpleNFTMarketplace { mapping(address => uint256) public balances; mapping(uint256 => address) public tokenOwners; function buyToken(uint256 _tokenId, uint256 _price) public { require(tokenOwners[_tokenId] != address(0), "Token does not exist"); require(balances[msg.sender] >= _price, "Insufficient balance"); balances[msg.sender] -= _price; balances[tokenOwners[_tokenId]] += _price; tokenOwners[_tokenId] = msg.sender; } } ``` This contract has a vulnerability that allows a user to manipulate the ownership of a token by calling the `buyToken` function. Specifically, if an attacker has already obtained ownership of a token, they can call the `buyToken` function with the same `_tokenId` parameter and a very low `_price` parameter. Since the attacker already owns the token, the `tokenOwners[_tokenId] != address(0)` check will pass, and the `require(balances[msg.sender] >= _price)` check will also pass since the attacker can set the price to a very low value. As a result, the attacker's balance will be decreased by the `_price` amount, while the previous owner's balance will be increased by the same amount. Additionally, the ownership of the token will be transferred to the attacker. This can be repeated multiple times by the attacker to keep acquiring ownership of the same token at a very low cost. This vulnerability is not a replay attack in the traditional sense, as it doesn't involve replaying transactions on different networks or with different nonces. Instead, it's a form of attack where a token owner can "buy" their own token at any price, potentially draining the contract's balance. One way to do this is by introducing a nonce parameter in the `buyToken` function that must be incremented every time the function is called. This would ensure that each transaction is unique but it doesn't directly address the issue of a token owner buying their own token. To address this issue, a more direct solution would be to add a `require` statement to the `buyToken` function that checks if `msg.sender` is not the current owner of the token. This would prevent the token owner from buying their own token, thus mitigating the vulnerability. As always, it's crucial to thoroughly test and audit your contract before deploying it to a live network. Here's the updated contract with the nonce check implemented: ```solidity solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract SimpleNFTMarketplace { mapping(address => uint256) public balances; mapping(uint256 => address) public tokenOwners; function buyToken(uint256 _tokenId, uint256 _price) public { require(tokenOwners[_tokenId] != address(0), "Token does not exist"); require(tokenOwners[_tokenId] != msg.sender, "Token already owned by buyer"); require(balances[msg.sender] >= _price, "Insufficient balance"); balances[msg.sender] -= _price; balances[tokenOwners[_tokenId]] += _price; tokenOwners[_tokenId] = msg.sender; } } ``` In this updated contract, the line `require(tokenOwners[_tokenId] != msg.sender, "Token already owned by buyer");` ensures that the buyer is not already the owner of the token. If they are, the function will revert due to the `require` statement. This prevents a token owner from buying their own token, which addresses the vulnerability in the original contract. ### Reentrancy attacks Now, let's go over the reentrancy issue spotted by Slither in `ReentrancyExample.sol` contract. The vulnerability identified by Slither was `Reentrancy in ReentrancyExample.withdraw(uint256) (src/ReentrancyExample.sol#11-16)` ```solidity solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract ReentrancyExample { mapping(address => uint) balances; function deposit() public payable { balances[msg.sender] += msg.value; } function withdraw(uint amount) public { require(balances[msg.sender] >= amount, "Insufficient balance."); (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed."); balances[msg.sender] -= amount; } function getBalance() public view returns (uint) { return balances[msg.sender]; } } ``` The Slither report indicates that there is a reentrancy vulnerability in the `withdraw` function of the `ReentrancyExample` contract. Remember, reentrancy is a type of attack where an attacker exploits a contract's code to execute a function multiple times before the previous execution has been completed. In this contract, an attacker could potentially call the `withdraw` function repeatedly before the `balances[msg.sender] -= amount;` line is executed, allowing them to repeatedly withdraw funds from their balance and drain the contract's balance. Here's the potential attack scenario: 1. An attacker deposits some ether into your contract, thereby establishing a non-zero balance. 2. The attacker calls the `withdraw` function. Your contract starts executing the `withdraw` function. 3. During the `withdraw` call, before the balance is deducted, the attacker's contract fallback function is triggered by the `call` function. 4. Within the attacker's fallback function, the attacker again calls the `withdraw` function. 5. Since the contract's state has not yet been updated (the balance deduction happens after the `msg.sender.call`), the contract still sees the balance as not being withdrawn, so it processes the nested `withdraw` call, and sends the funds to the attacker. 6. This can be repeated until the contract's balance is drained. To fix this vulnerability, the contract should use a mutex to prevent reentrancy. A common way to do this is to use the "checks-effects-interactions" pattern, which means that a contract should first check all of the preconditions for executing a function, then update the contract's state, and then interact with external contracts or send ether. > [!NOTE] > > Note that this contract is an example only and does not mean other improvements cannot be made. For example, the `withdraw` function could be external instead of public, and the mappings could be private. > > Even with this example, Slither will still give a reentrancy warning even though we took action against it; static analysis tools such as Slither analyze code in a very systematic and rule-based way, looking for specific patterns that may suggest potential vulnerabilities. Here is an updated version of the contract that uses the "checks-effects-interactions" pattern: ```solidity solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity 0.8.0; contract ReentrancyExample { mapping(address => uint) balances; mapping(address => bool) locked; function deposit() public payable { balances[msg.sender] += msg.value; } function withdraw(uint amount) public { require(balances[msg.sender] >= amount, "Insufficient balance."); require(!locked[msg.sender], "Reentrancy detected."); locked[msg.sender] = true; balances[msg.sender] -= amount; (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed."); locked[msg.sender] = false; } function getBalance() public view returns (uint) { return balances[msg.sender]; } } ``` In this version of the contract, we have added a new `locked` mapping to keep track of which accounts are currently executing the `withdraw` function. Before updating the contract's state or interacting with external contracts, we check if the account is already locked. If it is, we revert the transaction to prevent reentrancy. If it is not locked, we set the `locked` flag to `true`, update the balance and perform the transfer, and then set the flag back to `false` to release the lock. To demonstrate the vulnerability, an attacker could create a contract that repeatedly calls the `withdraw` function of the `ReentrancyExample` contract before the previous call has been completed. > [!WARNING] > > Remember to be careful and consider all the edge cases when using this kind of locking mechanism. For instance, if a call to an external contract fails (like if the external contract doesn't have a fallback function or if it reverts for some reason), then the lock will not be released, locking that address out of the `withdraw` function permanently. > > You might want to use a try-catch block to handle potential exceptions from the external call, allowing you to unlock even when the external call fails. Here is a simple PoC contract that demonstrates the attack: ```solidity solidity theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./ReentrancyVul.sol"; contract ReentrancyAttack { ReentrancyExample public target; uint public count; constructor(address _target) { target = ReentrancyExample(_target); } function attack() public payable { count++; if (count < 10) { target.withdraw(1 ether); } } receive() external payable { if (count < 10) { target.withdraw(1 ether); } } } ``` This contract creates a new `ReentrancyAttack` contract and sets the `target` to the address of the `ReentrancyExample` contract. The `attack` function is called repeatedly to execute the `withdraw` function of the `ReentrancyExample` contract. The `receive` function is a fallback function that is called when the contract receives ether. This function also calls the `withdraw` function, allowing the attacker to repeatedly withdraw funds from the `ReentrancyExample` contract. To protect against this attack, we can deploy the updated `ReentrancyExample` contract with the mutex protection described earlier, and also we could use the [reentrancy guard contract from OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol). ## Conclusion In conclusion, smart contract auditing is crucial to ensure the safety of assets managed by smart contracts on the blockchain. Auditing can be done manually or with the use of automated tools, but in this article, the focus was on manual auditing with tools like Slither and Foundry. The need for smart contract auditing is driven by the immutable nature of smart contracts, which makes it difficult to fix security vulnerabilities once they are deployed. The auditing process involves examining the code line-by-line and identifying potential security issues, which are then documented and reported on. I hope this article has provided valuable insights into the importance of smart contract auditing and how to do it effectively. Thank you for reading. ### About the author > [card] Natachi Nnamaka > > Junior blockchain developer > \ > Enjoy working with React, Next.js, Solidity, Jamstack > > [https://github.com/natachigram](https://github.com/natachigram) > > [https://twitter.com/natachijs](https://twitter.com/natachijs) > > [https://www.linkedin.com/in/natachijs/](https://www.linkedin.com/in/natachijs/) # Kaia methods Source: https://docs.chainstack.com/docs/kaia-methods Complete reference of supported Kaia JSON-RPC methods on Chainstack nodes, including kaia, eth, debug, and governance namespace availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | kaia\_accountCreated | | | | kaia\_accounts | | | | kaia\_blockNumber | | | | kaia\_call | | | | kaia\_chainId | | | | kaia\_clientVersion | | | | kaia\_decodeAccountKey | | | | kaia\_encodeAccountKey | | | | kaia\_estimateComputationCost | | | | kaia\_estimateGas | | | | kaia\_feeHistory | | | | kaia\_gasPrice | | | | kaia\_getAccount | | | | kaia\_getAccountKey | | | | kaia\_getBalance | | | | kaia\_getBlockByHash | | | | kaia\_getBlockByNumber | | | | kaia\_getBlockTransactionCountByHash | | | | kaia\_getBlockTransactionCountByNumber | | | | kaia\_getBlockWithConsensusInfoByHash | | | | kaia\_getBlockWithConsensusInfoByNumber | | | | kaia\_getBlockWithConsensusInfoByNumberRange | | | | kaia\_getChainConfig | | | | kaia\_getCode | | | | kaia\_getCommittee | | | | kaia\_getCommitteeSize | | | | kaia\_getCouncil | | | | kaia\_getCouncilSize | | | | kaia\_getFilterChanges | | | | kaia\_getHeaderByHash | | | | kaia\_getHeaderByNumber | | | | kaia\_getProof | | | | kaia\_getRewards | | | | kaia\_getStakingInfo | | | | kaia\_getStorageAt | | | | kaia\_getTransactionByBlockHashAndIndex | | | | kaia\_getTransactionByBlockNumberAndIndex | | | | kaia\_getTransactionByHash | | | | kaia\_getTransactionBySenderTxHash | | | | kaia\_getTransactionCount | | | | kaia\_getTransactionReceipt | | | | kaia\_getTransactionReceiptBySenderTxHash | | | | kaia\_isContractAccount | | | | kaia\_isSenderTxHashIndexingEnabled | | | | kaia\_lowerBoundGasPrice | | | | kaia\_maxPriorityFeePerGas | | | | kaia\_newBlockFilter | | | | kaia\_newFilter | | | | kaia\_newPendingTransactionFilter | | | | kaia\_nodeAddress | | | | kaia\_pendingTransactions | | | | kaia\_protocolVersion | | | | kaia\_recoverFromMessage | | | | kaia\_sendRawTransaction | | | | kaia\_signTransaction | | | | kaia\_subscribe | | | | kaia\_syncing | | | | kaia\_uninstallFilter | | | | kaia\_unsubscribe | | | | kaia\_upperBoundGasPrice | | | | kaia\_sha3 | | | | klay\_accountCreated | | | | klay\_accounts | | | | klay\_blockNumber | | | | klay\_call | | | | klay\_chainId | | | | klay\_clientVersion | | | | klay\_decodeAccountKey | | | | klay\_encodeAccountKey | | | | klay\_estimateComputationCost | | | | klay\_estimateGas | | | | klay\_feeHistory | | | | klay\_gasPrice | | | | klay\_getAccount | | | | klay\_getAccountKey | | | | klay\_getBalance | | | | klay\_getBlockByHash | | | | klay\_getBlockByNumber | | | | klay\_getBlockTransactionCountByHash | | | | klay\_getBlockTransactionCountByNumber | | | | klay\_getBlockWithConsensusInfoByHash | | | | klay\_getBlockWithConsensusInfoByNumber | | | | klay\_getBlockWithConsensusInfoByNumberRange | | | | klay\_getChainConfig | | | | klay\_getCode | | | | klay\_getCommittee | | | | klay\_getCommitteeSize | | | | klay\_getCouncil | | | | klay\_getCouncilSize | | | | klay\_getFilterChanges | | | | klay\_getHeaderByHash | | | | klay\_getHeaderByNumber | | | | klay\_getProof | | | | klay\_getRewards | | | | klay\_getStakingInfo | | | | klay\_getStorageAt | | | | klay\_getTransactionByBlockHashAndIndex | | | | klay\_getTransactionByBlockNumberAndIndex | | | | klay\_getTransactionByHash | | | | klay\_getTransactionBySenderTxHash | | | | klay\_getTransactionCount | | | | klay\_getTransactionReceipt | | | | klay\_getTransactionReceiptBySenderTxHash | | | | klay\_isContractAccount | | | | klay\_isSenderTxHashIndexingEnabled | | | | klay\_lowerBoundGasPrice | | | | klay\_maxPriorityFeePerGas | | | | klay\_newBlockFilter | | | | klay\_newFilter | | | | klay\_newPendingTransactionFilter | | | | klay\_nodeAddress | | | | klay\_pendingTransactions | | | | klay\_protocolVersion | | | | klay\_recoverFromMessage | | | | klay\_sendRawTransaction | | | | klay\_signTransaction | | | | klay\_subscribe | | | | klay\_syncing | | | | klay\_uninstallFilter | | | | klay\_unsubscribe | | | | klay\_upperBoundGasPrice | | | | klay\_sha3 | | | | txpool\_content | | | | txpool\_inspect | | | | txpool\_contentFrom | | | | txpool\_status | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_getTrieFlushInterval | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Kaia (ex. Klaytn): Contract Sizzle 100 Source: https://docs.chainstack.com/docs/klaytn-contract-sizzle-100 Deploy and interact with a sizzle-100 contract on the Kaia network using Hardhat and a Chainstack endpoint for on-chain gaming and DeFi applications. **TLDR:** * This tutorial shows how to scan recent blocks on Klaytn Mainnet to find the top 3 most interacted-with contracts. * It uses web3.py, checks if addresses are contracts by retrieving bytecode, and caches results for efficiency. * A heap is used to store and retrieve the top 3 busiest contracts, and the script supports multithreading. * Can be adapted for analytics, alerts, or bot flows leveraging real-time contract interaction data. ## Main article Klaytn is an EVM-compatible protocol with a few modifications. Check the Klaytn docs for a full break-down on the compatibility: [Ethereum Compatibility](https://docs.klaytn.foundation/docs/learn/transactions/ethereum/). In this tutorial, we'll build a quick Python project called Contract Sizzle 100. Contract Sizzle 100 prints the top 3 hottest contracts on the Klaytn Mainnet (aka Cypress Network) over the past 100 blocks. The way it works is very simple: the script ingests each new incoming block from the Klaytn Mainnet, extracts all contract interactions, counts them and prints the top3 contracts that had the most interactions. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a Klaytn Mainnet node * [web3.py](https://web3py.readthedocs.io/) ## Step-by-step ### Get a Klaytn node Log in to your [Chainstack account](https://console.chainstack.com/) and deploy a node. ### Create the script A few details on the implementation. How do you actually identify the contract addresses? One easy way is to: 1. Retrieve off the network each block with all transactions in the block. 2. Extract the `to:` address. 3. Check if the extracted address is a contract by doing an `eth_getCode` to the address. This process in Python, however, can be slow, so let's optimize it a bit: * After doing an `eth_getCode` to an extracted address, cache the results so that if we get this address again, we don't do a `eth_getCode` to it as we already know this is a contract address. * Multithread the script by using our tutorial [Mastering multithreading in Python for Web3 requests: A comprehensive guide](/docs/mastering-multithreading-in-python-for-web3-requests-a-comprehensive-guide). Here's the final script: ```python Python theme={"system"} from web3 import Web3 from collections import defaultdict import heapq from concurrent.futures import ThreadPoolExecutor # Connect to a Klaytn node w3 = Web3(Web3.HTTPProvider('CHAINSTACK_NODE')) # Data structure to hold the count of interactions per contract contract_interactions = defaultdict(int) # Priority queue to maintain top 3 contracts top_contracts = [] # Cache for storing contract check results is_contract_cache = {} def is_contract(address): if address not in is_contract_cache: code = w3.eth.get_code(address) is_contract = code != '0x' is_contract_cache[address] = is_contract return is_contract_cache[address] def process_block(block_number): print(f"Processing block {block_number}") block = w3.eth.get_block(block_number, full_transactions=True) for tx in block.transactions: if tx.to and is_contract(tx.to): contract_interactions[tx.to] += 1 # Main loop to process 100 blocks def main(): latest_block = w3.eth.block_number print("Starting to process blocks...") with ThreadPoolExecutor(max_workers=10) as executor: executor.map(process_block, range(latest_block - 100, latest_block)) # Identify top 3 contracts for contract, interactions in contract_interactions.items(): heapq.heappush(top_contracts, (interactions, contract)) if len(top_contracts) > 3: heapq.heappop(top_contracts) # Print top 3 contracts print("Top contracts:", top_contracts) while top_contracts: interactions, contract = heapq.heappop(top_contracts) print(f'Contract {contract} had {interactions} interactions') if __name__ == '__main__': try: main() except Exception as e: print(f"An error occurred: {e}") ``` where * CHAINSTACK\_NODE — your Klaytn node deployed with Chainstack * `max_workers=10` — set to however parallel threads you feel is reasonable. Make sure don't hit the limits: [Limits](/docs/limits). ## Conclusion This tutorial guided you through creating a basic setup to live-track the hottest contract on the Klaytn Network. There are many fun ways you can use it as basis to build upon — from passing the data to a Twitter bot account to setting up your own alerts or bot flow. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Kaia (ex. Klaytn) tooling Source: https://docs.chainstack.com/docs/klaytn-tooling Set up MetaMask, Hardhat, and the Kaia SDK to build and deploy smart contracts on the Kaia network through your Chainstack RPC node endpoints. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## caver-js [caver-js](https://docs.kaia.io/references/sdk/caver-js/) is a JavaScript library enabling developers to interact with a Klaytn node via HTTP connection. It supports all [Klaytn APIs](https://docs.klaytn.foundation/docs/references/sdk/caver-js/api/). 1. Install [caver-js](https://docs.kaia.io/references/sdk/caver-js/). 2. Connect over HTTP: ```javascript Javascript theme={"system"} const Caver = require("caver-js"); const caver = new Caver("YOUR_CHAINSTACK_ENDPOINT"); async function getData() { const blockNumber = await caver.rpc.klay.getBlockNumber(); console.log(blockNumber); } getData(); ``` ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Klaytn nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Ethers.js Extension for Kaia See [Ethers.js Extension for Kaia](https://docs.kaia.io/references/sdk/ethers-ext/getting-started/). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Klaytn nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Klaytn nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Php theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Throughput guidelines Source: https://docs.chainstack.com/docs/limits View Chainstack platform limits for node deployments, API requests, concurrent connections, and resource quotas across different subscription plan tiers. ## Rate limits ### Solana-specific * Solana Mainnet: * Developer plan: 5 requests per second (RPS) * Growth plan: 50 requests per second (RPS) * Solana Devnet: * Developer plan: 25 requests per second (RPS) * Growth plan: 250 requests per second (RPS) ### Arbitrum-specific * Arbitrum Mainnet: `debug_traceBlockByNumber` 20 RPS on all plans ### Cronos-specific * Cronos Mainnet archive: `debug_traceBlockByNumber` 10 RPS on all plans ### All other protocols For global plan RPS limits across all other protocols, see [Requests per second (RPS) plan limits](/docs/rps-plan-limits). ## EVM Range limits For `eth_newFilter` requests, Developer subscription plan users get capped at 10,000 blocks per request. For the `eth_getLogs`, the caps are: * Developer plan — 100 blocks * Growth plan — 10,000 blocks * Pro plan — 10,000 blocks * Business plan — 10,000 blocks * Enterprise — 10,000 blocks. Customization available on request. > [!NOTE] > > Learn more about `eth_getLogs` limits by reading [Understanding eth\_getLogs limitations](/docs/understanding-eth-getlogs-limitations). Need a larger range? [Dedicated Nodes](/docs/dedicated-node) support custom block-range limits on request. ## EVM `eth_getProof` history limits `eth_getProof` returns a proof only for blocks inside the node's proof history. On Global Nodes that history is fixed per network, and a request for an older block returns an error instead of a proof. A proof for a block 127 or more blocks behind the tip is also an archive request and costs 2 RUs — see [Full vs archive by protocol](/docs/request-units#full-vs-archive-by-protocol). | Global Node network | Blocks with proofs | Error for an older block | | - | - | - | | Avalanche C-Chain, Linea, Mantle, Scroll, Sonic, Robinhood Chain Testnet | Every block from genesis | — | | Stable | Every block, with an explicit block number; the proof is a Cosmos IAVL proof — see [Stable methods](/docs/stable-methods) | — | | Ethereum Mainnet, Ethereum Sepolia | The latest 10,000 blocks | `-32602` `distance to target block exceeds maximum proof window` | | X Layer Mainnet, X Layer Testnet | The latest 256 blocks | `-32602` `distance to target block exceeds maximum proof window` | | BNB Smart Chain | Blocks less than 127 behind the tip | `-32602` `distance to target block exceeds maximum proof window` | | Polygon | Blocks less than 127 behind the tip | `-32000` `missing trie node` | | Elysium | Blocks less than 127 behind the tip | `-32000` `historical state is not available` | | Kaia, opBNB | Blocks less than 127 behind the tip — these Global Nodes run in full mode only | `-32002` | | Arbitrum, Robinhood Chain Mainnet | About the latest 128 blocks | `-32000` `missing trie node` | | Cronos | Recent blocks only — older state is pruned | `-32000` `target version is pruned` | | Arc, Base, Berachain, Celo, Ethereum Hoodi, Ink, Optimism, Plasma, Ronin, Tempo, Unichain, World Chain, Zora | The latest block only (`latest`) | `-32602` `distance to target block exceeds maximum proof window` | | Hyperliquid (HyperEVM), Monad, ZKsync Era | Not served — the network's node client does not implement `eth_getProof`. On ZKsync Era, use `zks_getProof`, which proves storage against an L1 batch | `-32601` `Method not found` | | Fantom | Not served — the Sonic node client no longer supports `eth_getProof` on the Fantom chain | `-32000` `method handler crashed` | | MegaETH | Not served — MegaETH does not support `eth_getProof` | `-32603` `internal eth error` | Proof generation time grows with block depth. On Ethereum Mainnet, a proof about 9,000 blocks behind the tip takes over a minute, so raise your client's request timeout for deep proofs. Need proofs further back? A [Dedicated Node](/docs/dedicated-node) running Reth supports a longer proof window on request, up to 1,209,600 blocks (about 168 days on Ethereum Mainnet). Generation time still grows with depth, so a proof hundreds of thousands of blocks back takes tens of minutes. ## EVM disabled debug methods The following debug methods are disabled on EVM chains: * `debug_executionWitness` * `debug_executionWitnessByBlockHash` * `debug_executePayload` ## Custom tracers on EVMs EVM debug and trace methods accept two kinds of tracers: * Native (built-in) tracers — selected by name, such as `callTracer`. Available on [Global Nodes](/docs/global-elastic-node); the exact set depends on the protocol (see below). * [Custom JavaScript](/docs/mastering-custom-javascript-tracing-for-ethereum-virtual-machine) tracers — where you pass raw JavaScript as the `tracer` argument. These are disabled on Global Nodes and are available as customized solutions on the [Enterprise plan](https://chainstack.com/pricing/) on [Dedicated Nodes](/docs/dedicated-node). ### Available native tracers The following native tracers are enabled by default on EVM Global Nodes: * `4byteTracer` * `callTracer` * `prestateTracer` * `noopTracer` On Global Nodes, the extended set — `flatCallTracer`, `muxTracer`, and `erc7562Tracer` — is enabled per protocol rather than everywhere. Requesting one where it is not enabled returns the [disabled tracer error](#disabled-tracer-error). | Protocol | `flatCallTracer` | `muxTracer` | `erc7562Tracer` | | - | - | - | - | | Ethereum | Available | Available | Available | | Polygon | Available | Not available | Available | | Elysium | Available | Available | Available | | All other EVM networks | Not available | Not available | Not available | On Polygon, `flatCallTracer` is what replaces the retired Erigon `trace_*` namespace, because it returns traces in the Parity-style flat format — see [Polygon methods](/docs/polygon-methods). Elysium also accepts `stylusTracer`, the Nitro tracer that records the host I/O calls of Stylus (WebAssembly) contracts for `cargo stylus replay`. It returns an empty array for a transaction that makes no Stylus calls. Three networks accept a narrower default set. The limit comes from the network's node client, so these tracers return a client error rather than the disabled tracer error: * Monad — `callTracer` and `prestateTracer` only. `4byteTracer` and `noopTracer` return `-32602`. * ZKsync Era — `callTracer` only. `prestateTracer`, `4byteTracer`, and `noopTracer` return `-32602`. * Kaia — `noopTracer` returns `-32000`. The other three default tracers work. See [Debug and Trace | Ethereum](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) for a description of each tracer. ### Disabled tracer error Requesting a tracer that isn't enabled on the node—a custom JavaScript tracer, or a native tracer not available on that protocol—returns: ```json theme={"system"} { "error": { "code": -32612, "message": "Custom tracers are disabled by default. See https://docs.chainstack.com/docs/limits for details." } } ``` ## Ethereum `eth_simulateV1` supports only full node Running [eth\_simulateV1 | Ethereum](/reference/ethereum-simulatev1) will yield only a full node response—i.e. the data from the latest 128 blocks. Archive data is not supported for this call and the node will respond with `missing trie node`. ## Fantom method limits The following limits are applied on all subscription plans: * `debug_traceBlockByNumber`: 5 RPS * `debug_traceBlockByHash`: 5 RPS ## Solana method limits The following per-method RPS limits apply to Solana Mainnet [Global Nodes](/docs/global-elastic-node). On [Dedicated Nodes](/docs/dedicated-node), per-method limits are configured per node. | Method | RPS | | - | - | | `getBlockTime` | 500 | | `getBlock` | 400 | | `getTokenSupply` | 500 | | `getMultipleAccounts` | 300 | | `getSupply` | 300 | | `getTokenAccountsByOwner` | 150 | | `getProgramAccounts` | Depends on the program size and on filters — see [getProgramAccounts limits](#getprogramaccounts-limits) | | `getLargestAccounts` | 0 — available on [Dedicated Nodes](/docs/dedicated-node) only | | `getTokenLargestAccounts` | 0 — available on [Dedicated Nodes](/docs/dedicated-node) only | > [!NOTE] > > The `getConfirmedBlock` method was removed in Agave 2.0 and returns `Method not found` on current validators. Use [`getBlock`](/reference/solana-getblock) instead. Architectural limits (set by Solana, not Chainstack): * `getBlocks`: 500,000 blocks range — [Solana architecture limit](https://solana.com/docs/rpc/http/getblocks). * `getBlocksWithLimit`: 500,000 blocks range — [Solana architecture limit](https://solana.com/docs/rpc/http/getblockswithlimit). ### getProgramAccounts limits On Solana Mainnet Global Nodes, the [`getProgramAccounts`](/reference/solana-getprogramaccounts) rate limit depends on the size of the program you query. Each program is assigned a size tier by its approximate number of accounts, and each tier has one limit for requests that include `filters` and a lower limit for requests without them. Filters raise the limit only within the program's tier — a filtered request to a program with 1M or more accounts is limited to 3 requests per 20 seconds. | Program size (accounts) | With filters | Without filters | | - | - | - | | Under 10K | 20 RPS | 10 RPS | | 10K–30K | 15 RPS | 5 RPS | | 30K–60K | 15 RPS | 3 RPS | | 60K–200K | 15 RPS | 2 RPS | | 200K–500K | 10 RPS | 2 RPS | | 500K–1M | 5 RPS | 2 RPS | | 1M and more | 3 requests per 20 seconds | 2 requests per 20 seconds | The following rules also apply: * A program without an assigned size tier, such as a recently deployed program, is limited to 2 RPS with or without filters. * Requests to some high-traffic programs at `finalized` or `confirmed` commitment are served from a cache under separate per-program limits instead of the size tiers. * Unfiltered requests to the SPL Token Program (`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`), Token-2022 (`TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`), and Kin (`kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6`) are rejected with error `-32602` — see [getProgramAccounts filters](/reference/solana-getprogramaccounts#filters). The SPL Token Program and Kin are also [excluded from indexing](#solana-accounts-excluded-from-indexing). * A request over the limit returns HTTP `429` with JSON-RPC error `-32005`. ## Solana scan method limits The account-scan methods — [`getProgramAccounts`](/reference/solana-getprogramaccounts), [`getTokenAccountsByOwner`](/reference/solana-gettokenaccountsbyowner), [`getTokenAccountsByDelegate`](/reference/solana-gettokenaccountsbydelegate), and [`getSupply`](/reference/solana-getsupply) — read across a program's accounts, which on a large program can be hundreds of millions of accounts. To keep one heavy scan from tying up a node, Solana Mainnet Global Nodes run each scan under a 60-second execution-time limit and a per-request memory limit, and run a limited number of scans at once. A scan that waits more than 15 seconds for a free scan slot is dropped without running. A scan that exceeds the execution-time or memory limit is aborted and returns error [`-32012`](/docs/error-reference#json-rpc-error-codes) rather than hanging: ```json theme={"system"} { "error": { "code": -32012, "message": "scan aborted: the scan was aborted (result size limit exceeded or externally cancelled)" } } ``` To keep the scan within the limits, narrow the query — for example with `dataSize` or `memcmp` filters on `getProgramAccounts`. A scan dropped from the queue also returns error `-32012`, with the message `scan aborted: scan abandoned after waiting Nms for a scan slot`, where `N` is the time the request spent queued in milliseconds. The scan never ran, so the same request can succeed later. Retry it with exponential backoff. ## Solana accounts excluded from indexing The following Solana accounts are excluded from the `program-id` secondary index and cannot be queried with [`getProgramAccounts`](/reference/solana-getprogramaccounts). Use [`getTokenAccountsByOwner`](/reference/solana-gettokenaccountsbyowner) instead: * `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` — SPL Token Program * `kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6` — Kin ## Solana method availability The following methods are available only on the paid plans: * `getProgramAccounts` * `getLargestAccounts` — this method is available only on [Dedicated Nodes](/docs/dedicated-node). * `getSupply` * `getTokenAccountsByOwner` ## Solana archive methods availability While most methods are supported on Solana [Global Nodes](/docs/global-elastic-node), only the following methods can fetch archive data: * `getSignaturesForAddress` * `getTransaction` * `getBlock` * `getBlocks` * `getBlockHeight` * `getBlockTime` * `getBlocksWithLimit` Note that this is only true for mainnet as there are no Solana archive nodes for Devnet. # Linea: Real-time transaction activity monitor with Python Source: https://docs.chainstack.com/docs/linea-real-time-transaction-monitor-python Learn how to build a real-time transaction activity monitor for Linea blockchain using Python and web3.py through Chainstack RPC endpoints. **TLDR:** * Demonstrates building a real-time transaction activity monitor for Linea blockchain using Python and web3.py. * Shows how to handle Proof-of-Authority (POA) networks with proper middleware configuration in web3.py. * Includes practical examples of block polling and transaction analysis. ## Overview Linea is a high-performance Layer 2 blockchain that offers fast transaction processing with approximately 2-second block times. This tutorial is a very easy quickstart guide to create a real-time transaction monitor that visualizes blockchain activity as it happens. You'll learn how to connect to the Linea mainnet and efficiently poll the chain. How to handle POA networks correctly, and create beautiful terminal interfaces that provide instant insights into network activity. ## Prerequisites * [web3.py](https://web3py.readthedocs.io/) * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Linea RPC endpoint](https://chainstack.com/build-better-with-linea/) ## Understanding Linea Linea is a Layer 2 rollup to Ethereum developed by Consensys. Linea is fully EVM compatible and has a fast block time of approximately 2 seconds. Linea is also a zero-knowledge (zk) rollup. ## Live transaction monitor Now let's create a simple live transaction monitor script. The script connects to the Linea mainnet over a Chainstack Linea node RPC endpoint. Then the script polls the mainnet chain for each new block as it comes in, checks the number of transactions and prints the data in the terminal along with the adjustable visuals for easy viewing. This is an easy tutorial script, so the main goal here is to check: * How to use web3.py * How to connect to the Linea mainnet * How to handle POA networks correctly * How to poll the chain for new blocks and extract the number of transactions Here's the full script: ```python theme={"system"} import asyncio, math from web3 import AsyncHTTPProvider, AsyncWeb3 from web3.middleware import ExtraDataToPOAMiddleware from rich.console import Console from rich.text import Text # Chainstack Linea mainnet endpoint RPC = "YOUR_CHAINSTACK_ENDPOINT" w3 = AsyncWeb3(AsyncHTTPProvider(RPC)) # Add POA middleware for Linea w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) c = Console() # Tweak these bands to taste (transaction counts) BANDS = [5, 20, 50, 100] # quiet > busy (transactions per block) BAR_LEN = 30 # number of "pixels" def mood_color(tx_count: int) -> str: if tx_count < BANDS[0]: return "green" if tx_count < BANDS[1]: return "bright_green" if tx_count < BANDS[2]: return "yellow" if tx_count < BANDS[3]: return "orange" return "red" def mood_bar(tx_count: int) -> Text: # % of BAR_LEN that should be "hot" pct = min(tx_count / BANDS[-1], 1.0) hot = math.ceil(pct * BAR_LEN) bar = Text() for i in range(BAR_LEN): color = "white" if i < hot: color = mood_color(tx_count) bar.append("█", style=color) return bar async def poll(): # Get initial block number last_block_number = None while True: # Get latest block header blk = await w3.eth.get_block("latest") # Only process if it's a new block if last_block_number is None or blk.number > last_block_number: last_block_number = blk.number # Count transactions in this block tx_count = len(blk.transactions) line = Text.assemble( ("blk ", "cyan"), (str(blk.number).ljust(8), "bold cyan"), (" | txs ", "grey50"), (f"{tx_count}".ljust(11), "bold"), (" | ", "grey50"), mood_bar(tx_count) ) yield line # Check for new blocks more frequently (Linea has ~2 second block times) await asyncio.sleep(1) async def main(): c.print("[dim]Powered by Chainstack—spin up your own Linea endpoint in 30s[/]") c.print("[bold cyan]🎯 Linea transaction activity monitor[/]") c.print() async for txt in poll(): c.print(txt) asyncio.run(main()) ``` ## Conclusion This tutorial provided a comprehensive guide to building real-time blockchain monitoring applications for Linea using Python. The monitoring approach demonstrated here can be easily extended to track specific contracts, tokens, or transaction patterns. > [!NOTE] > > ### See also > > > [card] [Linea documentation](https://docs.linea.build/) > > > [card] [web3.py documentation](https://web3py.readthedocs.io/) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Linea tooling Source: https://docs.chainstack.com/docs/linea-tooling Complete Linea tooling guide for Chainstack nodes. Use MetaMask, web3.py, ethers.js, Hardhat, Remix IDE, Foundry, and more to build dApps on Linea blockchain. Get started with a [reliable Linea RPC endpoint](https://chainstack.com/build-better-with-linea/) to use the tools below. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Linea nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Linea nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.JsonRpcProvider(urlInfo.url, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * NETWORK\_ID — Linea network ID: * Mainnet: `59144` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Linea nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # List your app on marketplace Source: https://docs.chainstack.com/docs/list-your-app-on-marketplace List your blockchain tool or API on the Chainstack Marketplace to reach developers, with integration guidelines, approval process, and billing setup. This guide is designed to familiarize you with these requirements and provide instructions on submitting a request. ## Listing requirements We recommend that you read and consider the following minimum requirements before submitting a request. ## Security requirements All Chainstack Marketplace apps should do the following: * Encrypt sensitive customer data. * Not require the user to provide their Chainstack password. * Delete all Chainstack user data within 30 days of receiving a request from the user, or within 30 days of the end of the user's legal relationship with Chainstack. * Store client ID and client secret keys securely. * Encrypt data transferred over the public internet using HTTPS, with a valid TLS certificate. ## Integration requirements Ensure that your app meets the following necessary technical integration requirements: * Authorization — one of the following methods: * OAuth 2.0 integration — OAuth 2.0 is a protocol that lets external apps request authorization to private details in a user's account without accessing their password. * JSON web token integration — JWT is a digitally signed string of encoded information that contains user identity and authorization data, and it's commonly used for secure authentication in web applications. Learn more about JWT in the [Mastering JSON web tokens](/docs/tutorial-mastering-jwt-how-to-implement-secure-user-authentication) article. * Webhooks — for privacy-compliance purposes, your app must be set up to use webhooks. OAuth 2.0 authorization is required to access webhooks. * Scopes — apps must only request scopes that are required for your app. Before you apply, we recommend that you check to ensure that your scopes are not requesting unnecessary access. * Notifications to customers — support for handling and notifications to customers about plan upgrades or downgrades. ## Support requirements We require the following information to ensure that customers are provided with ongoing quality support: * A **one-pager** for the Chainstack support team, summarizing the key features of your app. * An **SLA** for our support team (community, business processes from the app side, etc.). * The app **support team contact details** and the **estimated response time**. * **FAQs** of the app. * An up-to-date list of any **known issues** of the app. ## Submit your request Once you have read and are sure that you can meet the minimum requirements, you can submit the [Chainstack Marketplace request form](https://chainstack.com/marketplace/#submit-app). You must complete and accurately fill out the following fields of the form: * **Name** — your full name. * **Email** — your contact email address. * **Product name** — the name of the product as it will be displayed on the Chainstack Marketplace. * **Developer name and website URL** — the name of the product developer and a link to the website. * **Description** — enter an informative, well-written description of your app. * **Screenshots** — some screenshots of your app that we can include on the Chainstack Marketplace. * **User documentation** — a URL to your up-to-date user documentation. * **Category** — choose from Application, Service, or Developer tool. * **Relevant protocols** — select all protocols from the list that are relevant to your app. * **Integration instructions** — a URL to a step-by-step guide for integrating your app. * **Terms of Service and Privacy Policy URLs** — links to your up-to-date terms of service and privacy policy. * **Pricing plan** — details of at least one pricing plan for your app. * **Support details** — the contact details of your main support channels and the estimated response time. ## What happens next? When you complete the form, the Chainstack team will review your submission. You will then receive an email notifying you if your request has been accepted or not. If accepted, you will be provided with details on how to proceed. # Make your dApp more reliable with Chainstack Source: https://docs.chainstack.com/docs/make-your-dapp-more-reliable-with-chainstack Improve dApp reliability with Chainstack Global Nodes for automatic geographic load balancing, failover routing, and low-latency RPC connections. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR:** * Explains how Chainstack’s Global Node feature can boost your DApp’s reliability by balancing traffic automatically based on user location. * Demonstrates a JavaScript load balancer script using multiple Chainstack endpoints, distributing requests across different regions to avoid single-point failures. * Shows examples with both web3.js and ethers.js, detailing how to fail over to the next endpoint if one fails. * Concludes that both Global Nodes and custom load-balancing approaches help ensure your blockchain app can handle high traffic and unexpected downtimes. ## Main article Every developer wants the most reliable DApp. In this guide, we'll explore how to use multiple Chainstack nodes using load balancer logic to make your DApp more performant and reliable. Think of it as a well-coordinated team where the workload is evenly distributed, ensuring efficiency and eliminating any single point of failure. Whether you're a seasoned developer or a newcomer to the blockchain scene, this step-by-step guide will provide practical knowledge to enhance your applications. So, let's get started and dive into the world of efficient blockchain application management. This guide will show you how using a Chainstack Global Node can make your DApp more reliable and also how to use multiple Chainstack RPC nodes to create a load balancer in JavaScript. ## What is a load balancer In simplest terms, a load balancer is a technology that distributes network or application traffic across multiple servers or nodes, in our case. Imagine you're at a busy intersection; a load balancer is the one directing traffic, ensuring that no single server gets overwhelmed with too many requests. This way, it helps to optimize resource use, maximize throughput, minimize response time, and avoid system overload. In blockchain applications, a load balancer can help distribute incoming requests evenly across multiple nodes, ensuring your application runs smoothly and efficiently. So, it's a pretty handy tool in your blockchain toolkit. ## Global Nodes Chainstack provides [Global Nodes](/docs/global-elastic-node), but how can they help you make your DApp more reliable, and how do they differ from [Trader Nodes](/docs/trader-node)? [Trader Nodes](/docs/trader-node) represent traditional endpoints where users can select from various available locations. While this provides considerable flexibility, it also comes with certain limitations. For instance, there is less redundancy if the node encounters issues, and users may experience varying performance levels. This variation can occur if, for example, an application sends requests to the node from the client, which the user's location can influence. On the other hand, Global Nodes function as load-balanced nodes that direct requests to the nearest available location for a specific protocol based on the caller's location. This design ensures efficient service access for users worldwide by routing requests optimally. The main advantages of Global Nodes are the following: * **Enhanced load balancing**—Global Nodes include a large load balancer that can switch nodes if one fails or falls behind the chain head, thus ensuring uninterrupted service. * **Reduced latency**—by distributing traffic to the nearest endpoint, the Global Node reduces latency, leading to faster transactions and improved user experience. * **Global reach**—Anyone from any location can access Global Nodes. These nodes direct users to the endpoints nearest to their location, maximizing service availability and responsiveness. * **High availability**—Global Nodes are designed to be 99.95% available. This ensures that your DApp continues to run with minimal interruptions. * **Instant deployment** — unlike Trader Nodes, which take 3-6 minutes to deploy, the Global Node is ready in under a second. This leads to significant time savings. > [!NOTE] > > Check modes and protocols available for [Global Nodes](/docs/global-elastic-node). Opting for a Global Node is generally the preferred choice. However, what if you require a protocol or mode that isn't currently supported? For such cases, we can BUIDL a simple load-balancing script using JavaScript. ## JavaScript load balancer project This project will be a simple implementation using node.js, with examples using `web3.js` and `ethers.js`. Therefore, it's essential to ensure a well-configured development environment before proceeding. Go over our node.js setup guide for Web3 projects, [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project), if you are starting from zero. ### Prerequisites For this project, make sure you have the following: * node.js V18 * web3.js * ethers.js > [!NOTE] > > Install the web3.js library with `npm i web3`. > > Install the ethers.js library with `npm i ethers`. ### The logic of the project In this simple project, we are trying to create a basic load balancer; in this case, we'll use multiple Chainstack Trader Nodes and alternate them between requests to spread the load between them. It's important to remember that this is only proof of concept and will require further optimization for deployment in a production environment. Let's deploy three Ethereum Trader Nodes in three locations to set things up. > [!NOTE] > > Learn how to [deploy a node with Chainstack](/docs/manage-your-node). This configuration guarantees global coverage, and the use of various hosting providers adds an extra layer of redundancy. This is the power of the geo-distributed infrastructure provided by Chainstack. > [!NOTE] > > Remeber that the deployments of multiple nodes is available starting from a [paid plan](https://chainstack.com/pricing/). ### Coding the load balancer Now that you have access to three RPC nodes, it's time to store them in a `.env` file located in your project's root directory. If you haven't already, install the `dotenv` package using the command `npm i dotenv`. This approach helps us manage sensitive information, preventing accidental pushes to a version control platform. ```Text .env theme={"system"} VIRGINIA_RPC="YOUR_VIRGNIA_CHAINSTACK_ENDPOINT" LONDON_RPC="YOUR_LONDON_CHAINSTACK_ENDPOINT" SINGAPORE_RPC="YOUR_SINGAPORE_CHAINSTACK_ENDPOINT" ``` After configuring the endpoints, create a new `index.js` file and insert the following code. For illustrative purposes, we're employing a fairly straightforward use case. The script executes the `eth_getBlockByNumber` method every 10 seconds to fetch details of the latest block. Notably, each request is served by a different endpoint. ### Web3.js example ```javascript web3.js theme={"system"} const {Web3} = require("web3"); require("dotenv").config(); // Initialize RPCs from environment variables const RPC_NODES = { web3Virginia: process.env.VIRGINIA_RPC, web3London: process.env.LONDON_RPC, web3Singapore: process.env.SINGAPORE_RPC, }; // Create Web3 instances for each RPC const web3Instances = {}; for (const [key, url] of Object.entries(RPC_NODES)) { web3Instances[key] = new Web3(url); } // Array of keys to cycle through const keys = Object.keys(web3Instances); // Counter to keep track of the current Web3 instance let counter = 0; async function getBlock(blockNumber) { // Select the current Web3 instance const key = keys[counter]; const web3 = web3Instances[key]; console.log(`Using ${key} RPC`); // Start the timer console.time("getBlock"); try { // Try to get the latest block const block = await web3.eth.getBlock(blockNumber, false); // Extract some fields to keep the response cleaner const blockSummary = { blockNumber: block.number, blockHash: block.hash, parentHash: block.parentHash, size: block.size, }; console.log(blockSummary); } catch (error) { // Log the error console.error(`Error fetching block: ${error.message}`); // Increment the counter and reset it if it's larger than the array length counter = (counter + 1) % keys.length; // Retry the request on the next Web3 instance console.log("Retrying request on next RPC..."); return getBlock(blockNumber); } // End the timer and log the time console.timeEnd("getBlock"); // Increment the counter and reset it if it's larger than the array length counter = (counter + 1) % keys.length; } // Call getBlock every 10 seconds console.log("Running load balanced script..."); setInterval(() => getBlock("latest"), 10000); ``` ### Ethers.js example ```javascript ethers.js theme={"system"} const ethers = require('ethers'); require('dotenv').config(); // Initialize RPC nodes from environment variables const RPC_NODES = { ethersVirginia: process.env.VIRGINIA_RPC, ethersLondon: process.env.LONDON_RPC, ethersSingapore: process.env.SINGAPORE_RPC, }; // Create ethers providers for each RPC URL const providers = {}; for (const [key, url] of Object.entries(RPC_NODES)) { providers[key] = new ethers.JsonRpcProvider(url); } // Keys to cycle through providers const keys = Object.keys(providers); let counter = 0; // Counter to keep track of the current provider // Function to get the latest block using the current provider const getBlock = async () => { const key = keys[counter]; const provider = providers[key]; console.log(`Using ${key} RPC`); console.time("getBlock"); // Start timing the operation try { // Fetch the latest block information const blockByNumber = await provider.send("eth_getBlockByNumber", ["latest", false]); // Extract some fields to keep the response cleaner const blockSummary = { blockNumber: blockByNumber.number, blockHash: blockByNumber.hash, parentHash: blockByNumber.parentHash, size: blockByNumber.size, }; console.log(blockSummary); } catch (error) { // If there's an error, log it and move to the next provider console.error(`Error fetching block: ${error.message}`); counter = (counter + 1) % keys.length; // Increment and wrap the counter if necessary console.log("Retrying request on next RPC..."); return getBlock(); // Retry with the next provider } console.timeEnd("getBlock"); // End timing the operation counter = (counter + 1) % keys.length; // Move to the next provider for the next call }; // Start the process and call getBlock every 10 seconds console.log("Running load-balanced script with ethers.js..."); setInterval(getBlock, 2000); ``` ### Code breakdown Let's go over what we are doing in the code step by step. #### Initialize the Web3 instances ```javascript Javascript theme={"system"} // Initialize RPCs from environment variables const RPC_NODES = { web3Virginia: process.env.VIRGINIA_RPC, web3London: process.env.LONDON_RPC, web3Singapore: process.env.SINGAPORE_RPC, }; // Create Web3 instances for each RPC const web3Instances = {}; for (const [key, url] of Object.entries(RPC_NODES)) { web3Instances[key] = new Web3(url); } // Array of keys to cycle through const keys = Object.keys(web3Instances); // Counter to keep track of the current Web3 instance let counter = 0; ``` * **Initialize RPC URLs from environment variables**. The script sets up an object named `RPC_NODES` that maps the variables of the Web3 instances to their respective RPC URLs, which are fetched from environment variables. This is mostly done so we can print in the console which variable we are using each time we send a request. * **Create Web3 instances for each RPC**. The script creates a new Web3 instance for each RPC URL and stores them in the `web3Instances` object. This object's keys are the instances' names, and the values are the instances themselves. This way, the instances are all created at once when the script is started; this logic also keeps the code more maintainable as you only need to edit the `RPC_NODES` object if you want to add or remove endpoints. * **Set up an array of keys and a counter**. The keys array contains the keys of the `web3Instances` object and the counter variable keep track of the current Web3 instance. #### Call the function to get the latest block details ```javascript Javascript theme={"system"} async function getBlock(blockNumber) { // Select the current Web3 instance const key = keys[counter]; const web3 = web3Instances[key]; console.log(`Using ${key} RPC`); // Start the timer console.time("getBlock"); try { // Try to get the latest block const block = await web3.eth.getBlock(blockNumber, false); // Extract some fields to keep the response cleaner const blockSummary = { blockNumber: block.number, blockHash: block.hash, parentHash: block.parentHash, size: block.size, }; console.log(blockSummary); } catch (error) { // Log the error console.error(`Error fetching block: ${error.message}`); // Increment the counter and reset it if it's larger than the array length counter = (counter + 1) % keys.length; // Retry the request on the next Web3 instance console.log("Retrying request on next RPC..."); return getBlock(blockNumber); } // End the timer and log the time console.timeEnd("getBlock"); // Increment the counter and reset it if it's larger than the array length counter = (counter + 1) % keys.length; } ``` This asynchronous function fetches the latest block from the Ethereum blockchain using one of the Web3 instances. It selects the Web3 instance based on the counter, starts a timer, fetches the block, logs some information about the block, stops the timer and logs the elapsed time, and finally increments the counter (or resets it if it's larger than the number of Web3 instances). This function also incorporates an error handling mechanism. Specifically, if an endpoint becomes unavailable, the script will not halt. Instead, it will promptly switch to the next RPC to continue making requests, increasing reliability. The following part then calls the function every 10 seconds: ```javascript Javascript theme={"system"} // Call getBlock every 10 seconds console.log("Running load balanced script..."); setInterval(() => getBlock("latest"), 10000); ``` ### Run the code Now to run the code, use the `node index.js` command in your terminal; the script will start and call the function every 10 seconds. Here is an example of what the response looks like: ``` Running load balanced script... Using web3Virginia RPC { blockNumber: 17629512, blockHash: '0xcf1535e6f7b84ba51e8ebb9bbf09be7f5caf99f6fc3ac063a5563be02d93f32f', parentHash: '0x7f812fd3d738b91c053a058d8bbc73fba839143f924f63371285545b35b7b460', size: 60657 } getBlock: 573.178ms Using web3London RPC { blockNumber: 17629513, blockHash: '0x1243ec3a24465f01758cdf0bb40f02a64964832a47972fcb26fef488293392a7', parentHash: '0xcf1535e6f7b84ba51e8ebb9bbf09be7f5caf99f6fc3ac063a5563be02d93f32f', size: 24745 } getBlock: 650.523ms Using web3Singapore RPC { blockNumber: 17629514, blockHash: '0x3a44d78b3c02e209ad671d6fa113b8e6ff4bfeafe8ee416573f4af668ac1fbed', parentHash: '0x1243ec3a24465f01758cdf0bb40f02a64964832a47972fcb26fef488293392a7', size: 50705 } getBlock: 1.768s ``` Note how each request uses a different endpoint, and the execution times accurately reflect their respective locations. The Virginia endpoint, the closest to my location, provides a quicker response, while Singapore, the furthest, takes a bit longer. ## Conclusion In this guide, we explored the robustness that Chainstack's Global Nodes can bring to your DApp. Additionally, we went into the creation of a load-balanced script using web3.js. This script not only distributes the load across various endpoints in different regions but also ensures redundancy, thereby enhancing the reliability and performance of your application. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Manage your account Source: https://docs.chainstack.com/docs/manage-your-account Manage your Chainstack account settings including email, password, two-factor authentication, API keys, and notification preferences in the console. ## Create an account [Sign up](https://console.chainstack.com/user/account/create) with Chainstack in a couple of steps. > [!NOTE] > > ### Email verification > > Make sure you verify your email at the last step of the account creation process. Otherwise, your access to Chainstack will be restricted. For subscription plan details, see [Pricing](https://chainstack.com/pricing/). ## Change your password 1. Go to **Settings** > **Personal** in your account. 2. Under **Security**, select **Change password**. 3. Enter your current password. 4. Enter your new password twice to confirm. 5. Click **Save** to apply the change. ## Enable two-factor authentication Two-factor authentication (2FA) adds an extra layer of security to your Chainstack account. Once enabled, you will be prompted to enter a code generated by your mobile device each time you log in. You can use apps like Authy, Google Authenticator, or Microsoft Authenticator. To enable 2FA: 1. Go to **Settings** > **Personal** in your account. 2. Under **Security**, select **Enable two-factor authentication**. 3. Provide your current password. 4. Download, print, or copy your 2FA recovery codes. These are the backup codes that you will need to restore your 2FA access if you lose it. 5. Scan the QR code with your authenticator app or enter the code manually. 6. Enter the code provided by the authenticator app in the 2FA wizard. Click **Enable 2FA**. > [!WARNING] > > The 2FA recovery codes are shown once and cannot be retrieved. > > Make sure you keep the recovery codes secure. ## Disable two-factor authentication Two-factor authentication (2FA) adds an extra layer of security to your Chainstack account. Disabling 2FA will keep your account protected only with your password. To disable 2FA: 1. Go to **Settings** > **Personal** in your account. 2. Under **Security**, click **Disable two-factor authentication**. 3. Provide your current password. Click **Disable 2FA**. ## If you lost access to your account Pick the scenario that matches yours. ### You forgot your password (2FA not enabled) 1. Go to the [log in](https://console.chainstack.com/) page. 2. Click **Forgot password?** and follow the instructions sent to your registered email. ### You signed up with a social account and lost access to it If you signed up via Google, GitHub, X (Twitter), or Microsoft and never set a Chainstack password, you cannot use **Forgot password?** to recover. Recover access to the social account with the provider first, then sign in to Chainstack as usual. ### You lost your 2FA device but kept the recovery codes Use a recovery code as described in [Recover your account protected with two-factor authentication](#recover-your-account-protected-with-two-factor-authentication). ### You lost your 2FA device and the recovery codes Email [support@chainstack.com](mailto:support@chainstack.com) **directly from the email address registered on the account** — contact-form submissions are not eligible for recovery. Attach both: * a bank statement showing a Chainstack charge, and * the matching Chainstack purchase receipt or invoice. Support cross-checks the documents against Stripe and, once verified, asks Engineering to remove 2FA from your account. ### Your account uses enterprise SSO If your organization signs in to Chainstack through SSO, account recovery is handled by your identity provider's administrator. Chainstack support cannot reset SSO access on your behalf. ## Recover your account protected with two-factor authentication When you enable two-factor authentication (2FA) for your Chainstack account, you are provided with a list of recovery codes. These are the backup codes that you must use to restore your 2FA access if you lose it. The default name of the file that you might have downloaded with the recovery codes is `recoveryCodes.txt`. To recover your account: 1. Provide your username and password at the [log in](https://console.chainstack.com/) page. 2. Click **Having problems? Enter your recovery code**. 3. Provide your recovery code. Click **Verify**. 4. Click **Verify**. > [!WARNING] > > Each recovery code can only be used once. # Manage your billing Source: https://docs.chainstack.com/docs/manage-your-billing Manage your Chainstack billing, payment methods, invoices, and crypto payment options to keep your blockchain infrastructure running without interruption. > [!NOTE] > > ### 150+ cryptocurrencies > > Chainstack supports topping up your balance with 150+ cryptocurrencies. Your crypto balance is the first to be charged for subscription, support, and node usage costs. If the balance is insufficient, your credit card will be charged for the outstanding amount. The billing page is available for users with the [Admin role](/docs/manage-your-organization#users-and-their-roles). Here you can: * Top up your balance. * Edit your credit card details. * Check for failed payments and settle them. * View your current metered usage by category and its cost. * View a per-node usage breakdown in [Usage details](/docs/node-usage). * Manage your organization subscription plan and support level. ## Manage the extra usage setting With the extra usage setting enabled in your [Billing](https://console.chainstack.com/user/settings/billing), your Chainstack services will stay operational on reaching your plan's quota limit. Going over the limit will trigger the extra usage charges. With the extra usage setting disabled, you introduce a hard limit to your plan. On reaching the quota, your Chainstack services will stop and you won't be charged on going over the limit. In both cases, you will receive at least two email notifications—on using up 80% of your plan's quota and on using up 100%. ## Top up your balance with crypto You can top up your balance directly with cryptocurrency using NOWPayments. If there's a token that you'd like to top up with, it's most likely supported. See the [150+ supported tokens and coins](https://nowpayments.io/supported-coins) with NOWPayments. ### Top up with NOWPayments 1. Go to [Billing](https://console.chainstack.com/user/settings/billing). 2. Click **Top up**. 3. Click **Crypto**. 4. Click **NOWPayments**. 5. Pick an amount to top up with. 6. Pick a token to pay with. Proceed to pay ### Top up with a non-custodial wallet You can top up the balance using your preferred non-custodial wallet. For example, [MetaMask](https://metamask.io/), [Coinbase Wallet](https://www.coinbase.com/wallet), and others. To top up the balance with a non-custodial wallet: 1. Go to [Billing](https://console.chainstack.com/user/settings/billing). 2. Click **Top up with crypto**. 3. Select a predefined amount or type in your amount. Click **Next**. 4. Select the cryptocurrency. 5. Send the cryptocurrency to the respective generated address. ## Edit your credit card details If you need to edit your credit card details, do the following: 1. Go to [Billing](https://console.chainstack.com/user/settings/billing). 2. Next to **Credit card**, click **Edit**. 3. Enter new details. 4. Click **Update**. The billing page will reload automatically and show your new credit card details. ## Card was declined Chainstack processes card payments through Stripe. If a card is rejected when you add it, one of the cases below usually applies. ### Card is already linked to another Chainstack account Each card can only be attached to one Chainstack account at a time. Search every inbox you have for Chainstack invoices or receipts — the card may already be on an earlier account. Reuse that account, or remove the card from it before adding it elsewhere. If you have a legitimate reason to use the same card on multiple accounts (for example, separate org workspaces under the same finance owner), email [support@chainstack.com](mailto:support@chainstack.com) — support can whitelist the email to allow it. ### Card declined by the issuer The issuing bank can decline the card attachment regardless of available balance. The most common Stripe decline code is [`do_not_honor`](https://support.stripe.com/questions/decline-code-do-not-honor) — the bank rejected the charge without giving a reason. Chainstack cannot override this. Contact your bank to authorize the charge, then retry, or add a different card. ### Card is blocked after a previous decline If your card was declined by the bank on an earlier attempt, Stripe stops trying it again until you update the payment method — even if you fix things on the bank side. The error message you see in this state is: > You previously attempted to charge this card. When the customer's bank declined that payment, it directed Stripe to block future attempts. The fix is to attach a different card. Re-adding the same card from the same source will not clear the block. ### Country restrictions Stripe does not process cards issued in countries on its [restricted businesses list](https://stripe.com/legal/restricted-businesses) or under active sanctions. Use a card issued in a supported country. If none of the above applies, email [support@chainstack.com](mailto:support@chainstack.com) or [submit this form](https://chainstack.com/contact/). ## Prevent suspicious card activity Two scenarios cover most fraudulent card activity on Chainstack. Act on both fronts — your bank and Chainstack — for either case. ### Someone has access to your Chainstack account If charges appear on your card from an account you still control: 1. **Block the card.** Contact your bank and stop further charges immediately. 2. **Contact Chainstack support.** Email [support@chainstack.com](mailto:support@chainstack.com) or [submit this form](https://chainstack.com/contact/). Include the unauthorized invoices or charge dates — support can lock the account, terminate active sessions, and refund charges from confirmed unauthorized activity. 3. **Enable two-factor authentication.** See [Enable two-factor authentication](/docs/manage-your-account#enable-two-factor-authentication). 4. **Rotate your password.** Change it on Chainstack and on any other service where you reused the same email/password pair. ### Someone used your card on a new Chainstack account If your card was used to create a Chainstack account you do not recognize: 1. **Block the card and dispute the charges with your bank.** Report the fraud and request an investigation. 2. **Contact Chainstack support.** Email [support@chainstack.com](mailto:support@chainstack.com) or [submit this form](https://chainstack.com/contact/) with the card's last four digits and the dates of the charges. Support locates the unauthorized account via the Stripe customer record, deletes it, and addresses any sibling accounts created with the same card. If neither path produces a resolution within a reasonable window, the bank chargeback you opened in step 1 is the fallback. ## Settle failed payments If both the crypto balance and credit card deduction tries were unsuccessful, a payment is considered failed. Your Chainstack services get suspended on the first failed payment attempt or remain operational for three days depending on the plan level and your API calls will stop processing as a result. Your Chainstack endpoints will remain the same and will be back to operational when the failed payments are settled. If the failed payments are not settled within 14 days after the first failed attempt, your Chainstack endpoints will be deleted. There are the following ways to settle failed payments: * Add a new credit card. If its balance is sufficient, failed payments will be deducted automatically. * Retry your payment manually and deduct the due amount either from your balance or credit card. To retry your payment manually: 1. Make sure that a payment method of your choice has sufficient funds. 2. Go to [Billing](https://console.chainstack.com/user/settings/billing) > **Balance**. 3. Next to the failed payment, click **Retry with** and select one of the following: * **Credit card** — to deduct the amount due from a credit card linked to your account. * **Balance** — to deduct the amount due from your crypto balance. See how to [top it up](/docs/manage-your-billing#top-up-your-balance-with-crypto). The amount due will be deducted from your crypto or credit card balance and the payment status will change to **Paid**. ## Manage your organization subscription plan and support level You can manage two major options of your organization billing: * Your subscription plan * Your support level Chainstack offers five types of subscription plans which differ depending on your Web3 objectives. The final price of each subscription plan is made up of a base charge for the features included and the number of API requests made to your deployed nodes. [Each subscription plan also has a default support level](https://chainstack.com/pricing/) included into it. You can change your subscription plan any time, while the support level can be changed only for paid subscriptions. Switching between subscription plans and support levels occurs independently from each other. ### Changing your subscription plan To change your subscription plan: 1. In the left navigation bar, click **Billing**. 2. Next to **Plan**, click **Change**. 3. Select a new subscription plan and click **Next**. 4. Check the details of your new subscription plan and click **Confirm**. Your subscription plan changes immediately. You will also be billed for any uninvoiced metered usage from the portion of your previous subscription that you used. #### What happens to your balance on a plan change Eligible unused amounts are returned as credits to your Chainstack account balance and automatically applied against future charges. The **Balance change** column shows the outcome for each action: upgrades are prorated as immediate invoices, add-on or Chainstack Marketplace app changes can return prorated credits, and actions marked **Not prorated** do not receive account credits. | Action | Proration | Balance change | | - | - | - | | Upgrade (for example, Growth to Pro) | Prorated; the difference is invoiced for the remainder of the cycle | Invoice issued immediately for the prorated difference | | Downgrade (for example, Business to Growth) | Not prorated; new plan starts immediately | Unused funds not credited | | Account cancellation (closing your Chainstack account) | Not prorated | Unused funds not credited | | Add-on or Chainstack Marketplace app change or removal | Prorated | Prorated credit applied to account balance | > [!TIP] > > To maximize value, consider timing downgrades or cancellations close to your renewal date. > [!WARNING] > > ### Rate limits for Developer plans > > A free Developer plan has a rate limit of 30 request per second. However, this does not affect paid subscription plans. ### Changing your support level To change your support level: 1. In the left navigation bar, click **Billing**. 2. Next to **Support level**, click **Change**. 3. Select a new support level and click **Next**. 4. Check the details of your new support level and click **Confirm**. Your support level changes immediately. Funds that you haven’t spent during your previous upgrade option will be automatically used as a part of your new support level charge if it’s Professional or credited to your Chainstack account balance if you have switched to Standard. ## Cancel your subscription To cancel your subscription, suspend your organization first, then optionally request full account deletion. ### Suspend your organization 1. Delete all nodes and projects in the organization — suspension won't proceed otherwise. 2. Go to [Billing](https://console.chainstack.com/user/settings/billing). 3. Scroll to the bottom and click **Suspend Organization**. Suspension is not instant — you have a **2-day grace period** during which you can reverse the action by logging back in. ### Request full account deletion Suspension stops billing and freezes the organization, but the account record stays. If you want to permanently delete your account and all stored data, email [support@chainstack.com](mailto:support@chainstack.com) (or [submit this form](https://chainstack.com/contact/)) and include either: * Your Organization ID, or * The email address registered to the account. Support will confirm and process the deletion. > [!NOTE] > > If you suspend but never request deletion, the subscription stays suspended until you log in again — at which point it reactivates. ## Chainstack Marketplace billing details The Chainstack Marketplace is a collection of applications, including add-ons, plugins, and tools designed to extend the functionality of your nodes and infrastructure. Each application may have its own pricing model—such as free, subscription-based, or one-time payment—which is billed separately and in addition to your main Chainstack subscription plan. For proration behavior when you remove a paid application, see [What happens to your balance on a plan change](#what-happens-to-your-balance-on-a-plan-change). # Manage your networks Source: https://docs.chainstack.com/docs/manage-your-networks Create, configure, and manage blockchain networks on Chainstack including adding nodes, monitoring sync status, and adjusting network-level settings. 1. Select a public chain project and click **Get started** or **Join network**. If you don't have a project yet, [create one](/docs/manage-your-project#create-a-project). 2. Select a network and a node you want to deploy. ## View network status You can view the node and network status in the **Status** column of your nodes list or your networks list. A network can be in one of the following states: | Network status | Description | | - | - | | Running | The node is running and everything is okay. | | Failed | The node is `Failed` or in `Error`. | ## Delete a network > [!WARNING] > > To be able to delete a network, you must first delete all nodes associated with the network. See [Delete a node](/docs/manage-your-node#delete-a-node). To delete a network: 1. Click your project. 2. Click a network in the project. 3. Click **Edit** > **Delete**. # Manage your nodes Source: https://docs.chainstack.com/docs/manage-your-node View and rotate node credentials, access endpoints, monitor sync progress, configure add-ons, and manage your Chainstack blockchain nodes from the console. ## Add a node to a network On the [Nodes](https://console.chainstack.com/nodes) page, add a node; The status will change from **Pending** to **Running** once deployed. ## View node access and credentials To view the access information: 1. Click your project. 2. Click your network. 3. Click the node name. This will give you the access and credentials to your nodes. The examples below are for an Ethereum Mainnet Global Node. Dedicated Node endpoints use a per-node host, such as `nd-123-456-789.p2pify.com`, in the same formats. Examples of your HTTPS node connection endpoints: ```sh Key-protected theme={"system"} https://ethereum-mainnet.core.chainstack.com/3c6e0b8a9c15224a8228b9a98ca1531d ``` ```sh Password-protected theme={"system"} https://user-name:pass-word-pass-word-pass-word@ethereum-mainnet.core.chainstack.com ``` Examples of your WSS node connection endpoints: ```sh Key-protected theme={"system"} wss://ethereum-mainnet.core.chainstack.com/3c6e0b8a9c15224a8228b9a98ca1531d ``` ```sh Password-protected theme={"system"} wss://user-name:pass-word-pass-word-pass-word@ethereum-mainnet.core.chainstack.com ``` Examples of your gRPC node connection endpoints (available for select protocols like Sui and Solana): ```sh gRPC endpoint theme={"system"} sui-mainnet.core.chainstack.com:443 ``` ```sh x-token theme={"system"} b2a7442099925280a3184aef2cedd3a1 ``` > [!NOTE] > > gRPC endpoints use x-token authentication passed in the request metadata, rather than URL-based auth tokens. See [authentication methods](/docs/authentication-methods-for-different-scenarios) for details. To call several nodes with one auth token instead of each node's own auth token, attach the nodes to a multichain endpoint. See [Use Multichain API](/docs/multichain-api). ## Rotate node credentials You can replace a node's credentials on Chainstack without downtime. You generate new credentials, move your applications to them while the old and new credentials both work, and then deactivate the old credentials. Rotating credentials regularly is good practice, for example on a schedule or when your team changes. A rotation replaces all of the node's credentials together: * The auth token in the HTTPS and WSS endpoint URLs * The username and password for password-protected access * The x-token for gRPC endpoints To rotate node credentials, you need the **Admin** or **Editor** role in the organization that owns the node. See [Manage your organization](/docs/manage-your-organization) for roles. **1. Generate new credentials** Open the node, click **Rotate credentials** next to **Access and credentials**, and then click **Generate new credentials**. A **Credential rotation in progress** banner appears, and **Access and credentials** lists two sets of credentials, each with its own endpoints: * **New credentials** — marked **Active** * **Old credentials** — marked **Active until rotation is completed** **2. Update your applications** In your applications, replace the old endpoint URLs, username and password, and x-token with the values listed under **New credentials**. The old and new credentials both keep working until you complete the rotation, with no time limit. **3. Complete the rotation** Click **Complete rotation**, select **I have updated my applications to use the new endpoint URLs**, and then click **Deactivate old credentials**. > [!WARNING] > > Completing a rotation is permanent. Chainstack rejects requests that use the old credentials from the moment you click **Deactivate old credentials**. How a rotation runs: * Each set of credentials has at most one rotation in progress. You can click **Rotate credentials** again after you complete the current rotation. * You end a rotation only by deactivating the old credentials. The new credentials stay active. * If your node has more than one active set of credentials, the **API token** selector lists them. Select a set to rotate it, and rotate each set separately. * A rotation doesn't change a [multichain auth token](/docs/multichain-api). ## View node requests metrics You can view the data on requests made to your public chain project node. To view the requests data: 1. Click your project. 2. Click your network. 3. Click the node name. This will show you the data on the requests made to your node: * Requests made — a chart of the total requests over HTTP and WebSocket for the selected period. * Method calls — a breakdown of the method calls to the node over the selected period. Only available for HTTP requests. * Response codes — a breakdown of the HTTP response status codes over the selected period. Only available for HTTP requests. The data granularity is 1 minute. The available timeframes are—1 hour, 6 hours, 12 hours, 24 hours, 7 days. You can also see your total organization's [statistics](/docs/see-statistics). ## Switch the period To switch the period of the displayed requests data: 1. Navigate to the **Metrics** section. 2. On your right, select the period. ## Download the aggregate data To download the aggregate requests data: 1. In the **Requests made** section, click the hamburger button. 2. Click the format to download the aggregate data. ## Check Dedicated Node performance A Dedicated Node shows its current CPU, RAM, storage, and latest block number as tiles in the **Node performance** section of the node's **Overview** tab. To view the node performance: 1. Click your project. 2. Click your network. 3. Click the node name. For what each tile shows, availability, and how to scrape the same metrics into your own monitoring stack, see [Dedicated Node monitoring](/docs/dedicated-node-monitoring#node-performance-on-chainstack). ## Delete a node > [!WARNING] > > To be able to delete a project or a network, you must first delete all nodes associated with the network. To delete a node: 1. Click your project. 2. Click a network in the project. 3. Select a peer node to delete. Click **Edit** > **Delete**. # Manage your organization Source: https://docs.chainstack.com/docs/manage-your-organization Manage your Chainstack organization settings including user roles, team invitations, single sign-on, and multi-factor authentication configuration. ## Organization An organization is the hierarchical ancestor of all your projects and nodes at Chainstack. When you first sign up with Chainstack, an organization is automatically created for you and you become the Owner user of this organization. ## Requirements for an existing user to accept an invitation to another organization If you already belong to an organization on Chainstack, you can accept an invitation to join a different organization only when all of the following conditions are met: * Single‑member organization — you are the sole user in your current organization * Plan tier — your account is on the [Developer plan](https://chainstack.com/pricing/) * Positive or zero balance — your [billing balance](/docs/manage-your-billing) is not negative * No add‑ons — you have no [add-ons enabled](/docs/add-ons) * No active infrastructure — you have no nodes deployed ## Users and their roles A user is an account invited to become a part of an organization. Each user in an organization can be assigned one of the following roles: * Admin — the role can access and change the organization's billing settings, invite users, create and delete projects, deploy and delete nodes and networks, change user roles. * Editor — the role can create and delete projects, deploy and delete nodes and networks. * Viewer — the role can view existing projects, nodes, and networks. > [!NOTE] > > ### Is Owner a separate role? > > Owner is a special tag for a user who created the organization. Owner users have the same rights as Admin users but cannot change their role. ## Change a user role in the organization Once the invited user joins the organization, you can change the user's role. To change the role: 1. Click **Settings** > **Organization**. 2. Next to the user, click the edit icon. 3. Select the new role **Admin**, **Editor**, or **Viewer**. 4. Click **Update**. This will change the role of the user. The user whose role is changed will need to relogin for the changes to take effect. ## Delete a user from the organization To delete a user from the organization: 1. Click **Settings** > **Organization**. 2. Next to the user, click the trash bin icon. 3. Click **Yes, I'm sure** to confirm the action. The user will be instantly deleted from your organization. # Manage your projects Source: https://docs.chainstack.com/docs/manage-your-project Create, organize, and manage Chainstack projects to group your blockchain node deployments, networks, and team access under a single workspace. ## Create a project You must create a project (A project organizes your blockchain networks and nodes.) to be able to deploy a node or a network, or to join a network. To create a project: 1. Log in to your [Chainstack account](https://console.chainstack.com/). 2. Click **Create project**. 3. Provide **Project name** 4. Provide **Description** (optional). 5. Click **Create**. ## Add a network to a project A project (A project organizes your blockchain networks and nodes.) can have more than one network. To add a network to a project, see [Join a public network](/docs/manage-your-networks). ## Edit a project To edit a project: 1. Click your project. 2. Click **Edit**. 3. Edit your **Project name** and/or **Description**. 4. Click **Save** to apply changes. ## Delete a project > [!WARNING] > > To be able to delete a project, you must first delete all nodes associated with the project. See [Delete a node](/docs/manage-your-node#delete-a-node). To delete a project: 1. Click your project. 2. Click **Edit** > **Delete**. 3. Click **Yes, I'm sure** to confirm the action. # Mantle: Fetching token prices from Merchant Moe Source: https://docs.chainstack.com/docs/mantle-fetching-token-prices-from-merchant-moe Fetch real-time token prices from the Merchant Moe DEX on Mantle using smart contract calls and a Chainstack RPC endpoint for DeFi price discovery. **TLDR:** * Demonstrates fetching real-time token prices from Merchant Moe's Liquidity Book pools on Mantle Mainnet. * Shows how to work with Liquidity Book pool contracts and their unique bin-based pricing mechanism. * Includes practical examples of price calculation using bin IDs and continuous price monitoring. ## Overview Mantle is an OP Stack Layer 2 chain. Merchant Moe is a leading DEX on Mantle that uses a unique Liquidity Book model instead of traditional constant product AMMs. Liquidity Book pools use discrete price bins to provide liquidity. Check [Merchant Moe docs](https://docs.merchantmoe.com/) for more information. This tutorial guides you through creating a real-time token price monitor that fetches prices from Merchant Moe's unique Liquidity Book pools. You'll learn how to connect to the Mantle network, interact with Liquidity Book contracts, understand the bin-based pricing mechanism, and create a continuous price monitoring system. ## Prerequisites * [web3.py](https://web3py.readthedocs.io/) * A Chainstack Mantle node endpoint—register on [Chainstack](https://console.chainstack.com/) ## Token price fetching from Liquidity Book pools Now let's create a token price monitoring script that fetches real-time prices from Merchant Moe. The script connects to the Mantle network over a Chainstack node RPC endpoint, then queries a specific Liquidity Book pool to get the current active bin ID and calculate token prices. The example focuses on the cmETH/USDe trading pair, demonstrating how to work with Liquidity Book's unique bin-based pricing mechanism. Here are the links & addresses: * [Merchant Moe cmETH/USDe pool](https://merchantmoe.com/pool/v22/0xe6829d9a7ee3040e1276fa75293bde931859e8fa/0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34/20) * [cmETH/USDe Liquidity Book pool contract](https://mantlescan.xyz/address/0x38E2a053E67697e411344B184B3aBAe4fab42cC2) * [cmETH contract](https://mantlescan.xyz/address/0xe6829d9a7ee3040e1276fa75293bde931859e8fa) * [USDe contract](https://mantlescan.xyz/address/0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34) This tutorial script covers: * How to use web3.py with Mantle * How to interact with Liquidity Book pool contracts * How to calculate prices from bin IDs and bin steps * How to continuously monitor token prices * How to handle token decimals and reserve calculations Here's the full script and the comments: ```python theme={"system"} import time from decimal import Decimal from web3 import Web3 # Config RPC_URL = "YOUR_CHAINSTACK_ENDPOINT" POOL = Web3.to_checksum_address("0x38E2a053E67697e411344B184B3aBAe4fab42cC2") CMETH_ADDR = Web3.to_checksum_address("0xE6829d9a7eE3040e1276Fa75293Bde931859e8fA") USDE_ADDR = Web3.to_checksum_address("0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34") # Minimal ERC-20 ABI ERC20_ABI = [ {"constant":True,"name":"decimals","inputs":[],"outputs":[{"name":"","type":"uint8"}],"type":"function"}, {"constant":True,"name":"symbol","inputs":[],"outputs":[{"name":"","type":"string"}],"type":"function"}, {"constant":True,"name":"balanceOf","inputs":[{"name":"owner","type":"address"}], "outputs":[{"name":"","type":"uint256"}],"type":"function"}, ] # Liquidity Book Pool ABI (minimal for price reading) LB_POOL_ABI = [ {"inputs":[],"name":"getActiveId","outputs":[{"internalType":"uint24","name":"activeId","type":"uint24"}],"stateMutability":"view","type":"function"}, {"inputs":[],"name":"getBinStep","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"pure","type":"function"}, {"inputs":[],"name":"getTokenX","outputs":[{"internalType":"contract IERC20","name":"tokenX","type":"address"}],"stateMutability":"pure","type":"function"}, {"inputs":[],"name":"getTokenY","outputs":[{"internalType":"contract IERC20","name":"tokenY","type":"address"}],"stateMutability":"pure","type":"function"}, ] # Connect to RPC w3 = Web3(Web3.HTTPProvider(RPC_URL)) assert w3.is_connected(), "❌ RPC not reachable" cmeth = w3.eth.contract(address=CMETH_ADDR, abi=ERC20_ABI) usde = w3.eth.contract(address=USDE_ADDR, abi=ERC20_ABI) pool = w3.eth.contract(address=POOL, abi=LB_POOL_ABI) dec_cmeth = cmeth.functions.decimals().call() dec_usde = usde.functions.decimals().call() sym_cmeth = cmeth.functions.symbol().call() sym_usde = usde.functions.symbol().call() # Get bin step for price calculation bin_step = pool.functions.getBinStep().call() # Determine token order in the pool token_x = pool.functions.getTokenX().call() token_y = pool.functions.getTokenY().call() def calculate_price_from_bin_id(active_id, bin_step): """Calculate price from Liquidity Book bin ID and bin step""" # LB price formula: price = (1 + binStep / 10_000) ^ (activeId - 8388608) # 8388608 = 2^23 = reference bin ID where tokenX:tokenY price ratio = 1:1 # This is a protocol constant, not the actual trading bin (which is activeId) BASE_BIN_ID = 8388608 bin_step_decimal = Decimal(bin_step) / Decimal(10000) price_multiplier = (Decimal(1) + bin_step_decimal) ** (Decimal(active_id) - Decimal(BASE_BIN_ID)) return price_multiplier def fetch_price(): """Return cmETH price in USDe using Liquidity Book active bin""" # Get current active bin ID active_id = pool.functions.getActiveId().call() # Calculate the price from the active bin bin_price = calculate_price_from_bin_id(active_id, bin_step) # Get reserves for display bal_cmeth = cmeth.functions.balanceOf(POOL).call() bal_usde = usde.functions.balanceOf(POOL).call() cmeth_float = Decimal(bal_cmeth) / (10 ** dec_cmeth) usde_float = Decimal(bal_usde) / (10 ** dec_usde) # Determine which token is X and which is Y to get correct price direction if token_x.lower() == CMETH_ADDR.lower(): # cmETH is tokenX, USDe is tokenY # bin_price gives us tokenY/tokenX = USDe/cmETH cmeth_in_usde = bin_price usde_in_cmeth = Decimal(1) / bin_price if bin_price else Decimal(0) else: # USDe is tokenX, cmETH is tokenY # bin_price gives us tokenY/tokenX = cmETH/USDe usde_in_cmeth = bin_price cmeth_in_usde = Decimal(1) / bin_price if bin_price else Decimal(0) return cmeth_in_usde, usde_in_cmeth, usde_float, cmeth_float if __name__ == "__main__": print(f"📡 Reading Merchant Moe {sym_cmeth}/{sym_usde} pool … ⛓️ Mantle") while True: cmeth_usde, usde_cmeth, r_usde, r_cmeth = fetch_price() print( f"💧Reserves {r_usde:.2f} {sym_usde} | {r_cmeth:.5f} {sym_cmeth}\n" f"💰1 {sym_cmeth} ≈ {cmeth_usde:.2f} {sym_usde} | " f"1 {sym_usde} ≈ {usde_cmeth:.6f} {sym_cmeth}\n" f"⏱️ {time.strftime('%Y-%m-%d %H:%M:%S')}\n{'-'*60}" ) time.sleep(3) # poll every 3s ``` ## Conclusion This tutorial provided a comprehensive guide to fetching real-time token prices from Merchant Moe's Liquidity Book pools on Mantle using Python. The price fetching approach demonstrated here can be easily extended to monitor other trading pairs, implement price alerts, or integrate into larger DeFi applications requiring accurate price data. > [!NOTE] > > ### See also > > > [card] [Mantle documentation](https://docs.mantle.xyz/) > > > [card] [Merchant Moe documentation](https://docs.merchantmoe.com/) > > > [card] [web3.py documentation](https://web3py.readthedocs.io/) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Mantle tooling Source: https://docs.chainstack.com/docs/mantle-tooling Complete Mantle tooling guide for Chainstack nodes. Use MetaMask, web3.py, ethers.js, Hardhat, Remix IDE, Foundry, and more to build dApps on Mantle blockchain. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Mantle nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Mantle nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.JsonRpcProvider(urlInfo.url, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * NETWORK\_ID — Mantle network ID: * Mainnet: `5000` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Mantle nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Custom JavaScript tracing for EVM transactions Source: https://docs.chainstack.com/docs/mastering-custom-javascript-tracing-for-ethereum-virtual-machine Write custom JavaScript tracers for the EVM debug_traceTransaction method to extract specific execution data from Ethereum transactions on Chainstack. **TLDR:** * Provides an overview of custom JavaScript tracers on Ethereum (Geth or Erigon) for advanced debugging and selective data collection. * Demonstrates using JS code with debug\_traceTransaction (and others) to log or transform EVM execution details. * Explains how to flatten tracer scripts into JSON-RPC payloads, including a Node.js example to automate string conversion. * Highlights key methods (step, enter, exit, fault, result) for capturing and returning custom trace data. ## Main article > [!NOTE] > > ### Available on customized Dedicated Nodes only > > Custom JavaScript tracers are available as customized solutions on the [Enterprise plan](https://chainstack.com/pricing/) on [Dedicated Nodes](/docs/dedicated-node). If you've landed on this article, it's likely that you're already acquainted with the concept of tracing. If not, we recommend starting with our [Deep dive into Ethereum debug\_trace APIs](https://chainstack.com/deep-dive-into-ethereum-trace-apis/) for a comprehensive understanding of tracing and the prevalent EVM tracing methods. Tracing, in the context of Ethereum, refers to the process where Ethereum clients execute or re-execute a transaction or block, gathering crucial information produced during the execution. This process serves as an invaluable tool for debugging, performance analysis, and a multitude of other applications. Ethereum provides built-in tracing functionality through its JSON-RPC API. There are three types of tracing supported by Ethereum: > [card] [Basic tracing](https://geth.ethereum.org/docs/developers/evm-tracing/basic-traces) > [card] [Built-in tracer tracing](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers) > [card] [Custom tracing](https://geth.ethereum.org/docs/developers/evm-tracing/custom-tracer) > [!NOTE] > > You can also find details about [built-in tracers](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) and [custom tracing](/reference/custom-js-tracing-ethereum) in the Chainstack documentation. **Basic tracing** and **Built-in tracers** serve as powerful debugging tools. They generate all the crucial data for most general purposes. Nevertheless, there can be situations that demand more specific details to align with particular requirements, or sometimes the standard tracer output too much data when only something specific is needed. This is where custom JavaScript tracing comes into play, allowing for enhanced customization of the tracing output. This article will discuss custom JS tracing, which is one of the custom tracing methods that Ethereum supports. ## What is custom JavaScript tracing? Custom JavaScript tracing enables developers to create custom tracing scripts that can be executed on the Ethereum node. It is available on both Geth and Erigon. > [!NOTE] > > For an in-depth understanding of the RPC methods available on Geth and Erigon, we invite you to explore our comprehensive guide [Geth vs Erigon: Deep dive into RPC methods on Ethereum clients](/docs/geth-vs-erigon-deep-dive-into-rpc-methods-on-ethereum-clients). Users can use custom JavaScript tracing in conjunction with the following popular tracing methods: > [card] [debug\_traceCall](/reference/ethereum-tracecall) > [card] [debug\_traceTransaction](/reference/ethereum-tracetransaction) > [card] [debug\_traceBlockByHash](/reference/ethereum-traceblockbyhash) > [card] [debug\_traceBlockByNumber](/reference/ethereum-traceblockbynumber) Please note that JS tracing is a feature that can be disabled, and not all nodes or RPC providers support JS tracing. The simplest way to test if your endpoint supports JS tracing is to submit a sample request to the endpoint. To use JS tracing the vanilla way, pass the JS code to the "tracer" as a parameter and send it over an HTTPS JSON request. Below are some examples of using JS tracing. The following sample requests are taken from [the official Geth documentation](https://geth.ethereum.org/docs/developers/evm-tracing/custom-tracer). The code simply finds all the stack positions for all "CALL" opcodes using the `log.stack.peek` function. ### Example with the `debug_traceCall` method In this case, the custom tracer object encompasses JavaScript code that will execute at every stage of the Ethereum virtual machine (EVM) during transaction execution. This specific tracer captures the first value from the EVM's stack each time a `CALL` opcode is detected, storing the collected values in the `data` array. ```bash cURL theme={"system"} curl --location 'YOUR_CHAINSTACK_ENDPOINT' \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "method": "debug_traceCall", "params": [ { "from": "0xdeadbeef29292929192939494959594933929292", "to": "0xde929f939d939d393f939393f93939f393929023", "gas": "0x7a120", "data":"0xf00d4b5d00000000000000000000000001291230982139282304923482304912923823920000000000000000000000001293123098123928310239129839291010293810" }, "0x7765", { "tracer":"{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == '\''CALL'\'') this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}" } ], "id": 1 }' ``` ### Example with the `debug_traceTransaction` method The custom tracer here checks each operation (step) in the transaction, and if the operation is a `CALL`, it pushes the top item on the stack (`log.stack.peek(0)`) into the `data` array. The result of the tracer is this 'data' array. ```bash cURL theme={"system"} curl --location 'YOUR_CHAINSTACK_ENDPOINT' \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "method": "debug_traceTransaction", "params": ["0x2595b06198245b5b2c92b1316c5c5e92edac0a611250ae53f8961468a73a55a2", { "tracer":"{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == '\''CALL'\'') this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}" }], "id": 1 }' ``` If running those examples returns an error, it means your node does not support custom JS tracing. ## node.js example In theory, it's possible to use custom JavaScript (JS) tracing solely through an HTTPS endpoint. However, this approach carries numerous restrictions. If your objective is to exclusively use HTTP requests, there are certain considerations you must take into account: * When using a JSON object in request parameters, double quote strings are not supported. Code like `log.op.toString() == "CALL"` needs to be converted to single quotes `log.op.toString() == 'CALL'` before using it as a parameter. * Simplify the code by flattening it and eliminating all comments. The [official example](https://geth.ethereum.org/docs/developers/evm-tracing/javascript-tutorial) demonstrates the construction of tracer objects as strings. ```javascript Javascript theme={"system"} tracer = function (tx) { return debug.traceTransaction(tx, { tracer: '{' + 'retVal: [],' + 'step: function(log,db) {this.retVal.push(log.getPC() + ":" + log.op.toString())},' + 'fault: function(log,db) {this.retVal.push("FAULT: " + JSON.stringify(log))},' + 'result: function(ctx,db) {return this.retVal}' + '}' }); // return debug.traceTransaction ... }; // tracer = function ... ``` This is not a scalable approach and is incompatible with integrated development environments (IDEs) like Visual Studio Code. It also requires reformatting the code into a lengthy string every time you want to make changes before submitting it to the server. In the next section, we'll develop a simple JavaScript program that handles the process in a more elegant way. This code will handle a JavaScript tracer object, automating its transformation into the requisite tracer string. ## Prerequisites To follow along with this tutorial, ensure you have the following prerequisites: * An [Ethereum node that supports debug and trace API](/docs/debug-and-trace-apis), for example, Chainstack's node running on Erigon * [node.js](https://nodejs.org/en/download) and npm are installed on your system. ### Step 1: Initialize In this example, we will use node.js as our primary platform. Create an empty project directory and initialize the project using the following command: ```shell Shell theme={"system"} npm init ``` ### Step 2: Install dependencies To communicate with a server over an HTTP connection, install the `node-fetch` package. ```shell Shell theme={"system"} npm install node-fetch@2 ``` ### Step 3: Define the tracing script We will define a custom tracing script that outputs the execution trace of a smart contract. First, create a new JS file called `trace.js`: ```shell Shell theme={"system"} echo $null >> trace.js ``` Paste the following code in `trace.js`: ```javascript trace.js theme={"system"} const fetch = require("node-fetch"); const url = "YOUR_CHAINSTACK_ENDPOINT" tracer = { retVal: [], afterSload: false, callStack: [], byte2Hex: function(byte) { if (byte < 0x10) return "0" + byte.toString(16); return byte.toString(16); }, array2Hex: function(arr) { let retVal = ""; for (let i = 0; i < arr.length; i++) retVal += this.byte2Hex(arr[i]); return retVal; }, getAddr: function(log) { return this.array2Hex(log.contract.getAddress()); }, step: function(log, db) { let opcode = log.op.toNumber(); // SLOAD if (opcode == 0x54) { this.retVal.push(log.getPC() + ": SLOAD " + this.getAddr(log) + ":" + log.stack.peek(0).toString(16)); this.afterSload = true; } // SLOAD Result if (this.afterSload) { this.retVal.push(" Result: " + log.stack.peek(0).toString(16)); this.afterSload = false; } // SSTORE if (opcode == 0x55) this.retVal.push(log.getPC() + ": SSTORE " + this.getAddr(log) + ":" + log.stack.peek(0).toString(16) + " <- " + log.stack.peek(1).toString(16)); // End of step }, fault: function(log, db) { this.retVal.push("FAULT: " + JSON.stringify(log)) }, result: function(ctx, db) { return this.retVal } }; // Flatten tracer's code function getTracerString(tracer) { result = "{" for (property in tracer) { if (typeof tracer[property] == "function") result = result + property.toString() + ": " + tracer[property] else result = result + property.toString() + ": " + JSON.stringify(tracer[property]) result += "," } result += "}" return result.replace(/"/g, "'") } async function main() { const response = await fetch(url, { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ "method": "debug_traceTransaction", "params": ["0x2f1c5c2b44f771e942a8506148e256f94f1a464babc938ae0690c6e34cd79190", { "tracer": getTracerString(tracer) }], "id": 1, "jsonrpc": "2.0" }), }); result = await response.json() console.log(JSON.stringify(result)); console.log("***end***") } main() ``` To see the trace in action, add your Chainstack endpoint and run `node trace`. This example traces the contract creation code for USDT at transaction [0x2f1c5c2b44f771e942a8506148e256f94f1a464babc938ae0690c6e34cd79190](https://etherscan.io/tx/0x2f1c5c2b44f771e942a8506148e256f94f1a464babc938ae0690c6e34cd79190). ## Understanding the code The main function runs a POST request with the `debug_traceTransaction` method on a transaction hash. The `getTracerString` function is used to convert the `tracer` object to a string representation. This is necessary because the `tracer` object must be passed as a string in the JSON-RPC request. The `tracer` object comes equipped with a suite of methods designed to process transaction details: * `byte2Hex` and `array2Hex` — these are our utility functions responsible for converting byte and array data into hexadecimal form. * `getAddr` — this function is used to extract the contract's address from the log. * `step` — invoked for every opcode (operation code) in the transaction, this method is crucial for tracking operations and documenting any modifications to storage. For clarity, `SLOAD` and `SSTORE` refer to EVM opcodes for data loading and storage. * `fault` — if an error or exception arises within the transaction, this method gets called and logs the error for record-keeping. * `result` — at the end of execution, this method provides an array with all the logs collected during the process. ## How does JavaScript interact with Ethereum clients You may have observed that custom JavaScript tracing requires providing JavaScript code to the Ethereum client. But how does this work? The key lies in Ethereum clients' ability to execute JavaScript code, made possible through a JavaScript implementation written in the Go programming language. Let’s take Geth as an example, which uses [Goja](https://github.com/dop251/goja), which enables [JavaScript tracing on Geth](https://github.com/ethereum/go-ethereum/blob/9231770811cda0473a7fa4e2bccc95bf62aae634/eth/tracers/js/goja.go#L97). ### What is Goja? Goja is an ECMAScript 5.1 (JavaScript) implementation in Go, offering the ability to run, manipulate, and test JavaScript within Go applications. It enables JavaScript scripting in a Go environment, interoperability with JavaScript code, and server-side rendering. When JavaScript tracing code is sent to Geth, Goja interprets it and executes it alongside the main program. ### Custom JS tracing syntax To use JS tracing, you must define the following functions in your code: * `result` (mandatory) — defines what is returned to the RPC caller, and takes two arguments: `ctx` and `db`. * `fault` (mandatory) — is invoked when an error occurs during tracing, and takes two arguments: `log` and `db`. Information about the error can be obtained using the `log.getError()` method. * `setup` — is invoked once at the beginning when the tracer is being constructed. It takes in one argument, `config`, which is tracer-specific and allows users to pass in [options](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#config) to the tracer. * `step` — is called for each execution step during tracing, and is the main execution function for JS tracing. It takes two arguments: `log` and `db`. * `enter` and `exit` (must be used in combination with each other) — are called when stepping in and out of an internal call. They are specifically called for the `CALL` and `CREATE` variants, as well as for the transfer implied by a `SELFDESTRUCT`. The `enter` function takes a `callFrame` object as an argument, and the `exit` function takes a `frameResult` object as an argument. To learn more about how these functions work, refer to [this official tutorial](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#config). ## Conclusion Custom JS tracing allows developers to create scripts that can be executed on the Ethereum node using popular methods like `debug_traceCall`, `debug_traceTransaction`, `debug_traceBlockByHash`, and `debug_traceBlockByNumber`. The article provides practical examples of how to use custom JS tracing, including running sample requests to endpoints and implementing a custom tracer script using node.js, which serves as a more scalable approach compared to constructing tracer objects as strings. This concludes the tutorial. I hope you found it useful. Thank you for reading. If you have any questions, feel free to reach out to me on Chainstack's [Telegram](https://t.me/chainstack) or [Discord](https://discord.gg/Cymtg2f7pX) or on my social media. Cheers! ### About the author > [card] Wuzhong Zhu > > ![Wuzhong Zhu](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1548860905064017921/xCKUgveS_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=075e8dc4db13d9384c63bf4904eb520d) > > Developer Advocate @ Chainstack > \ > BUIDLs on Ethereum, zkEVMs, and The Graph protocol > > [https://twitter.com/wuzhongzhu](https://twitter.com/wuzhongzhu) > > [https://www.linkedin.com/in/wuzhong-zhu-44563589/](https://www.linkedin.com/in/wuzhong-zhu-44563589/) > > [https://github.com/wuzhong-zhu](https://github.com/wuzhong-zhu) # Multithreading in Python for Web3 requests Source: https://docs.chainstack.com/docs/mastering-multithreading-in-python-for-web3-requests-a-comprehensive-guide Speed up blockchain data fetching with Python multithreading and asyncio for concurrent web3 requests across multiple Chainstack RPC node endpoints. **TLDR:** * Demonstrates how to use Python multithreading (via ThreadPoolExecutor and asyncio) to speed up I/O-bound Ethereum RPC calls. * Sequential requests can lead to heavy wait times; sending them concurrently leverages idle CPU cycles and improves efficiency. * Shows how to handle exceptions cleanly, share state safely, and choose a suitable number of worker threads. * While the GIL can limit CPU-bound threading, network requests benefit significantly from concurrency for faster performance. ## Main article This guide is designed for developers using the web3.py library to interact with Ethereum. We aim to unravel the mystique of multithreading and show you how to bring this powerful technique to bear on your Web3 requests. But first, what is multithreading? Multithreading allows a program to run multiple processes simultaneously, like sending a request while processing another. This is particularly useful when dealing with I/O tasks such as network requests where the program spends a lot of time waiting. Multithreading can significantly speed up your program by allowing it to do other tasks during these waiting times, which also allows you to use system resources efficiently. By the end of this guide, you'll have a good idea about working with and without multithreading, handling exceptions, and following best practices to ensure your code is efficient and robust. ## Understanding multithreading Multithreading is a technique that allows multiple parts of a single program to run concurrently. But what does that mean, exactly? Let's break it down. In the simplest terms, a thread is the smallest unit of processing that can be performed in an OS (operating system). In the context of programming, a thread is the flow of execution of a program. A single-threaded program has just one flow and can perform one operation at a time. Conversely, multithreading enables a program to control multiple threads, essentially allowing multiple operations to run in parallel. > [!NOTE] > > Multithreading is a technique to achieve asynchronous behavior in a program. This is where things get exciting. With multithreading, we can run different parts of our program at the same time, as if they were separate miniature programs. This is particularly handy when performing I/O operations, like network requests or file reads and writes, which can often take significant time to complete. Multithreading allows us to start multiple operations simultaneously instead of waiting for one operation to finish before the next. This means we can fetch data from one part of a blockchain while sending a transaction request to another, all within the same program. Multithreading is accessible in Python primarily because the language offers built-in support via the `threading` and `concurrent.futures` modules, among others. These modules provide high-level, easy-to-use APIs for creating and managing threads. They handle a lot of the complex details of thread management behind the scenes, making it easier for developers to leverage multithreading in their applications. But why does this matter? Well, think about the benefits: * **Efficiency**. By allowing multiple operations to run concurrently, we make better use of our system's resources. We can start processing a new request as soon as we've sent an old one rather than waiting for a response. * **Performance**. Multithreading can significantly speed up our programs. This is particularly noticeable in programs that make a lot of I/O requests, like those interacting with a blockchain. * **Responsiveness**. In user-facing applications, multithreading can make our program feel more responsive. We can continue to interact with the user interface while waiting for I/O operations to complete. However, it's important to note that multithreading comes with its own set of challenges. Issues such as thread synchronization, deadlocks, and race conditions need to be handled carefully; we'll tackle these in later sections. In the next sections, we'll dive into how to use multithreading in Python to improve the performance of your Chainstack RPC endpoint. You'll learn how to send multiple requests to your RPC node concurrently, which can significantly speed up tasks like fetching data from the blockchain or waiting for transactions to be mined. Let's get started! ## Setting up your environment Before we dive into the code, let's set up our development environment. This will ensure you have all the necessary tools and libraries installed to follow along with this guide. Here's what you'll need: * **Python**. We'll be using Python for our examples because of its readability and the robust libraries it offers for working with Ethereum. We recommend using Python 3.8 or newer. You can download Python from the [official website](https://www.python.org/downloads/) if you don't have it installed already. * **web3.py**. This is a Python library for interacting with Ethereum. It's a comprehensive tool that lets us connect to Ethereum nodes, make requests, and work with Ethereum data. You can install [web3.py](https://github.com/ethereum/web3.py) using pip, the Python package installer. Open a terminal and run the following command: ```shell Shell theme={"system"} pip install web3 ``` * **An Ethereum archive node**. We'll interact with the Ethereum network for this guide. We'll need access to an Ethereum archive node since we’ll query older states. To get an RPC endpoint, follow these steps: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Creating a simple Web3 script without multithreading Let's start with a simple example of making Web3 requests without multithreading. We'll write a script fetching an Ethereum address's balance at various block numbers. Here's the code: ```python Python theme={"system"} from web3 import Web3 import time web3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) address = "0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326" start_block = web3.eth.block_number end_block = start_block - 500 def get_balance_at_block(block_num): balance = web3.eth.get_balance(address, block_identifier=block_num) print(f"Balance at block {block_num}: {web3.from_wei(balance, 'ether')} ETH") start = time.time() for block_num in range(start_block, end_block, -1): get_balance_at_block(block_num) print(f"Time taken: {time.time() - start}") ``` Now, let's walk through the code, line by line: 1. **Import necessary modules**. We start by importing the `Web3` module from the `web3` package. We also import the `time` module, which we'll use to measure the execution time of our script. 2. **Set up the Web3 provider**. We set up a connection to our Ethereum node using `Web3.HTTPProvider`. Replace `YOUR_CHAINSTACK_ENDPOINT` with the link to your Ethereum node. 3. **Define the Ethereum address and block range**. We specify the Ethereum address we're interested in and the range of block numbers we want to check. We use `web3.eth.block_number` to get the latest block number and subtract 500 to get the start of our range. 4. **Define the function for fetching the balance**. We define a function `get_balance_at_block` that takes a block number as input, fetches the balance of our specified address at that block number, and prints the balance in ether (converted from wei). 5. **Fetch the balance at each block number**. We loop over the range of block numbers from `start_block` to `end_block`, calling `get_balance_at_block` for each one. Since we're not using multithreading, these requests are made sequentially, meaning the script waits for each request to complete before moving on to the next one. 6. **Measure and print the execution time**. We use `time.time()` to get the current time at the start and end of our script, subtract the two to get the total execution time, and print the result. This script provides a baseline for understanding how long it takes to make these Web3 requests without multithreading. The result is heavily dependent on your location compared to the node’s location. The next section modifies this script to use multithreading and compares the performance. ## Creating a simple Web3 script with multithreading Now let's take our previous example and modify it to use multithreading. This will allow us to make multiple Web3 requests concurrently, potentially speeding up our script. Here's the modified code: ```python Python theme={"system"} import asyncio from concurrent.futures import ThreadPoolExecutor from web3 import Web3 import time web3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) address = "0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326" start_block = web3.eth.block_number end_block = start_block - 500 max_workers = 100 def get_balance_at_block(block_num): balance = web3.eth.get_balance(address, block_identifier=block_num) print(f"Balance at block {block_num}: {web3.from_wei(balance, 'ether')} ETH") async def main(): with ThreadPoolExecutor(max_workers=max_workers) as executor: tasks = [ loop.run_in_executor( executor, get_balance_at_block, block_num ) for block_num in range(start_block, end_block, -1) ] await asyncio.gather(*tasks) loop = asyncio.get_event_loop() start = time.time() loop.run_until_complete(main()) print(f"Time taken: {time.time() - start}") ``` Now let's go through the changes step by step: 1. **Additional imports**. We import `asyncio` and `ThreadPoolExecutor` from `concurrent.futures`. `asyncio` is a library for writing single-threaded concurrent code using coroutines, multiplexing I/O access over sockets and other resources, running network clients and servers, and other related primitives. `ThreadPoolExecutor` is a class that creates a pool of worker threads and provides a simple way to offload tasks to them. 2. **Creating the `ThreadPoolExecutor`**. Inside the `main` function, we create a `ThreadPoolExecutor` with a maximum of 100 worker threads. These threads will be used to run our `get_balance_at_block` function concurrently. 3. **Creating tasks**. We create a list of tasks, where each task is a call to `get_balance_at_block` for a different block number. Each of these tasks is run in the executor, meaning it's run in a separate thread. This is done using the `loop.run_in_executor` method, which schedules the callable to be executed and returns a `Future` object representing the execution of the callable. 4. **Running tasks concurrently**. We use `asyncio.gather(*tasks)` to run these tasks concurrently. This function returns a Future aggregating result from the given Future or coroutine objects. This Future completes when all of the given Futures are complete. 5. **Running the event loop**. Finally, we use `loop.run_until_complete(main())` to run the event loop until the `main()` function has been completed. This starts the execution of the tasks in the executor and waits for them to complete. Using a `ThreadPoolExecutor` and `asyncio`, we can make multiple Web3 requests concurrently, potentially speeding up our script significantly compared to the sequential version. In the next section, we'll compare the performance of this multithreaded version with the sequential version and discuss some of the considerations and best practices when using multithreading in Python. ### Explain `ThreadPoolExecutor` and workers Now, let's dive deeper into `ThreadPoolExecutor` and the concept of worker threads, as this is the main concept. In Python's `concurrent.futures` module, a `ThreadPoolExecutor` is a class that creates a pool of worker threads and provides methods to submit tasks to this pool. Once a task is submitted, a worker thread picks it up and executes it. When a worker thread finishes executing a task, it becomes available to pick up another task. The parameter `max_workers` defines the maximum number of worker threads the executor can use. This doesn't mean it will always use this many threads; it won't use more than this. If you submit more tasks than `max_workers`, the executor will queue the extra tasks and execute them as worker threads become available. Choosing the right value for `max_workers` depends on the nature of the tasks and the resources available. * If the tasks are I/O-bound (for example, making network requests), like in this case, you can benefit from a relatively high number of worker threads, as these tasks spend much of their time waiting for I/O operations to complete. While one thread is waiting for its I/O operation, other threads can be executing. * However, if the tasks are CPU-bound, having more worker threads than the number of CPUs can lead to context switching overhead and won't usually provide any speedup due to Python's global interpreter lock (GIL). * You also need to consider the resources available. Each thread consumes some amount of memory, so having many worker threads could consume a lot of memory. In the example, we set `max_workers` to 100, which means the executor will use up to 100 threads to execute the `get_balance_at_block` function concurrently. I used this number because although my machine runs a 16-core CPU, the tasks are I/O, so we can leverage the CPU idle time while waiting for the server to respond. If the task was CPU bound, we would want to cap the workers to 16. Also, after running multiple tests, this number of workers gives me the best performance between speed, resource consumption, and server response/stability. This provides a significant speedup compared to the sequential version, as while one thread is waiting for a response from the Ethereum node, other threads can send their requests or process received data. Be aware that the Ethereum node might also have limits on how many concurrent requests it can handle. If you make too many requests at once, it might slow down and start rejecting requests. However, Chainstack does not throttle the requests, meaning you should not experience issues if you keep the requests under 3,000 requests per second. ## Understanding the differences Now that we've seen both a sequential and a multithreaded approach to making Web3 requests let's compare the two and understand the differences in performance and efficiency. * **Sequential approach**. In the sequential version of the script, we made one request at a time. After sending a request, we waited for the response before sending the next request. This is a straightforward approach, and it's easy to understand what's happening at each step. However, this approach doesn't make efficient use of our resources. While waiting for a response from the Ethereum node, our program isn't doing anything else. In my test, this approach took 88 seconds to complete. * **Multithreaded approach**. In the multithreaded version of the script, we used a `ThreadPoolExecutor` to create a pool of worker threads. We then used these threads to send multiple requests concurrently. While one thread is waiting for a response, other threads can send their requests or process received data. This approach can be more efficient because it allows us to do more work at the same time. In my test, the multithreaded approach took only 2 seconds, about 97% faster compared to the sequential approach. These results demonstrate the potential benefits of multithreading for making Web3 requests. By sending multiple requests concurrently, we can significantly speed up our script and make better use of our system's resources. However, it's important to note that multithreading comes with its own set of challenges. Managing multiple threads can add complexity to our code, and we need to be careful to avoid issues like race conditions, where two threads try to modify a shared resource at the same time. Also, if we try to make too many requests at once, we might overwhelm the Ethereum node or run into rate limits. ## Organizing the response While running the code, you may observe that the results are presented in the order they are received, rather than in a block-wise sequence, which would seem more logical. With a small modification to the code, we can align the output to display results in accordance with their respective block numbers. ```python Python theme={"system"} import asyncio from concurrent.futures import ThreadPoolExecutor from web3 import Web3 import time web3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) address = "0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326" start_block = web3.eth.block_number end_block = start_block - 500 max_workers = 100 def get_balance_at_block(block_num): try: balance = web3.eth.get_balance(address, block_identifier=block_num) return (block_num, web3.from_wei(balance, 'ether')) # Return a tuple except Exception as e: print(f"Error occurred while getting balance at block {block_num}: {e}") async def main(): with ThreadPoolExecutor(max_workers=max_workers) as executor: loop = asyncio.get_event_loop() futures = [ loop.run_in_executor( executor, get_balance_at_block, block_num ) for block_num in range(start_block, end_block, -1) ] results = [] for future in asyncio.as_completed(futures): try: # This will raise an exception if the thread raised an exception result = await future results.append(result) # Collect the results except Exception as e: print(f"Error occurred in thread: {e}") # Sort the results by block number and print them results.sort(key=lambda x: x[0]) for block_num, balance in results: print(f"Balance at block {block_num}: {balance} ETH") loop = asyncio.get_event_loop() start = time.time() loop.run_until_complete(main()) print(f"Time taken: {time.time() - start}") ``` In this version, the `get_balance_at_block` function returns a tuple `(block_num, balance)`. These tuples are collected in the `results` list. After all futures are completed, the `results` list is sorted by block number (the first element of each tuple), and the results are printed in order. ## Handling errors and exceptions in multithreaded architecture As with any code, errors and exceptions can occur in a multithreaded architecture. Handling these errors properly is crucial for ensuring the robustness and reliability of your code. In a multithreaded context, error handling can be slightly more complex because errors can occur in multiple threads simultaneously. Here are some common errors and exceptions that you might encounter and how to handle them: 1. Rate limit errors. Since multithreading allows you to make more requests quickly, you might encounter a rate limit. Chainstack does not pose limits, but you might get errors due to too many requests, so it is recommended to stay below 3,000 RPS. Rate limit errors can be handled by catching the appropriate exception in a `try`/`except` block. You should also include logic to delay or reduce the rate of your requests if you encounter a rate limit error. 2. Thread errors. These are errors related to the management of threads, such as creating too many threads or problems with thread synchronization. If too many threads are created, you might run into system limits or performance issues due to the overhead of managing a large number of threads. 3. Synchronization errors. These occur when multiple threads try to modify a shared resource at the same time, leading to race conditions, inconsistent results, or even data corruption. While these errors are less likely to occur in the context of Web3 requests, they're an important consideration when working with multithreaded applications. 4. Unhandled exceptions in threads. If an unhandled exception occurs in a thread, it can cause the thread to terminate unexpectedly. This might lead to resource leaks if the thread doesn't clean up its resources before terminating, and it can also lead to incomplete results if the thread doesn't finish its task. Handling these errors and exceptions properly is crucial for ensuring the robustness and reliability of your multithreaded application. Further, let us discuss some strategies. **Handling thread errors**. You can handle thread errors by limiting the number of threads that your program creates and by catching and handling any exceptions that occur when creating or managing threads. For example: ```python Python theme={"system"} import asyncio from concurrent.futures import ThreadPoolExecutor from web3 import Web3 import time web3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) address = "0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326" start_block = web3.eth.block_number end_block = start_block - 500 max_workers = 100 def get_balance_at_block(block_num): try: balance = web3.eth.get_balance(address, block_identifier=block_num) print(f"Balance at block {block_num}: {web3.from_wei(balance, 'ether')} ETH") except Exception as e: print(f"Error occurred while getting balance at block {block_num}: {e}") async def main(): with ThreadPoolExecutor(max_workers=max_workers) as executor: loop = asyncio.get_event_loop() futures = [ loop.run_in_executor( executor, get_balance_at_block, block_num ) for block_num in range(start_block, end_block, -1) ] for future in asyncio.as_completed(futures): try: # This will raise an exception if the thread raised an exception result = await future except Exception as e: print(f"Error occurred in thread: {e}") loop = asyncio.get_event_loop() start = time.time() loop.run_until_complete(main()) print(f"Time taken: {time.time() - start}") ``` **Handling synchronization errors**. If your threads are sharing resources, you should use synchronization primitives like locks, semaphores, or condition variables to ensure that only one thread modifies the shared resource at a time. **Handling unhandled exceptions in threads**. To handle unhandled exceptions in threads, you can catch and handle exceptions within each thread, or you can use the `Future.result()` method, which reraises any exception that occurred in the thread. If an exception occurs in a thread, it's stored in the `Future` object for that thread, and calling `Future.result()` will raise that exception in the main thread. This allows you to handle the exception in the main thread and decide how to proceed. ## Best practices for multithreaded Web3 requests When implementing multithreading, you can follow several best practices to ensure your application is efficient, robust, and easy to maintain. * **Choose an appropriate number of threads**. The optimal number of threads depends on a variety of factors, including the nature of the tasks (I/O-bound vs CPU-bound), the specifications of the system (number of CPUs, memory), and the server's capacity to handle concurrent requests. Too many threads can lead to excessive context switching, memory usage, and potentially hitting rate limits on the Ethereum node. Too few threads might not fully utilize the available resources. Typically, for I/O-bound tasks like network requests, a higher number of threads can be beneficial. Start with a reasonable number and adjust based on performance observations and system characteristics. * **Handle exceptions properly**. Unhandled exceptions in a thread can cause the thread to terminate unexpectedly, which can lead to unpredictable behavior and resource leaks. Use try/except blocks to catch and handle exceptions in each thread. Also, consider using the `Future.result()` method, which reraises any exception that occurred in the thread, allowing you to handle the exception in the main thread. * **Manage thread lifecycles**. Be sure to clean up after your threads when they're done, especially for long-running applications. Using a context manager (the `with` keyword) with `ThreadPoolExecutor` automatically starts and stops the threads. * **Avoid shared state when possible**. Shared state can lead to race conditions and make your code harder to reason. Whenever possible, design your threads to be independent of each other. * **Use appropriate synchronization primitives**. If you must use shared state, use locks, semaphores, or other synchronization primitives to ensure that threads don't interfere. However, be aware that improper use of synchronization primitives can lead to deadlocks and other issues. * **Don't ignore the GIL**. Python's global interpreter lock can limit the performance benefits of multithreading for CPU-bound tasks. However, for I/O-bound tasks like making web requests, multithreading can still provide significant performance benefits. * **Respect the server's limits**. Be aware that there may be limits on the number of requests you can make per minute or day to a server. Making too many requests in a short period of time may lead to your requests being throttled or your IP being blocked. ## Conclusion Multithreading is a powerful technique that can help you make the most efficient use of your resources when making blockchain requests. By making multiple requests concurrently, you can significantly speed up your scripts and get more work done in the same amount of time. In this guide, we've explored how to set up your Python environment for multithreading, create a simple script using both sequential and multithreaded approaches, and handle errors and exceptions in multithreaded code. We've also discussed some best practices for writing multithreaded code, such as choosing an appropriate number of threads, managing thread lifecycles, and handling shared states properly. However, it's important to remember that multithreading comes with its own set of challenges. Managing multiple threads can add complexity to your code, and you need to be careful to avoid issues like race conditions and resource leaks. Also, you need to be aware of the server's capacity and respect any rate limits that might be in place. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Mastering Web3 LLMs and AI use Source: https://docs.chainstack.com/docs/mastering-web3-llms-and-ai-use Explore guides on using large language models with blockchain data, building AI trading agents, and integrating MCP servers for Web3 AI development. Check the sections on the left pane. [YouTube video player](https://www.youtube.com/embed/2wQn_nvAQp8?si=7XbLziOdDaFAx9az) > [!NOTE] > > Project repository: [Web3 AI trading agent](https://github.com/chainstacklabs/web3-ai-trading-agent) # MegaETH methods Source: https://docs.chainstack.com/docs/megaeth-methods Complete reference of supported MegaETH JSON-RPC methods on Chainstack nodes, including eth, debug, and trace namespace availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blobBaseFee | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_callBundle | | | | eth\_callMany | | | | eth\_chainId | | | | eth\_createAccessList | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getHeaderByHash | | | | eth\_getHeaderByNumber | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getRawTransactionByBlockHashAndIndex | | | | eth\_getRawTransactionByBlockNumberAndIndex | | | | eth\_getRawTransactionByHash | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleByBlockHashAndIndex | | | | eth\_getUncleByBlockNumberAndIndex | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_hashrate | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_protocolVersion | | | | eth\_sendRawTransaction | | | | eth\_sendTransaction | | | | eth\_sign | | | | eth\_signTransaction | | | | eth\_signTypedData | | | | eth\_simulateV1 | | | | eth\_submitHashrate | | | | eth\_subscribe | | WebSocket only | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | WebSocket only | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_accountRange | | | | debug\_dumpBlock | | | | debug\_getBadBlocks | | | | debug\_getModifiedAccountsByHash | | | | debug\_getModifiedAccountsByNumber | | | | debug\_getRawBlock | | | | debug\_getRawHeader | | | | debug\_getRawReceipts | | | | debug\_getRawTransaction | | | | debug\_getRawTransactions | | | | debug\_intermediateRoots | | | | debug\_preimage | | | | debug\_setHead | | | | debug\_storageRangeAt | | | | debug\_traceBadBlock | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceCallMany | | | | debug\_traceTransaction | | | | trace\_block | | | | trace\_blockOpcodeGas | | | | trace\_call | | | | trace\_callMany | | | | trace\_filter | | | | trace\_get | | | | trace\_rawTransaction | | | | trace\_replayBlockTransactions | | | | trace\_replayTransaction | | | | trace\_transaction | | | | trace\_transactionOpcodeGas | | | | admin\_addPeer | | | | admin\_nodeInfo | | | | admin\_peers | | | | admin\_removePeer | | | | txpool\_content | | Available on Dedicated Nodes | | txpool\_contentFrom | | Available on Dedicated Nodes | | txpool\_inspect | | Available on Dedicated Nodes | | txpool\_status | | Available on Dedicated Nodes | # MegaETH tooling Source: https://docs.chainstack.com/docs/megaeth-tooling Complete MegaETH tooling guide for Chainstack nodes. Use MetaMask, Hardhat, ethers.js, viem, Foundry, and more to build dApps on MegaETH blockchain. Get started with a [reliable MegaETH RPC endpoint](https://chainstack.com/build-better-with-megaeth/) to use the tools below. > [!NOTE] > > MegaETH is EVM compatible, so standard Ethereum development tools work. Note that MegaETH uses a multidimensional gas model with separate compute and storage gas components. Use the RPC endpoint for gas estimation rather than local simulation tools, as they may undercount gas. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Rabby [Rabby](https://rabby.io/) is a non-custodial multi-chain wallet with network auto-detection and transaction risk alerts. 1. Install [Rabby](https://rabby.io/) browser extension. 2. In Rabby, navigate to **Settings** > **Add Custom Network**. 3. Add your Chainstack endpoint with chain ID `4326`. If Rabby already supports the network, use **Settings** > **Modify RPC URL** instead. See [Adding a custom RPC endpoint to Rabby](/docs/adding-a-custom-rpc-endpoint-to-rabby). ## Trust Wallet [Trust Wallet](https://trustwallet.com/) has native MegaETH network support. To use your Chainstack endpoint, open the wallet and navigate to **Settings** > **Network** to add a custom network with your Chainstack RPC endpoint. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your MegaETH nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.8.19", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. For a detailed tutorial with Remix IDE, see [Trust fund account with Remix](/docs/ethereum-tutorial-trust-fund-account-with-remix). ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and MegaETH nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number: ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and MegaETH nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: "YOUR_CHAINSTACK_ENDPOINT", }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: "YOUR_CHAINSTACK_ENDPOINT", user: "USERNAME", password: "PASSWORD", }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — MegaETH network ID: * Mainnet: `4326` * Testnet: `6342` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider( "YOUR_CHAINSTACK_ENDPOINT", NETWORK_ID ); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — MegaETH network ID: * Mainnet: `4326` * Testnet: `6342` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## viem Build DApps using [viem](https://viem.sh/) and MegaETH nodes deployed with Chainstack. 1. Install [viem](https://www.npmjs.com/package/viem). 2. Connect over HTTP or WebSocket. ### HTTP Use the `createPublicClient` function to connect to your node endpoint and get the latest block number: ```javascript Javascript theme={"system"} import { createPublicClient, http } from 'viem' const client = createPublicClient({ transport: http('YOUR_CHAINSTACK_ENDPOINT') }) const blockNumber = await client.getBlockNumber() console.log(blockNumber) ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password ### WebSocket Use the `createPublicClient` function with WebSocket transport: ```javascript Javascript theme={"system"} import { createPublicClient, webSocket } from 'viem' const client = createPublicClient({ transport: webSocket('YOUR_CHAINSTACK_ENDPOINT') }) const blockNumber = await client.getBlockNumber() console.log(blockNumber) ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node WSS endpoint protected either with the key or password ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. > [!WARNING] > > MegaETH's multidimensional gas model means local gas estimation in Forge may undercount. Use `--skip-simulation` with `forge script` and manually specify a gas limit, or rely on the RPC endpoint for estimation. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```bash Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```bash Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Mempool configurations Source: https://docs.chainstack.com/docs/mempool-configuration Configure mempool access on your Chainstack nodes for pending transaction monitoring, MEV research, and real-time transaction pool data on EVM networks. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Mempool configurations vary wildly across different protocols & node client configurations. The table here is a maintained reference of mempool configurations & specifics across all the protocols that Chainstack supports. ## What gives you mempool access Two different things get called mempool access, and they have different requirements. On the EVM chains with a public mempool — Ethereum, Polygon, and BNB Smart Chain: | What you want | Methods | Global Nodes and Trader Nodes | Dedicated Nodes | | - | - | - | - | | Watch pending transactions as they arrive | `eth_subscribe("newPendingTransactions")`, `eth_newPendingTransactionFilter` | Available | Available | | Read the transaction pool contents | `txpool_content`, `txpool_contentFrom`, `txpool_inspect` | Not available | Available on request | | Count pending and queued transactions | `txpool_status` | Depends on the network and mode | Available on request | For a live feed of pending transactions, any node type is enough, in the full mode or the archive mode. The feed is the node's view of the public mempool — transactions that reached it over the p2p network, not only the ones you submitted through it. No node sees every pending transaction, as each node's view depends on its peers. Global Nodes and Trader Nodes block `txpool_content`, `txpool_contentFrom`, and `txpool_inspect` on EVM networks, in every mode and on every plan. A call returns error code `-32601`: ```json theme={"system"} {"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"the method txpool_content is not supported yet"}} ``` `txpool_status` is not blocked. It returns the pending and queued transaction counts on nodes whose client serves the `txpool` namespace, which varies by network and mode. To read the pool contents, deploy a [Dedicated Node](/docs/dedicated-node) and ask for the `txpool_*` namespace when you deploy. It is a [customization](/docs/features-availability-across-subscription-plans#node-customization) that works in the full mode and the archive mode. `txpool_*` is also a separate namespace from `debug_*` and `trace_*`, and each one has its own gate: * `txpool_*` — the node type decides whether you can read the pool contents, not your plan. * `debug_*` and `trace_*` — your [plan](/docs/features-availability-across-subscription-plans#debug-and-trace-apis) enables them. They do not bring `txpool_*` with them. > [!WARNING] > > On Arc, the node software blocks pending-transaction visibility: `eth_newPendingTransactionFilter` and `eth_subscribe("newPendingTransactions")` return `-32001`, and both `eth_getBlockByNumber("pending")` and `eth_getTransactionBySenderAndNonce` return `null`. See [Arc methods](/docs/arc-methods). > [!NOTE] > > On protocols with no public mempool, pending-transaction methods like `eth_newPendingTransactionFilter` and `eth_subscribe("newPendingTransactions")` return empty by design — pending transactions are visible only to the sequencer. On Optimism Mainnet, these methods report transactions as the sequencer includes them in subblocks. To follow transactions in real time, use Flashblocks on Base and Subblocks on Optimism — see the how-to guides ([Base](/docs/base-streaming-transactions-flashblocks), [Optimism](/docs/optimism-streaming-transactions-flashblocks)) or how they work ([Flashblocks on Base](/docs/flashblocks-on-base), [Subblocks on Optimism](/docs/flashblocks-on-optimism)). On X Layer Mainnet, see [Flashblocks on X Layer Mainnet](/docs/xlayer-tooling#flashblocks-on-x-layer-mainnet). > [!WARNING] > > A subscription that is accepted is not a subscription that delivers. On **Ink**, **World Chain**, **X Layer**, and **Stable**, `eth_subscribe("newPendingTransactions")` and `eth_newPendingTransactionFilter` both succeed and hand back an ID, then deliver nothing. > > Ink, World Chain, and X Layer are OP Stack chains, so their pending transactions sit with the sequencer. On World Chain the `txpool_*` methods are not served at all. On Ink and X Layer, `txpool_status` responds but always reads empty, and the other `txpool_*` methods return `-32601`. On Stable, `txpool_status` responds, but it reported `pending: 0x0, queued: 0x0` in every one of 30 samples taken over two minutes while 52 transactions confirmed in new blocks over the same window — the pool is not exposed through the EVM JSON-RPC layer. Do not build a pending-transaction feed on any of these chains. The table structure: * Protocol — protocol name. * Protocol availability — details of the mempool availability on the protocol level. * Chainstack availability — what you need to deploy for the protocol's pool-inspection methods on Chainstack. * Client configuration — the default node client configuration for the mempool as deployed at Chainstack. * Example — a simple curl example. Remember to replace YOUR\_CHAINSTACK\_NODE with your Chainstack node endpoint for that particular protocol. Remember that whatever the default configuration, we can always [customize](/docs/features-availability-across-subscription-plans#node-customization) it for you on a [Dedicated Node](/docs/dedicated-node) . | Protocol | Protocol availability | Chainstack availability | Client configuration | Example | | - | - | - | - | - | | Ethereum | Available to everyone | On a [Dedicated Node](/docs/dedicated-node) | 4096 pending transactions | [Ethereum txpool\_content](#ethereum-txpool_content) | | Polygon | Available to everyone | On a [Dedicated Node](/docs/dedicated-node) | 4096 pending transactions | [Polygon txpool\_content](#polygon-txpool_content) | | BNB Smart Chain | Available to everyone | On a [Dedicated Node](/docs/dedicated-node) | 4096 pending transactions | [BSC txpool\_content](#bsc-txpool_content) | | Arc | Available, but the node blocks pending-transaction visibility | `txpool_status` only | Not published | [Arc txpool\_status](/reference/arc-txpool-status) | | Base | [Private to Sequencer](https://github.com/base/node/issues/78). | N/A | N/A | N/A | | World Chain | [Private to Sequencer](https://docs.optimism.io/stack/differences#mempool-rules) — OP Stack | N/A — the `txpool_*` methods are not served | N/A | N/A | | Ink | [Private to Sequencer](https://docs.optimism.io/stack/differences#mempool-rules) — OP Stack | `txpool_status` only, and it always reads empty | N/A | `txpool_status` | | X Layer | [Private to Sequencer](https://docs.optimism.io/stack/differences#mempool-rules) — OP Stack | `txpool_status` only, and it always reads empty | N/A | `txpool_status` | | Stable | Not exposed through the EVM JSON-RPC — see [Stable methods](/docs/stable-methods#pending-transactions-are-accepted-but-never-delivered) | `txpool_status` only, and it always reads empty | N/A | `txpool_status` | | Avalanche | [Available only to validators](https://support.avax.network/en/articles/6158842-nodes-faq). | Not available on Chainstack | N/A | N/A | | TON | External messages are available in mempool | Available | | | | Arbitrum | No mempool. Only [Sequencer FIFO](https://docs.arbitrum.io/learn-more/faq#does-arbitrum-have-a-mempool). | N/A | N/A | N/A | | Elysium | No public mempool. [Single sequencer](https://elysium.kinetiq.xyz/docs/security-and-data-availability) | N/A | N/A | N/A | | zkSync Era | Sequencer only | N/A | N/A | N/A | | Optimism | [Private to Sequencer](https://docs.optimism.io/stack/differences#mempool-rules) | N/A | N/A | N/A | | Solana | [No mempool](https://docs.solana.com/integrations/retrying-transactions) | N/A | N/A | N/A | | Scroll | [Publicly available through Sequencer](https://docs.scroll.io/en/technology/chain/rollup/). | N/A | N/A | N/A | | Ronin | Available to everyone | Not available on Chainstack | N/A | N/A | | Aptos | [Available to everyone](https://aptos.dev/concepts/validator-nodes#mempool) but there is no [transaction stream](https://github.com/aptos-labs/aptos-core/issues/5200) | [Full or archive node deployment](/docs/protocols-modes-and-types) | N/A | N/A | | Gnosis Chain | Available to everyone | On a [Dedicated Node](/docs/dedicated-node). | Any configuration | [Gnosis txpool\_content](#gnosis-txpool_content) | | Cronos | No mempool. [Only Sequencer FIFO](https://docs.cronos.org/cronos-chain-protocol/cronos-general-faq#if-i-increase-the-gas-price-does-it-help-to-speed-up-my-transaction). | N/A | N/A | N/A | | Filecoin | Available to everyone | [Full node deployment](/docs/protocols-modes-and-types) | 20,000 pending messages | [Filecoin MpoolPending](#filecoin-mpoolpending) | | Sonic | Available to everyone | On a [Dedicated Node](/docs/dedicated-node) | 4096 pending transactions | `txpool_*` | | Fantom | Available to everyone | On a [Dedicated Node](/docs/dedicated-node) | 4096 pending transactions | `txpool_*` | | TRON | Available to everyone | On a [Dedicated Node](/docs/dedicated-node) | 4096 pending transactions | `txpool_*` | | Starknet | Private to Sequencer | N/A | N/A | N/A | | Bitcoin | Available to everyone | [Full node deployment](/docs/protocols-modes-and-types) | mempool size: 300 MB mempool tx expiry: 336 hours (14 days) | [Bitcoin getrawmempool](#bitcoin-getrawmempool) | | Zcash | Available to everyone | [Full node deployment](/docs/protocols-modes-and-types) | Not published | `getrawmempool`, `getmempoolinfo` | ## Ethereum txpool\_content ```bash Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","method":"txpool_content","params":[],"id":1}' \ YOUR_CHAINSTACK_NODE ``` ## Polygon txpool\_content ```bash Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","method":"txpool_content","params":[],"id":1}' \ YOUR_CHAINSTACK_NODE ``` ## BSC txpool\_content ```bash Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","method":"txpool_content","params":[],"id":1}' \ YOUR_CHAINSTACK_NODE ``` ## Gnosis txpool\_content ```bash Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","method":"txpool_content","params":[],"id":1}' \ YOUR_CHAINSTACK_NODE ``` ## Filecoin MpoolPending ```bash Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "Filecoin.MpoolPending", "params": [null], "id": 1 }' \ YOUR_CHAINSTACK_NODE ``` ## Fantom txpool\_content ```bash Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","method":"txpool_content","params":[],"id":1}' \ YOUR_CHAINSTACK_NODE ``` ## Bitcoin getrawmempool ```bash Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc": "2.0", "id":"curltest", "method": "getrawmempool", "params": [] }' \ YOUR_CHAINSTACK_NODE ``` > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # MESC (Multi-Endpoint Shared Configuration) setup Source: https://docs.chainstack.com/docs/mesc-and-chainstack Set up Multi-Endpoint Shared Configuration to manage multiple Chainstack RPC endpoints from a single config file across all your development tools. **TLDR:** * `getrawtransaction` retrieves raw or decoded Bitcoin transaction data by `txid`, optionally with a specific block hash. * Verbose mode (`true`) returns a detailed JSON, while omitting it yields the raw hex string. * Transactions typically remain in the mempool for around 14 days; dropped transactions require resubmission with adequate fees. * Bitcoin Core runs many operations single-threaded, so complex or parallel requests may slow under heavy load. ## Main article In Web3 and blockchain, we get a new chain or a new RPC provider almost daily, and the ability to interact with multiple blockchain networks through RPC endpoints is crucial for developers, applications, and services. The Multi-Endpoint Shared Configuration (MESC) emerges as a pivotal standard in this ecosystem, addressing the pain points of configuring and managing RPC endpoints across diverse tools and platforms. MESC introduces a standard for configuring RPC endpoints in DApps, emphasizing the ease of sharing configuration data and managing many endpoints. It focuses on making RPC configuration sharing across tools, languages, and environments straightforward, alongside simplifying the management of numerous RPC endpoints. This guide will teach us how to use the MESC CLI using Chainstack endpoints. ## Key components of MESC MESC represents a significant leap toward simplifying the complexities developers face daily. Let's get into the core aspects that make MESC an indispensable tool for developers navigating the Web3 space. ### Typical usage scenario Consider a decentralized voting application, `VoteChain`, designed to operate across multiple blockchain networks. This application needs to interact with different RPC endpoints to submit votes or query vote counts. The traditional method requires hardcoding or manually specifying the RPC URL for each network, which is error-prone and cumbersome, especially when aiming to support numerous networks. ### Common interface One of MESC's standout features is its common interface, implemented across different programming languages and platforms. This uniformity allows developers to interact with the MESC configuration seamlessly, regardless of their development environment. Whether it's Python, Rust, or any other supported language, the interface remains consistent, ensuring developers can easily switch between tools and languages without relearning how to access and manage their RPC configurations. ### MESC specifications MESC employs a detailed schema to encapsulate all necessary information for RPC configurations, comprising three key-value data schemas: `RpcConfig`, `Endpoint`, and `Profile`. Each schema is meticulously designed to cover all aspects of RPC configuration, from global settings to specific endpoint details and user profiles. * **RpcConfig schema**: The backbone of MESC, detailing the version, default endpoints, and default networks, mapping chain IDs to endpoint names, network names to chain IDs, and specific endpoint configurations. It also includes profiles for user-specific settings and global metadata for additional information. * **Endpoint schema**: Specifies individual RPC endpoints, including their names, URLs, associated chain IDs, and any metadata related to the endpoint. This schema ensures each endpoint is uniquely identifiable and adequately linked to its network. * **Profile schema**: Users can define custom configurations, including a default endpoint, network defaults, and profile-specific metadata. Enabling or disabling MESC for specific profiles allows users to manage their RPC connections. The specification enforces strict requirements to maintain integrity and consistency within the configuration. All keys within the `RpcConfig` and `Endpoint` schemas are mandatory, with no allowance for unspecified keys except within metadata sections. It mandates that endpoint names referenced in the configuration must correspond to actual endpoints defined within the schema, ensuring referential integrity. > [!NOTE] > > Find the full [spefications](https://github.com/paradigmxyz/mesc/blob/main/SPECIFICATION.md) in the MESC repository. #### MESC's role in simplifying configurations By leveraging MESC, `VoteChain` can dynamically select the appropriate RPC endpoint based on user input or application context, such as the specific blockchain network for casting votes. This is made possible through MESC's ability to interpret various forms of identifiers: 1. **Direct URL**: For direct access to a specific RPC endpoint. 2. **Network name**: Specify the blockchain network using familiar names. 3. **Chain ID**: Enabling selection based on the unique identifier of the blockchain. 4. **Custom profile**: Supporting configurations for various deployment scenarios or user preferences. This functionality allows `VoteChain` to be highly adaptable and user-friendly, significantly reducing the barrier to entry for users unfamiliar with the specific details of blockchain networks. #### Hypothetical implementation To bring this scenario to life, imagine `VoteChain` incorporates a feature allowing users to select the blockchain network for their voting campaign directly from the application interface. Behind the scenes, `VoteChain` uses MESC to manage and resolve the appropriate RPC endpoints: **For user-friendly network selection**: Users can choose a network by name through the `VoteChain` interface. The application then queries the MESC configuration to retrieve the corresponding RPC URL, ensuring seamless interaction with the chosen network. #### Pseudo code example ```pseudo pseudo theme={"system"} # Assume user_input is the network name or a custom endpoint identifier user_input = get_user_input() # MESC resolves the appropriate RPC endpoint if is_custom_endpoint(user_input): endpoint = mesc.get_endpoint_by_name(user_input) else: endpoint = mesc.get_endpoint_by_network_name(user_input) # Use the resolved endpoint URL in VoteChain rpc_url = endpoint.url use_rpc_endpoint(rpc_url) ``` ## Get started with the MESC CLI Now that we understand MESC well and what it's trying to solve, let's learn how to use the MESC CLI. ### Prerequisites Before we install the MESC CLI, ensuring your development environment is properly set up is essential. The MESC CLI is developed in Rust; having Rust and Cargo (the Rust package manager) installed on your system is a prerequisite. To install Rust and Cargo, execute the following command in your terminal: ```shell Shell theme={"system"} curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` This command downloads and runs the Rust installation script, setting up Rust and Cargo on your system. #### Get Chainstack endpoints To configure the MESC CLI you'll also need some endpoints. Follow these steps to get them from Chainstack; remember, you can use any EVM-compatible endpoint. > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) ### Instal the MESC CLI With Rust and Cargo ready, installing the MESC CLI is straightforward. Use Cargo to install the MESC CLI with the following command: ```shell Shell theme={"system"} cargo install mesc_cli ``` This command fetches the latest version of the MESC CLI from [crates.io](https://crates.io/crates/mesc_cli) (the Rust package registry) and installs it on your system. > [!NOTE] > > For additional installation methods and further information, consult the [MESC User Guide](https://paradigmxyz.github.io/mesc/cli.html#installation). ### Understand `mesc.json` configuration After installing the MESC CLI, the next step is configuring your MESC environment. This is where `mesc.json` comes into play. `mesc.json` is a JSON-formatted configuration file MESC utilizes to manage your RPC endpoints. It is the central repository for all your endpoint configurations, including the default endpoint, network-specific defaults, and any custom profiles you may need for different projects or environments. The `mesc.json` file is structured to include vital details such as the version of MESC being used (`mesc_version`), a list of RPC endpoints (`endpoints`), and mappings for network defaults (`network_defaults`) and network names (`network_names`). This structured approach allows for a high degree of flexibility and control over how your applications interact with blockchain networks. ### Create `mesc.json` There are a few ways to manage the `mesc.json` file. The first method we'll explore is creating one directly from the MESC CLI. Open a terminal on your system and run: ```shell Shell theme={"system"} mesc setup ``` This will start the MESC CLI and prompt you to set up your environment. Select `1) File (recommended)` and follow the instructions to add your endpoints and create `mesc.json`. Here is how the process will look like in the console: ```shell Shell theme={"system"} MESC is disabled because no MESC env vars are set To enable MESC, set one of the MESC env vars Do you want to store your MESC config in a file or in an env var? File (recommended) Where to save MESC config file? ~/mesc.json Need to set MESC_PATH in order to use this file MESC_PATH can be automatically updated for 2 shell config files: 1. /Users/name/.bashrc 2. /Users/name/.profile What do you want to do? Edit these files automatically (recommended) Edited 2 config files Config file does not exist, do you want to create one? Yes Created blank config at /Users/name/mesc.json Current config has 0 endpoints and 0 profiles What do you want to do? Add new endpoint New endpoint URL? https://ethereum-mainnet.core.chainstack.com/AUTH Querying chain id... Using chain_id 1 New endpoint name? eth_chainstack New endpoint added What do you want to do? Add new endpoint New endpoint URL? https://avalanche-mainnet.core.chainstack.com/ext/bc/C/rpc/AUTH Querying chain id... Using chain_id 43114 New endpoint name? avax_chainstack New endpoint added What do you want to do? Exit and save changes config written to /Users/name/mesc.json Shell config files were modified. Restart shell to load these files. ``` The CLI leads you through the creation of the `mesc.json` file, which is saved by default in the root user directory. Additionally, it automatically sets the `MESC_PATH` environment variable in both `.bashrc` and `.profile`. > [!NOTE] > > `.bashrc` and `.profile` are shell scripts for configuring user environment variables and settings. The `mesc.json` file created will look like this: ```Json JSON theme={"system"} { "mesc_version": "0.2.0", "default_endpoint": null, "endpoints": { "avax_chainstack": { "name": "avax_chainstack", "url": "https://avalanche-mainnet.core.chainstack.com/ext/bc/C/rpc/AUTH", "chain_id": "43114", "endpoint_metadata": {} }, "eth_chainstack": { "name": "eth_chainstack", "url": "https://ethereum-mainnet.core.chainstack.com/AUTH", "chain_id": "1", "endpoint_metadata": {} } }, "network_defaults": {}, "network_names": {}, "profiles": {}, "global_metadata": {} } ``` ### Edit `mesc.json` We have a basic MESC configuration file; let's customize it a bit, adding default networks and default endpoints. In this example, I created the file with two Chainstack endpoints, Ethereum mainnet and Avalanche mainnet. Let's assume I want to default to Ethereum; we can configure the file as follows: ``` { "mesc_version": "0.2.0", "default_endpoint": "eth_chainstack", "endpoints": { "avax_chainstack": { "name": "avax_chainstack", "url": "https://avalanche-mainnet.core.chainstack.com/ext/bc/C/rpc/AUTH", "chain_id": "43114", "endpoint_metadata": { "host": "chainstack", "node_client": "avalanchego/v0.12.7", "namespaces": ["eth", "net", "web3"], "location": "Global" } }, "eth_chainstack": { "name": "eth_chainstack", "url": "https://ethereum-mainnet.core.chainstack.com/AUTH", "chain_id": "1", "endpoint_metadata": { "host": "chainstack", "node_client": "Geth/v1.13.2-stable/linux-amd64/go1.21.1", "namespaces": ["eth", "net", "web3", "debug", "trace"], "location": "Global" } } }, "network_defaults": { "1": "eth_chainstack", "43114": "avax_chainstack" }, "network_names": { "eth_mainnet" : "1", "avax_mainnet": "43114" }, "profiles": { "my_special_tool": { "name": "my_special_tool", "default_endpoint": "eth_chainstack", "network_defaults": { "1": "eth_chainstack" }, "profile_metadata": {}, "use_mesc": true } }, "global_metadata": { "conceal": true } } ``` We added metadata to the endpoints, network names, and a profile here. With extra metadata and configurations, we are ready to test it and learn how to use the MESC CLI to send requests without ever touching the endpoints again. Note that you can also add metadata directly from the setup interface. > [!NOTE] > > Learn more about the MESC configuration and metadata options in the [MESC User Guide](https://paradigmxyz.github.io/mesc/using_mesc.html) ### MESC CLI: Overview of basic commands Let's get into the essentials with the MESC CLI. Starting with `mesc --help`, you'll uncover a comprehensive list of commands and their structure alongside valuable help topics for an in-depth understanding of specific commands or steps. > [!NOTE] > > If the `MESC_PATH` environment variable isn't correctly configured, you might encounter issues. Use `mesc help setup` for guidance on proper setup. In case you've stored the `mesc.json` file in its default location, setting the environment variable is straightforward: > > Shell > > ``` > export MESC_PATH=~/mesc.json > ``` #### Explore available endpoints Use the `mesc ls` command to display a detailed list of available endpoints. This command also serves as a quick check to ensure MESC is correctly set up, indicating any setup errors directly: ```shell Shell theme={"system"} mesc ls ``` Expect an output similar to the following, listing configured endpoints: ```shell Shell theme={"system"} endpoint │ network │ url ─────────────────┼───────────┼────────── eth_chainstack │ 1 │ ******** avax_chainstack │ 43114 │ ******** ``` You can also use the `mesc endpoint` command to print the full info about endpoints. ```shell Shell theme={"system"} mesc endpoint ``` This command will print the data about the default endpoint: ```shell Shell theme={"system"} Endpoint: eth_chainstack - url: https://ethereum-mainnet.core.chainstack.com/AUTH - chain_id: 1 - metadata: {"location": String("Global"), "node_client": String("Geth/v1.13.2-stable/linux-amd64/go1.21.1"), "namespaces": Array [String("eth"), String("net"), String("web3")], "host": String("chainstack")} ``` Add the name of the endpoint to print data about a specific endpoint: ```shell Shell theme={"system"} mesc endpoint avax_chainstack ``` #### Verify endpoint connectivity To test the connectivity and performance of the endpoints, use the `mesc ping` command. This command not only verifies the operational status of each endpoint but also provides metrics such as latency and the latest block number: ```shell Shell theme={"system"} mesc ping ``` Sample output showcasing the performance metrics: ```shell Shell theme={"system"} endpoint │ network │ latency │ block ─────────────────┼───────────┼───────────┼────────── avax_chainstack │ 43114 │ 564.4 │ 41465592 eth_chainstack │ 1 │ 574.9 │ 19192953 2 endpoints responded without error Additional fields available: ip, client, location ``` To retrieve more specific data, such as the client information, append the desired field to the ping command: ```shell Shell theme={"system"} mesc ping client ``` In scenarios where endpoints fail to respond, consider specifying a manual timeout to differentiate between genuinely offline endpoints and those that merely exceed the default timeout period: ```shell Shell theme={"system"} mesc ping --timeout 5 ``` ## Send requests using MESC At this point, MESC is fully set up, and we can start using it to send RPC requests. The most straightforward way to test and explore its features is by sending cURL requests. The power of MESC lies in the, as we discussed earlier, MESC allows us to access endpoints intuitively like **Endpoint Name** or **Network Name**. This is the structure to send a basic cURL request: ```shell Shell theme={"system"} curl $(mesc url) --data RPC_REQUEST ``` `$(mesc url)` will use the default endpoint, in our case `eth_chainstack`, let's fetch the chain ID, for instance: ```shell Shell theme={"system"} curl $(mesc url) --data '{"jsonrpc": "2.0", "method": "eth_chainId", "id": "mesc_test"}' ``` Which will return: ```shell Shell theme={"system"} {"jsonrpc":"2.0","id":"mesc_test","result":"0x1"} ``` We can confirm that it used the Ethereum endpoint as the chain ID returned is `1`. To use the Avalanche endpoint, simply add its name to the command: ```shell Shell theme={"system"} curl $(mesc url avax_chainstack) --data '{"jsonrpc": "2.0", "method": "eth_chainId", "id": "mesc_test"}' ``` And in this case, we'll get Avalanche chain ID: ```shell Shell theme={"system"} {"jsonrpc":"2.0","id":"mesc_test","result":"0xa86a"} ``` > [!NOTE] > > You can use the [Chainstack EVM-Knife](https://web3tools.chainstacklabs.com/hexadecimal-decimal) to convert hexadecimal values to decimal. > > `0xa86a` is `43114` as decimal, which is Avalanche chain ID. Using the endpoint's name is only one way to use MESC; you can also pick the endpoint by a network default, giving the chain ID: ```shell Shell theme={"system"} curl $(mesc url 43114) --data '{"jsonrpc": "2.0", "method": "eth_chainId", "id": "mesc_test"}' ``` Or by naming a network directly: ```shell Shell theme={"system"} curl $(mesc url avax_mainnet) --data '{"jsonrpc": "2.0", "method": "eth_chainId", "id": "mesc_test"}' ``` You can also invoke the default endpoint associated with a profile: ```shell Shell theme={"system"} curl $(mesc url --profile my_special_tool) --data '{"jsonrpc": "2.0", "method": "eth_chainId", "id": "mesc_test"}' ``` > [!NOTE] > > Check out the [MESC User Guide](https://paradigmxyz.github.io/mesc/cli.html)for more information. ## Conclusion In the rapidly evolving landscape of blockchain development, the Multi-Endpoint Shared Configuration (MESC) stands out as a critical innovation for simplifying RPC endpoint management. By centralizing and standardizing RPC configurations, MESC significantly reduces the complexity and enhances the security of interacting with blockchain networks. Developers gain the ability to manage endpoints efficiently without directly handling sensitive endpoint details, thereby reducing exposure to security risks. The streamlined process fosters better organization, collaboration, and productivity by allowing quick switches between networks and configurations with minimal overhead. # MEV protection Source: https://docs.chainstack.com/docs/mev-protection Enable MEV protection on your Chainstack Ethereum and BNB Smart Chain nodes to shield transactions from front-running, sandwich attacks, and other value extraction methods. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). MEV protection is a Marketplace [add-on](/docs/add-ons) you manage from your node's **Add-ons** tab. It is available on these networks and node types: | Network | Node types | | - | - | | Ethereum Mainnet | [Global Nodes](/docs/global-elastic-node), [Trader Nodes](/docs/trader-node) | | BNB Smart Chain Mainnet | Global Nodes, Trader Nodes | On Global Nodes, MEV protection is available on every plan, including the Developer plan. On L2 sequencer chains — Arbitrum, Base, Arc, and Robinhood Chain — transactions are not broadcast to a public mempool, so there is no third-party front-running to shield against. Use [optimized submission](/docs/optimized-submission) instead. ### How it works 1. You construct and sign your transaction as usual and send it through our endpoint. This is sent as `eth_sendRawTransaction`. 2. Instead of broadcasting to the global mempool, our proxy forwards it to the partner network for direct inclusion consideration by block builders. 3. A builder includes your transaction in a block. Once confirmed, it appears on-chain as normal. If you have the transaction hash, you can retrieve it from the private pool with the regular [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) but only through the same Chainstack node endpoint you used to submit the transaction. Once the transaction is in the block, you can retrieve it using all regular methods, of course: > [card] [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) > [card] [eth\_getTransactionReceipt](/reference/ethereum-gettransactionreceipt) > [card] [eth\_getTransactionByBlockHashAndIndex](/reference/ethereum-gettransactionbyblockhashandindex) > [card] [eth\_getTransactionByBlockNumberAndIndex](/reference/ethereum-gettransactionbyblocknumberandindex) and so on. ### What happens if private routing fails While MEV protection is on, Chainstack sends `eth_sendRawTransaction` only through the private route to block builders. If the private route fails, the request returns a JSON-RPC error, and Chainstack does not broadcast the transaction to the public mempool. Handle the error by sending the same signed transaction again. A signed transaction can be included in a block only once, so a retry can't execute it twice. MEV protection applies to the node's existing HTTPS and WSS endpoints and to every [authentication method](/docs/authentication-methods-for-different-scenarios) of the node, both the auth token in the URL and the username and password. The node has no separate protected endpoint. To send transactions through the public mempool, [disable MEV protection](#managing-mev-protection). ### Benefits * Reduced front-running risk — your transaction isn’t visible in the public mempool, minimizing the chance of adversary trades. * Reliable delivery — direct builder routes ensure predictable inclusion. ### Managing MEV protection MEV protection is on by default for a new node. When you deploy a node on Chainstack, MEV protection is already selected under **Add-ons** on the last step. To deploy without it, click **Disable** on the **MEV protection** card. A node you create with the [Platform API](/reference/chainstack-platform-api-v2-create-node) gets MEV protection installed automatically. The node's **Add-ons** tab shows whether MEV protection is on: its card shows **Active** with a **Disable** button when it is enabled, and **Enable** when it is not. The **Add-ons** tab is the only place that shows this status; no JSON-RPC method or Platform API field reports it. To enable MEV protection on a node that doesn't have it, open your node on Chainstack, select the **Add-ons** tab, click **Enable** on the **MEV protection** card, and complete the install. See [Install a node add-on](/docs/add-ons#install-a-node-add-on). To disable MEV protection, click **Disable** on the **MEV protection** card. The add-on is removed immediately, and your transactions route through the public mempool as usual. You can enable it again from the same tab. # Migrate to Chainstack with AI Source: https://docs.chainstack.com/docs/migrate-to-chainstack-with-ai AI agent runbook to migrate your project's blockchain RPC endpoints to Chainstack using the Chainstack MCP server with any coding agent. This page is both a migration guide and an executable runbook for AI coding agents. Paste the one-liner below into any AI coding agent — Claude Code, Codex CLI, Cursor, Windsurf, Gemini CLI, Antigravity — and the agent will fetch this page and follow it. ## Quick start Paste this into your AI agent: ```text theme={"system"} get docs.chainstack.com/docs/migrate-to-chainstack-with-ai.md ``` The agent fetches this page and walks through the migration in three phases: Assess → Plan → Implement. It stops after Plan and waits for your approval before touching the codebase. ## Tools used The runbook calls the [Chainstack MCP server](/docs/chainstack-mcp-server) — a single remote server that covers docs search, deployment options, node and project management, platform status, faucets, and pricing. Two URLs, two different purposes: * [mcp.chainstack.com](https://mcp.chainstack.com) — the agent onboarding page. Point any AI agent at this URL and it reads setup instructions, tool reference, and install options. * `https://mcp.chainstack.com/mcp` — the actual JSON-RPC 2.0 endpoint the agent calls once it's set up. > [!NOTE] > > You don't have to register the MCP server with your LLM client. The server accepts JSON-RPC 2.0 over plain HTTP, so the agent can call it directly — on demand, no persistent connection, no forced tool list in the context window. If you prefer a lighter install, the server ships a skill file at [mcp.chainstack.com/skill](https://mcp.chainstack.com/skill) that drops into `~/.claude/skills/chainstack/` (or the equivalent for your agent) and triggers HTTP calls only when relevant. Formal MCP registration works too — pick whichever fits your workflow. The Assess phase mostly runs on no-auth tools (`search_docs`, `get_doc_page`, `get_chainstack_pricing`, `contact_chainstack`). The `get_deployment_options`, Dry run, and Implement steps require a Chainstack [API key](https://console.chainstack.com/user/settings/api-keys). > [!TIP] > > **How to read this page.** When the runbook links to another Chainstack doc page (for example, `/docs/dedicated-node`), call `get_doc_page` to fetch it — don't follow the relative link directly. *** ## Agent instructions Migrate this project's blockchain RPC endpoints to Chainstack. ### Assess #### Coverage check 1. Scan the codebase for all blockchain RPC endpoint URLs that are not Chainstack. Check source code, environment variables, config files, and hardcoded URLs. Recognize provider-specific hosts — for example, Google Blockchain Node Engine (`*.blockchainnodeengine.com`) and Google Blockchain RPC endpoints, both of which Google shuts down on December 15, 2026. 2. For each endpoint found, identify: * The blockchain protocol and network (mainnet/testnet). * The RPC methods used (standard, debug/trace, archive queries like historical block lookups). * Whether WebSocket, gRPC, or Solana Geyser is needed. 3. Check what Chainstack supports. Do not use web search for Chainstack docs — call the MCP server directly: * `search_docs` — search for each protocol found in the codebase to check supported networks, node types, available API methods, and features like debug/trace or archive. * `get_doc_page` — get the full doc page for details on a specific chain. * `get_deployment_options` (API key required) — all supported chains, networks, node tiers, and features (full, archive, debug\_trace). 4. Report what's fully covered, what needs a specific node tier, what's unsupported, and any endpoints that are already dead or deprecated. > [!WARNING] > > Do not rely on training data for what Chainstack supports. Always verify against the Chainstack docs and `get_deployment_options`. Docs are the primary source of truth — they cover features, methods, node configurations, and console-only capabilities. `get_deployment_options` supplements docs with exact deployable chains, tiers, and features. If something can't be verified through either, flag it as uncertain and direct the user to [Chainstack support](https://support.chainstack.com/). For pricing questions, call `get_chainstack_pricing`. For unsupported chains that may be available as dedicated deployments, sales inquiries, or anything else that needs a Chainstack human, call `contact_chainstack` — it works for any user, including those who don't use Telegram. Flag the following if the codebase needs them — they require manual setup in the Chainstack console and are not available through the API: * [Dedicated Node](/docs/dedicated-node) deployment — call `contact_chainstack` to reach the team. [@ChainstackGrowthBot](https://t.me/ChainstackGrowthBot) on Telegram is an alternative for users who prefer it. * Solana [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) — node **Add-ons** tab. * [Unlimited Node](/docs/unlimited-node-add-on) add-on for high throughput — node **Add-ons** tab. * [MEV protection](/docs/mev-protection) — node details page. * [Warp transactions](/docs/warp-transactions) — node details page. * [IP allowlisting and access rules](/docs/access-rules) — node **Security** tab. Answer any follow-up questions — use the MCP server tools to provide accurate, specific answers. #### Cost and savings estimate If the user is migrating from another RPC provider — **QuickNode**, **Alchemy**, or any other — estimate their Chainstack cost and the savings versus their current bill. 5. Get the user's current usage from the source provider — pick the lowest-friction path: * **QuickNode** — the Admin API exposes usage directly. Ask the user for a QuickNode API key (created at [dashboard.quicknode.com/api-keys](https://dashboard.quicknode.com/api-keys)) and fetch their billing-period usage: ```bash theme={"system"} curl -s 'https://api.quicknode.com/v0/usage/rpc' \ -H 'accept: application/json' -H 'x-api-key: ' ``` This returns API Credits used and remaining for the period. Per-method and per-chain breakdowns are at `/v0/usage/rpc/by-method` and `/v0/usage/rpc/by-chain` — `by-method` also flags each method as `archive: true`/`false`, which maps to Chainstack's 2 RU per archive request (vs 1 RU standard), so you can convert precisely instead of by an average. If the user would rather not create a key, a **screenshot** of the **Usage & Billing** page works too — read the figures straight from the image. * **Alchemy** — the [Alchemy CLI](https://www.alchemy.com/docs/alchemy-cli) exposes usage programmatically. Have the user install it (`npm i -g @alchemy/cli`, which needs Node.js 22+) and run `alchemy auth login` once — a one-time browser sign-in to their own Alchemy account (there is no headless API-key path for `usage`). Then fetch their spend and compute-unit consumption: ``` alchemy --json usage summary ``` Read `data.totals.last30Days.usd` (30-day spend), `data.totals.last30Days.amount` (Compute Units), and `data.usageLimit` (their plan cap). For a per-network breakdown over time, use `alchemy --json usage timeseries --granularity day --metrics usd,amount --group-by network --start-date ` — note the time-series range is plan-limited (1 day on Free, 7 on Pay-as-you-go), whereas the `summary` figures above are not range-limited. If the user would rather not install the CLI, a **screenshot** of their dashboard **Usage** (Settings → Usage) and **Billing** page works too — read the figures straight from the image. * **Google Blockchain Node Engine / Blockchain RPC** — both shut down on December 15, 2026. BNE billed per node-hour (per Dedicated Node), not per request, and neither product exposes a usage API, so there's no request history to import. Estimate from what the app calls: count the BNE nodes and their networks, or for Blockchain RPC use its free-tier ceiling (100 requests/second, 1M requests/day) as the upper bound. See the [Blockchain Node Engine migration guide](/docs/migrating-from-google-cloud-blockchain-node-engine-to-chainstack) for endpoint and node-type mapping. * **Other provider / no programmatic access** — ask for a **screenshot** of their usage/billing dashboard, or their approximate monthly request volume and current spend. 6. Call `get_chainstack_pricing` for live Chainstack plans and the Request Unit (RU) model. The snapshot already includes the per-provider conversion multipliers (QuickNode API Credits → RU, Alchemy Compute Units → RU) and a worked migration example — use those live values rather than restating numbers here. Convert the source usage to RU, fit it to the cheapest plan that covers it, and present the monthly cost beside their current bill. 7. Route on the estimate: high volume, dedicated-node needs, or Enterprise → `contact_chainstack` (or [@ChainstackGrowthBot](https://t.me/ChainstackGrowthBot)); self-serve plans → [console.chainstack.com](https://console.chainstack.com). #### Dry run If the user already has Chainstack nodes deployed (API key required): 8. Use `list_nodes` to get all deployed Chainstack endpoints. 9. For each RPC method found in the codebase, send a test request to the corresponding Chainstack endpoint to verify it works. 10. Optionally, if the codebase has RPS patterns (batch calls, concurrent requests, polling intervals), test at that throughput. Use `search_docs` to find additional information and recommended optimizations. 11. Report what works, what fails, and any rate limit or method support gaps. ### Plan 12. For each endpoint to migrate, lay out the specific replacement — which existing Chainstack node covers it, or whether a new node needs to be created. Flag anything that needs the user's input (tier choice, project assignment, unsupported chains). > [!WARNING] > > STOP here. Present the plan and wait for the user to approve or adjust before making any changes. Do not proceed to implementation until the user explicitly confirms. Answer any follow-up questions — use the MCP server tools to provide accurate, specific answers. For implementation issues, direct the user to [Chainstack support](https://support.chainstack.com/). ### Implement 13. Create any nodes needed via `create_node` (use Global Nodes for instant deployment). 14. Replace each endpoint with the corresponding Chainstack URL from `list_nodes`. 15. Summarize what changed. ## Support For implementation issues, contact [Chainstack support](https://support.chainstack.com/). For unsupported chains that may be available as dedicated deployments, reach out to [@ChainstackGrowthBot](https://t.me/ChainstackGrowthBot) on Telegram. # Migrating from Google Cloud Blockchain Node Engine to Chainstack Source: https://docs.chainstack.com/docs/migrating-from-google-cloud-blockchain-node-engine-to-chainstack Step-by-step guide to migrate from Google Cloud Blockchain Node Engine and Blockchain RPC to Chainstack before the December 15, 2026 shutdown, covering node-type mapping, endpoint and authentication changes, and code examples. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR:** * Google Cloud is shutting down Blockchain Node Engine (BNE) and Blockchain RPC on December 15, 2026, and will delete all nodes and endpoints on that date. * New BNE node and endpoint creation has been disabled since June 15, 2026, so you can migrate existing workloads but you can't provision new BNE resources. * Migrating to Chainstack is mostly an endpoint swap: point your application at a Chainstack URL, with the API key carried in that URL. * Chainstack covers every chain BNE served—Ethereum, Polygon, and Solana—plus 70+ more protocols. * The easiest path is to let an AI coding agent do the whole migration for you; the manual steps are below if you prefer. ## Migrate with AI The fastest way to migrate off BNE is to let an AI coding agent do it for you. If you use Claude Code, Codex CLI, Cursor, Windsurf, Gemini CLI, Antigravity, or any other agent, paste this into your agent: ```text theme={"system"} get docs.chainstack.com/docs/migrate-to-chainstack-with-ai.md ``` The agent scans your codebase for BNE endpoints, checks coverage against Chainstack, plans the replacement, and—after you approve—swaps the endpoints. It uses the [Chainstack MCP server](/docs/chainstack-mcp-server) for live deployment options, pricing, and node management. Prefer to migrate by hand? The rest of this guide walks through it manually. See [migrate to Chainstack with AI](/docs/migrate-to-chainstack-with-ai) for the full runbook. ## Overview This guide helps you move your Ethereum, Polygon, and Solana workloads from Google Cloud Blockchain Node Engine to Chainstack with minimal code changes. It covers both products Google is retiring: BNE Dedicated Nodes and the shared Blockchain RPC service. Google has [announced](https://docs.cloud.google.com/blockchain-node-engine/docs/release-notes) the end of life for both. The two dates that matter are below—plan your cutover around them. ## Migration timeline * June 15, 2026 — Google disabled creation of new BNE nodes and new Blockchain RPC endpoints. Existing resources keep running and receive critical updates until the shutdown. * December 15, 2026 — Google shuts down BNE and Blockchain RPC and deletes all nodes and endpoints. Any application still pointing at a BNE endpoint stops working. > [!WARNING] > > Complete your migration well before December 15, 2026. After that date Google deletes your nodes and endpoints, and there is no grace period—unmigrated workloads fail. ## What you're replacing ### Supported networks Every network either Google product served has a direct Chainstack equivalent, with two testnet exceptions worth noting. Blockchain Node Engine served the full list below; Blockchain RPC served only Ethereum mainnet and the Holesky testnet. | BNE network | Chainstack equivalent | Notes | | - | - | - | | Ethereum Mainnet | Ethereum Mainnet | Direct swap | | Ethereum Sepolia | Ethereum Sepolia | Direct swap | | Ethereum Holesky | Ethereum Hoodi or Sepolia | Holesky is being retired across the ecosystem. Move Holesky workloads to Hoodi, the long-lived testnet, or to Sepolia. | | Ethereum Goerli | Ethereum Sepolia or Hoodi | Goerli is long deprecated. Migrate to a supported testnet. | | Polygon Mainnet | Polygon Mainnet | Direct swap | | Polygon Amoy | Polygon Amoy | Direct swap | | Solana | Solana | Direct swap. Chainstack adds Yellowstone gRPC, Trader Nodes, and Warp transactions. | > [!NOTE] > > Chainstack supports 70+ protocols beyond BNE's chains, so you can consolidate other infrastructure on the same provider. See [protocols and networks](/docs/protocols-networks) for the full list. ### Node type mapping BNE offered dedicated full and archive nodes plus a separate shared Blockchain RPC service. Here is how each maps to a Chainstack node type. | What you had on Google | Recommended Chainstack option | Why | | - | - | - | | Blockchain RPC (shared endpoint) | [Global Node](/docs/global-elastic-node) | Shared, geo-distributed, load-balanced, deploys in under a second | | BNE full node (dedicated) | [Global Node](/docs/global-elastic-node), or [Dedicated Node](/docs/dedicated-node) for isolation | Global covers most workloads. Dedicated gives you isolated compute, like BNE's per-project model. | | BNE archive node (Ethereum Mainnet) | Global Node or [Dedicated Node](/docs/dedicated-node) in archive mode | Full historical state | | Predictable, high steady volume | Global Node with the [Unlimited Node](/docs/unlimited-node-add-on) add-on | Flat monthly fee, no per-request metering | | Private access (VPC Service Controls) | [Dedicated Node](/docs/dedicated-node) | Isolated infrastructure, with hybrid deployment options for teams that need nodes in their own cloud | ## Choosing your Chainstack node type For most Ethereum and Polygon migrations, a Global Node is the right default. Choose a different option when you need flat-fee billing, isolation, archive data, or the lowest-latency path for trading. See [Chainstack pricing](https://chainstack.com/pricing/) for current rates. ### Global Node A load-balanced endpoint that routes requests to the nearest healthy location and fails over automatically. It deploys in under a second and suits general-purpose applications, development, and variable workloads. This is the closest match for both Blockchain RPC users and most BNE full-node users. ### Unlimited Node An add-on that converts a node to a flat monthly fee with unlimited requests at a fixed requests-per-second tier. Chainstack counts one request as one request regardless of method, so there is no per-method weighting to model. This suits production applications with steady traffic and teams that want predictable cost—much like BNE's flat per-node billing. ### Dedicated Node Exclusive infrastructure deployed just for you, not shared with other customers. Dedicated Nodes support archive mode, debug and trace methods, custom configuration, and isolation for compliance requirements. This is the closest analog to a BNE Dedicated Node, and the right choice if you relied on VPC Service Controls or private access. Dedicated Nodes require a Pro, Business, or Enterprise subscription. ### Trader Node Regional, low-latency endpoints you deploy in a specific location for the fastest round-trip to your application. Trader Nodes support Full and Archive modes and the [Warp transactions](/docs/warp-transactions) add-on, which propagates and lands transactions at the fastest speed possible on Solana, Ethereum, and BNB Smart Chain. This suits trading bots and latency-sensitive workloads. Available on paid plans. > [!NOTE] > > Migrating Solana workloads off BNE? On top of the node types above, Chainstack adds the [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) for high-performance streaming. ## RPC endpoint migration What changes depends on which product you're leaving: * From Blockchain Node Engine — the host changes from a `blockchainnodeengine.com` URL to your Chainstack endpoint, and the API key moves from a request header or query parameter into the endpoint URL itself. * From Blockchain RPC — the API key is already embedded in your endpoint URL, so it's a pure host swap: replace the Google endpoint with your Chainstack endpoint. > [!NOTE] > > Copy your full endpoint URL from the [Chainstack console](https://console.chainstack.com/). See [node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Endpoint and authentication On BNE you authenticated with a Google Cloud API key, passed either as the `X-goog-api-key` header or as a `key` query parameter: ```text theme={"system"} # Google BNE — JSON-RPC over HTTPS (key as query parameter) https://json-rpc.NODE_ID.blockchainnodeengine.com?key=YOUR_BNE_KEY # Google BNE — JSON-RPC over HTTPS (key as header) # https://json-rpc.NODE_ID.blockchainnodeengine.com # header: X-goog-api-key: YOUR_BNE_KEY # Google BNE — WebSocket wss://ws.NODE_ID.blockchainnodeengine.com?key=YOUR_BNE_KEY # Google BNE — Beacon (consensus-layer) API https://beacon.NODE_ID.blockchainnodeengine.com ``` On Chainstack the authentication key is part of the endpoint URL path, so there is no separate header or query parameter to set: ```text theme={"system"} # Chainstack — key is part of the endpoint URL https://ethereum-mainnet.core.chainstack.com/YOUR_AUTH_KEY wss://ethereum-mainnet.core.chainstack.com/YOUR_AUTH_KEY ``` > [!TIP] > > If you authenticated to BNE with the `X-goog-api-key` header, remove the custom header entirely after migrating. Your Chainstack credentials live in the URL. Migrating a Blockchain RPC endpoint is simpler still. Blockchain RPC is a shared endpoint with the API key already embedded in the URL—the same model Chainstack uses—so there is no header or query parameter to relocate. Copy your endpoint from the Google Cloud console and swap the host: ```text theme={"system"} # Google Blockchain RPC — shared endpoint, key already embedded (copy from the Google Cloud console) YOUR_BLOCKCHAIN_RPC_ENDPOINT # Chainstack — same key-in-URL model https://ethereum-mainnet.core.chainstack.com/YOUR_AUTH_KEY ``` ### HTTP JSON-RPC The migration is a URL swap. Your existing application logic works unchanged. ```python Python theme={"system"} from web3 import Web3 # Before (Google BNE) — key as query parameter # w3 = Web3(Web3.HTTPProvider("https://json-rpc.NODE_ID.blockchainnodeengine.com?key=YOUR_BNE_KEY")) # After (Chainstack) — key in the URL path w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) print(w3.eth.block_number) ``` ```javascript web3.js theme={"system"} import { Web3 } from "web3"; // Before (Google BNE) // const web3 = new Web3("https://json-rpc.NODE_ID.blockchainnodeengine.com?key=YOUR_BNE_KEY"); // After (Chainstack) const web3 = new Web3("YOUR_CHAINSTACK_ENDPOINT"); console.log(await web3.eth.getBlockNumber()); ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; // Before (Google BNE) // const provider = new JsonRpcProvider("https://json-rpc.NODE_ID.blockchainnodeengine.com?key=YOUR_BNE_KEY"); // After (Chainstack) const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); console.log(await provider.getBlockNumber()); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; import { mainnet } from "viem/chains"; // Before (Google BNE) // transport: http("https://json-rpc.NODE_ID.blockchainnodeengine.com?key=YOUR_BNE_KEY") // After (Chainstack) const client = createPublicClient({ chain: mainnet, transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); console.log(await client.getBlockNumber()); ``` ### WebSocket subscriptions Swap the BNE `wss://ws.NODE_ID.blockchainnodeengine.com` URL for your Chainstack WSS endpoint. Subscription methods such as `eth_subscribe` work the same way. For guidance on when to use HTTP versus WebSocket, see [node connection: HTTP vs WebSocket](/docs/evm-node-connection-http-vs-websocket). ### Beacon Chain API BNE exposed consensus-layer data through a separate `beacon.NODE_ID.blockchainnodeengine.com` endpoint. On Chainstack, every Ethereum node comes with both an execution-layer endpoint and a consensus-layer (Beacon Chain) endpoint, so it's a direct replacement. The REST paths follow the standard [Beacon API specification](https://ethereum.github.io/beacon-APIs/), so point your client at the Chainstack Beacon Chain endpoint and keep your existing calls. See the [Beacon Chain API reference](/reference/beacon-chain) for examples. ## Archive data BNE offered archive nodes on Ethereum Mainnet. On Chainstack, deploy a Global Node or Dedicated Node in archive mode to query full historical state. Archive requests are billed at a higher request-unit rate than standard requests—see [pricing](https://chainstack.com/pricing/) for details. ## Debug and trace BNE disabled the `debug` and `admin` namespaces by default. Chainstack provides `debug` and `trace` methods on Global Nodes (paid plans) and [Dedicated Nodes](/docs/dedicated-node). See [debug and trace APIs](/docs/debug-and-trace-apis) for per-protocol availability, and the [Ethereum methods reference](/docs/ethereum-methods) for the full list of supported calls. ## Private and VPC access If you relied on VPC Service Controls or private IP access on BNE, [Dedicated Nodes](/docs/dedicated-node) give you isolated infrastructure, and Chainstack offers [hybrid deployment](/docs/hybrid-hosting) for teams that need nodes inside their own cloud account. This keeps your infrastructure isolated and lets you stay on your existing cloud provider. [Contact Chainstack](https://support.chainstack.com/) to scope a hybrid or dedicated deployment. ## Validate and cut over **1. Deploy your Chainstack node** Sign up at [console.chainstack.com](https://console.chainstack.com/) and deploy a node for each network you run on BNE. **2. Test your RPC methods** Send the RPC calls your application uses against the new Chainstack endpoint and confirm the responses match. **3. Check throughput** Test at your real request rate. Review the [throughput guidelines](/docs/limits) and add the [Unlimited Node](/docs/unlimited-node-add-on) add-on if you need a flat-fee high-volume tier. **4. Repoint your application** Update your application code, environment variables, and config files to the Chainstack endpoint URL. **5. Decommission BNE** Delete your legacy BNE nodes in the Google Cloud console. Google deletes Blockchain RPC endpoints for you on December 15, 2026. > [!TIP] > > Your application runs entirely on Chainstack endpoints, and no code path references a `blockchainnodeengine.com` URL. ## Next steps * [Deploy a Global Node](/docs/global-elastic-node) * [Migrate to Chainstack with AI](/docs/migrate-to-chainstack-with-ai) * [Review throughput guidelines](/docs/limits) * [Set up access rules and IP allowlisting](/docs/access-rules) * [Chainstack pricing](https://chainstack.com/pricing/) For migration help, contact [Chainstack support](https://support.chainstack.com/). # Migrating from Grove to Chainstack Source: https://docs.chainstack.com/docs/migrating-from-grove-to-chainstack Migration guide from Grove (grove.city) to Chainstack, covering the endpoint URL swap, authentication, and chain mapping. **TLDR:** * Grove — the RPC gateway built by Pocket Network — has wound down, and its `rpc.grove.city` endpoints are retired. * Grove's traffic moved to Pocket Network's free public endpoints in Nov 2025; Grove the company closed in Jul 2026. * Migrating to Chainstack is a one-line change: swap the endpoint URL. The JSON-RPC methods and responses are identical. * The one code difference is authentication — a Chainstack endpoint carries an auth token, where Grove endpoints were open. ## What happened to Grove Grove ran the Grove Portal, the first gateway for Pocket Network, serving RPC across 60+ chains. It has now wound down in two stages: * In Nov 2025, Grove's public and paid RPC traffic moved to Pocket Network's free public endpoints at `api.pocket.network`, and Grove stepped away from operating the gateway. * In Jul 2026, Grove the company [closed entirely](https://grove.city/). The `rpc.grove.city` and `docs.grove.city` domains no longer resolve. Pocket Network itself continues under the Pocket Network Foundation, so the `api.pocket.network` public endpoints still work. They are free, shared, and rate-limited, with no SLA — fine for prototyping, but a production app wants dedicated capacity, higher throughput, and support. ## Who should migrate * Apps still calling `https://.rpc.grove.city/v1/` — these endpoints are offline, so the calls now fail. * Apps that followed Grove's traffic to the free Pocket public endpoints — these still respond, but Chainstack adds dedicated capacity, higher rate limits, archive and debug/trace access, and support. ## Change your endpoint URL Migrating is a one-line change — point your client at a Chainstack node endpoint instead of your old Grove endpoint. Everything else in your code stays the same, because both speak standard JSON-RPC. | Provider | Endpoint (Ethereum example) | | - | - | | Grove (retired) | `https://eth.rpc.grove.city/v1/YOUR_APP_ID` | | Chainstack | `YOUR_CHAINSTACK_ENDPOINT` | The same swap applies in your application code: ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); // Before (Grove — retired): // const provider = new ethers.JsonRpcProvider("https://eth.rpc.grove.city/v1/YOUR_APP_ID"); // After (Chainstack): const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const block = await provider.getBlockNumber(); console.log(`Current block: ${block}`); ``` ```python web3.py theme={"system"} from web3 import Web3 # Before (Grove — retired): # web3 = Web3(Web3.HTTPProvider("https://eth.rpc.grove.city/v1/YOUR_APP_ID")) # After (Chainstack): web3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) print(f"Current block: {web3.eth.block_number}") ``` Non-EVM chains migrate the same way. On Solana, for example, you point your client at your Chainstack Solana endpoint and keep calling the standard Solana JSON-RPC methods. ## Authentication is the one difference Grove endpoints were open — no key, or an optional `app-id` in the URL path. A Chainstack node authenticates every request. The default method is an auth token already embedded in the endpoint URL, so you copy the full HTTPS endpoint from Chainstack and use it as-is — no code changes beyond the URL. For basic auth, the platform API key, or the gRPC x-token, see [Authentication methods](/docs/authentication-methods-for-different-scenarios). ## Map your chains Chainstack supports the chains most Grove users run. Match the chain from your Grove endpoint subdomain to the Chainstack protocol: | Grove subdomain | Chainstack protocol | | - | - | | `eth` | Ethereum | | `base` | Base | | `arb-one` | Arbitrum | | `op` | Optimism | | `poly` | Polygon | | `bsc` | BNB Smart Chain | | `avax` | Avalanche | | `gnosis` | Gnosis Chain | | `linea` | Linea | | `scroll` | Scroll | | `sonic` | Sonic | | `opbnb` | opBNB | | `harmony` | Harmony | | `kaia` | Kaia | | `solana` | Solana | | `sui` | Sui | | `tron` | TRON | For the authoritative, current list of supported chains and networks, see [Protocols and networks](/docs/protocols-networks). ## Get your Chainstack endpoint **1. Create a Chainstack account** [Sign up](https://console.chainstack.com/) with your GitHub, X, Google, or Microsoft account. No credit card is required to start. **2. Deploy a node** Create a project, then deploy a [Global Node](/docs/global-elastic-node) for your protocol and network. A Global Node is the load-balanced default that fits most workloads; for isolated infrastructure or debug and trace access, deploy a [Dedicated Node](/docs/dedicated-node). **3. Copy the endpoint** Open the node's **Access and credentials** tab and copy the HTTPS endpoint — it already includes your auth token. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). **4. Swap the URL and test** Replace the Grove URL in your app with the Chainstack endpoint, then send a test request to confirm you get a response. > [!TIP] > > Your migration is complete when a test call to your Chainstack endpoint returns the same result your Grove endpoint did for the same chain. ## Next steps * [Deploy your first node](/docs/manage-your-node) * [Authentication methods](/docs/authentication-methods-for-different-scenarios) * [Protocols and networks](/docs/protocols-networks) # Migrate from Helius getTokenAccounts to Solana RPC Source: https://docs.chainstack.com/docs/migrating-from-helius-gettokenaccounts-to-standard-solana-rpc-methods Replace Helius-specific getTokenAccounts calls with standard Solana RPC methods like getTokenAccountsByOwner for provider-portable token data queries. **TLDR:** * Helius’ `getTokenAccounts` can be replaced with standard Solana JSON-RPC calls like [`getProgramAccounts`](/reference/solana-getprogramaccounts) and [`getTokenAccountBalance`](/reference/solana-gettokenaccountbalance). * `getProgramAccounts` with filters and pagination helps gather all token accounts for a mint, though be mindful of rate limits and data size. * For larger tokens (like USDC), consider chunking requests and storing results in a database for performance. * Overall, the standard RPC approach is fully portable and avoids provider lock-in. ## Main article For starters, this is not a poke at Helius. It's something that builders ask for and we provide a simple tutorial. ## Overview This tutorial shows how to migrate from Helius' custom `getTokenAccounts` method to standard Solana JSON-RPC methods when switching node providers. We'll demonstrate how to achieve the same functionality using `getProgramAccounts` with proper pagination and data filtering. ## Implementation For illustration purposes, we'll provide you with a Helius script, a standard methods script, and a sample script to print token holders using standard methods. We'll be using the [ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx](https://solscan.io/token/ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx) token in all examples. So feel free to replace is with any other token. Although do note that tokens with higher holder count USDC will most likely need a more optimized approach. Also be sure to check [Limits](/docs/limits) as `getProgramAccounts` is a heavy call and in general this sort of operation on Solana infrastructurally is definitely not even in the cruiserweight class. ### Helius Here's a simple Helius example: Remember to replace `ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx` with your token if want. ```python Python theme={"system"} import requests import logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.StreamHandler()] ) url = "HELIUS_RPC" def get_token_accounts(contract_address: str, batch_size: int = 1000): cursor = None while True: try: params = { "limit": batch_size, "mint": contract_address } if cursor: params["cursor"] = cursor response = requests.post( url, headers={"Content-Type": "application/json"}, json={ "jsonrpc": "2.0", "id": "helius-test", "method": "getTokenAccounts", "params": params }, timeout=30 ) response.raise_for_status() data = response.json() if not data.get("result"): logging.error("Invalid response format - missing 'result' field") break token_accounts = data["result"].get("token_accounts", []) if not token_accounts: logging.info("No more token accounts to process") break for account in token_accounts: logging.info(f"Account owner: {account['owner']}, Amount: {account['amount']}") cursor = data["result"].get("cursor") if not cursor: logging.info("Reached end of pagination - all accounts processed") break except requests.exceptions.RequestException as e: logging.error(f"API request failed: {str(e)}") break if __name__ == "__main__": atlas_contract_address = "ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx" get_token_accounts(atlas_contract_address) ``` ### Standard JSON-RPC example Here's a standard one: Remember to replace `ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx` with your token if want. ```python Python theme={"system"} import requests import logging import time from base64 import b64decode logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.StreamHandler()] ) url = "CHAINSTACK_RPC" TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" def get_token_accounts(contract_address: str, batch_size: int = 25): total_processed = 0 try: response = requests.post( url, headers={"Content-Type": "application/json"}, json={ "jsonrpc": "2.0", "id": 1, "method": "getProgramAccounts", "params": [ TOKEN_PROGRAM_ID, { "dataSlice": { "offset": 0, "length": 0 }, "filters": [ { "dataSize": 165 }, { "memcmp": { "offset": 0, "bytes": contract_address } } ] } ] }, timeout=60 ) response.raise_for_status() data = response.json() total_accounts = len(data.get("result", [])) logging.info(f"Total accounts to process: {total_accounts}") # Now fetch accounts in smaller batches with minimal data for offset in range(0, total_accounts, batch_size): logging.info(f"Fetching batch starting at offset {offset}") response = requests.post( url, headers={"Content-Type": "application/json"}, json={ "jsonrpc": "2.0", "id": 1, "method": "getProgramAccounts", "params": [ TOKEN_PROGRAM_ID, { "encoding": "base64", "filters": [ { "dataSize": 165 }, { "memcmp": { "offset": 0, "bytes": contract_address } } ], "dataSlice": { "offset": 32, "length": 32 }, "limit": batch_size } ] }, timeout=60 ) response.raise_for_status() data = response.json() if "result" not in data: logging.error("Invalid response format - missing 'result' field") break accounts = data["result"] if not accounts: break for account in accounts: try: raw_data = account["account"]["data"][0] owner_bytes = b64decode(raw_data) owner = ''.join([f'{b:02x}' for b in owner_bytes]) logging.info(f"Account owner: {owner}") total_processed += 1 except (KeyError, IndexError, TypeError) as e: logging.error(f"Error processing account data: {e}") continue logging.info(f"Processed {total_processed}/{total_accounts} accounts") if offset + batch_size < total_accounts: time.sleep(0.5) except requests.exceptions.RequestException as e: logging.error(f"API request failed: {str(e)}") if __name__ == "__main__": atlas_token = "ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx" get_token_accounts(atlas_token, batch_size=25) ``` ### Standard JSON-RPC example with DB connection Here's a *sample* of how you'd be using the standard [getProgramAccounts](/reference/solana-getprogramaccounts) method to monitor the holders of a token and then dumping the data to a PostgreSQL DB and keeping key-value store of accounts holding the token and the number of tokens up to date. ```python Python theme={"system"} import requests from decimal import getcontext, Decimal, InvalidOperation import time from datetime import datetime from typing import Dict, List, Tuple, Any, Optional from base64 import b64decode import psycopg2 # PostgreSQL connection configuration db_params = { 'host': '', 'database': '', 'user': '', 'password': '', 'port': '5432' } url = "CHAINSTACK_RPC" getcontext().prec = 18 TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" def get_token_accounts(contract_address: str, batch_size: int = 25) -> Dict[str, int]: """ Gets token holders using standard Solana RPC methods and returns holder data """ holder_data = {} total_processed = 0 try: # Initial request to get total count response = requests.post( url, headers={"Content-Type": "application/json"}, json={ "jsonrpc": "2.0", "id": 1, "method": "getProgramAccounts", "params": [ TOKEN_PROGRAM_ID, { "dataSlice": { "offset": 0, "length": 0 }, "filters": [ { "dataSize": 165 }, { "memcmp": { "offset": 0, "bytes": contract_address } } ] } ] }, timeout=60 ) response.raise_for_status() data = response.json() total_accounts = len(data.get("result", [])) # Process accounts in batches for offset in range(0, total_accounts, batch_size): response = requests.post( url, headers={"Content-Type": "application/json"}, json={ "jsonrpc": "2.0", "id": 1, "method": "getProgramAccounts", "params": [ TOKEN_PROGRAM_ID, { "encoding": "base64", "filters": [ { "dataSize": 165 }, { "memcmp": { "offset": 0, "bytes": contract_address } } ], "dataSlice": { "offset": 32, "length": 32 }, "limit": batch_size } ] }, timeout=60 ) response.raise_for_status() data = response.json() if "result" not in data: continue accounts = data["result"] if not accounts: break for account in accounts: try: raw_data = account["account"]["data"][0] owner_bytes = b64decode(raw_data) owner = ''.join([f'{b:02x}' for b in owner_bytes]) # Get token balance balance_response = requests.post( url, headers={"Content-Type": "application/json"}, json={ "jsonrpc": "2.0", "id": 1, "method": "getTokenAccountBalance", "params": [owner] }, timeout=30 ) balance_data = balance_response.json() if "result" in balance_data and "value" in balance_data["result"]: amount = int(balance_data["result"]["value"]["amount"]) if amount > 0: # Only count non-zero balances holder_data[owner] = amount total_processed += 1 except (KeyError, IndexError, TypeError) as e: continue if offset + batch_size < total_accounts: time.sleep(0.5) except requests.exceptions.RequestException: pass return holder_data def update_holder_data(contract_address: str, holder_data: Dict[str, int]): """ Updates the database with new holder data, tracking changes """ conn = None try: conn = psycopg2.connect(**db_params) cursor = conn.cursor() # Get existing holder data cursor.execute(""" SELECT holder_address, amount FROM token_holders WHERE contract_address = %s """, (contract_address,)) existing_holders = {row[0]: row[1] for row in cursor.fetchall()} # Calculate changes current_time = int(time.time()) date_time = datetime.fromtimestamp(current_time) # Insert new/updated holders for holder, amount in holder_data.items(): if holder not in existing_holders or existing_holders[holder] != amount: cursor.execute(""" INSERT INTO token_holders (contract_address, holder_address, amount, timestamp, date_time) VALUES (%s, %s, %s, %s, %s) ON CONFLICT (contract_address, holder_address) DO UPDATE SET amount = EXCLUDED.amount, timestamp = EXCLUDED.timestamp, date_time = EXCLUDED.date_time """, (contract_address, holder, amount, current_time, date_time)) # Record total holder count total_holders = len([amount for amount in holder_data.values() if amount > 0]) cursor.execute(""" INSERT INTO holder_counts (contract_address, timestamp, date_time, count) VALUES (%s, %s, %s, %s) """, (contract_address, current_time, date_time, total_holders)) conn.commit() except (psycopg2.Error, Exception) as e: if conn: conn.rollback() raise finally: if conn: conn.close() def main(): try: conn = psycopg2.connect(**db_params) cursor = conn.cursor() # Get contracts to process cursor.execute(""" SELECT contract_address FROM contracts WHERE active = true """) contracts = [row[0] for row in cursor.fetchall()] for contract in contracts: holder_data = get_token_accounts(contract) update_holder_data(contract, holder_data) time.sleep(1) # Rate limiting between contracts except Exception as e: print(f"Error in main process: {str(e)}") finally: if 'conn' in locals() and conn: conn.close() if __name__ == "__main__": # Example token addresses tokens = [ "ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx", # ATLAS token # Add more token addresses here ] for token in tokens: try: holder_data = get_token_accounts(token) update_holder_data(token, holder_data) time.sleep(1) # Rate limiting between tokens except Exception as e: print(f"Error processing token {token}: {str(e)}") ``` > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Migrating from Syndica to Chainstack Source: https://docs.chainstack.com/docs/migrating-from-syndica-to-chainstack Step-by-step migration guide from Syndica to Chainstack for Solana RPC access, covering endpoint configuration, SDK setup, and feature comparison. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * Syndica has announced plans to retire their Solana RPC and ChainStream APIs. * Migrating RPC endpoints is straightforward—swap the URL. * ChainStream users should migrate to [Yellowstone gRPC](/docs/yellowstone-grpc-geyser-plugin) for streaming, which offers lower latency and is the Solana ecosystem standard. * Chainstack offers four node types: Global Node, [Unlimited Node](/docs/unlimited-node), Dedicated Node, and Trader Node—choose based on your workload. ## Overview This guide helps you migrate from Syndica to Chainstack for your Solana infrastructure needs. Whether you're using Syndica's standard RPC or their ChainStream API, we'll show you how to switch with minimal code changes. ## Choosing the right Chainstack node type Chainstack offers four node types, each optimized for different use cases. ### Syndica to Chainstack tier mapping | Syndica tier | Recommended Chainstack option | ChainStream replacement | Why | | - | - | - | - | | Standard (free) | Global Node on Developer plan | WebSocket subscriptions | Similar free tier with room to grow | | Scale | Global Node on Growth plan | Yellowstone gRPC (add-on) | Flexible scaling with real-time streaming | | Scale (high volume) | Global Node with Unlimited add-on | Yellowstone gRPC (add-on) | Flat-fee unlimited requests | | HyperScale (custom) | Dedicated Node or Trader Node | Yellowstone gRPC on Dedicated only | Isolated infrastructure or trading-optimized | > [!NOTE] > > Yellowstone gRPC requires Growth plan or higher. Developer plan users can use standard WebSocket subscriptions for real-time data, or upgrade to Growth to access Yellowstone gRPC. See [Chainstack pricing](https://chainstack.com/pricing/) for current rates. ### Node type comparison | Node type | Distribution | Billing | Yellowstone gRPC | Best for | | - | - | - | - | - | | **Global Node** | Geo-distributed | Per request (plan quota) | Yes (add-on) | General use, variable load, spikes | | **Unlimited Node** | Geo-distributed | Flat monthly fee | Yes (add-on) | Predictable steady usage, cost control | | **Dedicated Node** | Geo-specific | Compute hours + storage | Yes (customizable) | Custom solutions, isolation, compliance | | **Trader Node** | Geo-specific | Per request (plan quota) | No | Trading bots, snipers | ### Global Node **What it is:** A load-balanced endpoint that automatically routes requests to the nearest healthy location. **Key features:** * Deploys in under a second * Automatic failover if a node underperforms * Warp transactions support via bloXroute * [Yellowstone gRPC](/docs/yellowstone-grpc-geyser-plugin) available as add-on (mainnet) **Best for:** General-purpose applications, development, variable workloads. ### Unlimited Node **What it is:** An add-on that converts any node to flat-fee, unlimited requests at a fixed RPS tier. **Key features:** * RPS tiers: 25, 100, 250, 500, 1000 requests per second * Zero plan quota consumption * No overage charges—you get rate-limited instead of cut off * Predictable monthly cost **Best for:** Production applications with steady traffic, cost-conscious teams. ### Dedicated Node **What it is:** Exclusive infrastructure deployed just for you, not shared with other customers. **Key features:** * Fully isolated compute resources * Debug and trace APIs enabled * Highly customizable configuration * Full or Archive modes available * [Yellowstone gRPC](/docs/yellowstone-grpc-geyser-plugin) available with custom limits **Best for:** Enterprise applications, compliance requirements, custom configurations. **Requires:** Pro, Business, or Enterprise subscription. ### Trader Node **What it is:** Regional endpoints you deploy in a specific location for lowest latency to your app or bot, and fastest transaction landing. **Key features:** * **Warp transactions** via bloXroute (routes directly to leader on Solana) * Geo-specific deployment (US, Europe, Asia) * Full, Archive, or Warp modes * Dedicated gateways available on Enterprise **Best for:** Trading bots, snipers, latency-sensitive operations. ## Common migration scenarios ### Simple RPC application If you're using Syndica for standard RPC calls: 1. Sign up at [console.chainstack.com](https://console.chainstack.com/) 2. Deploy a Solana Global Node 3. Replace your Syndica endpoint URL with your Chainstack endpoint ### ChainStream user If you were using ChainStream for real-time data: 1. Deploy a Solana Global Node 2. Enable the [Yellowstone gRPC](/docs/yellowstone-grpc-geyser-plugin) add-on 3. Update your streaming code (see examples below) For lighter use cases, standard WebSocket subscriptions may be sufficient. ### Trading bot If you're running a trading bot and need fast transaction landing with real-time data: 1. Deploy a Solana Global Node 2. Enable [Warp transactions](/docs/warp-transactions) for faster transaction landing 3. Enable [Yellowstone gRPC](/docs/yellowstone-grpc-geyser-plugin) for real-time data ### High-volume production application If you have predictable, steady traffic and want cost control: 1. Deploy a Solana Global Node 2. Add the [Unlimited Node](/docs/unlimited-node) add-on at your required RPS tier ### Regional latency requirements If you need your node deployed in a specific region for lowest latency to your app: 1. Deploy a [Trader Node](/docs/trader-node) in your preferred region (US, Europe, or Asia) 2. Enable Warp transactions for fast transaction landing Note: Yellowstone gRPC is not available on [Trader Nodes](/docs/solana-trader-nodes). If you need both regional deployment and real-time streaming, deploy a Global Node alongside your Trader Node. ### Enterprise or custom configuration If you need isolated infrastructure, custom limits, or compliance requirements: 1. Deploy a [Dedicated Node](/docs/dedicated-node) 2. Contact Chainstack for custom configuration options Dedicated Nodes support Yellowstone gRPC with customizable limits. ## RPC endpoint migration Migrating your RPC endpoint is the simplest part—it's essentially a URL swap. > [!NOTE] > > Navigate to your node credentials in the [Chainstack console](https://console.chainstack.com/) and copy the full endpoint URL. ### Using solana-py ```python Python theme={"system"} from solana.rpc.api import Client # Before (Syndica) # client = Client("https://solana-mainnet.api.syndica.io/api-key/YOUR_SYNDICA_KEY") # After (Chainstack) client = Client("YOUR_CHAINSTACK_ENDPOINT") # Your existing code works unchanged response = client.get_slot() print(f"Current slot: {response.value}") ``` ### Using @solana/web3.js ```javascript JavaScript theme={"system"} import { Connection } from "@solana/web3.js"; // Before (Syndica) // const connection = new Connection("https://solana-mainnet.api.syndica.io/api-key/YOUR_SYNDICA_KEY"); // After (Chainstack) const connection = new Connection("YOUR_CHAINSTACK_ENDPOINT"); // Your existing code works unchanged const slot = await connection.getSlot(); console.log(`Current slot: ${slot}`); ``` ## ChainStream to Chainstack streaming migration Syndica's ChainStream was a WebSocket-based streaming service that aggregated data from multiple validators. Chainstack offers two alternatives depending on your needs: ### Option 1: Standard WebSocket subscriptions For lighter streaming use cases like notifications, alerts, or monitoring specific accounts, standard Solana WebSocket subscriptions work well. | ChainStream subscription | Chainstack WebSocket equivalent | | - | - | | `transactionsSubscribe` | [logsSubscribe](/reference/logssubscribe-solana) | | `slotUpdatesSubscribe` | [slotSubscribe](/reference/slotsubscribe-solana) | | `blockSubscribe` | `blockSubscribe` (use with caution at scale) | | Account monitoring | [accountSubscribe](/reference/accountsubscribe-solana) | > [!NOTE] > > `logsSubscribe` returns transaction logs and program mentions, not full transaction data. If you need complete transaction details, fetch them with [`getTransaction`](/reference/gettransaction) after receiving a log, or use Yellowstone gRPC for full transaction streaming. **ChainStream WebSocket URL:** ``` wss://solana-mainnet.chainstream.syndica.io/api-key/YOUR_SYNDICA_KEY ``` **Chainstack WebSocket URL:** ``` YOUR_CHAINSTACK_WSS_ENDPOINT ``` where YOUR\_CHAINSTACK\_WSS\_ENDPOINT is your node WSS endpoint. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). #### Example: Migrating transaction monitoring ```python Python theme={"system"} import asyncio import websockets import json # Chainstack WebSocket endpoint CHAINSTACK_WSS = "YOUR_CHAINSTACK_WSS_ENDPOINT" async def monitor_logs(): async with websockets.connect(CHAINSTACK_WSS) as ws: # Subscribe to logs mentioning a specific program subscribe_msg = { "jsonrpc": "2.0", "id": 1, "method": "logsSubscribe", "params": [ {"mentions": ["YOUR_PROGRAM_ID"]}, {"commitment": "confirmed"} ] } await ws.send(json.dumps(subscribe_msg)) # Process incoming logs while True: response = await ws.recv() data = json.loads(response) if "params" in data: print(f"Log received: {data['params']['result']}") if __name__ == "__main__": asyncio.run(monitor_logs()) ``` ```javascript JavaScript theme={"system"} const WebSocket = require("ws"); const CHAINSTACK_WSS = "YOUR_CHAINSTACK_WSS_ENDPOINT"; const ws = new WebSocket(CHAINSTACK_WSS); ws.on("open", () => { // Subscribe to logs mentioning a specific program const subscribeMsg = { jsonrpc: "2.0", id: 1, method: "logsSubscribe", params: [ { mentions: ["YOUR_PROGRAM_ID"] }, { commitment: "confirmed" } ] }; ws.send(JSON.stringify(subscribeMsg)); }); ws.on("message", (data) => { const response = JSON.parse(data); if (response.params) { console.log("Log received:", response.params.result); } }); ``` ### Option 2: Yellowstone gRPC (recommended for high-performance streaming) For trading bots, indexers, or any application that needs the fastest possible data, [Yellowstone gRPC](/docs/yellowstone-grpc-geyser-plugin) is the better choice. It streams data directly from validator memory via gRPC, offering lower latency than WebSocket-based solutions. Yellowstone gRPC is available on mainnet only. See [Chainstack pricing](https://chainstack.com/pricing/) for Yellowstone gRPC rates and tier details. #### Enabling Yellowstone gRPC Yellowstone gRPC is available as an add-on for Solana Global Nodes on mainnet, starting from the Growth plan. 1. Deploy a Solana Global Node in the [Chainstack console](https://console.chainstack.com/) 2. Click **Add-ons** > **Yellowstone gRPC Geyser Plugin** > **Install** 3. Your gRPC endpoint and token will appear in the node credentials. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). #### Example: Streaming with Yellowstone gRPC See our detailed guides: * [Listening to programs using Geyser and Yellowstone gRPC in Node.js](/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js) * [Listening to pump.fun token mints using Geyser](/docs/solana-listening-to-pumpfun-token-mint-using-geyser) For more Yellowstone gRPC examples, see: * [pump.fun and bonk.fun trading bot](https://github.com/chainstacklabs/pumpfun-bonkfun-bot) * [gRPC Geyser tutorial](https://github.com/chainstacklabs/grpc-geyser-tutorial) * [Video walkthrough](https://www.youtube.com/watch?v=ICBF1wdD-sM) ```javascript Node.js theme={"system"} import Client, { CommitmentLevel } from "@triton-one/yellowstone-grpc"; const ENDPOINT = "https://yellowstone-solana-mainnet.core.chainstack.com:443"; // From Access and credentials > Yellowstone gRPC; this client needs the https:// scheme const TOKEN = "YOUR_X_TOKEN"; async function main() { const client = new Client(ENDPOINT, TOKEN); await client.connect(); const stream = await client.subscribe(); // Handle incoming data stream.on("data", (data) => { if (data.transaction) { console.log("Transaction received:", data.transaction.transaction.signature); } }); // Subscribe to transactions for a specific program await stream.write({ transactions: { mySubscription: { accountInclude: ["YOUR_PROGRAM_ID"], accountExclude: [], accountRequired: [], }, }, commitment: CommitmentLevel.CONFIRMED, }); // Keep connection alive setInterval(async () => { await stream.write({ ping: { id: 1 } }); }, 10000); } main(); ``` ## Next steps * [Explore Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) * [Learn about Trader Nodes](/docs/trader-node) * [Review throughput guidelines](/docs/limits) # Monad tooling Source: https://docs.chainstack.com/docs/monad-tooling Complete Monad tooling guide for Chainstack nodes. Use MetaMask, Hardhat, ethers.js, viem, Foundry, Remix IDE, and more to build dApps on Monad blockchain. Get started with a [reliable Monad RPC endpoint](https://chainstack.com/build-better-with-monad/) to use the tools below. > [!NOTE] > > Monad is EVM compatible at the bytecode level, so all standard Ethereum development tools work out of the box without modification. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Phantom [Phantom](https://phantom.app/) is a secure multi-chain wallet that supports Monad. 1. Install [Phantom](https://phantom.app/) browser extension or mobile app. 2. In Phantom, navigate to **Settings** > **Developer Settings** > **Add Network**. 3. Enter the network details: * Network name — `Monad Mainnet` * RPC URL — your Chainstack endpoint * Chain ID — `143` * Currency symbol — `MON` * Block explorer URL — `https://monvision.io` 4. Click **Save**. ## Backpack [Backpack](https://www.backpack.app/) is a next-level wallet and exchange that supports Monad. 1. Install [Backpack](https://www.backpack.app/) browser extension or mobile app. 2. Navigate to **Settings** > **Networks** > **Add Custom Network**. 3. Enter the network details: * Network name — `Monad Mainnet` * RPC URL — your Chainstack endpoint * Chain ID — `143` * Currency symbol — `MON` * Block explorer URL — `https://monvision.io` 4. Save the configuration. ## Rabby [Rabby](https://rabby.io/) is a non-custodial multi-chain wallet with network auto-detection and transaction risk alerts. 1. Install [Rabby](https://rabby.io/) browser extension. 2. In Rabby, navigate to **Settings** > **Add Custom Network**. 3. Add your Chainstack endpoint with chain ID `143`. If Rabby already supports the network, use **Settings** > **Modify RPC URL** instead. See [Adding a custom RPC endpoint to Rabby](/docs/adding-a-custom-rpc-endpoint-to-rabby). ## HaHa [HaHa](https://haha.me/) is a smart wallet with DeFi integrations, account abstraction support, and hardware wallet compatibility. 1. Install [HaHa](https://haha.me/) browser extension or mobile app. 2. Navigate to wallet settings and select **Add Network**. 3. Enter the network details: * Network name — `Monad Mainnet` * RPC URL — your Chainstack endpoint * Chain ID — `143` * Currency symbol — `MON` * Block explorer URL — `https://monvision.io` 4. Save the configuration. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Monad nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.8.19", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. For a detailed tutorial with Remix IDE, see [Trust fund account with Remix](/docs/ethereum-tutorial-trust-fund-account-with-remix). ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Monad nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number: ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Monad nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Key Protected theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Monad nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Monad nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: "YOUR_CHAINSTACK_ENDPOINT", }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: "YOUR_CHAINSTACK_ENDPOINT", user: "USERNAME", password: "PASSWORD", }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Monad network ID: * Mainnet: `143` * Testnet: `10143` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider( "YOUR_CHAINSTACK_ENDPOINT", NETWORK_ID ); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — Monad network ID: * Mainnet: `143` * Testnet: `10143` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## viem Build DApps using [viem](https://viem.sh/) and Monad nodes deployed with Chainstack. 1. Install [viem](https://www.npmjs.com/package/viem). 2. Connect over HTTP or WebSocket. ### HTTP Use the `createPublicClient` function to connect to your node endpoint and get the latest block number: ```javascript Javascript theme={"system"} import { createPublicClient, http } from 'viem' const client = createPublicClient({ transport: http('YOUR_CHAINSTACK_ENDPOINT') }) const blockNumber = await client.getBlockNumber() console.log(blockNumber) ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password ### WebSocket Use the `createPublicClient` function with WebSocket transport: ```javascript Javascript theme={"system"} import { createPublicClient, webSocket } from 'viem' const client = createPublicClient({ transport: webSocket('YOUR_CHAINSTACK_ENDPOINT') }) const blockNumber = await client.getBlockNumber() console.log(blockNumber) ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node WSS endpoint protected either with the key or password ## Brownie 1. Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). 2. Use the `brownie networks add` command with the node endpoint. For example, Monad mainnet: ```shell Shell theme={"system"} brownie networks add Monad ID name="NETWORK_NAME" host=YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `chainstack-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password * NETWORK\_ID — Monad network ID: * Mainnet: `143` * Testnet: `10143` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```bash Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```bash Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password ### Verify contracts You can verify your smart contracts on Monad block explorers. For MonadExplorer (BlockVision), use Sourcify: ```bash Shell theme={"system"} forge verify-contract CONTRACT_ADDRESS CONTRACT_NAME \ --verifier sourcify \ --verifier-url https://sourcify-api-monad.blockvision.org \ --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` For Monadscan (Etherscan-based): ```bash Shell theme={"system"} forge verify-contract CONTRACT_ADDRESS CONTRACT_NAME \ --verifier etherscan \ --verifier-url https://api.monadscan.com/api \ --etherscan-api-key YOUR_MONADSCAN_API_KEY \ --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` # Monad: Deploying and verifying smart contracts Source: https://docs.chainstack.com/docs/monad-tutorial-deploy-verify-smart-contracts Deploy and verify smart contracts on Monad mainnet using Hardhat and Foundry with Chainstack nodes, including Sourcify and Monad Explorer verification. This tutorial teaches you how to deploy smart contracts to Monad mainnet and verify them on block explorers. You'll use both Hardhat and Foundry, the two most popular Ethereum development frameworks. **TLDR:** * Deploy a simple smart contract to Monad mainnet using Hardhat * Verify contracts on MonadExplorer (Sourcify) and Monadscan (Etherscan-based) * Alternative deployment using Foundry * Understand Monad's 1-second finality benefits for deployment ## Prerequisites * [Chainstack account](https://console.chainstack.com/) with a Monad node endpoint * Node.js v16+ (for Hardhat) or [Foundry](https://getfoundry.sh/) installed * Basic Solidity knowledge * MON tokens for gas fees ## Overview Monad is EVM-compatible at the bytecode level, so your existing Ethereum development tools work without modification. The main differences you'll notice: * **1-second finality**: Your contract is confirmed immediately after deployment * **No reorganizations**: Once deployed, your contract address is permanent * **300M gas limit**: Complex contracts deploy without hitting gas limits This tutorial covers deployment with both Hardhat (JavaScript) and Foundry (Rust-based CLI), plus verification on two block explorers. ## Deploy with Hardhat ### Set up the project Create a new directory and initialize a Hardhat project: ```bash theme={"system"} mkdir monad-contract && cd monad-contract npm init -y npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox dotenv npx hardhat init ``` Select "Create a JavaScript project" when prompted. ### Configure environment variables Create a `.env` file in your project root: ```bash .env theme={"system"} CHAINSTACK_ENDPOINT="YOUR_CHAINSTACK_MONAD_ENDPOINT" PRIVATE_KEY="YOUR_WALLET_PRIVATE_KEY" MONADSCAN_API_KEY="YOUR_MONADSCAN_API_KEY" ``` > [!WARNING] > > Never commit your `.env` file to version control. Add it to `.gitignore`. ### Configure Hardhat for Monad Replace the contents of `hardhat.config.js`: ```javascript hardhat.config.js theme={"system"} require("@nomicfoundation/hardhat-toolbox"); require("dotenv").config(); module.exports = { solidity: "0.8.24", networks: { monad: { url: process.env.CHAINSTACK_ENDPOINT, chainId: 143, accounts: [process.env.PRIVATE_KEY], }, }, etherscan: { apiKey: { monad: process.env.MONADSCAN_API_KEY, }, customChains: [ { network: "monad", chainId: 143, urls: { apiURL: "https://api.monadscan.com/api", browserURL: "https://monadscan.com", }, }, ], }, sourcify: { enabled: true, apiUrl: "https://sourcify-api-monad.blockvision.org", browserUrl: "https://monadexplorer.com", }, }; ``` ### Write the smart contract Create `contracts/Counter.sol`: ```solidity contracts/Counter.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract Counter { uint256 public count; event CountChanged(uint256 newCount, address changedBy); constructor(uint256 _initialCount) { count = _initialCount; } function increment() public { count += 1; emit CountChanged(count, msg.sender); } function decrement() public { require(count > 0, "Counter: cannot decrement below zero"); count -= 1; emit CountChanged(count, msg.sender); } function setCount(uint256 _count) public { count = _count; emit CountChanged(count, msg.sender); } } ``` ### Create the deployment script Create `scripts/deploy.js`: ```javascript scripts/deploy.js theme={"system"} const hre = require("hardhat"); async function main() { console.log("Deploying Counter contract to Monad..."); const initialCount = 0; const counter = await hre.ethers.deployContract("Counter", [initialCount]); await counter.waitForDeployment(); const address = await counter.getAddress(); console.log(`Counter deployed to: ${address}`); console.log(`Initial count: ${initialCount}`); // Wait a few seconds before verification console.log("Waiting for block confirmations..."); await new Promise((resolve) => setTimeout(resolve, 5000)); return address; } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); }); ``` ### Deploy the contract Run the deployment: ```bash theme={"system"} npx hardhat run scripts/deploy.js --network monad ``` You'll see output like: ``` Deploying Counter contract to Monad... Counter deployed to: 0x1234567890abcdef1234567890abcdef12345678 Initial count: 0 Waiting for block confirmations... ``` ### Verify on Monadscan Verify using the Etherscan-compatible API: ```bash theme={"system"} npx hardhat verify --network monad CONTRACT_ADDRESS 0 ``` Replace `CONTRACT_ADDRESS` with your deployed address. The `0` is the constructor argument (initial count). ### Verify on MonadExplorer (Sourcify) Verify using Sourcify: ```bash theme={"system"} npx hardhat verify --network monad --verifier sourcify CONTRACT_ADDRESS 0 ``` ## Deploy with Foundry Foundry offers faster compilation and a streamlined CLI experience. ### Set up the project ```bash theme={"system"} mkdir monad-foundry && cd monad-foundry forge init ``` ### Write the contract Replace `src/Counter.sol`: ```solidity src/Counter.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract Counter { uint256 public count; event CountChanged(uint256 newCount, address changedBy); constructor(uint256 _initialCount) { count = _initialCount; } function increment() public { count += 1; emit CountChanged(count, msg.sender); } function decrement() public { require(count > 0, "Counter: cannot decrement below zero"); count -= 1; emit CountChanged(count, msg.sender); } function setCount(uint256 _count) public { count = _count; emit CountChanged(count, msg.sender); } } ``` ### Deploy with forge ```bash theme={"system"} forge create src/Counter.sol:Counter \ --constructor-args 0 \ --private-key YOUR_PRIVATE_KEY \ --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` You'll see output including the deployed contract address. ### Verify on MonadExplorer (Sourcify) ```bash theme={"system"} forge verify-contract CONTRACT_ADDRESS src/Counter.sol:Counter \ --verifier sourcify \ --verifier-url https://sourcify-api-monad.blockvision.org \ --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` ### Verify on Monadscan ```bash theme={"system"} forge verify-contract CONTRACT_ADDRESS src/Counter.sol:Counter \ --verifier etherscan \ --verifier-url https://api.monadscan.com/api \ --etherscan-api-key YOUR_MONADSCAN_API_KEY \ --rpc-url YOUR_CHAINSTACK_ENDPOINT \ --constructor-args $(cast abi-encode "constructor(uint256)" 0) ``` ## Interact with your contract After deployment, interact with your contract using cast (Foundry) or a script. ### Using cast ```bash theme={"system"} # Read the count cast call CONTRACT_ADDRESS "count()" --rpc-url YOUR_CHAINSTACK_ENDPOINT # Increment the counter cast send CONTRACT_ADDRESS "increment()" \ --private-key YOUR_PRIVATE_KEY \ --rpc-url YOUR_CHAINSTACK_ENDPOINT # Check the new count cast call CONTRACT_ADDRESS "count()" --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` ### Using ethers.js ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); const counterABI = [ "function count() view returns (uint256)", "function increment()", "function decrement()", "function setCount(uint256)", "event CountChanged(uint256 newCount, address changedBy)", ]; const counter = new ethers.Contract("CONTRACT_ADDRESS", counterABI, wallet); async function main() { // Read current count const currentCount = await counter.count(); console.log(`Current count: ${currentCount}`); // Increment const tx = await counter.increment(); console.log(`Transaction hash: ${tx.hash}`); await tx.wait(); // Read new count const newCount = await counter.count(); console.log(`New count: ${newCount}`); } main(); ``` ## Monad-specific notes > [!NOTE] > > **Key differences from other EVM chains:** > > * **1-second finality**: No need to wait for multiple confirmations. Once the transaction is included in a block, it's final. > * **No pending state**: Transactions go directly from submitted to confirmed. The mempool works differently than Ethereum. > * **High throughput**: You can deploy multiple contracts in rapid succession without congestion. > * **Same tooling**: All Ethereum tools (Hardhat, Foundry, Remix, etc.) work without modification. ## Block explorers After deployment, view your contract on: * **MonadExplorer** (BlockVision): `https://monadexplorer.com/address/CONTRACT_ADDRESS` * **Monadscan**: `https://monadscan.com/address/CONTRACT_ADDRESS` * **MonVision**: `https://monvision.io/address/CONTRACT_ADDRESS` ## Troubleshooting ### Verification fails with "contract not found" Wait 10-15 seconds after deployment before verifying. Monad's fast finality means the contract is deployed quickly, but explorers may need time to index it. ### Gas estimation errors Monad has a 300M gas limit per block. If you're hitting gas errors, check: * Your contract isn't infinite looping * Constructor logic is reasonable * You have enough MON for gas fees ### RPC connection issues Ensure your Chainstack endpoint is correct and includes the full URL with any authentication. ## Next steps Now that you can deploy and verify contracts, you can: * Build more complex smart contracts * Create a frontend to interact with your contract * Set up continuous deployment pipelines * Explore Monad's high-throughput capabilities with batch transactions # Monad: Fetching token prices from DEXs Source: https://docs.chainstack.com/docs/monad-tutorial-dex-price-fetching Query real-time token prices from Uniswap V2-style DEXs on Monad using ethers.js and web3.py with pair reserves and routing through Chainstack nodes. This tutorial teaches you how to fetch token prices directly from decentralized exchanges on Monad. You'll learn to query Uniswap V2-style liquidity pools, calculate prices from reserves, and build real-time price monitors. **TLDR:** * Query token prices from Uniswap V2-style pools on Monad * Calculate prices from liquidity pool reserves * Handle token decimals correctly * Monitor price changes in real-time * Both JavaScript and Python implementations ## Prerequisites * [Chainstack account](https://console.chainstack.com/) with a Monad node endpoint * Node.js v16+ or Python 3.8+ * Basic understanding of AMM mechanics ## Overview Monad's 1-second finality makes it ideal for DeFi applications where price accuracy matters: * **Near real-time prices**: Query prices that are at most 1 second old * **Reliable data**: No reorganizations mean prices are immediately trustworthy * **High throughput**: DEXs can handle high trading volume without congestion This tutorial focuses on Uniswap V2-style AMMs, which are the most common DEX architecture on EVM chains. ## Understanding AMM pricing Uniswap V2 pools use the constant product formula: ``` x * y = k ``` Where: * `x` = reserve of token A * `y` = reserve of token B * `k` = constant (product of reserves) The price of token A in terms of token B is: ``` price_A = reserve_B / reserve_A ``` > [!NOTE] > > This is the "spot price" before any trade. Actual trade prices include slippage based on trade size relative to pool liquidity. ## Key contract addresses For this tutorial, you'll need: | Contract | Address | Description | | - | - | - | | WMON | `0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A` | Wrapped MON on Monad Mainnet | | WMON | `0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541` | Wrapped MON on Monad Testnet | > [!WARNING] > > DEX contracts and token addresses change as the Monad ecosystem evolves. Verify current addresses on block explorers like [MonVision](https://monvision.io) or [Monadscan](https://monadscan.com). ## Query Uniswap V2 pool prices ### Uniswap V2 Pair ABI The minimal ABI needed to query prices: ```javascript theme={"system"} const PAIR_ABI = [ "function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)", "function token0() external view returns (address)", "function token1() external view returns (address)", ]; const ERC20_ABI = [ "function decimals() external view returns (uint8)", "function symbol() external view returns (string)", "function name() external view returns (string)", ]; ``` ### JavaScript price fetcher ```javascript fetch-price.js theme={"system"} const { ethers } = require("ethers"); const CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT"; const provider = new ethers.JsonRpcProvider(CHAINSTACK_ENDPOINT); const PAIR_ABI = [ "function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)", "function token0() external view returns (address)", "function token1() external view returns (address)", ]; const ERC20_ABI = [ "function decimals() external view returns (uint8)", "function symbol() external view returns (string)", ]; async function getPoolPrice(pairAddress) { const pair = new ethers.Contract(pairAddress, PAIR_ABI, provider); // Get reserves and token addresses const [reserves, token0Address, token1Address] = await Promise.all([ pair.getReserves(), pair.token0(), pair.token1(), ]); // Get token details const token0 = new ethers.Contract(token0Address, ERC20_ABI, provider); const token1 = new ethers.Contract(token1Address, ERC20_ABI, provider); const [decimals0, decimals1, symbol0, symbol1] = await Promise.all([ token0.decimals(), token1.decimals(), token0.symbol(), token1.symbol(), ]); // Extract reserves const reserve0 = reserves[0]; const reserve1 = reserves[1]; const lastUpdate = reserves[2]; // Calculate prices with decimal adjustment // price0 = how much token1 for 1 token0 // price1 = how much token0 for 1 token1 const decimalAdjustment = 10n ** BigInt(decimals1 - decimals0); const price0 = Number(reserve1 * decimalAdjustment) / Number(reserve0); const price1 = Number(reserve0) / Number(reserve1 * decimalAdjustment); return { pair: pairAddress, token0: { address: token0Address, symbol: symbol0, decimals: decimals0, reserve: ethers.formatUnits(reserve0, decimals0), }, token1: { address: token1Address, symbol: symbol1, decimals: decimals1, reserve: ethers.formatUnits(reserve1, decimals1), }, prices: { [`${symbol0}/${symbol1}`]: price0, [`${symbol1}/${symbol0}`]: price1, }, lastUpdate: new Date(Number(lastUpdate) * 1000).toISOString(), }; } async function main() { // Replace with an actual Monad DEX pair address const pairAddress = "YOUR_DEX_PAIR_ADDRESS"; console.log("Fetching pool price...\n"); const priceData = await getPoolPrice(pairAddress); console.log(`Pool: ${priceData.pair}`); console.log(`\nToken 0: ${priceData.token0.symbol}`); console.log(` Address: ${priceData.token0.address}`); console.log(` Reserve: ${priceData.token0.reserve}`); console.log(`\nToken 1: ${priceData.token1.symbol}`); console.log(` Address: ${priceData.token1.address}`); console.log(` Reserve: ${priceData.token1.reserve}`); console.log(`\nPrices:`); for (const [pair, price] of Object.entries(priceData.prices)) { console.log(` ${pair}: ${price.toFixed(8)}`); } console.log(`\nLast update: ${priceData.lastUpdate}`); } main().catch(console.error); ``` ### Python price fetcher ```python fetch_price.py theme={"system"} from web3 import Web3 from decimal import Decimal CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT" web3 = Web3(Web3.HTTPProvider(CHAINSTACK_ENDPOINT)) PAIR_ABI = [ { "inputs": [], "name": "getReserves", "outputs": [ {"type": "uint112", "name": "reserve0"}, {"type": "uint112", "name": "reserve1"}, {"type": "uint32", "name": "blockTimestampLast"} ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "token0", "outputs": [{"type": "address"}], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "token1", "outputs": [{"type": "address"}], "stateMutability": "view", "type": "function" } ] ERC20_ABI = [ { "inputs": [], "name": "decimals", "outputs": [{"type": "uint8"}], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "symbol", "outputs": [{"type": "string"}], "stateMutability": "view", "type": "function" } ] def get_pool_price(pair_address): """Fetch price data from a Uniswap V2-style pool.""" pair = web3.eth.contract(address=pair_address, abi=PAIR_ABI) # Get reserves and token addresses reserves = pair.functions.getReserves().call() token0_address = pair.functions.token0().call() token1_address = pair.functions.token1().call() reserve0, reserve1, last_update = reserves # Get token details token0 = web3.eth.contract(address=token0_address, abi=ERC20_ABI) token1 = web3.eth.contract(address=token1_address, abi=ERC20_ABI) decimals0 = token0.functions.decimals().call() decimals1 = token1.functions.decimals().call() symbol0 = token0.functions.symbol().call() symbol1 = token1.functions.symbol().call() # Calculate prices with decimal adjustment decimal_adjustment = 10 ** (decimals1 - decimals0) price0 = (reserve1 * decimal_adjustment) / reserve0 # token0 price in token1 price1 = reserve0 / (reserve1 * decimal_adjustment) # token1 price in token0 return { 'pair': pair_address, 'token0': { 'address': token0_address, 'symbol': symbol0, 'decimals': decimals0, 'reserve': reserve0 / (10 ** decimals0) }, 'token1': { 'address': token1_address, 'symbol': symbol1, 'decimals': decimals1, 'reserve': reserve1 / (10 ** decimals1) }, 'prices': { f'{symbol0}/{symbol1}': price0, f'{symbol1}/{symbol0}': price1 }, 'last_update': last_update } def main(): # Replace with an actual Monad DEX pair address pair_address = "YOUR_DEX_PAIR_ADDRESS" print("Fetching pool price...\n") price_data = get_pool_price(pair_address) print(f"Pool: {price_data['pair']}") print(f"\nToken 0: {price_data['token0']['symbol']}") print(f" Address: {price_data['token0']['address']}") print(f" Reserve: {price_data['token0']['reserve']:,.4f}") print(f"\nToken 1: {price_data['token1']['symbol']}") print(f" Address: {price_data['token1']['address']}") print(f" Reserve: {price_data['token1']['reserve']:,.4f}") print(f"\nPrices:") for pair, price in price_data['prices'].items(): print(f" {pair}: {price:.8f}") if __name__ == "__main__": main() ``` ## Monitor price changes Build a real-time price monitor: ### JavaScript price monitor ```javascript monitor-price.js theme={"system"} const { ethers } = require("ethers"); const CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT"; const provider = new ethers.JsonRpcProvider(CHAINSTACK_ENDPOINT); const PAIR_ABI = [ "function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)", "function token0() external view returns (address)", "function token1() external view returns (address)", ]; const ERC20_ABI = [ "function decimals() external view returns (uint8)", "function symbol() external view returns (string)", ]; class PriceMonitor { constructor(pairAddress) { this.pairAddress = pairAddress; this.pair = new ethers.Contract(pairAddress, PAIR_ABI, provider); this.lastPrice = null; this.token0Symbol = null; this.token1Symbol = null; this.decimals0 = null; this.decimals1 = null; } async initialize() { const token0Address = await this.pair.token0(); const token1Address = await this.pair.token1(); const token0 = new ethers.Contract(token0Address, ERC20_ABI, provider); const token1 = new ethers.Contract(token1Address, ERC20_ABI, provider); [this.decimals0, this.decimals1, this.token0Symbol, this.token1Symbol] = await Promise.all([ token0.decimals(), token1.decimals(), token0.symbol(), token1.symbol(), ]); console.log(`Monitoring ${this.token0Symbol}/${this.token1Symbol} price`); console.log(`Pair: ${this.pairAddress}\n`); } async getPrice() { const reserves = await this.pair.getReserves(); const reserve0 = reserves[0]; const reserve1 = reserves[1]; const decimalAdjustment = 10n ** BigInt(this.decimals1 - this.decimals0); const price = Number(reserve1 * decimalAdjustment) / Number(reserve0); return price; } async monitor() { await this.initialize(); this.lastPrice = await this.getPrice(); console.log( `Initial price: ${this.lastPrice.toFixed(8)} ${this.token1Symbol}/${this.token0Symbol}` ); setInterval(async () => { try { const currentPrice = await this.getPrice(); if (currentPrice !== this.lastPrice) { const change = ((currentPrice - this.lastPrice) / this.lastPrice) * 100; const direction = change > 0 ? "📈" : "📉"; const timestamp = new Date().toISOString().split("T")[1].split(".")[0]; console.log( `${timestamp} ${direction} ${currentPrice.toFixed(8)} ${this.token1Symbol}/${this.token0Symbol} (${change > 0 ? "+" : ""}${change.toFixed(4)}%)` ); this.lastPrice = currentPrice; } } catch (error) { console.error("Error:", error.message); } }, 1000); } } // Replace with an actual Monad DEX pair address const pairAddress = "YOUR_DEX_PAIR_ADDRESS"; const monitor = new PriceMonitor(pairAddress); monitor.monitor(); ``` ### Python price monitor ```python monitor_price.py theme={"system"} from web3 import Web3 import time from datetime import datetime CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT" web3 = Web3(Web3.HTTPProvider(CHAINSTACK_ENDPOINT)) PAIR_ABI = [ { "inputs": [], "name": "getReserves", "outputs": [ {"type": "uint112", "name": "reserve0"}, {"type": "uint112", "name": "reserve1"}, {"type": "uint32", "name": "blockTimestampLast"} ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "token0", "outputs": [{"type": "address"}], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "token1", "outputs": [{"type": "address"}], "stateMutability": "view", "type": "function" } ] ERC20_ABI = [ { "inputs": [], "name": "decimals", "outputs": [{"type": "uint8"}], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "symbol", "outputs": [{"type": "string"}], "stateMutability": "view", "type": "function" } ] class PriceMonitor: def __init__(self, pair_address): self.pair_address = pair_address self.pair = web3.eth.contract(address=pair_address, abi=PAIR_ABI) self.last_price = None def initialize(self): token0_address = self.pair.functions.token0().call() token1_address = self.pair.functions.token1().call() token0 = web3.eth.contract(address=token0_address, abi=ERC20_ABI) token1 = web3.eth.contract(address=token1_address, abi=ERC20_ABI) self.decimals0 = token0.functions.decimals().call() self.decimals1 = token1.functions.decimals().call() self.symbol0 = token0.functions.symbol().call() self.symbol1 = token1.functions.symbol().call() print(f"Monitoring {self.symbol0}/{self.symbol1} price") print(f"Pair: {self.pair_address}\n") def get_price(self): reserves = self.pair.functions.getReserves().call() reserve0, reserve1, _ = reserves decimal_adjustment = 10 ** (self.decimals1 - self.decimals0) price = (reserve1 * decimal_adjustment) / reserve0 return price def monitor(self): self.initialize() self.last_price = self.get_price() print(f"Initial price: {self.last_price:.8f} {self.symbol1}/{self.symbol0}") while True: try: current_price = self.get_price() if current_price != self.last_price: change = ((current_price - self.last_price) / self.last_price) * 100 direction = "📈" if change > 0 else "📉" timestamp = datetime.now().strftime("%H:%M:%S") sign = "+" if change > 0 else "" print(f"{timestamp} {direction} {current_price:.8f} {self.symbol1}/{self.symbol0} ({sign}{change:.4f}%)") self.last_price = current_price time.sleep(1) except Exception as e: print(f"Error: {e}") time.sleep(1) if __name__ == "__main__": # Replace with an actual Monad DEX pair address pair_address = "YOUR_DEX_PAIR_ADDRESS" monitor = PriceMonitor(pair_address) monitor.monitor() ``` ## Calculate price impact Estimate the price impact of a trade before executing: ```javascript price-impact.js theme={"system"} const { ethers } = require("ethers"); function calculatePriceImpact(reserveIn, reserveOut, amountIn, decimalsIn, decimalsOut) { // Uniswap V2 formula: amountOut = (amountIn * 997 * reserveOut) / (reserveIn * 1000 + amountIn * 997) const amountInWithFee = amountIn * 997n; const numerator = amountInWithFee * reserveOut; const denominator = reserveIn * 1000n + amountInWithFee; const amountOut = numerator / denominator; // Spot price (before trade) const decimalAdjustment = 10n ** BigInt(decimalsOut - decimalsIn); const spotPrice = Number(reserveOut * decimalAdjustment) / Number(reserveIn); // Effective price (what you actually get) const effectivePrice = Number(amountOut) / Number(amountIn) * Number(10n ** BigInt(decimalsIn - decimalsOut)); // Price impact percentage const priceImpact = ((spotPrice - effectivePrice) / spotPrice) * 100; return { amountOut: amountOut, spotPrice: spotPrice, effectivePrice: effectivePrice, priceImpact: priceImpact, }; } // Example: Calculate impact for swapping 1 token const reserveIn = 1000000000000000000000n; // 1000 tokens (18 decimals) const reserveOut = 500000000000000000000n; // 500 tokens (18 decimals) const amountIn = 10000000000000000000n; // 10 tokens const result = calculatePriceImpact(reserveIn, reserveOut, amountIn, 18, 18); console.log(`Amount out: ${ethers.formatEther(result.amountOut)}`); console.log(`Spot price: ${result.spotPrice.toFixed(8)}`); console.log(`Effective price: ${result.effectivePrice.toFixed(8)}`); console.log(`Price impact: ${result.priceImpact.toFixed(4)}%`); ``` ## Working with WMON WMON (Wrapped MON) is essential for DEX trading since most pools pair tokens against WMON rather than native MON. ### Wrap MON to WMON ```javascript wrap-mon.js theme={"system"} const { ethers } = require("ethers"); const CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT"; const PRIVATE_KEY = "YOUR_PRIVATE_KEY"; const WMON_ADDRESS = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541"; // WMON on Monad Testnet const WMON_ABI = [ "function deposit() public payable", "function withdraw(uint256 amount) public", "function balanceOf(address account) public view returns (uint256)", ]; const provider = new ethers.JsonRpcProvider(CHAINSTACK_ENDPOINT); const wallet = new ethers.Wallet(PRIVATE_KEY, provider); const wmon = new ethers.Contract(WMON_ADDRESS, WMON_ABI, wallet); async function wrapMON(amount) { console.log(`Wrapping ${ethers.formatEther(amount)} MON to WMON...`); const tx = await wmon.deposit({ value: amount }); console.log(`Transaction: ${tx.hash}`); await tx.wait(); console.log("Wrap complete!"); const balance = await wmon.balanceOf(wallet.address); console.log(`WMON balance: ${ethers.formatEther(balance)}`); } async function unwrapWMON(amount) { console.log(`Unwrapping ${ethers.formatEther(amount)} WMON to MON...`); const tx = await wmon.withdraw(amount); console.log(`Transaction: ${tx.hash}`); await tx.wait(); console.log("Unwrap complete!"); } // Wrap 0.1 MON wrapMON(ethers.parseEther("0.1")); ``` ## Monad-specific notes > [!NOTE] > > **Why Monad excels for DEX price fetching:** > > * **1-second freshness**: Prices are at most 1 second old, crucial for arbitrage and trading bots > * **Immediate finality**: No need to wait for confirmations. The price you see is the price you get. > * **High throughput**: DEXs can handle more trades per second, leading to more liquid markets > * **No reorg risk**: Price data is reliable immediately. No worrying about chain reorganizations invalidating trades. > [!WARNING] > > **Price fetching best practices:** > > * **Always check liquidity**: Low liquidity pools can have unreliable prices > * **Account for fees**: Uniswap V2 charges 0.3% per swap > * **Handle decimals carefully**: Token decimals vary (6 for USDC, 18 for most tokens) > * **Use multiple sources**: Cross-check prices across pools for accuracy ## Next steps Now that you can fetch DEX prices on Monad, you can: * Build trading bots that react to price changes * Create price aggregators across multiple DEXs * Implement arbitrage detection systems * Build portfolio tracking dashboards * Create price alerts and notifications # Monad: Monitoring events and transactions Source: https://docs.chainstack.com/docs/monad-tutorial-event-monitoring Build real-time smart contract event monitoring on Monad using ethers.js and web3.py with eth_subscribe WebSocket streaming and eth_getLogs polling. This tutorial teaches you how to monitor on-chain events and transactions on Monad. You'll use WebSocket subscriptions (`eth_subscribe`) for real-time streaming, with HTTP polling via `eth_getLogs` as an alternative pattern for environments where WebSocket isn't an option. **TLDR:** * Stream real-time events with `eth_subscribe("logs", {...})` over WebSocket * Stream new blocks with `eth_subscribe("newHeads")` * Alternative: query logs over HTTP with `eth_getLogs` * Monitor ERC-20 Transfer events on WMON (Wrapped MON) * Handle Monad's high-throughput blocks efficiently * Both JavaScript and Python implementations ## Prerequisites * [Chainstack account](https://console.chainstack.com/) with a Monad node endpoint * Node.js v16+ or Python 3.8+ * Basic understanding of Ethereum events and logs ## Overview Event monitoring on Monad differs from Ethereum in a few ways: * **WebSocket subscriptions** — `eth_subscribe("newHeads")` and `eth_subscribe("logs", {...})` are supported for real-time streaming. `eth_subscribe("newPendingTransactions")` and `eth_subscribe("syncing")` are not supported and return `-32602 Invalid params`. * **1-second blocks** — events appear roughly every second * **High transaction volume** — blocks contain many transactions, so when using `eth_getLogs`, query small block ranges * **Immediate finality** — no need to wait for confirmations or handle reorgs This tutorial shows you how to build efficient monitoring tools that work with Monad's architecture. > [!NOTE] > > Use your Chainstack node's WSS endpoint (`wss://…`) for subscription examples. See [Node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). The examples watch the Wrapped MON (WMON) contract on Monad Mainnet, `0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A`. On Monad Testnet, WMON is at `0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541`. ## Understanding Monad's event system Events (logs) on Monad work the same as Ethereum: 1. Smart contracts emit events during execution 2. Events are stored in transaction receipts 3. You stream events in real time with `eth_subscribe("logs", {...})` over WebSocket, or query historical events with `eth_getLogs` over HTTP Pick the pattern that fits your use case: * **Real-time streaming** — use `eth_subscribe` over WebSocket. Lower latency, no polling loop, no missed blocks * **Historical queries or HTTP-only environments** — use `eth_getLogs`. Use small block ranges (1–10 blocks) per query, and process logs immediately since they're final ## Stream events in real time with `eth_subscribe` `eth_subscribe("logs", {...})` streams matching events to you over a WebSocket connection as soon as a block containing them lands. No polling, no missed blocks, no extra request budget burned on idle intervals. ### JavaScript — subscribe to WMON Transfers ```javascript subscribe-wmon.js theme={"system"} const { ethers } = require("ethers"); const CHAINSTACK_WSS_ENDPOINT = "YOUR_CHAINSTACK_WSS_ENDPOINT"; const WMON_ADDRESS = "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A"; const TRANSFER_TOPIC = ethers.id("Transfer(address,address,uint256)"); const provider = new ethers.WebSocketProvider(CHAINSTACK_WSS_ENDPOINT); function decodeTransfer(log) { const from = "0x" + log.topics[1].slice(26); const to = "0x" + log.topics[2].slice(26); const value = BigInt(log.data); return { from, to, value: ethers.formatEther(value), blockNumber: log.blockNumber, transactionHash: log.transactionHash, }; } async function subscribe() { console.log(`Subscribing to WMON Transfer events at ${WMON_ADDRESS}`); const filter = { address: WMON_ADDRESS, topics: [TRANSFER_TOPIC], }; provider.on(filter, (log) => { const t = decodeTransfer(log); console.log(`Block ${t.blockNumber}: ${t.value} WMON from ${t.from} to ${t.to}`); console.log(` Tx: ${t.transactionHash}`); }); } subscribe(); ``` Run: ```bash theme={"system"} node subscribe-wmon.js ``` Under the hood, `provider.on(filter, handler)` sends a raw `eth_subscribe` request: ```json theme={"system"} {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["logs",{"address":"0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]} ``` ### Python — subscribe to WMON Transfers ```python subscribe_wmon.py theme={"system"} import asyncio import json import websockets CHAINSTACK_WSS_ENDPOINT = "YOUR_CHAINSTACK_WSS_ENDPOINT" WMON_ADDRESS = "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A" # keccak256("Transfer(address,address,uint256)") TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" async def subscribe(): async with websockets.connect(CHAINSTACK_WSS_ENDPOINT) as ws: await ws.send(json.dumps({ "jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["logs", { "address": WMON_ADDRESS, "topics": [TRANSFER_TOPIC], }], })) sub = json.loads(await ws.recv()) print(f"Subscribed: {sub['result']}") async for raw in ws: msg = json.loads(raw) if msg.get("method") != "eth_subscription": continue log = msg["params"]["result"] from_addr = "0x" + log["topics"][1][26:] to_addr = "0x" + log["topics"][2][26:] value_wei = int(log["data"], 16) value = value_wei / 10**18 print(f"Block {int(log['blockNumber'], 16)}: {value} WMON " f"from {from_addr} to {to_addr}") print(f" Tx: {log['transactionHash']}") asyncio.run(subscribe()) ``` Run: ```bash theme={"system"} pip install websockets python subscribe_wmon.py ``` ### Subscribe to new block headers Use `eth_subscribe("newHeads")` to react to every new block as it's produced: ```javascript subscribe-newheads.js theme={"system"} const { ethers } = require("ethers"); const CHAINSTACK_WSS_ENDPOINT = "YOUR_CHAINSTACK_WSS_ENDPOINT"; const provider = new ethers.WebSocketProvider(CHAINSTACK_WSS_ENDPOINT); provider.on("block", async (blockNumber) => { const block = await provider.getBlock(blockNumber); const utilization = ((Number(block.gasUsed) / Number(block.gasLimit)) * 100).toFixed(2); console.log(`Block ${blockNumber}: ${block.transactions.length} txs, gas ${utilization}%`); }); ``` ## Alternative: poll with `eth_getLogs` If you can't use WebSocket — for example, in a serverless function, a browser environment without persistent sockets, or a constrained runtime — poll `eth_getLogs` over HTTP instead. ### JavaScript polling implementation ```javascript monitor-wmon.js theme={"system"} const { ethers } = require("ethers"); const CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT"; const WMON_ADDRESS = "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A"; // Transfer event signature: keccak256("Transfer(address,address,uint256)") const TRANSFER_TOPIC = ethers.id("Transfer(address,address,uint256)"); const provider = new ethers.JsonRpcProvider(CHAINSTACK_ENDPOINT); async function getTransferEvents(fromBlock, toBlock) { const filter = { address: WMON_ADDRESS, topics: [TRANSFER_TOPIC], fromBlock: fromBlock, toBlock: toBlock, }; const logs = await provider.getLogs(filter); return logs; } function decodeTransferEvent(log) { // Transfer(address indexed from, address indexed to, uint256 value) const from = "0x" + log.topics[1].slice(26); const to = "0x" + log.topics[2].slice(26); const value = BigInt(log.data); return { from, to, value: ethers.formatEther(value), blockNumber: log.blockNumber, transactionHash: log.transactionHash, logIndex: log.index, }; } async function monitorTransfers() { let lastBlock = await provider.getBlockNumber(); console.log(`Starting WMON Transfer monitor at block ${lastBlock}`); console.log(`Contract: ${WMON_ADDRESS}\n`); // Poll every second setInterval(async () => { try { const currentBlock = await provider.getBlockNumber(); if (currentBlock > lastBlock) { // Query new blocks const fromBlock = lastBlock + 1; const toBlock = currentBlock; console.log(`Checking blocks ${fromBlock} to ${toBlock}...`); const logs = await getTransferEvents(fromBlock, toBlock); if (logs.length > 0) { console.log(`Found ${logs.length} Transfer events:\n`); for (const log of logs) { const transfer = decodeTransferEvent(log); console.log(`Block ${transfer.blockNumber}:`); console.log(` From: ${transfer.from}`); console.log(` To: ${transfer.to}`); console.log(` Amount: ${transfer.value} WMON`); console.log(` Tx: ${transfer.transactionHash}\n`); } } lastBlock = currentBlock; } } catch (error) { console.error("Error:", error.message); } }, 1000); } monitorTransfers(); ``` Run: ```bash theme={"system"} node monitor-wmon.js ``` ### Python polling implementation ```python monitor_wmon.py theme={"system"} from web3 import Web3 import time CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT" WMON_ADDRESS = "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A" # Transfer event signature TRANSFER_TOPIC = Web3.keccak(text="Transfer(address,address,uint256)").hex() web3 = Web3(Web3.HTTPProvider(CHAINSTACK_ENDPOINT)) print(f"Connected: {web3.is_connected()}") def get_transfer_events(from_block, to_block): """Fetch Transfer events in a block range.""" filter_params = { 'address': WMON_ADDRESS, 'topics': [TRANSFER_TOPIC], 'fromBlock': from_block, 'toBlock': to_block } logs = web3.eth.get_logs(filter_params) return logs def decode_transfer_event(log): """Decode a Transfer event log.""" from_address = '0x' + log['topics'][1].hex()[26:] to_address = '0x' + log['topics'][2].hex()[26:] value = int(log['data'].hex(), 16) return { 'from': Web3.to_checksum_address(from_address), 'to': Web3.to_checksum_address(to_address), 'value': web3.from_wei(value, 'ether'), 'block_number': log['blockNumber'], 'transaction_hash': log['transactionHash'].hex(), 'log_index': log['logIndex'] } def monitor_transfers(): """Monitor WMON Transfer events continuously.""" last_block = web3.eth.block_number print(f"Starting WMON Transfer monitor at block {last_block}") print(f"Contract: {WMON_ADDRESS}\n") while True: try: current_block = web3.eth.block_number if current_block > last_block: from_block = last_block + 1 to_block = current_block print(f"Checking blocks {from_block} to {to_block}...") logs = get_transfer_events(from_block, to_block) if logs: print(f"Found {len(logs)} Transfer events:\n") for log in logs: transfer = decode_transfer_event(log) print(f"Block {transfer['block_number']}:") print(f" From: {transfer['from']}") print(f" To: {transfer['to']}") print(f" Amount: {transfer['value']} WMON") print(f" Tx: {transfer['transaction_hash']}\n") last_block = current_block # Poll every second time.sleep(1) except Exception as e: print(f"Error: {e}") time.sleep(1) if __name__ == "__main__": monitor_transfers() ``` Run: ```bash theme={"system"} python monitor_wmon.py ``` ## Build a block monitor with polling Monitor all transactions in new blocks over HTTP. For a WebSocket equivalent, see the `eth_subscribe("newHeads")` example above. ### JavaScript block monitor ```javascript monitor-blocks.js theme={"system"} const { ethers } = require("ethers"); const CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT"; const provider = new ethers.JsonRpcProvider(CHAINSTACK_ENDPOINT); async function processBlock(blockNumber) { const block = await provider.getBlock(blockNumber, true); // true = include transactions const timestamp = new Date(block.timestamp * 1000).toISOString(); const gasUsed = block.gasUsed.toString(); const gasLimit = block.gasLimit.toString(); const utilization = ((Number(block.gasUsed) / Number(block.gasLimit)) * 100).toFixed(2); console.log(`\n=== Block ${blockNumber} ===`); console.log(`Timestamp: ${timestamp}`); console.log(`Transactions: ${block.transactions.length}`); console.log(`Gas used: ${gasUsed} / ${gasLimit} (${utilization}%)`); console.log(`Miner: ${block.miner}`); // Process individual transactions if needed if (block.prefetchedTransactions && block.prefetchedTransactions.length > 0) { // Show first 5 transactions const txs = block.prefetchedTransactions.slice(0, 5); console.log(`\nFirst ${txs.length} transactions:`); for (const tx of txs) { const value = ethers.formatEther(tx.value); console.log(` ${tx.hash.slice(0, 18)}... | ${tx.from.slice(0, 10)}... -> ${tx.to?.slice(0, 10) || 'Contract creation'}... | ${value} MON`); } if (block.prefetchedTransactions.length > 5) { console.log(` ... and ${block.prefetchedTransactions.length - 5} more`); } } } async function monitorBlocks() { let lastBlock = await provider.getBlockNumber(); console.log(`Starting block monitor at block ${lastBlock}`); setInterval(async () => { try { const currentBlock = await provider.getBlockNumber(); if (currentBlock > lastBlock) { for (let i = lastBlock + 1; i <= currentBlock; i++) { await processBlock(i); } lastBlock = currentBlock; } } catch (error) { console.error("Error:", error.message); } }, 1000); } monitorBlocks(); ``` ### Python block monitor ```python monitor_blocks.py theme={"system"} from web3 import Web3 import time from datetime import datetime CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT" web3 = Web3(Web3.HTTPProvider(CHAINSTACK_ENDPOINT)) def process_block(block_number): """Process and display block information.""" block = web3.eth.get_block(block_number, full_transactions=True) timestamp = datetime.fromtimestamp(block.timestamp).isoformat() gas_used = block.gasUsed gas_limit = block.gasLimit utilization = (gas_used / gas_limit) * 100 print(f"\n=== Block {block_number} ===") print(f"Timestamp: {timestamp}") print(f"Transactions: {len(block.transactions)}") print(f"Gas used: {gas_used:,} / {gas_limit:,} ({utilization:.2f}%)") print(f"Miner: {block.miner}") # Show first 5 transactions if block.transactions: txs = block.transactions[:5] print(f"\nFirst {len(txs)} transactions:") for tx in txs: value = web3.from_wei(tx.value, 'ether') to_addr = tx.to[:10] + '...' if tx.to else 'Contract creation' print(f" {tx.hash.hex()[:18]}... | {tx['from'][:10]}... -> {to_addr} | {value:.4f} MON") if len(block.transactions) > 5: print(f" ... and {len(block.transactions) - 5} more") def monitor_blocks(): """Monitor new blocks continuously.""" last_block = web3.eth.block_number print(f"Starting block monitor at block {last_block}") while True: try: current_block = web3.eth.block_number if current_block > last_block: for block_num in range(last_block + 1, current_block + 1): process_block(block_num) last_block = current_block time.sleep(1) except Exception as e: print(f"Error: {e}") time.sleep(1) if __name__ == "__main__": monitor_blocks() ``` ## Monitor custom contract events Track events from any contract by defining the event signature: ```javascript monitor-custom.js theme={"system"} const { ethers } = require("ethers"); const CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT"; const provider = new ethers.JsonRpcProvider(CHAINSTACK_ENDPOINT); // Example: Monitor any ERC-20 Approval events const APPROVAL_TOPIC = ethers.id("Approval(address,address,uint256)"); // Or monitor a specific contract const CONTRACT_ADDRESS = "YOUR_CONTRACT_ADDRESS"; const CUSTOM_EVENT_TOPIC = ethers.id("YourEvent(address,uint256)"); async function monitorEvents(address, topics, eventName) { let lastBlock = await provider.getBlockNumber(); console.log(`Monitoring ${eventName} events from block ${lastBlock}`); setInterval(async () => { try { const currentBlock = await provider.getBlockNumber(); if (currentBlock > lastBlock) { const filter = { address: address, // null for all contracts topics: topics, fromBlock: lastBlock + 1, toBlock: currentBlock, }; const logs = await provider.getLogs(filter); if (logs.length > 0) { console.log(`\nFound ${logs.length} ${eventName} events:`); for (const log of logs) { console.log(` Block ${log.blockNumber}: ${log.transactionHash}`); } } lastBlock = currentBlock; } } catch (error) { console.error("Error:", error.message); } }, 1000); } // Monitor all ERC-20 Approvals on Monad monitorEvents(null, [APPROVAL_TOPIC], "Approval"); ``` ## Filter events by address Monitor transfers to or from a specific address: ```javascript monitor-address.js theme={"system"} const { ethers } = require("ethers"); const CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT"; const WMON_ADDRESS = "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A"; const WATCH_ADDRESS = "YOUR_ADDRESS_TO_WATCH"; const TRANSFER_TOPIC = ethers.id("Transfer(address,address,uint256)"); const provider = new ethers.JsonRpcProvider(CHAINSTACK_ENDPOINT); async function monitorAddressTransfers(address) { let lastBlock = await provider.getBlockNumber(); // Pad address to 32 bytes for topic matching const paddedAddress = ethers.zeroPadValue(address, 32); console.log(`Monitoring WMON transfers for ${address}`); console.log(`Starting at block ${lastBlock}\n`); setInterval(async () => { try { const currentBlock = await provider.getBlockNumber(); if (currentBlock > lastBlock) { // Incoming transfers (address is 'to') const incomingFilter = { address: WMON_ADDRESS, topics: [TRANSFER_TOPIC, null, paddedAddress], fromBlock: lastBlock + 1, toBlock: currentBlock, }; // Outgoing transfers (address is 'from') const outgoingFilter = { address: WMON_ADDRESS, topics: [TRANSFER_TOPIC, paddedAddress, null], fromBlock: lastBlock + 1, toBlock: currentBlock, }; const [incomingLogs, outgoingLogs] = await Promise.all([ provider.getLogs(incomingFilter), provider.getLogs(outgoingFilter), ]); for (const log of incomingLogs) { const from = "0x" + log.topics[1].slice(26); const value = ethers.formatEther(BigInt(log.data)); console.log(`📥 INCOMING: ${value} WMON from ${from}`); console.log(` Tx: ${log.transactionHash}\n`); } for (const log of outgoingLogs) { const to = "0x" + log.topics[2].slice(26); const value = ethers.formatEther(BigInt(log.data)); console.log(`📤 OUTGOING: ${value} WMON to ${to}`); console.log(` Tx: ${log.transactionHash}\n`); } lastBlock = currentBlock; } } catch (error) { console.error("Error:", error.message); } }, 1000); } monitorAddressTransfers(WATCH_ADDRESS); ``` ## Best practices for Monad > [!NOTE] > > **Optimize your monitoring for Monad's characteristics:** > > 1. **Prefer `eth_subscribe` over polling** — WebSocket subscriptions are lower latency and don't burn request budget on idle intervals. Reach for `eth_getLogs` polling only when WebSocket isn't an option. > > 2. **Use small block ranges when polling** — query 1–10 blocks at a time. Monad blocks can contain thousands of transactions. > > 3. **Match polling cadence to block time** — Monad produces blocks every \~1 second. Polling faster wastes requests; slower misses events. > > 4. **Handle high volume** — be prepared for blocks with many events. Process asynchronously if needed. > > 5. **No reorg handling needed** — Monad has instant finality. Once you see an event, it's permanent. > > 6. **Batch your queries** — if monitoring multiple contracts, combine filters where possible. > [!WARNING] > > **`eth_getLogs` limitations:** > > * Most nodes limit the block range per query (typically 1,000–10,000 blocks) > * For historical data, paginate through block ranges > * For real-time monitoring, stick to recent blocks only — or use `eth_subscribe("logs", {...})` instead ## Monad-specific notes > [!NOTE] > > **Key differences from Ethereum monitoring:** > > * **`eth_subscribe` subset** — `newHeads` and `logs` are supported. `eth_subscribe("newPendingTransactions")` and `eth_subscribe("syncing")` are not supported on Monad and return `-32602 Invalid params`. > * **1-second blocks** — events appear faster than on Ethereum. Your monitor needs to keep up. > * **No pending transactions** — you can't monitor the mempool. Only confirmed transactions are visible. > * **Immediate finality** — no need to wait for confirmations. Events are final when you see them. > * **High throughput** — expect more events per block than on Ethereum. Design accordingly. ## Complete monitoring script A production-ready monitor combining all techniques: ```javascript monitor-complete.js theme={"system"} const { ethers } = require("ethers"); const CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT"; const provider = new ethers.JsonRpcProvider(CHAINSTACK_ENDPOINT); class MonadEventMonitor { constructor(config) { this.lastBlock = 0; this.config = config; this.isRunning = false; } async start() { this.lastBlock = await provider.getBlockNumber(); this.isRunning = true; console.log(`Monitor started at block ${this.lastBlock}`); this.poll(); } stop() { this.isRunning = false; console.log("Monitor stopped"); } async poll() { while (this.isRunning) { try { const currentBlock = await provider.getBlockNumber(); if (currentBlock > this.lastBlock) { await this.processNewBlocks(this.lastBlock + 1, currentBlock); this.lastBlock = currentBlock; } await this.sleep(1000); } catch (error) { console.error("Poll error:", error.message); await this.sleep(1000); } } } async processNewBlocks(fromBlock, toBlock) { for (const filter of this.config.filters) { const filterWithBlocks = { ...filter.params, fromBlock, toBlock, }; const logs = await provider.getLogs(filterWithBlocks); if (logs.length > 0) { filter.handler(logs); } } } sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } } // Example usage const WMON = "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A"; const TRANSFER = ethers.id("Transfer(address,address,uint256)"); const monitor = new MonadEventMonitor({ filters: [ { name: "WMON Transfers", params: { address: WMON, topics: [TRANSFER], }, handler: (logs) => { console.log(`\n📊 ${logs.length} WMON transfers detected`); for (const log of logs.slice(0, 3)) { const value = ethers.formatEther(BigInt(log.data)); console.log(` ${value} WMON in block ${log.blockNumber}`); } if (logs.length > 3) { console.log(` ... and ${logs.length - 3} more`); } }, }, ], }); monitor.start(); // Stop after 60 seconds (for demo) // setTimeout(() => monitor.stop(), 60000); ``` ## Next steps Now that you can monitor events on Monad, you can: * Build real-time dashboards for DEX activity * Create alerting systems for large transfers * Track NFT mints and sales * Monitor your own smart contract events * Build analytics pipelines for on-chain data # Monad: Local fork testing with Foundry Source: https://docs.chainstack.com/docs/monad-tutorial-local-fork-testing Fork Monad mainnet locally with Foundry Anvil for reproducible smart contract testing without testnet dependencies, using a Chainstack RPC endpoint. This tutorial teaches you how to set up a local Monad fork using Foundry's Anvil. You'll learn to test smart contracts against real mainnet state without spending gas or relying on testnet availability. **TLDR:** * Fork Monad mainnet locally using Anvil and a Chainstack RPC endpoint * Run tests against real on-chain state without spending gas * Impersonate any account to test interactions with deployed contracts * Avoid testnet instability and re-genesis disruptions ## Prerequisites * [Chainstack account](https://console.chainstack.com/) with a Monad node endpoint * [Foundry](https://getfoundry.sh/) installed * Basic Solidity and command-line knowledge ## Why fork locally? Testing on public testnets comes with challenges: * **Testnet resets**: Networks occasionally undergo re-genesis, wiping all deployed contracts and balances * **Faucet limitations**: Getting test tokens can be slow or rate-limited * **Shared state**: Other developers' transactions can interfere with your tests * **Network issues**: Testnets may experience downtime or congestion A local fork solves these problems by copying the mainnet state to your machine. You get: * **Reproducible tests**: Same state every time you run tests * **Instant execution**: No network latency, no waiting for blocks * **Free transactions**: No gas costs for testing * **Account impersonation**: Test as any address, including whales and protocols ## Set up the fork ### Install Foundry If you haven't installed Foundry yet: ```bash theme={"system"} curl -L https://foundry.paradigm.xyz | bash foundryup ``` ### Start the fork Launch Anvil with your Chainstack Monad endpoint: ```bash theme={"system"} anvil --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT ``` You'll see output like: ``` _ _ (_) | | __ _ _ __ __ __ _ | | / _` | | '_ \ \ \ / / | | | | | (_| | | | | | \ V / | | | | \__,_| |_| |_| \_/ |_| |_| 0.2.0 (abcdef 2024-01-01T00:00:00.000000000Z) https://github.com/foundry-rs/foundry Available Accounts ================== (0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000.000000000000000000 ETH) (1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000.000000000000000000 ETH) ... Listening on 127.0.0.1:8545 ``` Anvil provides 10 pre-funded accounts with 10,000 ETH each (displayed as ETH but these are MON on Monad forks). ### Fork configuration options Customize your fork with additional flags: ```bash theme={"system"} # Fork from a specific block anvil --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT --fork-block-number 12345678 # Increase the number of accounts anvil --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT --accounts 20 # Set a custom chain ID (useful for wallet compatibility) anvil --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT --chain-id 31337 # Enable auto-mining with a specific interval anvil --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT --block-time 1 ``` ## Interact with forked state With the fork running, you can query real mainnet data using the local RPC. ### Query account balances ```bash theme={"system"} # Check a mainnet address balance on your fork cast balance 0xYOUR_ADDRESS --rpc-url http://127.0.0.1:8545 ``` ### Read contract state ```bash theme={"system"} # Call a view function on a deployed contract cast call CONTRACT_ADDRESS "balanceOf(address)" 0xYOUR_ADDRESS --rpc-url http://127.0.0.1:8545 ``` ### Send transactions Use one of Anvil's pre-funded accounts: ```bash theme={"system"} # Send MON from a test account cast send 0xRECIPIENT --value 1ether \ --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ --rpc-url http://127.0.0.1:8545 ``` The private key above is Anvil's first default account—safe to use in local testing. ## Impersonate accounts One of the most powerful features of local forking is account impersonation. You can send transactions as any address without needing its private key. ### Impersonate a whale ```bash theme={"system"} # Unlock an account for impersonation cast rpc anvil_impersonateAccount 0xWHALE_ADDRESS --rpc-url http://127.0.0.1:8545 # Send a transaction as the whale cast send 0xRECIPIENT --value 100ether \ --from 0xWHALE_ADDRESS \ --unlocked \ --rpc-url http://127.0.0.1:8545 # Stop impersonating cast rpc anvil_stopImpersonatingAccount 0xWHALE_ADDRESS --rpc-url http://127.0.0.1:8545 ``` ### Test protocol interactions Impersonation is useful for testing how your contract interacts with existing protocols: ```bash theme={"system"} # Impersonate a DEX router to test swap callbacks cast rpc anvil_impersonateAccount 0xDEX_ROUTER_ADDRESS --rpc-url http://127.0.0.1:8545 # Call your contract as if the DEX router is calling it cast send YOUR_CONTRACT "swapCallback(uint256,uint256,bytes)" 1000 2000 0x \ --from 0xDEX_ROUTER_ADDRESS \ --unlocked \ --rpc-url http://127.0.0.1:8545 ``` ## Write fork tests with Forge Foundry's Forge test framework has built-in fork testing support. ### Create a test file Create `test/ForkTest.t.sol`: ```solidity test/ForkTest.t.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "forge-std/Test.sol"; contract ForkTest is Test { function setUp() public { // Fork is configured via command line or foundry.toml } function testForkBlockNumber() public view { // Verify we're on a Monad fork uint256 blockNum = block.number; console.log("Current block number:", blockNum); assertGt(blockNum, 0, "Should be on a fork with blocks"); } function testChainId() public view { // Monad mainnet chain ID is 143 uint256 chainId = block.chainid; console.log("Chain ID:", chainId); assertEq(chainId, 143, "Should be Monad chain"); } function testImpersonateAndSend() public { // Create fresh addresses for testing address whale = makeAddr("whale"); address recipient = makeAddr("recipient"); // Give the whale some MON vm.deal(whale, 100 ether); // Impersonate the whale vm.startPrank(whale); // Send MON (bool success,) = recipient.call{value: 1 ether}(""); require(success, "Transfer failed"); vm.stopPrank(); // Verify transfer assertEq(recipient.balance, 1 ether); assertEq(whale.balance, 99 ether); } function testDealNativeToken() public { address recipient = makeAddr("recipient"); // Give recipient some MON vm.deal(recipient, 50 ether); assertEq(recipient.balance, 50 ether); } } ``` ### Configure fork in foundry.toml Add your RPC endpoint to `foundry.toml`: ```toml foundry.toml theme={"system"} [profile.default] src = "src" out = "out" libs = ["lib"] [rpc_endpoints] monad = "${CHAINSTACK_MONAD_ENDPOINT}" [profile.default.fuzz] runs = 256 ``` ### Run fork tests ```bash theme={"system"} # Run all tests against the fork forge test --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT # Run with verbose output forge test --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT -vvv # Run a specific test forge test --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT --match-test testImpersonateTransfer # Use environment variable export CHAINSTACK_MONAD_ENDPOINT="your-endpoint-here" forge test --fork-url $CHAINSTACK_MONAD_ENDPOINT ``` ### Fork from a specific block For reproducible tests, pin to a specific block: ```bash theme={"system"} forge test --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT --fork-block-number 12345678 ``` Or in `foundry.toml`: ```toml foundry.toml theme={"system"} [profile.default] fork_block_number = 12345678 ``` ## Advanced fork techniques ### Snapshot and revert Save and restore fork state during tests: ```solidity theme={"system"} function testWithSnapshot() public { address target = makeAddr("target"); uint256 snapshot = vm.snapshotState(); // Make changes vm.deal(target, 1000 ether); assertEq(target.balance, 1000 ether); // Revert to snapshot vm.revertToState(snapshot); // State is restored assertEq(target.balance, 0); } ``` ### Manipulate block properties ```solidity theme={"system"} function testTimeTravel() public { // Move forward 1 day vm.warp(block.timestamp + 1 days); // Move forward 100 blocks vm.roll(block.number + 100); // Set block base fee vm.fee(1 gwei); } ``` ### Mock contract calls ```solidity theme={"system"} function testMockCall() public { // Mock a specific call to return custom data vm.mockCall( address(token), abi.encodeWithSelector(IERC20.balanceOf.selector, address(this)), abi.encode(1_000_000) ); // Now this returns 1_000_000 regardless of actual balance assertEq(token.balanceOf(address(this)), 1_000_000); } ``` ## Example: Testing a DEX interaction Here's a complete example testing a swap on a forked DEX: ```solidity test/DexForkTest.t.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "forge-std/Test.sol"; interface IRouter { function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); } interface IERC20 { function balanceOf(address) external view returns (uint256); function approve(address, uint256) external returns (bool); } contract DexForkTest is Test { // Replace with actual Monad mainnet addresses address constant ROUTER = 0xROUTER_ADDRESS; address constant TOKEN_A = 0xTOKEN_A_ADDRESS; address constant TOKEN_B = 0xTOKEN_B_ADDRESS; address constant WHALE = 0xWHALE_WITH_TOKEN_A; IRouter router; IERC20 tokenA; IERC20 tokenB; function setUp() public { router = IRouter(ROUTER); tokenA = IERC20(TOKEN_A); tokenB = IERC20(TOKEN_B); } function testSwap() public { uint256 swapAmount = 1000e18; // Impersonate whale vm.startPrank(WHALE); // Approve router tokenA.approve(ROUTER, swapAmount); // Build swap path address[] memory path = new address[](2); path[0] = TOKEN_A; path[1] = TOKEN_B; // Get initial balance uint256 initialBalance = tokenB.balanceOf(WHALE); // Execute swap router.swapExactTokensForTokens( swapAmount, 0, // Accept any amount for testing path, WHALE, block.timestamp + 1 hours ); vm.stopPrank(); // Verify swap succeeded assertGt(tokenB.balanceOf(WHALE), initialBalance); console.log("Received:", tokenB.balanceOf(WHALE) - initialBalance); } } ``` Run the test: ```bash theme={"system"} forge test --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT --match-test testSwap -vvv ``` ## Troubleshooting ### Fork is slow to start Large state can take time to cache. Subsequent runs are faster. You can also: ```bash theme={"system"} # Limit the number of storage slots cached anvil --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT --no-storage-caching ``` ### RPC rate limiting If you hit rate limits, Chainstack paid plans offer higher limits. You can also: ```bash theme={"system"} # Reduce concurrent requests anvil --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT --compute-units-per-second 100 ``` ### State mismatch errors If contract state doesn't match expectations, ensure you're forking from the correct block: ```bash theme={"system"} # Check current block number cast block-number --rpc-url YOUR_CHAINSTACK_MONAD_ENDPOINT # Fork from that specific block anvil --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT --fork-block-number BLOCK_NUMBER ``` ### Transaction reverts unexpectedly Enable tracing to debug: ```bash theme={"system"} forge test --fork-url YOUR_CHAINSTACK_MONAD_ENDPOINT -vvvv ``` The extra `v` flags show detailed call traces. ## Next steps Now that you can test against forked mainnet state, you can: * Build integration tests for complex protocol interactions * Test upgrades against production contract state * Debug mainnet transaction failures locally # Monad: Minting and managing NFTs Source: https://docs.chainstack.com/docs/monad-tutorial-nft-minting-erc721 Build and deploy an ERC-721 NFT collection on Monad mainnet with Hardhat and OpenZeppelin, then mint and query NFTs programmatically This tutorial teaches you how to create, deploy, and interact with an ERC-721 NFT collection on Monad. You'll build a mintable NFT contract with OpenZeppelin and learn to mint NFTs using both JavaScript and Python. **TLDR:** * Create an ERC-721 NFT collection contract with OpenZeppelin * Deploy to Monad mainnet with Hardhat * Mint NFTs programmatically using ethers.js and web3.py * Query NFT ownership and metadata * Understand why Monad's 1-second finality is ideal for NFTs ## Prerequisites * [Chainstack account](https://console.chainstack.com/) with a Monad node endpoint * Node.js v16+ and Python 3.8+ * Basic Solidity knowledge * MON tokens for gas fees ## Overview NFTs on Monad benefit from: * **Instant ownership confirmation**: 1-second finality means buyers see their NFT immediately * **High throughput**: Mint thousands of NFTs without network congestion * **Low latency**: Real-time updates for marketplaces and galleries * **No reorganizations**: Ownership is permanent once confirmed This tutorial covers the full lifecycle: contract creation, deployment, minting, and querying. ## Create the NFT contract ### Set up the project ```bash theme={"system"} mkdir monad-nft && cd monad-nft npm init -y npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox dotenv npm install @openzeppelin/contracts npx hardhat init ``` Select "Create a JavaScript project" when prompted. ### Configure environment variables Create a `.env` file: ```bash .env theme={"system"} CHAINSTACK_ENDPOINT="YOUR_CHAINSTACK_MONAD_ENDPOINT" PRIVATE_KEY="YOUR_WALLET_PRIVATE_KEY" ``` ### Configure Hardhat Replace `hardhat.config.js`: ```javascript hardhat.config.js theme={"system"} require("@nomicfoundation/hardhat-toolbox"); require("dotenv").config(); module.exports = { solidity: "0.8.24", networks: { monad: { url: process.env.CHAINSTACK_ENDPOINT, chainId: 143, accounts: [process.env.PRIVATE_KEY], }, }, }; ``` ### Write the NFT contract Create `contracts/MonadNFT.sol`: ```solidity contracts/MonadNFT.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MonadNFT is ERC721, ERC721URIStorage, Ownable { uint256 private _nextTokenId; uint256 public maxSupply; uint256 public mintPrice; event NFTMinted(address indexed to, uint256 indexed tokenId, string tokenURI); constructor( string memory name, string memory symbol, uint256 _maxSupply, uint256 _mintPrice ) ERC721(name, symbol) Ownable(msg.sender) { maxSupply = _maxSupply; mintPrice = _mintPrice; } function mint(address to, string memory uri) public payable returns (uint256) { require(_nextTokenId < maxSupply, "Max supply reached"); require(msg.value >= mintPrice, "Insufficient payment"); uint256 tokenId = _nextTokenId; _nextTokenId++; _safeMint(to, tokenId); _setTokenURI(tokenId, uri); emit NFTMinted(to, tokenId, uri); return tokenId; } function ownerMint(address to, string memory uri) public onlyOwner returns (uint256) { require(_nextTokenId < maxSupply, "Max supply reached"); uint256 tokenId = _nextTokenId; _nextTokenId++; _safeMint(to, tokenId); _setTokenURI(tokenId, uri); emit NFTMinted(to, tokenId, uri); return tokenId; } function totalSupply() public view returns (uint256) { return _nextTokenId; } function withdraw() public onlyOwner { uint256 balance = address(this).balance; payable(owner()).transfer(balance); } // Required overrides function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); } function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) { return super.supportsInterface(interfaceId); } } ``` This contract includes: * **Mintable NFTs** with customizable metadata URIs * **Max supply** to limit collection size * **Mint price** for public mints * **Owner minting** for free mints by the contract owner * **Events** for tracking mints ## Deploy the contract Create `scripts/deploy.js`: ```javascript scripts/deploy.js theme={"system"} const hre = require("hardhat"); async function main() { console.log("Deploying MonadNFT contract..."); const name = "Monad Collection"; const symbol = "MNFT"; const maxSupply = 10000; const mintPrice = hre.ethers.parseEther("0.01"); // 0.01 MON const nft = await hre.ethers.deployContract("MonadNFT", [ name, symbol, maxSupply, mintPrice, ]); await nft.waitForDeployment(); const address = await nft.getAddress(); console.log(`MonadNFT deployed to: ${address}`); console.log(`Name: ${name}`); console.log(`Symbol: ${symbol}`); console.log(`Max supply: ${maxSupply}`); console.log(`Mint price: 0.01 MON`); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); }); ``` Deploy: ```bash theme={"system"} npx hardhat run scripts/deploy.js --network monad ``` Save the deployed contract address for the next steps. ## Mint NFTs with JavaScript Create `scripts/mint.js`: ```javascript scripts/mint.js theme={"system"} const { ethers } = require("ethers"); require("dotenv").config(); const NFT_ADDRESS = "YOUR_DEPLOYED_CONTRACT_ADDRESS"; const NFT_ABI = [ "function mint(address to, string memory uri) public payable returns (uint256)", "function ownerMint(address to, string memory uri) public returns (uint256)", "function totalSupply() public view returns (uint256)", "function ownerOf(uint256 tokenId) public view returns (address)", "function tokenURI(uint256 tokenId) public view returns (string)", "function balanceOf(address owner) public view returns (uint256)", "function mintPrice() public view returns (uint256)", "event NFTMinted(address indexed to, uint256 indexed tokenId, string tokenURI)", ]; async function main() { const provider = new ethers.JsonRpcProvider(process.env.CHAINSTACK_ENDPOINT); const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider); const nft = new ethers.Contract(NFT_ADDRESS, NFT_ABI, wallet); console.log(`Connected wallet: ${wallet.address}`); // Get mint price const mintPrice = await nft.mintPrice(); console.log(`Mint price: ${ethers.formatEther(mintPrice)} MON`); // Get current supply const supplyBefore = await nft.totalSupply(); console.log(`Current supply: ${supplyBefore}`); // Mint an NFT const tokenURI = "ipfs://QmExample123456789/metadata.json"; console.log("\nMinting NFT..."); const tx = await nft.mint(wallet.address, tokenURI, { value: mintPrice }); console.log(`Transaction hash: ${tx.hash}`); const receipt = await tx.wait(); console.log(`Transaction confirmed in block: ${receipt.blockNumber}`); // Parse the NFTMinted event const mintEvent = receipt.logs.find( (log) => log.topics[0] === ethers.id("NFTMinted(address,uint256,string)") ); if (mintEvent) { const tokenId = parseInt(mintEvent.topics[2], 16); console.log(`\nMinted token ID: ${tokenId}`); // Query the newly minted NFT const owner = await nft.ownerOf(tokenId); const uri = await nft.tokenURI(tokenId); console.log(`Owner: ${owner}`); console.log(`Token URI: ${uri}`); } // Get updated supply const supplyAfter = await nft.totalSupply(); console.log(`\nNew total supply: ${supplyAfter}`); } main().catch(console.error); ``` Run: ```bash theme={"system"} node scripts/mint.js ``` ## Mint NFTs with Python Install web3.py: ```bash theme={"system"} pip install web3 ``` Create `mint.py`: ```python mint.py theme={"system"} from web3 import Web3 import os # Configuration CHAINSTACK_ENDPOINT = "YOUR_CHAINSTACK_MONAD_ENDPOINT" PRIVATE_KEY = "YOUR_PRIVATE_KEY" NFT_ADDRESS = "YOUR_DEPLOYED_CONTRACT_ADDRESS" # Connect to Monad web3 = Web3(Web3.HTTPProvider(CHAINSTACK_ENDPOINT)) print(f"Connected: {web3.is_connected()}") # Set up account account = web3.eth.account.from_key(PRIVATE_KEY) print(f"Wallet address: {account.address}") # Contract ABI (minimal for minting) NFT_ABI = [ { "inputs": [ {"type": "address", "name": "to"}, {"type": "string", "name": "uri"} ], "name": "mint", "outputs": [{"type": "uint256"}], "stateMutability": "payable", "type": "function" }, { "inputs": [], "name": "totalSupply", "outputs": [{"type": "uint256"}], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "mintPrice", "outputs": [{"type": "uint256"}], "stateMutability": "view", "type": "function" }, { "inputs": [{"type": "uint256", "name": "tokenId"}], "name": "ownerOf", "outputs": [{"type": "address"}], "stateMutability": "view", "type": "function" }, { "inputs": [{"type": "uint256", "name": "tokenId"}], "name": "tokenURI", "outputs": [{"type": "string"}], "stateMutability": "view", "type": "function" }, { "inputs": [{"type": "address", "name": "owner"}], "name": "balanceOf", "outputs": [{"type": "uint256"}], "stateMutability": "view", "type": "function" } ] # Create contract instance nft = web3.eth.contract(address=NFT_ADDRESS, abi=NFT_ABI) def mint_nft(to_address, token_uri): """Mint an NFT to the specified address.""" # Get mint price mint_price = nft.functions.mintPrice().call() print(f"Mint price: {web3.from_wei(mint_price, 'ether')} MON") # Get current supply supply_before = nft.functions.totalSupply().call() print(f"Current supply: {supply_before}") # Build transaction tx = nft.functions.mint(to_address, token_uri).build_transaction({ 'from': account.address, 'value': mint_price, 'gas': 200000, 'gasPrice': web3.eth.gas_price, 'nonce': web3.eth.get_transaction_count(account.address), 'chainId': 143 }) # Sign and send signed_tx = web3.eth.account.sign_transaction(tx, PRIVATE_KEY) tx_hash = web3.eth.send_raw_transaction(signed_tx.raw_transaction) print(f"Transaction hash: {tx_hash.hex()}") # Wait for confirmation receipt = web3.eth.wait_for_transaction_receipt(tx_hash) print(f"Confirmed in block: {receipt.blockNumber}") print(f"Gas used: {receipt.gasUsed}") # Get the minted token ID (it's the supply before minting) token_id = supply_before print(f"\nMinted token ID: {token_id}") return token_id def query_nft(token_id): """Query NFT details.""" owner = nft.functions.ownerOf(token_id).call() uri = nft.functions.tokenURI(token_id).call() print(f"\nToken ID: {token_id}") print(f"Owner: {owner}") print(f"Token URI: {uri}") def get_balance(address): """Get NFT balance for an address.""" balance = nft.functions.balanceOf(address).call() print(f"\nNFT balance for {address}: {balance}") return balance if __name__ == "__main__": # Mint an NFT token_uri = "ipfs://QmExample123456789/metadata.json" token_id = mint_nft(account.address, token_uri) # Query the minted NFT query_nft(token_id) # Check balance get_balance(account.address) ``` Run: ```bash theme={"system"} python mint.py ``` ## Query NFT data ### Get all NFTs owned by an address ```javascript theme={"system"} async function getOwnedNFTs(ownerAddress) { const balance = await nft.balanceOf(ownerAddress); console.log(`${ownerAddress} owns ${balance} NFTs`); // Note: This is a simple approach. For production, use events or indexing const totalSupply = await nft.totalSupply(); const ownedTokens = []; for (let i = 0; i < totalSupply; i++) { try { const owner = await nft.ownerOf(i); if (owner.toLowerCase() === ownerAddress.toLowerCase()) { const uri = await nft.tokenURI(i); ownedTokens.push({ tokenId: i, uri }); } } catch (e) { // Token might be burned } } return ownedTokens; } ``` ### Batch minting For minting multiple NFTs efficiently: ```javascript theme={"system"} async function batchMint(recipients, uris) { const mintPrice = await nft.mintPrice(); for (let i = 0; i < recipients.length; i++) { const tx = await nft.mint(recipients[i], uris[i], { value: mintPrice }); console.log(`Minting ${i + 1}/${recipients.length}: ${tx.hash}`); await tx.wait(); } console.log("Batch minting complete!"); } ``` ## Monad-specific notes > [!NOTE] > > **Why Monad is ideal for NFTs:** > > * **1-second finality**: Buyers see their NFT immediately after purchase. No waiting for confirmations. > * **No reorganizations**: Once minted, ownership is permanent. No risk of losing NFTs to chain reorgs. > * **High throughput**: Mint large collections or handle high-volume drops without network congestion. > * **Instant marketplace updates**: Listings and sales reflect immediately on-chain. > [!WARNING] > > **Token URI best practices:** > > * Store metadata on IPFS or Arweave for permanence > * Use a standard metadata format (OpenSea metadata standards) > * Consider using a base URI + token ID pattern for gas efficiency ## Complete project structure ``` monad-nft/ ├── contracts/ │ └── MonadNFT.sol ├── scripts/ │ ├── deploy.js │ └── mint.js ├── mint.py ├── hardhat.config.js ├── package.json └── .env ``` ## Next steps Now that you can mint NFTs on Monad, you can: * Add metadata to IPFS using services like Pinata or NFT.Storage * Build a minting frontend with React or Next.js * Create a marketplace interface * Implement royalties with ERC-2981 * Add batch minting for efficient large-scale mints # Monad: Querying the blockchain with JavaScript Source: https://docs.chainstack.com/docs/monad-tutorial-querying-blockchain-javascript Read blockchain data from Monad using ethers.js and a Chainstack node endpoint, covering blocks, transactions, balances, and contract state queries. This tutorial teaches you how to query Monad blockchain data using JavaScript and ethers.js. All examples are read-only operations that don't require any MON tokens, making this perfect for getting started. ## Prerequisites * Node.js installed (v16 or higher) * Basic JavaScript knowledge Install ethers.js: ```bash theme={"system"} npm install ethers ``` ## Connect to Monad testnet Create a provider to connect to Monad: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); ``` Monad testnet details: * Chain ID: 10143 * Block time: \~1 second * 100% EVM compatible ## Get latest block number The simplest query - get the current block height: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getLatestBlock() { const blockNumber = await provider.getBlockNumber(); console.log(`Latest block: ${blockNumber}`); } getLatestBlock(); ``` ## Check account balance Query the MON balance of any address: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getBalance() { const address = "0x0000000000000000000000000000000000001000"; const balance = await provider.getBalance(address); console.log(`Balance: ${ethers.formatEther(balance)} MON`); } getBalance(); ``` ## Get transaction count (nonce) Check how many transactions an address has sent: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getTransactionCount() { const address = "0x45f106ee4b44d0fe347faca381ee35d3300a8473"; const nonce = await provider.getTransactionCount(address); console.log(`Transaction count: ${nonce}`); } getTransactionCount(); ``` ## Fetch block by hash Retrieve detailed block information: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getBlock() { const blockHash = "0x936a2bd4e1248949fe93cc4ee34a3a19a5e77a823788e40610d953ad6bc44c2a"; const block = await provider.getBlock(blockHash); console.log(`Block number: ${block.number}`); console.log(`Timestamp: ${new Date(block.timestamp * 1000).toISOString()}`); console.log(`Transactions: ${block.transactions.length}`); console.log(`Gas used: ${block.gasUsed.toString()}`); } getBlock(); ``` ## Get transaction details Inspect a specific transaction: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getTransaction() { const txHash = "0x8e00e28d3a51bcae537f7dd2dbeed7678ab67f5a63a0c3b3962c5c804fd98047"; const tx = await provider.getTransaction(txHash); console.log(`From: ${tx.from}`); console.log(`To: ${tx.to}`); console.log(`Value: ${ethers.formatEther(tx.value)} MON`); console.log(`Gas price: ${ethers.formatUnits(tx.gasPrice, "gwei")} gwei`); } getTransaction(); ``` ## Get transaction receipt Check execution results and logs: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getReceipt() { const txHash = "0x8e00e28d3a51bcae537f7dd2dbeed7678ab67f5a63a0c3b3962c5c804fd98047"; const receipt = await provider.getTransactionReceipt(txHash); 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}`); } getReceipt(); ``` ## Call a smart contract Read data from a contract without sending a transaction. This example calls the `name()` function on the Wrapped Monad (WMON) contract: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function callContract() { // WMON contract address const contractAddress = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541"; // name() function selector const data = "0x06fdde03"; const result = await provider.call({ to: contractAddress, data: data }); // Decode the string result const name = ethers.AbiCoder.defaultAbiCoder().decode(["string"], result)[0]; console.log(`Token name: ${name}`); } callContract(); ``` ## Check if address is a contract Determine whether an address is a smart contract or an externally owned account (EOA): ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function isContract() { const address = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541"; const code = await provider.getCode(address); if (code === "0x") { console.log("Address is an EOA (not a contract)"); } else { console.log(`Contract found, bytecode length: ${(code.length - 2) / 2} bytes`); } } isContract(); ``` ## Read contract storage Access raw storage slots of a contract: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getStorage() { const contractAddress = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541"; const slot = 0; // Storage slot 0 const value = await provider.getStorage(contractAddress, slot); console.log(`Storage slot 0: ${value}`); } getStorage(); ``` ## Build a simple block monitor Stream new blocks in real time with `eth_subscribe("newHeads")` over WebSocket: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.WebSocketProvider("YOUR_CHAINSTACK_WSS_ENDPOINT"); async function monitorBlocks() { console.log("Subscribing to new blocks..."); provider.on("block", async (blockNumber) => { try { const block = await provider.getBlock(blockNumber); console.log(`Block ${blockNumber}: ${block.transactions.length} txs, gas used: ${block.gasUsed.toString()}`); } catch (error) { console.error("Error fetching block:", error.message); } }); } monitorBlocks(); ``` If you can't use WebSocket, poll instead: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function monitorBlocks() { let lastBlock = await provider.getBlockNumber(); console.log(`Starting monitor at block ${lastBlock}`); // Poll every second (Monad has ~1 second blocks) setInterval(async () => { try { const currentBlock = await provider.getBlockNumber(); if (currentBlock > lastBlock) { for (let i = lastBlock + 1; i <= currentBlock; i++) { const block = await provider.getBlock(i); console.log(`Block ${i}: ${block.transactions.length} txs, gas used: ${block.gasUsed.toString()}`); } lastBlock = currentBlock; } } catch (error) { console.error("Error fetching block:", error.message); } }, 1000); } monitorBlocks(); ``` ## Complete example Here's a script that demonstrates multiple queries: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function main() { console.log("=== Monad Blockchain Query Demo ===\n"); // Network info const network = await provider.getNetwork(); console.log(`Chain ID: ${network.chainId}`); // Latest block const blockNumber = await provider.getBlockNumber(); console.log(`Latest block: ${blockNumber}`); // Get block details const block = await provider.getBlock(blockNumber); console.log(`Block timestamp: ${new Date(block.timestamp * 1000).toISOString()}`); console.log(`Transactions in block: ${block.transactions.length}`); // Check a balance const address = "0x0000000000000000000000000000000000001000"; const balance = await provider.getBalance(address); console.log(`\nBalance of ${address}:`); console.log(`${ethers.formatEther(balance)} MON`); // Call WMON contract const wmonAddress = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541"; const nameData = "0x06fdde03"; const result = await provider.call({ to: wmonAddress, data: nameData }); const tokenName = ethers.AbiCoder.defaultAbiCoder().decode(["string"], result)[0]; console.log(`\nWMON token name: ${tokenName}`); // Gas price const feeData = await provider.getFeeData(); console.log(`\nCurrent gas price: ${ethers.formatUnits(feeData.gasPrice, "gwei")} gwei`); } main().catch(console.error); ``` ## Monad-specific notes > [!NOTE] > > **Key differences from other EVM chains**: > > * **1-second finality** — blocks are finalized immediately, no reorganizations > * **No pending transactions** — [`eth_getTransactionByHash`](/reference/monad-gettransactionbyhash) only returns confirmed transactions > * **`eth_subscribe` subset** — `newHeads` and `logs` are supported; `newPendingTransactions` and `syncing` return `-32602 Invalid params` > * **Block gas limit** — 300M gas per block ## Next steps Now that you can query blockchain data, you can: * Build dashboards to visualize network activity * Monitor specific addresses or contracts * Create alerts for on-chain events * Develop analytics tools For sending transactions, you'll need testnet MON tokens from a faucet. # Monad: Querying the blockchain with Python Source: https://docs.chainstack.com/docs/monad-tutorial-querying-blockchain-python Read blockchain data from Monad using web3.py and a Chainstack node endpoint, covering blocks, transactions, balances, and smart contract state queries. This tutorial teaches you how to query Monad blockchain data using Python and web3.py. All examples are read-only operations that don't require any MON tokens, making this perfect for getting started. ## Prerequisites * Python 3.8 or higher * Basic Python knowledge Install web3.py: ```bash theme={"system"} pip install web3 ``` ## Connect to Monad testnet Create a Web3 instance to connect to Monad: ```python theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Verify connection print(f"Connected: {web3.is_connected()}") ``` Monad testnet details: * Chain ID: 10143 * Block time: \~1 second * 100% EVM compatible ## Get latest block number The simplest query - get the current block height: ```python theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block_number = web3.eth.block_number print(f"Latest block: {block_number}") ``` ## Check account balance Query the MON balance of any address: ```python theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) address = "0x0000000000000000000000000000000000001000" balance = web3.eth.get_balance(address) print(f"Balance: {web3.from_wei(balance, 'ether')} MON") ``` ## Get transaction count (nonce) Check how many transactions an address has sent: ```python theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) address = "0x45f106ee4b44d0fe347faca381ee35d3300a8473" nonce = web3.eth.get_transaction_count(address) print(f"Transaction count: {nonce}") ``` ## Fetch block by hash Retrieve detailed block information: ```python theme={"system"} from web3 import Web3 from datetime import datetime node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0x936a2bd4e1248949fe93cc4ee34a3a19a5e77a823788e40610d953ad6bc44c2a" block = web3.eth.get_block(block_hash) print(f"Block number: {block.number}") print(f"Timestamp: {datetime.fromtimestamp(block.timestamp).isoformat()}") print(f"Transactions: {len(block.transactions)}") print(f"Gas used: {block.gasUsed}") ``` ## Get transaction details Inspect a specific transaction: ```python theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) tx_hash = "0x8e00e28d3a51bcae537f7dd2dbeed7678ab67f5a63a0c3b3962c5c804fd98047" tx = web3.eth.get_transaction(tx_hash) print(f"From: {tx['from']}") print(f"To: {tx['to']}") print(f"Value: {web3.from_wei(tx['value'], 'ether')} MON") print(f"Gas price: {web3.from_wei(tx['gasPrice'], 'gwei')} gwei") ``` ## Get transaction receipt Check execution results and logs: ```python theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) tx_hash = "0x8e00e28d3a51bcae537f7dd2dbeed7678ab67f5a63a0c3b3962c5c804fd98047" receipt = web3.eth.get_transaction_receipt(tx_hash) 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)}") ``` ## Call a smart contract Read data from a contract without sending a transaction. This example calls the `name()` function on the Wrapped Monad (WMON) contract: ```python theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # WMON contract address contract_address = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541" # name() function selector data = "0x06fdde03" result = web3.eth.call({ 'to': contract_address, 'data': data }) # Decode the string result # Skip first 64 bytes (offset + length) and decode name = result[64:].decode('utf-8').rstrip('\x00') print(f"Token name: {name}") ``` ### Using contract ABI For easier interaction, use the contract ABI: ```python theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Minimal ERC20 ABI for name() abi = [ { "inputs": [], "name": "name", "outputs": [{"type": "string"}], "stateMutability": "view", "type": "function" } ] contract_address = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541" contract = web3.eth.contract(address=contract_address, abi=abi) name = contract.functions.name().call() print(f"Token name: {name}") ``` ## Check if address is a contract Determine whether an address is a smart contract or an externally owned account (EOA): ```python theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) address = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541" code = web3.eth.get_code(address) if code == b'': print("Address is an EOA (not a contract)") else: print(f"Contract found, bytecode length: {len(code)} bytes") ``` ## Read contract storage Access raw storage slots of a contract: ```python theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) contract_address = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541" slot = 0 # Storage slot 0 value = web3.eth.get_storage_at(contract_address, slot) print(f"Storage slot 0: {value.hex()}") ``` ## Build a simple block monitor Stream new blocks in real time with `eth_subscribe("newHeads")` over WebSocket: ```python theme={"system"} import asyncio import json from datetime import datetime import websockets CHAINSTACK_WSS_ENDPOINT = "YOUR_CHAINSTACK_WSS_ENDPOINT" async def monitor_blocks(): async with websockets.connect(CHAINSTACK_WSS_ENDPOINT) as ws: await ws.send(json.dumps({ "jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["newHeads"], })) sub = json.loads(await ws.recv()) print(f"Subscribed: {sub['result']}") async for raw in ws: msg = json.loads(raw) if msg.get("method") != "eth_subscription": continue head = msg["params"]["result"] block_num = int(head["number"], 16) gas_used = int(head["gasUsed"], 16) ts = datetime.fromtimestamp(int(head["timestamp"], 16)).strftime("%H:%M:%S") print(f"Block {block_num} [{ts}]: gas used {gas_used}") if __name__ == "__main__": asyncio.run(monitor_blocks()) ``` If you can't use WebSocket, poll instead: ```python theme={"system"} from web3 import Web3 import time from datetime import datetime node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def monitor_blocks(): last_block = web3.eth.block_number print(f"Starting monitor at block {last_block}") while True: try: current_block = web3.eth.block_number if current_block > last_block: for block_num in range(last_block + 1, current_block + 1): block = web3.eth.get_block(block_num) timestamp = datetime.fromtimestamp(block.timestamp).strftime('%H:%M:%S') print(f"Block {block_num} [{timestamp}]: {len(block.transactions)} txs, gas: {block.gasUsed}") last_block = current_block # Poll every second (Monad has ~1 second blocks) time.sleep(1) except Exception as e: print(f"Error: {e}") time.sleep(1) if __name__ == "__main__": monitor_blocks() ``` ## Complete example Here's a script that demonstrates multiple queries: ```python theme={"system"} from web3 import Web3 from datetime import datetime node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def main(): print("=== Monad Blockchain Query Demo ===\n") # Network info chain_id = web3.eth.chain_id print(f"Chain ID: {chain_id}") # Latest block block_number = web3.eth.block_number print(f"Latest block: {block_number}") # Get block details block = web3.eth.get_block(block_number) timestamp = datetime.fromtimestamp(block.timestamp).isoformat() print(f"Block timestamp: {timestamp}") print(f"Transactions in block: {len(block.transactions)}") # Check a balance address = "0x0000000000000000000000000000000000001000" balance = web3.eth.get_balance(address) print(f"\nBalance of {address}:") print(f"{web3.from_wei(balance, 'ether')} MON") # Call WMON contract wmon_address = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541" abi = [{"inputs": [], "name": "name", "outputs": [{"type": "string"}], "stateMutability": "view", "type": "function"}] contract = web3.eth.contract(address=wmon_address, abi=abi) token_name = contract.functions.name().call() print(f"\nWMON token name: {token_name}") # Gas price gas_price = web3.eth.gas_price print(f"\nCurrent gas price: {web3.from_wei(gas_price, 'gwei')} gwei") if __name__ == "__main__": main() ``` ## Monad-specific notes > [!NOTE] > > **Key differences from other EVM chains**: > > * **1-second finality** — blocks are finalized immediately, no reorganizations > * **No pending transactions** — [`eth_getTransactionByHash`](/reference/monad-gettransactionbyhash) only returns confirmed transactions > * **`eth_subscribe` subset** — `newHeads` and `logs` are supported; `newPendingTransactions` and `syncing` return `-32602 Invalid params` > * **Block gas limit** — 300M gas per block ## Next steps Now that you can query blockchain data, you can: * Build dashboards to visualize network activity * Monitor specific addresses or contracts * Create alerts for on-chain events * Develop analytics tools For sending transactions, you'll need testnet MON tokens from a faucet. # Monitor transaction propagation to mempool in Python Source: https://docs.chainstack.com/docs/monitoring-transaction-propagation-from-node-to-mempool-in-evm-networks-with-python Track how transactions propagate from your node to the EVM mempool using Python with pending transaction filters and timing analysis on Chainstack. **TLDR:** * Demonstrates how to monitor new Ethereum transactions in the mempool using a Python script (web3.py + concurrency). * Explains why transaction propagation matters for network performance and how to approximate how quickly transactions spread across different nodes. * Shows how to tweak your endpoint usage (same vs. different) to measure local or network-wide propagation times more accurately. * Concludes that regular mempool propagation testing helps reveal bottlenecks and fortifies blockchain reliability. ## Main article With the adoption of Ethereum and other blockchains that use the same kind of technology (we call these EVM blockchains), it's important to make sure that the network is stable, safe, and efficient. One big thing developers and the people who manage the network need to keep an eye on is how transactions move around in the mempool. In this article, we're going to show you how to test how blocks move in the mempool for EVM nodes using Chainstack. We'll talk about why these tests are so important, the tools and tricks you'll need, and some common problems and slowdowns that can happen when blocks are moving around. ## Why test transaction propagation? The mempool is a temporary storage space where unconfirmed transactions wait to be included in a block. In a decentralized system, such as Ethereum, multiple nodes maintain separate mempools, but they constantly communicate with each other to sync up on the latest state of pending transactions. Propagation in the mempool predominantly affects two major factors: latency and network efficiency. Poor propagation can lead to increased transaction confirmation times and the risk of chain reorganizations or forks. Thus, testing propagation ensures the network performs optimally and maintains consensus. > [!NOTE] > > Learn more about the mempool by reading [A developer’s guide to transactions in Ethereum mempool](https://chainstack.com/a-developers-guide-to-the-transactions-in-mempool-metamask-edition/). ## Roll your own propagation test If you want to see how transactions and blocks are moving in a blockchain network, tools like [web3.js](/docs/web3-development-frameworks-and-libraries-glossary#web3js) or [web3.py](/docs/web3-development-frameworks-and-libraries-glossary#web3py) are your friends. They let you write your own scripts to understand better how things like the network setup, network conditions, and the number of transactions can make a difference. You can run these tests on your private networks or public ones like Sepolia and Hoodi to mimic what happens in the real world. Pair this with network monitoring tools; you'll get a clear image of your blockchain's performance and where things might be slowing down. This can help you adjust your setup and keep things moving smoothly. > [!NOTE] > > We'll be using the [web3.py](https://github.com/ethereum/web3.py) library to interact with the Ethereum network. ### Prerequisites * Python 3.6 or higher * A [Sepolia node](/reference/ethereum-getting-started#how-to-start-using-the-ethereum-api) with Chainstack * [web3.py](https://github.com/ethereum/web3.py) library. Install it with: ``` pip install web3 ``` ### Getting started Let's start by importing the libraries we need and connecting to an Ethereum node using `Web3 HTTPProvider`. ```javascript Javascript theme={"system"} from web3 import Web3 import time from web3.exceptions import TransactionNotFound from concurrent.futures import ThreadPoolExecutor, as_completed import threading # Connect to the Ethereum node w3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) # Target Ethereum address address = 'YOUR_ETHEREUM_ADDRESS' ``` > [!NOTE] > > Add your Sepolia node URL instead of `YOUR_CHAINSTACK_ENDPOINT`. ### Making responses look nice Next, we'll create a function to make the transaction details we get from the Ethereum network look pretty. This function will also wait for the transaction receipt to update the block hash and block number when the transaction gets validated. ```javascript Javascript theme={"system"} def pretty_print_transaction(tx): def wait_for_confirmation(tx_hash, block_info): while True: try: receipt = w3.eth.get_transaction_receipt(tx_hash) if receipt is not None and receipt['blockHash']: block_info['blockHash'] = receipt['blockHash'] block_info['blockNumber'] = receipt['blockNumber'] break except TransactionNotFound: pass time.sleep(1) block_info = {'blockHash': None, 'blockNumber': None} # Start a separate thread to wait for the block confirmation_thread = threading.Thread(target=wait_for_confirmation, args=(tx['hash'], block_info)) confirmation_thread.start() print("Transaction details:") print(f" Block hash: {block_info['blockHash']}") print(f" Block number: {block_info['blockNumber']}") print(f" From: {tx.get('from')}") print(f" Gas: {tx.get('gas')}") print(f" Gas price: {tx.get('gasPrice')}") if 'maxFeePerGas' in tx: print(f" Max fee per gas: {tx['maxFeePerGas']}") if 'maxPriorityFeePerGas' in tx: print(f" Max priority fee per gas: {tx['maxPriorityFeePerGas']}") print(f" Transaction hash: {tx.get('hash').hex()}") print(f" Input: {tx.get('input')}") print(f" Nonce: {tx.get('nonce')}") print(f" To: {tx.get('to')}") print(f" Transaction index: {tx.get('transactionIndex')}") print(f" Value: {tx.get('value')}") print(f" Type: {tx.get('type')}") print(f" Access list: {tx.get('accessList')}") print(f" Chain ID: {tx.get('chainId')}") print(f" v: {tx.get('v')}") print(f" r: {tx.get('r').hex() if tx.get('r') else None}") print(f" s: {tx.get('s').hex() if tx.get('s') else None}") # Wait for the confirmation thread to complete confirmation_thread.join() print(f"Updated block hash: {block_info['blockHash'].hex()}") print(f"Updated block number: {block_info['blockNumber']}") ``` ### Monitoring the mempool Now, let's whip up a two functions to check if there are any transactions from or to the address you're watching in the mempool: ```python Python theme={"system"} def check_pending_transaction(tx_hash, target_address_lower, w3): try: tx = w3.eth.get_transaction(tx_hash) if tx['from'].lower() == target_address_lower or ( tx['to'] and tx['to'].lower() == target_address_lower): return tx except TransactionNotFound: pass return None # Function to check if any transactions from/to the target address are in the mempool def find_mempool_transactions(target_address): local_w3 = w3 transaction_list = [] target_address_lower = target_address.lower() current_block = local_w3.eth.block_number pending_transactions = local_w3.eth.get_block('pending')['transactions'] with ThreadPoolExecutor() as executor: futures = [ executor.submit(check_pending_transaction, tx_hash, target_address_lower, local_w3) for tx_hash in pending_transactions ] for future in as_completed(futures): result = future.result() if result is not None: transaction_list.append(result) return transaction_list ``` > [!NOTE] > > You'll notice we're using `ThreadPoolExecutor` to check pending transactions simultaneously, making everything quicker. ### Keeping an eye on transactions With those helper functions ready, we can put together the main function to keep an eye on the mempool for new transactions involving the address you're interested in: ```python Python theme={"system"} # Main function to monitor the mempool def monitor_mempool(address): seen_transactions = set() # Add the print statement here print("Mempool monitoring starting...") while True: current_block = w3.eth.block_number pending_block = w3.eth.get_block('pending') pending_transactions = pending_block['transactions'] print( f"Current block: {current_block}. Pending transactions: {len(pending_transactions)}" ) # Record the start time start_time = time.time() transactions = find_mempool_transactions(address) if transactions: new_transactions = [ tx for tx in transactions if tx.get('hash') not in seen_transactions ] if new_transactions: # Calculate the time taken time_taken = time.time() - start_time print(f"\nTime taken since last check: {time_taken:.2f} seconds\n") print( f"Found {len(new_transactions)} new transaction in the mempool involving {address}:" ) for tx in new_transactions: pretty_print_transaction(tx) seen_transactions.add(tx.get('hash')) break monitor_mempool(address) ``` When a new transaction involving the address you're watching pops up, we'll make it look pretty with `pretty_print_transaction()`. After that, the main function will stop, but you can tweak the loop if you want it to keep watching for new transactions. ### The full code Here you can find the entire code for the monitoring tool: ```python Python theme={"system"} from web3 import Web3 import time from web3.exceptions import TransactionNotFound from concurrent.futures import ThreadPoolExecutor, as_completed import threading # Connect to the Ethereum node w3 = Web3( Web3.HTTPProvider( 'YOUR_CHAINSTACK_ENDPOINT')) # Ethereum address to monitor address = 'YOUR_ETHEREUM_ADDRESS' def pretty_print_transaction(tx): def wait_for_confirmation(tx_hash, block_info): while True: try: receipt = w3.eth.get_transaction_receipt(tx_hash) if receipt is not None and receipt['blockHash']: block_info['blockHash'] = receipt['blockHash'] block_info['blockNumber'] = receipt['blockNumber'] break except TransactionNotFound: pass time.sleep(1) block_info = {'blockHash': None, 'blockNumber': None} # Start a separate thread to wait for the block confirmation_thread = threading.Thread(target=wait_for_confirmation, args=(tx['hash'], block_info)) confirmation_thread.start() print("Transaction details:") print(f" Block hash: {block_info['blockHash']}") print(f" Block number: {block_info['blockNumber']}") print(f" From: {tx.get('from')}") print(f" Gas: {tx.get('gas')}") print(f" Gas price: {tx.get('gasPrice')}") if 'maxFeePerGas' in tx: print(f" Max fee per gas: {tx['maxFeePerGas']}") if 'maxPriorityFeePerGas' in tx: print(f" Max priority fee per gas: {tx['maxPriorityFeePerGas']}") print(f" Transaction hash: {tx.get('hash').hex()}") print(f" Input: {tx.get('input')}") print(f" Nonce: {tx.get('nonce')}") print(f" To: {tx.get('to')}") print(f" Transaction index: {tx.get('transactionIndex')}") print(f" Value: {tx.get('value')}") print(f" Type: {tx.get('type')}") print(f" Access list: {tx.get('accessList')}") print(f" Chain ID: {tx.get('chainId')}") print(f" v: {tx.get('v')}") print(f" r: {tx.get('r').hex() if tx.get('r') else None}") print(f" s: {tx.get('s').hex() if tx.get('s') else None}") # Wait for the confirmation thread to complete confirmation_thread.join() print(f"Updated block hash: {block_info['blockHash'].hex()}") print(f"Updated block number: {block_info['blockNumber']}") def check_pending_transaction(tx_hash, target_address_lower, w3): try: tx = w3.eth.get_transaction(tx_hash) if tx['from'].lower() == target_address_lower or ( tx['to'] and tx['to'].lower() == target_address_lower): return tx except TransactionNotFound: pass return None # Function to check if any transactions from/to the target address are in the mempool def find_mempool_transactions(target_address): local_w3 = w3 transaction_list = [] target_address_lower = target_address.lower() current_block = local_w3.eth.block_number pending_transactions = local_w3.eth.get_block('pending')['transactions'] with ThreadPoolExecutor() as executor: futures = [ executor.submit(check_pending_transaction, tx_hash, target_address_lower, local_w3) for tx_hash in pending_transactions ] for future in as_completed(futures): result = future.result() if result is not None: transaction_list.append(result) return transaction_list # Main function to monitor the mempool def monitor_mempool(address): seen_transactions = set() # Add the print statement here print("Mempool monitoring starting...") while True: current_block = w3.eth.block_number pending_block = w3.eth.get_block('pending') pending_transactions = pending_block['transactions'] print( f"Current block: {current_block}. Pending transactions: {len(pending_transactions)}" ) # Record the start time start_time = time.time() transactions = find_mempool_transactions(address) if transactions: new_transactions = [ tx for tx in transactions if tx.get('hash') not in seen_transactions ] if new_transactions: # Calculate the time taken time_taken = time.time() - start_time print(f"\nTime taken since last check: {time_taken:.2f} seconds\n") print( f"Found {len(new_transactions)} new transaction in the mempool involving {address}:" ) for tx in new_transactions: pretty_print_transaction(tx) seen_transactions.add(tx.get('hash')) break monitor_mempool(address) ``` ## Running the test To conduct this test, you'll first need to execute the Python script. Then, initiate a transaction. For simplicity, we'll use MetaMask in this scenario. Your objective determines whether you use the same or a different endpoint in comparison to your script. If you wish to measure the speed at which a transaction reaches the mempool of your own node, stick with the same endpoint as used in the script. However, if your goal is to determine the time taken for the transaction to propagate across other nodes in the Ethereum network, opt for a different endpoint. This will provide a more accurate depiction of transaction propagation times across the network. > [!NOTE] > > Learn how to add your Chainstack endpoint to MetaMask by reading [Fault-tolerant transactions with MetaMask and Chainstack nodes](https://chainstack.com/reliable-transactions-with-chainstack/). In the Python script, input your endpoint and the Ethereum address that you wish to monitor. This could be either the sending or receiving address for the transaction, as the script is designed to track the transaction in both cases. Once the script detects a new transaction in the mempool involving the target address, it will display the transaction's details in the console. Additionally, it will provide an estimated duration that the script took to locate the transaction. While this figure might not be entirely accurate, it serves as a useful approximation of the transaction's propagation speed across the Ethereum network. 1. Start the script. 2. Send a transaction using MetaMask. You will receive a similar log in the console: ```shell Shell theme={"system"} Mempool monitoring starting... Current block: 3772987. Pending transactions: 84 Current block: 3772987. Pending transactions: 102 Time taken since last check: 2.22 seconds Found 1 new transaction in the mempool involving 0x8f8e7012F8F974707A8F11C7cfFC5d45EfF5c2Ae: Transaction details: Block hash: None Block number: None From: 0x8f8e7012F8F974707A8F11C7cfFC5d45EfF5c2Ae Gas: 21000 Gas price: 2181505086 Max fee per gas: 2181505086 Max priority fee per gas: 1500000000 Transaction hash: 0xebbeaa0ee6e787fa3486db9e1b8ad9ccb1e3ab982462c51fca8fa41143be053d Input: 0x Nonce: 59 To: 0x7ea178aE883bC78Fa540b15F36b1e2a8Ea90F7F7 Transaction index: None Value: 1000000000000000000 Type: 2 Access list: [] Chain ID: 11155111 v: 0 r: 0x1483859043ee02820eead543ce58bf9f5a6ec3cd3b339dc709e1860781aa1e57 s: 0x045fb5f1bb7caf42cbeb2d480fbb1a3ed1a85408154bcb052fbb17417eab5e84 Updated block hash: 0x2b120a75e3a97ba9b77d3764945c4c3c2a328699c13327538fb6dacc4642ff57 Updated block number: 3772988 ``` ## Conclusion Testing transaction propagation in mempool for EVM nodes is crucial to maintaining a reliable and efficient blockchain network. By simulating realistic scenarios and using a combination of network monitoring and custom tests, developers and infrastructure operators can ensure that nodes process transactions in a timely and secure manner. Regularly conducting propagation tests will help identify potential issues, optimize resources, and contribute to the overall health of your blockchain ecosystem. # Use Multichain API Source: https://docs.chainstack.com/docs/multichain-api Create a multichain endpoint on Chainstack, attach your Global Nodes and Dedicated Nodes, and call every attached node with one auth token. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Multichain API gives you one auth token for several nodes. You create a multichain endpoint on Chainstack, attach the Global Nodes and Dedicated Nodes you want, and put the multichain auth token in the URL of each node instead of managing one auth token per node. > [!NOTE] > > Multichain API is available on every plan, including the Developer plan. ## What is a multichain endpoint? A multichain endpoint is a named group of your existing nodes with one shared multichain auth token: * The multichain auth token authorizes requests to each node in the group, so you keep a single secret in your environment variables or CI instead of one per node. * Your nodes keep their own endpoint URLs and [auth tokens](/docs/authentication-methods-for-different-scenarios), and those auth tokens keep working. A multichain endpoint can hold the following: * One [Global Node](/docs/global-elastic-node) per protocol and network * Any number of [Dedicated Nodes](/docs/dedicated-node) You can't attach [Trader Nodes](/docs/trader-node). The same node can belong to several multichain endpoints. ## Create a multichain endpoint **1. Open Multichain API** On Chainstack, click **Multichain API** in the left menu. **2. Name the multichain endpoint** Click **Create endpoint**, enter a name of up to 30 characters, and click **Create endpoint** in the dialog. The name field starts with your organization name and the current date and time, which you can keep or replace. \[!TIP] Chainstack opens the new multichain endpoint's page, and the multichain auth token is under **API key**. After you create a multichain endpoint: * The multichain endpoint has a multichain auth token and no nodes, so the multichain auth token authorizes nothing until you attach a node. * The **Multichain API** page lists the multichain endpoint with its node count, a shortened multichain auth token, and the creation date. ## Attach nodes to a multichain endpoint **1. Open the multichain endpoint** On Chainstack, click **Multichain API** and click the name of the multichain endpoint. **2. Add the nodes** Under **Connected nodes**, click **Add node**. Search for a node by name or filter the list by type (**Global** or **Dedicated**), and click **Add** next to each node you want. Click **Done** when finished. \[!TIP] The nodes appear under **Connected nodes** and in the **Node** selector under **Connection details**. When you manage the nodes of a multichain endpoint: * You can add or remove nodes at any time, and the multichain auth token stays the same. * Chainstack dims a node that you can't add. Hover over the node's **Add** button to see the reason, for example, that a multichain endpoint can't hold two Global Nodes for the same protocol and network. ## Call a node with the multichain auth token The multichain auth token takes the place of the node's own auth token in the node's endpoint URLs. To get a URL: 1. Open the multichain endpoint. 2. Select a node under **Connection details**. 3. Copy the URL you need. The URL already contains the multichain auth token. How you use the multichain auth token depends on the node and the endpoint: * Global Node — the URL uses the `core.chainstack.com` host with the protocol and network in the subdomain. * Dedicated Node — the URL uses the node's own host, so copy the URL from **Connection details** instead of building it yourself. * REST API — the multichain auth token sits before the API path, the same as a node's auth token. * gRPC endpoint — use the node's own x-token, as described in [gRPC access via x-token](/docs/authentication-methods-for-different-scenarios#grpc-access-via-x-token). * Node that is stopped or in maintenance — you can attach the node, but **Connection details** shows no URLs for the node until it is running again. To call attached Ethereum Mainnet, Solana Mainnet, and Aptos Mainnet Global Nodes with one multichain auth token, put the multichain auth token in each node's URL: ```bash cURL theme={"system"} export CHAINSTACK_MULTICHAIN_TOKEN="YOUR_MULTICHAIN_AUTH_TOKEN" curl -X POST "https://ethereum-mainnet.core.chainstack.com/$CHAINSTACK_MULTICHAIN_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' curl -X POST "https://solana-mainnet.core.chainstack.com/$CHAINSTACK_MULTICHAIN_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"getSlot","params":[],"id":1}' curl "https://aptos-mainnet.core.chainstack.com/$CHAINSTACK_MULTICHAIN_TOKEN/v1" ``` Replace `YOUR_MULTICHAIN_AUTH_TOKEN` with the multichain auth token from the multichain endpoint's page. Each call returns the protocol's normal response: a JSON-RPC result for Ethereum and Solana, and the ledger information for Aptos. > [!TIP] > > A request to an attached node returns a normal response. A request to a node that isn't attached returns `401 Unauthorized`. ## How Chainstack handles requests with the multichain auth token * A request with the multichain auth token gets the same handling as a request with the node's own auth token, so the node's rate limits and [access rules](/docs/access-rules) apply. * A request with the multichain auth token to a node that isn't attached to the multichain endpoint returns `401 Unauthorized` with the message `Access token missing or invalid. Make sure you are using the correct node endpoint.` * A WSS connection with the multichain auth token to a node that isn't attached fails with `401` during the handshake. * When you delete a node on Chainstack, Chainstack removes the node from its multichain endpoints. The multichain endpoints and their multichain auth tokens stay. * Removing the last node from a multichain endpoint leaves the multichain endpoint and its multichain auth token in place with no nodes. ## Find the multichain auth token The multichain auth token starts with `mt_`. To copy the multichain auth token, use either of the following: * The multichain endpoint's page — the full multichain auth token is always under **API key**, and **Copy** puts it on your clipboard. * The **Multichain API** page — the list shows a shortened multichain auth token, and its copy icon copies the full value. Treat the multichain auth token as a secret. See [Best practices for securing your Chainstack endpoint](/docs/best-practices-for-securing-your-chainstack-endpoint) for guidance. ## Replace a multichain auth token You can't rotate the multichain auth token of a multichain endpoint. To replace the multichain auth token, for example after it leaks: **1. Create a new multichain endpoint** Create a multichain endpoint and attach the same nodes to it. **2. Update your applications** Switch your environment variables or CI secrets to the new multichain auth token. **3. Delete the old multichain endpoint** Delete the old multichain endpoint, as described in [Remove a node or delete a multichain endpoint](#remove-a-node-or-delete-a-multichain-endpoint). ## Remove a node or delete a multichain endpoint To remove a node from a multichain endpoint, do one of the following: * Open the multichain endpoint and click the trash icon next to the node under **Connected nodes**. * Click **Remove** next to the node in the **Add nodes** dialog. After you remove a node: * Requests to the removed node with the multichain auth token return `401 Unauthorized`. * The other nodes in the multichain endpoint keep working. To delete a multichain endpoint: 1. Click **Delete endpoint** on the multichain endpoint's page, or hover over the multichain endpoint's row on the **Multichain API** page and click the trash icon. 2. In the **Delete endpoint** dialog, click **Yes, I'm sure**. If the multichain endpoint has nodes, the dialog shows how many nodes the multichain auth token stops working on. After you delete a multichain endpoint: * The multichain auth token stops working on every attached node. * You can't undo the deletion. * Your nodes stay as they are. # Choose the right blockchain network for your dApp Source: https://docs.chainstack.com/docs/navigating-the-web3-landscape-how-to-choose-the-right-blockchain-network-for-your-dapp Compare Layer 1 and Layer 2 blockchain networks to select the right chain for your dApp based on scalability, security, and ecosystem requirements. **TLDR:** * Blockchain solutions fall into two main categories, layer 1 (L1) and layer 2 (L2), each balancing scalability, decentralization, and security differently. * Layer 1 networks like Ethereum, BNB Smart Chain, and Bitcoin are foundational and often the go-to for robust smart contract functionality or simple store-of-value use cases. * Layer 2 solutions (e.g., Polygon, zkSync Era, Arbitrum) offload transactions from mainnets, resulting in faster throughput and lower costs, while still benefiting from the security of L1. * Chainstack supports a broad range of L1 and L2 networks, so you can pick the one that best matches your DApp’s performance, security, and cost requirements. ## Main article Blockchain technology has transformed various industries, offering transparency, security, and decentralization. Selecting the right blockchain network can significantly impact the performance, security, and user experience of your DApp. With many networks available, this article will guide you in making an informed decision. Chainstack supports many networks, including Ethereum, Bitcoin, BNB Smart Chain, and many more. ## Layer 1 vs layer 2: A brief overview Blockchain solutions come in various flavors, each with unique features tailored for specific use cases. The two primary categories within the blockchain ecosystem are layer 1 (L1) and layer 2 (L2) blockchains. While L1 networks offer robust security and decentralization, L2 solutions might be more suitable for applications requiring high transaction speeds and lower fees. ### Layer 1 network Layer 1 networks form the foundation of the blockchain ecosystem, serving as the base layer for DApps and cryptocurrencies. Their key characteristics include: * Decentralization — highly decentralized with a distributed network of nodes. Examples: Bitcoin and Ethereum. * Security — robust consensus mechanisms like PoW or PoS ensure security. * Scalability — some L1 networks face scalability challenges, leading to congestion and high fees. * Smart contracts — support for decentralized applications with programmable logic. * Native tokens — used as gas fees or as a store of value. ### Layer 2 network Layer 2 networks address the scalability and cost issues of layer 1 networks, operating atop L1 networks and leveraging their security. L2 networks' key characteristics include: * Scalability solutions — designed to offload transactions from L1, increasing throughput and reducing congestion. * Cost efficiency — offer cost-effective alternatives for microtransactions. * Interoperability — can connect different L1 networks or bridge to traditional financial systems. * Security — benefit from the security of L1 but may have unique security considerations. * Use cases — ideal for high transaction throughput applications like gaming and DeFi. > [!NOTE] > > Read [Solving the blockchain trilemma: A look at some scaling solutions](https://chainstack.com/solving-the-blockchain-trilemma-scaling-solutions-for-ethereum/) to better understand layer 2 solutions. ## Major layer 1 networks supported by Chainstack * **Bitcoin**. The foundational technology behind the first cryptocurrency. *Best for*: secure, transparent transactions and a store of value. * **Ethereum**. A decentralized platform for DApps and smart contracts. *Best for*: decentralized applications requiring robust smart contract functionality. * **BNB Smart Chain**. High-performance network compatible with the Ethereum Virtual Machine. *Best for*: DApps requiring high-speed and low-cost transactions. * **Avalanche**. Offers scalable, customizable, and interoperable blockchain solutions with a unique consensus mechanism. *Best for*: developers and projects seeking customizable blockchain-based applications with specific requirements. * **Fantom**. Known for fast transaction processing, scalability, and many use cases. *Best for*: high-performance decentralized applications and diverse use cases. * **Filecoin**. A marketplace for storage providers and users to exchange digital storage space and services. *Best for*: decentralized data storage and retrieval using a network of storage nodes. ## Layer 2 networks supported by Chainstack * **Polygon**. A layer 2 scaling solution for Ethereum. *Best for*: addressing Ethereum's high gas fees and slow transaction times. * **zkSync Era**. Scales Ethereum with cutting-edge ZK tech and is fully compatible with the Ethereum Virtual Machine. *Best for*: applications requiring high scalability without compromising Ethereum compatibility. Account abstraction out of the box. * **Optimism**. A scaling solution for Ethereum using optimistic rollups. *Best for*: achieving high throughput and low fees on the Ethereum network. * **Base**. An Ethereum layer 2 scaling solution built on top of Optimism. *Best for*: secure, low-cost, and developer-friendly scaling on Ethereum. * **Arbitrum**. Designed to enhance the performance of Ethereum-based DApps by providing a more efficient platform. *Best for*: addressing scalability and high gas fee issues on Ethereum. * **Starknet**. Enables Ethereum to scale securely using zk-STARKs technology. *Best for*: enhancing data security, privacy, and scalability on Ethereum. * **Scroll**. A reliable and scalable layer 2 network that extends Ethereum's capabilities. *Best for*: scaling applications on Ethereum without compromising on performance. > [!NOTE] > > ### What other networks does Chainstack supports? > > Find the full list of [supported networks](https://chainstack.com/protocols/). ## Conclusion Choosing the right blockchain network is pivotal for the success of your DApp. When deciding, reflect on your application's specific requirements, such as transaction speed, security, and cost. With the diverse options available, you can find a solution tailored to your needs. # Node usage Source: https://docs.chainstack.com/docs/node-usage View a per-node, per-day breakdown of your Chainstack usage — compute hours, storage, and request units — filter it by period, protocol, network, and node, and export it to CSV. The **Usage details** table gives you a self-service, per-node breakdown of your organization's usage for a selected period, separate from your invoice. Use it to understand your metered charges, check a single node's consumption, and share the detail internally — without asking the Chainstack team. > [!NOTE] > > For an organization-wide view of request unit consumption over time, see [Statistics](/docs/see-statistics). To pull the same usage data programmatically, see [List node usage](/reference/chainstack-platform-api-v2-list-node-usage) in the Platform API reference. ## Where to find it You can view the usage breakdown in two places: * **Organization-wide** — go to [Billing](https://console.chainstack.com/user/settings/billing) and open the **Usage details** section. This shows every node in your organization. * **Per node** — open a node's detail page and select the **Usage** tab. This shows the breakdown for that node only. The **Usage details** section is available to users with the [Admin role](/docs/manage-your-organization#users-and-their-roles). ## Read the usage table Each row is one node's usage for one date in the selected period: | Column | Description | | - | - | | Node ID | The ID of the node, for example `ND-123-456-789`. | | Date | The day the usage was recorded. | | Node type | Whether the row is for a Dedicated Node or a Global Node. The CSV export and API use `dedicated` and `elastic`. | | Protocol | The node's protocol, for example Ethereum. | | Network | The node's network, for example Ethereum Mainnet. | | Compute hours | Compute hours consumed by the node that day. Applies to Dedicated Nodes. | | GB/hour | Storage consumed by the node that day, in GB-hours. Applies to Dedicated Nodes. | | Request units | Request units (RUs) consumed by the node that day. Applies to Global Nodes. | > [!NOTE] > > Metrics apply per node type. Dedicated Nodes report compute hours and storage; Global Nodes report request units. When a metric doesn't apply to a node — or the data isn't available — the cell shows `-`. ## Filter the table Narrow the breakdown with the following filters: * **Period** — defaults to your organization's current metered billing period. Select a custom range to look further back. * **Protocol** — show only the selected protocols. * **Network** — show only the selected networks. * **Node ID** — show only a specific node. * **Node type** — show only Dedicated Nodes or only Global Nodes. ## Data retention Usage data is retained for two months. You can view the current and the previous calendar month; the start of your selected period cannot be earlier than the first day of the previous calendar month. ## Export to CSV To download the breakdown for the selected period and filters, click **Export**. The export includes every matching row for the current filters and saves as `node-usage-report.csv`. ## Pull usage data with the API The table is backed by the Platform API v2, so you can retrieve the same data programmatically — for example, to build your own reports or automate reconciliation: * [List node usage](/reference/chainstack-platform-api-v2-list-node-usage) — paginated per-node usage metrics for your organization. * [Export node usage to CSV](/reference/chainstack-platform-api-v2-export-node-usage) — the full report as a CSV file. Both endpoints accept the same filters as the table and default to your current metered billing period when no dates are provided. # Available clouds, regions, & locations Source: https://docs.chainstack.com/docs/nodes-clouds-regions-and-locations Browse the cloud providers, regions, and geographic locations available for deploying Chainstack blockchain nodes. > [!NOTE] > > ### Dedicated Nodes are also available > > Dedicated Nodes are also available for the chains not listed here. For the full list, see the [Networks](/docs/protocols-networks) page. > [!NOTE] > > ### Warp transactions details > > See [Warp transactions](/docs/warp-transactions). ## Ethereum | Network | Cloud | Region | Location | Mode | Debug & trace | Warp transactions | | - | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | Available | | Mainnet | Chainstack Cloud | lon2 | London | Archive | Available | NA | | Mainnet | Chainstack Cloud | lon2 | London | Full | NA | Available | | Mainnet | Chainstack Cloud | sgp1 | Singapore | Full | NA | Available | | Mainnet | Chainstack Cloud | ash1 | Ashburn | Archive | Available | NA | | Mainnet | Chainstack Cloud | ash1 | Ashburn | Full | NA | Available | | Sepolia Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | NA | | Sepolia Testnet | Virtuozzo | fra1 | Frankfurt | Full | NA | NA | | Hoodi Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | NA | ## Solana | Network | Cloud | Region | Location | Mode | Warp transactions | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Mainnet | Chainstack Cloud | fra2 | Frankfurt | Full | Available | | Mainnet | Chainstack Cloud | nyc2 | New York | Full | Available | | Devnet | Chainstack Global Network | global1 | Worldwide | Full | NA | | Devnet | Chainstack Cloud | ash1 | Ashburn | Full | NA | ## BNB Smart Chain | Network | Cloud | Region | Location | Mode | Debug & trace | Warp transactions | | - | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | Available | | Mainnet | Chainstack Cloud | lon2 | London | Archive | Available | NA | | Mainnet | Chainstack Cloud | lon2 | London | Full | NA | Available | | Mainnet | Chainstack Cloud | sgp1 | Singapore | Full | NA | Available | | Mainnet | Chainstack Cloud | ash1 | Ashburn | Archive | Available | NA | | Mainnet | Chainstack Cloud | ash1 | Ashburn | Full | NA | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | NA | | Testnet | Chainstack Cloud | lon2 | London | Full | NA | NA | ## Arbitrum | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Mainnet | Chainstack Cloud | lax1 | Los Angeles | Archive | Available | | Mainnet | Chainstack Cloud | fra1 | Frankfurt | Archive | Available | | Mainnet | Chainstack Cloud | lax1 | Los Angeles | Full | NA | | Mainnet | Chainstack Cloud | fra1 | Frankfurt | Full | NA | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Arc | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Base | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Mainnet | Chainstack Cloud | lax1 | Los Angeles | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## TRON | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | NA | | Nile Testnet | Chainstack Global Network | global1 | Worldwide | Full | NA | ## TON | Network | Cloud | Region | Location | Mode | v2 | v3 | v4 | | - | - | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | Available | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | Available | Available | ## Unichain | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Sonic | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Polygon | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Mainnet | Chainstack Cloud | lon2 | London | Full | NA | | Mainnet | Chainstack Cloud | lon2 | London | Archive | Available | | Mainnet | Chainstack Cloud | ash1 | Ashburn | Full | NA | | Mainnet | Chainstack Cloud | ash1 | Ashburn | Archive | Available | | Amoy Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Optimism | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Mainnet | Chainstack Cloud | lax1 | Los Angeles | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Avalanche | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Mainnet | Chainstack Cloud | fra1 | Frankfurt | Archive | NA | | Mainnet | Chainstack Cloud | lax1 | Los Angeles | Full | NA | | Mainnet | Chainstack Cloud | fra1 | Frankfurt | Full | NA | | Fuji Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Fuji Testnet | Virtuozzo | fra1 | Frankfurt | Full | NA | ## Robinhood Chain | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Full | Available | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Full | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Ronin | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Sui | Network | Cloud | Region | Location | Mode | | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Extended | | Testnet | Chainstack Global Network | global1 | Worldwide | Extended | ## Berachain | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Full | Available | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Bepolia Testnet | Chainstack Global Network | global1 | Worldwide | Full | Available | | Bepolia Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Linea | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Archive | Available | ## Mantle | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Archive | Available | ## MegaETH | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Archive | Available | ## World Chain | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Full | Available | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Archive | Available | ## Ink | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Archive | Available | ## Stable | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Full | Available | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Archive | Available | ## Polkadot | Network | Cloud | Region | Location | Mode | | - | - | - | - | - | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Archive | ## Plasma | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Tempo | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | us-east-1 | US East (Ashburn) | Archive | Available | | Mainnet | Chainstack Global Network | eu-central-1 | EU (Frankfurt) | Archive | Available | | Mainnet | Chainstack Global Network | ap-southeast-1 | Asia (Singapore) | Archive | Available | | Moderato Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## ZKsync Era | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Mainnet | Chainstack Cloud | lax1 | Los Angeles | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Zora | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Archive | Available | ## Starknet | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Scroll | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## opBNB | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Partner Network | global2 | Worldwide | Full | Available | ## Fantom | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Mainnet | Chainstack Cloud | lax1 | Los Angeles | Archive | NA | | Mainnet | Chainstack Cloud | lax1 | Los Angeles | Full | NA | | Mainnet | Chainstack Cloud | fra1 | Frankfurt | Full | NA | ## Aptos | Network | Cloud | Region | Location | Mode | | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | | Mainnet | Chainstack Cloud | fra1 | Frankfurt | Archive | | Mainnet | Chainstack Cloud | fra1 | Frankfurt | Full | | Testnet | Chainstack Global Network | global1 | Worldwide | Full | ## Cronos | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Mainnet | Chainstack Cloud | fra1 | Frankfurt | Archive | Available | | Mainnet | Chainstack Cloud | fra1 | Frankfurt | Full | Available | ## Gnosis Chain | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Mainnet | Chainstack Cloud | lon2 | London | Archive | Available | | Mainnet | Chainstack Cloud | lon2 | London | Full | NA | | Testnet | Chainstack Global Network | global1 | Worldwide | Full | Available | | Testnet | Virtuozzo | fra1 | Frankfurt | Full | NA | ## Hyperliquid | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Full | NA | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Full | NA | ## Kaia (ex Klaytn) | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Full | Available | | Kairos Testnet | Chainstack Global Network | global1 | Worldwide | Full | Available | ## Celo | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Monad | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Full | Available | ## Stellar | Network | Cloud | Region | Location | Mode | | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Extended | | Testnet | Chainstack Global Network | global1 | Worldwide | Extended | ## XRP Ledger | Network | Cloud | Region | Location | Mode | | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Extended | | Testnet | Chainstack Global Network | global1 | Worldwide | Extended | ## Elysium | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## X Layer | Network | Cloud | Region | Location | Mode | Debug & trace | | - | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | | Testnet | Chainstack Global Network | global1 | Worldwide | Archive | Available | ## Zcash | Network | Cloud | Region | Location | Mode | | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Full | | Testnet | Chainstack Global Network | global1 | Worldwide | Full | ## Bitcoin | Network | Cloud | Region | Location | Mode | | - | - | - | - | - | | Mainnet | Chainstack Global Network | global1 | Worldwide | Full | | Mainnet | Chainstack Cloud | lax1 | Los Angeles | Full | | Mainnet | Chainstack Cloud | fra1 | Frankfurt | Full | | Testnet | Chainstack Global Network | global1 | Worldwide | Full | | Testnet | Virtuozzo | fra1 | Frankfurt | Full | | Signet Testnet | Chainstack Global Network | global1 | Worldwide | Full | | Testnet4 | Chainstack Global Network | global1 | Worldwide | Full | ## Harmony | Network | Mode | Cloud | Region | Location | | - | - | - | - | - | | Mainnet | Full | Virtuozzo | fra1 | Frankfurt | # opBNB: How to listen to deposits on the opBNB bridge Source: https://docs.chainstack.com/docs/opbnb-how-to-listen-deposits-bridge Monitor opBNB bridge deposit events using web3.py event listeners and a Chainstack endpoint to track cross-chain asset transfers from BNB Smart Chain. **TLDR:** * opBNB is a Layer 2 scalability solution for the BNB Smart Chain built on the Optimism OP Stack. * The opBNB Bridge contract emits `DepositFinalized` events whenever deposits from BNB Chain occur. * This tutorial shows how to listen for these deposit events using ethers.js, enabling real-time tracking of cross-chain activity. * By monitoring transaction hashes and deposit details, developers can interact directly with the opBNB ecosystem. ## Main article opBNB is an EVM-compatible Layer 2 scalable network that brings unique features. Developed as an extension of the Binance Smart Chain (BNB Chain) ecosystem, opBNB aims to provide high-performance blockchain solutions. It leverages the bedrock version of the Optimism OP Stack to offer a Layer 2 scaling solution for the BNB Smart Chain. The opBNB network enhances scalability by offloading transaction processing and resource usage from the BNB Smart Chain while posting data to the underlying mainnet. This approach enables high throughput and low fees, making opBNB an attractive choice for developers and users. One of the key components of opBNB is the opBNB Bridge, which facilitates the secure transfer of assets between BNB Chain and opBNB. This bridge is essential for those who need to move assets while maintaining the benefits of both networks. Users interact with the opBNB network by depositing funds from BNB and using applications and contracts on opBNB. This tutorial will guide you through setting up a listener for deposit events on the opBNB Bridge. You can then capture deposit events and extract relevant transaction data. Understanding how to monitor and interact with these on-chain events is crucial for developers leveraging opBNB's capabilities. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable opBNB RPC endpoint](https://chainstack.com/build-better-with-opbnb/) * [Node.js](https://nodejs.org/en) * ether.js ## Step-by-step ### Get an opBNB node Log in to your [Chainstack account](https://console.chainstack.com/) and deploy a node. ### Create a new ethers project Once you have Node.js installed, create a new project in a new directory. ``` npm init --yes ``` This will create a new project and a `package.json` file. You can then install the ethers library. ``` npm i ethers ``` > [!NOTE] > > Learn more about Node projects for Web3 by reading [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project). ### Create the script Now that the project is set, create a new file named `index.js`. With this DApp, we listen to the `DepositFinalized` event on the opBNB Bridge smart contract, `0x4200000000000000000000000000000000000010` in this case. This even is emitted when a deposit is made, you can use this data to monitor activity. Note that this address is a proxy contract. To find the contract event to use in the code, check the actual bridge contract: [0xc0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d30010](https://opbnbscan.com/address/0xc0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d3c0d30010?view=contract_code\&p=1\&tab=Contract#F7#L58). The event is defined in the contract as the following: ```sol L2StandardBridge.sol theme={"system"} event DepositFinalized( address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes extraData ); ``` Paste the following code. ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); // Connect to your opBNB node const provider = new ethers.JsonRpcProvider( "YOUR_CHAINSTACK_NODE" ); // Define the contract ABI const abi = [ "event DepositFinalized(address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes extraData)", ]; // Define the contract address const contractAddress = "0x4200000000000000000000000000000000000010"; // Create a contract instance const contract = new ethers.Contract(contractAddress, abi, provider); // Define the event filter (optional: add any specific filters if required) const filter = contract.filters.DepositFinalized(); console.log("Listening for DepositFinalized events on the opBNB Bridge..."); // Listen for the event contract.on(filter, (event) => { console.log("Deposit Finalized Event:"); console.log(`Transaction Hash: ${event.log.transactionHash}`); console.log( `Explorer link: https://opbnbscan.com/tx/${event.log.transactionHash}` ); console.log(`From: ${event.args[2]}`); // from console.log(`Amount: ${ethers.formatEther(event.args[4].toString())} BNB`); // amount console.log( "------------------------------------------------------------------------------------------" ); }); ``` where * `YOUR_CHAINSTACK_NODE` — your opBNB node endpoint ## Code breakdown Here's a brief breakdown explaining the code step-by-step: ### 1. Importing Ethers.js Library The code starts by importing the Ethers.js library, which is essential for interacting with the Ethereum network and its derivatives like opBNB. ### 2. Connecting to the opBNB Node Using a JSON-RPC provider, it connects to an opBNB node. Replace `"YOUR_CHAINSTACK_NODE"` with your actual Chainstack node URL. ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); // Connect to your opBNB node const provider = new ethers.JsonRpcProvider( "YOUR_CHAINSTACK_NODE" ); ``` ### 3. Defining the Contract ABI The ABI (Application Binary Interface) is defined for the `DepositFinalized` event. This ABI tells Ethers.js how to interpret the event data. ```javascript Javascript theme={"system"} // Define the contract ABI const abi = [ "event DepositFinalized(address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes extraData)", ]; ``` ### 4. Specifying the Contract Address The address of the opBNB Bridge contract is specified. This address points to the smart contract on the opBNB network that emits the `DepositFinalized` events. ```javascript Javascript theme={"system"} // Define the contract address const contractAddress = "0x4200000000000000000000000000000000000010"; ``` ### 5. Creating a Contract Instance The contract address, ABI, and provider are used to create an instance of the contract. This instance allows interaction with the contract and listening for events. ```javascript Javascript theme={"system"} // Create a contract instance const contract = new ethers.Contract(contractAddress, abi, provider); ``` ### 6. Defining the Event Filter A filter for the `DepositFinalized` event is defined. If needed, this filter can be customized to listen for specific events based on additional parameters. ```javascript Javascript theme={"system"} // Define the event filter (optional: add any specific filters if required) const filter = contract.filters.DepositFinalized(); ``` ### 7. Handling the Event The script sets up an event listener that triggers when a `DepositFinalized` event is detected. * **Logging Event Details**: It logs the event details, such as the transaction hash, which can be used to view the transaction on the opBNB block explorer. * **Extracting Event Data**: The `from` address and the `amount` of BNB deposited are extracted from the event arguments and logged. The amount is formatted to be readable in BNB units. ```javascript Javascript theme={"system"} // Listen for the event contract.on(filter, (event) => { console.log("Deposit Finalized Event:"); console.log(`Transaction Hash: ${event.log.transactionHash}`); console.log( `Explorer link: https://opbnbscan.com/tx/${event.log.transactionHash}` ); console.log(`From: ${event.args[2]}`); // from console.log(`Amount: ${ethers.formatEther(event.args[4].toString())} BNB`); // amount console.log( "------------------------------------------------------------------------------------------" ); }); ``` The script listens for `DepositFinalized` events on the opBNB Bridge and prints relevant details to the console whenever such an event is emitted by the smart contract. ## Conclusion By following this tutorial, you have successfully set up a listener for `DepositFinalized` events on the opBNB Bridge. This allows you to capture and analyze deposit events in real-time, providing valuable insights into the activity on the opBNB network. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # opBNB tooling Source: https://docs.chainstack.com/docs/opbnb-tooling Set up MetaMask, Hardhat, Foundry, and development tools to build and deploy smart contracts on opBNB through your Chainstack RPC node endpoints. Get started with a [reliable opBNB RPC endpoint](https://chainstack.com/build-better-with-opbnb/) to use the tools below. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and opBNB nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and opBNB nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.JsonRpcProvider(urlInfo.url, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — opBNB network ID: * opBNB Mainnet: `204` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your opBNB nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and opBNB nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Php theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where `YOUR_CHAINSTACK_ENDPOINT` is your node HTTPS endpoint protected either with the key or password # Optimism methods Source: https://docs.chainstack.com/docs/optimism-methods Complete reference of supported Optimism JSON-RPC methods on Chainstack, including eth, debug, trace, and rollup namespace availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Optimism API call examples](/reference/optimism-api-reference). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_simulateV1 | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | Reports transactions as the sequencer includes them in [subblocks](/docs/flashblocks-on-optimism) — mempool is private to the sequencer | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | eth\_sendRawTransactionSync | | [Subblocks](/docs/flashblocks-on-optimism) sync send | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | erigon\_blockNumber | | | | erigon\_forks | | | | erigon\_getBlockByTimestamp | | | | erigon\_getBlockReceiptsByBlockHash | | | | erigon\_getHeaderByHash | | | | erigon\_getHeaderByNumber | | | | erigon\_getLatestLogs | | | | erigon\_getLogsByHash | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_getTrieFlushInterval | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | trace\_block | | | | trace\_call | | | | trace\_callMany | | | | trace\_filter | | | | trace\_rawTransaction | | | | trace\_replayBlockTransactions | | | | trace\_replayTransaction | | | | trace\_transaction | | | | optimism\_outputAtBlock | | | | optimism\_syncStatus | | | | optimism\_rollupConfig | | | | optimism\_version | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Track real-time transactions on Optimism with Subblocks Source: https://docs.chainstack.com/docs/optimism-streaming-transactions-flashblocks Optimism exposes Subblocks (formerly Flashblocks) through the pending tag, not eth_subscribe. Build a real-time view of pre-confirmed transactions on Optimism by polling the pending block every 200ms, with tested Python and JavaScript. Optimism produces Subblocks, formerly called Flashblocks: partial blocks with pre-confirmed transactions, about every 200 ms. Optimism exposes them through the standard `pending` block tag, not through dedicated WebSocket subscriptions. To follow transactions in real time on Optimism, poll the `pending` block roughly every 200 ms and track the transactions that are new since the last poll. This guide does that with tested Python and JavaScript, then covers the pre-confirmed read methods. > [!NOTE] > > For how Subblocks work on Optimism, see [Subblocks on Optimism](/docs/flashblocks-on-optimism). For Base, which exposes Flashblocks as WebSocket subscriptions, see [Stream real-time transactions on Base with Flashblocks](/docs/base-streaming-transactions-flashblocks). ## Prerequisites * An Optimism mainnet HTTPS endpoint. On Chainstack, copy it from your Optimism node's **Access and credentials** tab — it looks like `https://optimism-mainnet.core.chainstack.com/`. The examples use `YOUR_OPTIMISM_HTTPS_ENDPOINT` as a placeholder. * Python 3.8+ with the `requests` library (`pip install requests`), or Node.js 18+ (the `fetch` API is built in, so there are no dependencies to install). ## Why Optimism uses the pending tag, not eth\_subscribe On Optimism, the Flashblocks subscriptions that exist on Base — `eth_subscribe("newFlashblockTransactions")`, `newFlashblocks`, and `pendingLogs` — are not exposed; calling them returns `-32602 Invalid params`. Like every OP Stack chain, Optimism has no public mempool (see [Mempool configurations](/docs/mempool-configuration)), so `eth_subscribe("newPendingTransactions")` reports only transaction hashes as the sequencer includes them in subblocks. The `pending` block tag gives you the transactions themselves and the pre-confirmed state, and it reflects the latest subblock and advances every \~200 ms. ## Track pre-confirmed transactions Poll `eth_getBlockByNumber("pending", false)` every 200 ms and emit transaction hashes you have not seen before. Each poll returns the block the sequencer is currently building; within one block number the transaction list grows every \~200 ms as new subblocks arrive. ```python Python theme={"system"} import time import requests HTTP = "YOUR_OPTIMISM_HTTPS_ENDPOINT" # https://optimism-mainnet.core.chainstack.com/ def rpc(method, params): response = requests.post( HTTP, json={"jsonrpc": "2.0", "id": 1, "method": method, "params": params}, timeout=20, ) return response.json()["result"] seen = set() while True: block = rpc("eth_getBlockByNumber", ["pending", False]) # False -> transactions are hashes for tx_hash in block["transactions"]: if tx_hash not in seen: seen.add(tx_hash) print(tx_hash) time.sleep(0.2) ``` ```javascript JavaScript theme={"system"} // Node.js 18+ has a built-in fetch — no dependencies required. const HTTP = "YOUR_OPTIMISM_HTTPS_ENDPOINT"; // https://optimism-mainnet.core.chainstack.com/ async function rpc(method, params) { const response = await fetch(HTTP, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }), }); return (await response.json()).result; } const seen = new Set(); while (true) { const block = await rpc("eth_getBlockByNumber", ["pending", false]); for (const txHash of block.transactions) { if (!seen.has(txHash)) { seen.add(txHash); console.log(txHash); } } await new Promise((resolve) => setTimeout(resolve, 200)); } ``` You get a continuous flow of newly pre-confirmed transaction hashes: ```text theme={"system"} 0xaf2b57f8c6b2b0d4b52d9c8d3b5d90e92170edcb1bb90b79eefd9327f12272b1 0xc63e87a5a7b79d1e7dd17bcbaacebc9f7ddbe463dce512374160970714602c2b 0x06ddb3029829356ca6aed0a7c85b5b7c1bde50c1110d727eeb84c24f86bbe259 ``` The `seen` set de-duplicates hashes that repeat across polls of the same in-progress block. In a 30-second test against Optimism Mainnet, the script emitted 230 transaction hashes, about 8 per second. ## How the pending block evolves Each poll of the `pending` tag returns the same block number with more transactions and a different pre-confirmation hash, until the block seals and the number advances. Polled with a 200 ms pause between requests: ```text theme={"system"} #158861051 hash=0x015e2ef4bb2cebaeb45a01963984f21f1200ca7da85fbea5a7f948d0272deaeb txs=14 #158861051 hash=0x58bafaf684a9490a0fceb3cc6e7b390dcb16c8d39cf8129235d47567e62ffa97 txs=51 #158861052 hash=0x9380d911407cec3754d9c9f2ebc40912c2e5f35111cab8a36063040ccd3d97e1 txs=7 #158861052 hash=0x388bed3af1dbdd80b45bc717d268146c9b2fdcf72fda67f002f304d42d4174ac txs=9 #158861053 hash=0xb65c6556b88fd5bc8cf48cc87fe99f50a67e36d120360f1aae3562a96d89b672 txs=6 ``` Unlike Base, where the streamed block carries a zero hash until it seals, Optimism's `pending` hash is non-zero and changes with each subblock. The `pending` hash is not the hash of the sealed block, and the `pending` block's `stateRoot` is all zeros. Track transactions by their own hash, not by the block hash. ## Read pre-confirmed state The `pending` tag also serves request-response reads against pre-confirmed state, which updates about every 200 ms instead of every 2 s with each canonical block: * `eth_getBlockByNumber`, `eth_call`, `eth_getBalance`, `eth_getTransactionCount`, `eth_getCode`, `eth_getStorageAt`, and `eth_estimateGas` with the `pending` tag. * [`eth_getTransactionReceipt`](/reference/optimism-gettransactionreceipt) and [`eth_getTransactionByHash`](/reference/optimism-gettransactionbyhash) return the pre-confirmed receipt or transaction as soon as it lands in a subblock. * [`eth_sendRawTransactionSync`](/reference/optimism-sendrawtransactionsync) submits a signed transaction and returns its pre-confirmed receipt in the same response. ```bash theme={"system"} curl -s -X POST YOUR_OPTIMISM_HTTPS_ENDPOINT \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["0x"]}' ``` ## Production considerations * Pre-confirmations are not final. The sequencer can abandon or reorganize the block in progress until the 2-second canonical block seals, so for settlement confirm against `latest` or a finalized block. * Poll at \~200 ms. That matches the subblock cadence; polling much faster mostly returns the same state. * De-duplicate by transaction hash. The same transaction appears in every poll of the block it belongs to until that block seals. * A poll that lands after a block seals misses the transactions added to that block since the previous poll. To capture every transaction, also read each sealed block with `eth_getBlockByNumber`. * `newPendingTransactions` carries transaction hashes only. Optimism has no public mempool, so the subscription reports a transaction when the sequencer includes it in a subblock. For transaction details, receipts, and pre-confirmed state, use the `pending` tag. ## Related * [Subblocks on Optimism](/docs/flashblocks-on-optimism) — how Subblocks work on Optimism, including the Optimism vs. Base comparison * [Stream real-time transactions on Base with Flashblocks](/docs/base-streaming-transactions-flashblocks) — Base's WebSocket-subscription approach * [Mempool configurations](/docs/mempool-configuration) — mempool availability by protocol * [`eth_getBlockByNumber`](/reference/optimism-getblockbynumber) # Optimism tooling Source: https://docs.chainstack.com/docs/optimism-tooling Complete Optimism tooling guide for Chainstack nodes. Use MetaMask, Hardhat, ethers.js, Foundry, GraphQL, and more to build dApps on Optimism Layer 2. Get started with a [reliable Optimism RPC endpoint](https://chainstack.com/build-better-with-optimism/) to use the tools below. ## MetaMask On the [node access page](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Optimism nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Optimism nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Optimism nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php Php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php Php theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Optimism nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint * USERNAME — your node access username * PASSWORD — your node access password See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Optimism nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Optimism network ID: * Mainnet: `10` * Testnet: `11155420` See [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Optimism network ID: * Mainnet: `10` * Testnet: `11155420` See [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Brownie 1. Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). 2. Use the `brownie networks add` command with the node endpoint: ```shell Shell theme={"system"} brownie networks add Optimism ID name="NETWORK_NAME" host= YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `optimism-testnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Testnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint * NETWORK\_ID — Optimism network ID: * Mainnet: `10` * Testnet: `11155420` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-testnet ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```bash Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password ## GraphQL You can use GraphQL on a Dedicated Node on the [paid plans](https://chainstack.com/pricing/). ### UI You can query data using the graphical interface. 1. On Chainstack, navigate to your dedicated Ethereum node. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). 2. Hover over **GraphQL IDE URL** and click **Open**. 3. In the graphical interface that opens, run a GraphQL query. Example to get the latest block number: ```graphql GraphQL theme={"system"} { block { number } } ``` ### Node.js You can build a web app to query data using node.js and [axios](https://www.npmjs.com/package/axios): ```javascript Javascript theme={"system"} const axios = require('axios'); const main = async () => { try { const result = await axios.post( 'YOUR_CHAINSTACK_ENDPOINT', { query: ` { block { number } } ` } ); console.log(result.data); } catch(error) { console.error(error); } } main(); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node GraphQL endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * `query` — your GraphQL query. In this case, to get the latest block number. # Optimism: Bridge ETH from L1 to L2 with the JS SDK Source: https://docs.chainstack.com/docs/optimism-tutorial-bridge-ether-from-ethereum-l1-to-optimism-l2-using-the-optimism-javascript-sdk Bridge ETH from Ethereum L1 to Optimism L2 using the Optimism JavaScript SDK and Chainstack RPC endpoints for cross-chain asset transfer scripting. **TLDR:** * Demonstrates how to bridge ETH from Ethereum Sepolia (L1) to Optimism Sepolia (L2) using the Optimism JavaScript SDK. * Sets up a crossChainMessenger with the user’s private key to deposit ETH onto L2. * Waits for the L2 relay to finalize and shows updated balances on both layers. * Code sample includes environment variable setup and Ethers integration for seamless bridging. ## Main article This tutorial will show you how to use the [Optimism JavaScript SDK](https://sdk.optimism.io/) to bridge ether from L1 to L2. ## Optimism Optimism is a next-generation solution that enhances the Ethereum blockchain by providing a supplementary layer 2 network. Optimism streamlines the transaction process on Ethereum, resulting in significantly lower fees and fast execution. The beauty of Optimism lies in its seamless integration with Ethereum—each transaction takes place on the Optimism network, yet its validity is confirmed via the Ethereum blockchain. ## Optimism's inner working Optimism leverages the breakthrough technology of optimistic rollups, a sophisticated compression technique developed by the team at the Optimism Foundation. Rollups are a new way to scale the Ethereum blockchain and come in optimistic rollups and zero-knowledge rollups (ZK rollups). Optimistic rollups streamline the transaction process by taking the bulk of data off-chain, resulting in faster processing times. Despite this off-chain approach, a small amount of data is still recorded on the Ethereum network for security purposes. What sets optimistic rollups apart from other scaling solutions is that they do not require cryptographic proofs to validate off-chain transactions. Instead, they rely on a system of fraud proofs and utilize the Optimistic Virtual Machine (OVM)—a sandboxed environment—to ensure secure and deterministic smart contract execution. The OVM acts as the interface between Layers 1 and 2, much like the Ethereum Virtual Machine (EVM). However, the OVM only executes computation, while the EVM handles all execution. The OVM and EVM work together through the Execution Manager to execute transactions in a virtualized environment. Learn more about the Optimism network in [Optimism blockchain – Ethereum Layer 2 scaling solution](https://chainstack.com/an-overview-of-optimism-and-communication-between-l2-and-l1/) on the Chainstack blog. ## Prerequisites * [Chainstack account](https://console.chainstack.com/user/login) to deploy a [reliable Optimism RPC endpoint](https://chainstack.com/build-better-with-optimism/). * Node.js ^16.17.0 — [install node.js](https://nodejs.org/en/download/). ## Dependencies * @eth-optimism/sdk: ^1.10.1 * dotenv: ^16.0.3 * Ethers: ^5.7.2 ## Overview To get from zero to a functioning bridge between Ethereum L1 and Optimism L2, do the following: 1. With Chainstack, create a public chain project (public chain project-A project to join public networks). 2. With Chainstack, join the Ethereum Sepolia testnet and Optimism Sepolia testnet. 3. With Chainstack, access your nodes' credentials. 4. Create an npm project. 5. Install the required dependencies. 6. Create a `.env` file to store the secrets. 7. With the Ethers library, create provider and wallet instances. 8. With Optimism JavaScript SDK, bridge ether between L1 and L2. 9. Run the script. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Ethereum Sepolia testnet and Optimism Sepolia testnet See [Join a public network](/docs/manage-your-networks). ### Get endpoints for your Ethereum Sepolia testnet and Optimism Sepolia testnet See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Create an npm project In your project directory, open the terminal and run `npm init`. Answer the questions in the terminal to create a sample `package.json` file for your project. ### Install the required dependencies To install the libraries and tools required for this bridge project, run: ```bash Shell theme={"system"} npm i @eth-optimism/sdk ethers dotenv ``` ### Create a `.env` file to store the secrets Create a `.env` file in the root directory of your project and paste and fill in the following: ```env env theme={"system"} PRIVATE_KEY="YOUR_WALLET_PRIVATE_KEY" SEPOLIA_CHAINSTACK="YOUR_CHAINSTACK_SEPOLIA_ENDPOINT" OPTIMISM_SEPOLIA_CHAINSTACK="YOUR_CHAINSTACK_OPTIMISM_SEPOLIA_ENDPOINT" ``` ### Use the Ethers library to create provider and wallet instances Create an `index.js` file in your project's root directory. The first step is to import the required packages and create the global variables. To do that, at the top of the file, paste the following: ```js JavaScript theme={"system"} const ethers = require("ethers") const optimismSDK = require("@eth-optimism/sdk") require('dotenv').config() // Environment variables const SEPOLIA_CHAINSTACK = process.env.SEPOLIA_CHAINSTACK; const OPTIMISM_SEPOLIA_CHAINSTACK = process.env.OPTIMISM_SEPOLIA_CHAINSTACK; const PRIVATE_KEY = process.env.PRIVATE_KEY; // Provider instances const l1Provider = new ethers.providers.JsonRpcProvider(SEPOLIA_CHAINSTACK); const l2Provider = new ethers.providers.JsonRpcProvider(OPTIMISM_SEPOLIA_CHAINSTACK); // Init Signers async function getSigners() { const privateKey = PRIVATE_KEY; const l1Wallet = new ethers.Wallet(privateKey, l1Provider); const l2Wallet = new ethers.Wallet(privateKey, l2Provider); return [l1Wallet, l2Wallet]; } ``` This part of the code does the following: * Imports libraries and tools. * Declares the required constants using the environment variables. * Creates provider instances for L1 (Ethereum Sepolia testnet) and L2 (Optimism Sepolia testnet). * Creates wallet instances. The `getSigners()` function uses the private key to create wallet instances with the Ethers library. These instances will be used to query balances and sign transactions. ### Use the Optimism JavaScript SDK to bridge ether between L1 and L2 Create a function to retrieve the networks' chain IDs and a `CrossChainMessenger` instance using the Optimism JavaScript SDK: ```js JavaScript theme={"system"} // Get Chain IDs using ethers async function chainIds() { const l1Network= await l1Provider.getNetwork(); const l2Network = await l2Provider.getNetwork(); const l1ChainId = l1Network.chainId const l2ChainId = l2Network.chainId return [l1ChainId, l2ChainId] } // Init crossChainMessenger using the Chain IDs and wallet instances async function initialize() { const [l1Signer, l2Signer] = await getSigners() const [l1ChainId, l2ChainId] = await chainIds() crossChainMessenger = new optimismSDK.CrossChainMessenger({ l1ChainId: l1ChainId, l2ChainId: l2ChainId, l1SignerOrProvider: l1Signer, l2SignerOrProvider: l2Signer }) } ``` The `chainIds()` function uses the Ethers library to query the Chainstack endpoints and retrieve the chain IDs that will then be used to create the `crossChainMessenger` instance. The `initialize()` function uses the `getSigners()` and `chainIds()` functions to retrieve the parameters required to create the `crossChainMessenger` using the Optimism JavaScript SDK. The [crossChainMessenger](https://sdk.optimism.io/classes/crosschainmessenger) instance allows us to interact with the L1 and L2 networks. ### Function to retrieve the wallets balances Add the following function to retrieve the wallet balances and display them in the console: ```js JavaScript theme={"system"} // Display balances from L1 and L2 using the Optimism SDK crossChainMessenger provider instance async function showBalances() { const l1Balance = (await crossChainMessenger.l1Signer.getBalance()).toString(); const l2Balance = (await crossChainMessenger.l2Signer.getBalance()).toString(); console.log(`Balance on L1: ${ethers.utils.formatEther(l1Balance).slice(0,-14)} ETH`); console.log(`Balance on L2: ${ethers.utils.formatEther(l2Balance).slice(0,-14)} ETH`); console.log("----------------------------------"); } ``` > [!NOTE] > > The `console.log` statements with the `-` are there only to make the response in the console more legible. ### Function to transfer ether between L1 and L2 Now you can create a function to bridge the ether between L1 and L2: ```js JavaScript theme={"system"} // Transfer an amount of ether from L1 to L2 async function bridgeEth() { const wei = BigInt(100000000000000000); // 0.1 ETH in Wei console.log("Fetching current balances..."); await showBalances(); console.log("Initiating ETH transfer from L1 to L2..."); const depositResponse = await crossChainMessenger.depositETH(wei); console.log(`Transaction hash for deposit from L1 to L2: ${depositResponse.hash}`); console.log(`See on Sepolia Etherscan: https://sepolia.etherscan.io/tx/${depositResponse.hash}`); await depositResponse.wait(); console.log("Waiting for deposit transaction to be relayed..."); console.log("----------------------------------"); await crossChainMessenger.waitForMessageStatus( depositResponse.hash, optimismSDK.MessageStatus.RELAYED ); console.log("ETH transfer from L1 to L2 is complete."); console.log("Updating current balances..."); console.log("----------------------------------"); await showBalances(); } ``` Note that the `const wei` holds the amount that will be transferred, expressed in the Wei unit. The default for this script is set to 0.1 ether, equivalent to 100,000,000,000,000,000 Wei. You can use a [Wei converter](https://eth-converter.com/) to include other amounts, or you can use the following: * 1 ether = 1,000,000,000,000,000,000 Wei * 0.1 ether = 100,000,000,000,000,000 Wei * 0.01 ether = 10,000,000,000,000,000 Wei * 0.001 ether = 1,000,000,000,000,000 Wei The `bridgeEth()` function transfers the amount of ether specified in the `wei` constant from Ethereum (L1) to Optimism (L2). It uses the [depositEth](https://sdk.optimism.io/classes/crosschainmessenger#depositETH-2) method of the `crossChainMessenger` instance. It then waits for the `MessageStatus` to become `RELAYED`. The `RELAYED` status indicates that a message has been successfully transmitted from one network to another and is under processing on the recipient network. > [!NOTE] > > ### Message statuses > > Find the different message statuses in the [Optimism JavaScript SDK docs](https://sdk.optimism.io/enums/messagestatus). After that, the `bridgeEth()` function gives some updates and retrieves the balances again. At the bottom of the file, add the `main` function and call it: ```js JavaScript theme={"system"} // Main function async function main() { await initialize() await bridgeEth() } // Run the main function and catch any error main().then(() => process.exit(0)) .catch((error) => { console.error(error) process.exit(1) }) ``` At this point, the entire code will look like this: ```js JavaScript theme={"system"} const ethers = require("ethers") const optimismSDK = require("@eth-optimism/sdk") require('dotenv').config() // Environment variables const SEPOLIA_CHAINSTACK = process.env.SEPOLIA_CHAINSTACK; const OPTIMISM_SEPOLIA_CHAINSTACK = process.env.OPTIMISM_SEPOLIA_CHAINSTACK; const PRIVATE_KEY = process.env.PRIVATE_KEY; // Provider instances const l1Provider = new ethers.providers.JsonRpcProvider(SEPOLIA_CHAINSTACK); const l2Provider = new ethers.providers.JsonRpcProvider(OPTIMISM_SEPOLIA_CHAINSTACK); // Init Signers async function getSigners() { const privateKey = PRIVATE_KEY; const l1Wallet = new ethers.Wallet(privateKey, l1Provider); const l2Wallet = new ethers.Wallet(privateKey, l2Provider); return [l1Wallet, l2Wallet]; } // Get Chain IDs async function chainIds() { const l1Network = await l1Provider.getNetwork(); const l2Network = await l2Provider.getNetwork(); const l1ChainId = l1Network.chainId const l2ChainId = l2Network.chainId return [l1ChainId, l2ChainId] } // Init crossChainMessenger using the Chain IDs and wallet instances async function initialize() { const [l1Signer, l2Signer] = await getSigners() const [l1ChainId, l2ChainId] = await chainIds() crossChainMessenger = new optimismSDK.CrossChainMessenger({ l1ChainId: l1ChainId, l2ChainId: l2ChainId, l1SignerOrProvider: l1Signer, l2SignerOrProvider: l2Signer }) } // Display balances from L1 and L2 async function showBalances() { const l1Balance = (await crossChainMessenger.l1Signer.getBalance()).toString(); const l2Balance = (await crossChainMessenger.l2Signer.getBalance()).toString(); console.log(`Balance on L1: ${ethers.utils.formatEther(l1Balance).slice(0,-14)} ETH`); console.log(`Balance on L2: ${ethers.utils.formatEther(l2Balance).slice(0,-14)} ETH`); console.log("----------------------------------"); } // Transfer an amount of ether from L1 to L2 async function bridgeEth() { const wei = BigInt(100000000000000000); // 0.1 ETH in Wei console.log("Fetching current balances..."); await showBalances(); console.log("Initiating ETH transfer from L1 to L2..."); const depositResponse = await crossChainMessenger.depositETH(wei); console.log(`Transaction hash for deposit from L1 to L2: ${depositResponse.hash}`); console.log(`See on Sepolia Etherscan: https://sepolia.etherscan.io/tx/${depositResponse.hash}`); await depositResponse.wait(); console.log("Waiting for deposit transaction to be relayed..."); console.log("----------------------------------"); await crossChainMessenger.waitForMessageStatus( depositResponse.hash, optimismSDK.MessageStatus.RELAYED ); console.log("ETH transfer from L1 to L2 is complete."); console.log("Updating current balances..."); console.log("----------------------------------"); await showBalances(); } // Main function async function main() { await initialize() await bridgeEth() } // Run the main function main().then(() => process.exit(0)) .catch((error) => { console.error(error) process.exit(1) }) ``` ### Run the script Now it's time to run the script and bridge some ether from L1 to L2. To do this, you will need some Sepolia ether in your wallet. To get testnet ether, you can use the following faucet: * [Chainstack's Sepolia faucet](https://faucet.chainstack.com) Once you have received some Sepolia ether, pick the amount you want to send and update the `wei` constant. To start the script, run the following command: ```bash Shell theme={"system"} node index ``` The console will log all of the steps and it will look similar to the following: ```bash Shell theme={"system"} Fetching current balances... Balance on L1: 9.7844 ETH Balance on L2: 0.9314 ETH ---------------------------------- Initiating ETH transfer from L1 to L2... Transaction hash for deposit from L1 to L2: 0x97455a64eb1c496f4ecc937ffcf2d9294228d9658504a16ab9dbfa638d32693a See on Sepolia Etherscan: https://sepolia.etherscan.io/tx/0x97455a64eb1c496f4ecc937ffcf2d9294228d9658504a16ab9dbfa638d32693a Waiting for deposit transaction to be relayed... ---------------------------------- ETH transfer from L1 to L2 is complete. Updating current balances... ---------------------------------- Balance on L1: 9.6842 ETH Balance on L2: 1.0314 ETH ---------------------------------- ``` As you can see, it prints the transaction hash and the link to check the transaction details using Sepolia Etherscan. ## Conclusion This tutorial guided you through creating a basic L1 => L2 bridge using the Optimism JavaScript SDK. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Optimized submission Source: https://docs.chainstack.com/docs/optimized-submission Enable optimized submission on your Chainstack Arbitrum, Base, Arc, and Robinhood Chain nodes to route transactions through a private, optimized path for faster inclusion. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Optimized submission is a Marketplace [add-on](/docs/add-ons) you manage from your node's **Add-ons** tab. It routes your transactions through a private, optimized path designed to reduce inclusion delays. It applies to L2 sequencer chains, where transactions are not broadcast to a public mempool. It is available on these networks and node types: | Network | Node types | | - | - | | Arbitrum Mainnet | [Global Nodes](/docs/global-elastic-node), [Trader Nodes](/docs/trader-node) | | Base Mainnet | Global Nodes, Trader Nodes | | Arc Mainnet | Global Nodes | | Robinhood Chain Mainnet | Global Nodes | On Global Nodes, optimized submission is available on every plan, including the Developer plan. On Ethereum and BNB Smart Chain, which have a public mempool where front-running is possible, use [MEV protection](/docs/mev-protection) instead. ### How it works 1. You construct and sign your transaction as usual and send it through your Chainstack endpoint as `eth_sendRawTransaction`. 2. Instead of the default node route, it is routed through a private, optimized submission path. 3. Once included in a block, it appears on-chain as normal, and you can retrieve it with the regular methods such as [eth\_getTransactionByHash](/reference/arbitrum-gettransactionbyhash). ### What happens if private routing fails If the optimized submission path fails, Chainstack sends the transaction through the node's standard route instead, so the transaction still reaches the network. Arbitrum, Base, Arc, and Robinhood Chain don't broadcast transactions to a public mempool, so the standard route doesn't expose the transaction to front-running. This fallback differs from [MEV protection](/docs/mev-protection), which returns an error rather than using the public mempool. Optimized submission applies to the node's existing HTTPS and WSS endpoints and to every [authentication method](/docs/authentication-methods-for-different-scenarios) of the node. ### Managing optimized submission Optimized submission is on by default for a new node. When you deploy a node on Chainstack, optimized submission is already selected under **Add-ons** on the last step. To deploy without it, click **Disable** on the **Optimized submission** card. A node you create with the [Platform API](/reference/chainstack-platform-api-v2-create-node) gets optimized submission installed automatically. The node's **Add-ons** tab shows whether optimized submission is on: its card shows **Active** with a **Disable** button when it is enabled, and **Enable** when it is not. The **Add-ons** tab is the only place that shows this status; no JSON-RPC method or Platform API field reports it. To enable optimized submission on a node that doesn't have it, open your node on Chainstack, select the **Add-ons** tab, click **Enable** on the **Optimized submission** card, and complete the install. See [Install a node add-on](/docs/add-ons#install-a-node-add-on). To use the standard node route, click **Disable** on the **Optimized submission** card. You can enable it again from the same tab. # Plasma methods Source: https://docs.chainstack.com/docs/plasma-methods Complete reference of supported Plasma JSON-RPC methods on Chainstack, including method availability for eth, debug, trace, and net namespace calls. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Plasma API call examples](/reference/plasma-getting-started). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_blobBaseFee | | | | eth\_call | | | | eth\_callMany | | | | eth\_chainId | | | | eth\_coinbase | | | | eth\_createAccessList | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_fillTransaction | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getHeaderByHash | | | | eth\_getHeaderByNumber | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getRawTransactionByBlockHashAndIndex | | | | eth\_getRawTransactionByBlockNumberAndIndex | | | | eth\_getRawTransactionByHash | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionBySenderAndNonce | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleByBlockHashAndIndex | | | | eth\_getUncleByBlockNumberAndIndex | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_hashrate | | | | eth\_maxPriorityFeePerGas | | | | eth\_mining | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_protocolVersion | | | | eth\_simulateV1 | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_getRawBlock | | | | debug\_getRawHeader | | | | debug\_getRawReceipts | | | | debug\_getRawTransaction | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceCallMany | | | | debug\_traceTransaction | | | | trace\_block | | | | trace\_call | | | | trace\_callMany | | | | trace\_filter | | | | trace\_get | | | | trace\_rawTransaction | | | | trace\_replayBlockTransactions | | | | trace\_replayTransaction | | | | reth\_getBalanceChangesInBlock | | | | rpc\_modules | | | # Plasma tooling Source: https://docs.chainstack.com/docs/plasma-tooling Plasma tooling guide for Chainstack nodes. Use MetaMask, web3.py, ethers.js, Hardhat, Foundry, and Remix with Plasma mainnet and testnet. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet** to inject your Chainstack endpoint automatically. If you need to add the network manually, use: * Network name: `Plasma Mainnet` * RPC URL: your Chainstack HTTPS endpoint * Chain ID: `9745` * Currency symbol: `XPL` * Block explorer URL: `https://plasmascan.to` For the testnet, use: * Network name: `Plasma Testnet` * RPC URL: your Chainstack HTTPS endpoint * Chain ID: `9746` * Currency symbol: `XPL` * Block explorer URL: `https://testnet.plasmascan.to` Refer to [node access details](/docs/manage-your-node#view-node-access-and-credentials) for your endpoint URL format and credentials. ## web3.py Build dapps using [web3.py](https://github.com/ethereum/web3.py) and Plasma nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s' % ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — the username to your node if you use a password-protected endpoint * PASSWORD — the password to your node if you use a password-protected endpoint * HOSTNAME — the host name of your node Plasma network IDs: * Mainnet: `9745` * Testnet: `9746` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## ethers.js Build dapps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Plasma nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP. ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT", { chainId: 9745, name: "plasma" }); provider.getBlockNumber().then(console.log); ``` For the testnet, set `chainId: 9746` instead. where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password. See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Plasma nodes. 1. Install Hardhat and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomicfoundation/hardhat-ethers"); ... module.exports = { solidity: "0.8.26", networks: { plasmaMainnet: { url: "YOUR_CHAINSTACK_ENDPOINT", chainId: 9745, accounts: ["YOUR_PRIVATE_KEY"] }, plasmaTestnet: { url: "YOUR_CHAINSTACK_ENDPOINT_FOR_TESTNET", chainId: 9746, accounts: ["YOUR_PRIVATE_KEY"] } } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_CHAINSTACK\_ENDPOINT\_FOR\_TESTNET — your Plasma testnet node endpoint. * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network plasmaMainnet` to deploy to mainnet or `--network plasmaTestnet` for the testnet. Plasma chain IDs are documented in the Plasma network reference. ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy & run transactions** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT --chain-id 9745 ``` To deploy to the testnet, switch to `--rpc-url YOUR_CHAINSTACK_ENDPOINT_FOR_TESTNET --chain-id 9746`. where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your mainnet node HTTPS endpoint protected either with the key or password * YOUR\_CHAINSTACK\_ENDPOINT\_FOR\_TESTNET — your testnet node HTTPS endpoint ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT --chain-id 9745 ``` Switch to your Plasma testnet endpoint and `--chain-id 9746` to target the testnet. # Plasma: Bridging assets with Symbiosis Source: https://docs.chainstack.com/docs/plasma-tutorial-bridging-assets-with-symbiosis Bridge USDT and other tokens to Plasma programmatically using the Symbiosis JS SDK, tracking cross-chain transaction status through Chainstack endpoints. ## Overview This tutorial shows how to bridge assets to Plasma programmatically using the Symbiosis cross-chain protocol. You will set up the Symbiosis JS SDK, execute a bridge transaction from Ethereum to Plasma, and monitor the transfer until completion. By the end, you will have working code that: * Connects to Symbiosis and initializes a swap * Bridges USDT from Ethereum to USDT0 on Plasma * Monitors the cross-chain transaction status * Handles failures with retry logic ## Prerequisites * [Chainstack account](https://console.chainstack.com/) with Plasma and Ethereum nodes deployed * Node.js 18 or later * A wallet with USDT on Ethereum (testnet or mainnet) * Basic familiarity with ethers.js > [!NOTE] > > This tutorial uses mainnet examples. For testing, use small amounts or deploy on testnets first. ## Network parameters | Network | Chain ID | Currency | USDT contract | | - | - | - | - | | Plasma Mainnet | `9745` | XPL | `0xB8CE59FC3717Ada4C02eadf9682A9e934F625ebb` | | Ethereum Mainnet | `1` | ETH | `0xdAC17F958D2ee523a2206206994597C13D831ec7` | ## Bridge options for Plasma Several bridges support Plasma: | Bridge | Type | Best for | | - | - | - | | [Symbiosis](https://symbiosis.finance/bridge-plasma) | DEX aggregator | Programmatic integration | | [deBridge](https://app.debridge.finance/) | Cross-chain | UI-based transfers | | [Rhino.fi](https://rhino.fi/) | Bridge aggregator | Multiple route options | This tutorial focuses on Symbiosis for its JavaScript SDK and API support. ## 1. Set up the project ```bash theme={"system"} mkdir plasma-bridge cd plasma-bridge npm init -y npm install symbiosis-js-sdk ethers dotenv ``` Create a `.env` file: ```bash theme={"system"} PRIVATE_KEY=your_wallet_private_key CHAINSTACK_ETH_URL=YOUR_CHAINSTACK_ETHEREUM_ENDPOINT CHAINSTACK_PLASMA_URL=YOUR_CHAINSTACK_PLASMA_ENDPOINT ``` > [!WARNING] > > Never commit private keys to version control. Use environment variables or a secrets manager. ## 2. Initialize the SDK Create `bridge.js`: ```javascript theme={"system"} import { Symbiosis, Token, TokenAmount } from 'symbiosis-js-sdk'; import { ethers } from 'ethers'; import dotenv from 'dotenv'; dotenv.config(); // Chain IDs const ETHEREUM_CHAIN_ID = 1; const PLASMA_CHAIN_ID = 9745; // Token addresses const USDT_ETHEREUM = '0xdAC17F958D2ee523a2206206994597C13D831ec7'; const USDT_PLASMA = '0xB8CE59FC3717Ada4C02eadf9682A9e934F625ebb'; // Initialize Symbiosis const symbiosis = new Symbiosis('mainnet', 'chainstack-bridge-tutorial'); // Set up providers const ethProvider = new ethers.JsonRpcProvider(process.env.CHAINSTACK_ETH_URL); const plasmaProvider = new ethers.JsonRpcProvider(process.env.CHAINSTACK_PLASMA_URL); const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, ethProvider); console.log('Wallet address:', wallet.address); ``` ## 3. Define tokens and amounts Add the token definitions: ```javascript theme={"system"} // Define source token (USDT on Ethereum) const tokenIn = new Token({ chainId: ETHEREUM_CHAIN_ID, address: USDT_ETHEREUM, decimals: 6, symbol: 'USDT', name: 'Tether USD', }); // Define destination token (USDT0 on Plasma) const tokenOut = new Token({ chainId: PLASMA_CHAIN_ID, address: USDT_PLASMA, decimals: 6, symbol: 'USDT0', name: 'Tether USD on Plasma', }); // Amount to bridge (10 USDT = 10_000_000 in 6 decimals) const amountIn = '10000000'; const tokenAmountIn = new TokenAmount(tokenIn, amountIn); ``` ## 4. Execute the bridge Add the bridging logic: ```javascript theme={"system"} async function bridgeToPlasma() { console.log(`\nBridging ${tokenAmountIn.toSignificant()} USDT to Plasma...`); // Check USDT balance before proceeding const usdtContract = new ethers.Contract( USDT_ETHEREUM, ['function balanceOf(address) view returns (uint256)'], ethProvider ); const balance = await usdtContract.balanceOf(wallet.address); if (BigInt(amountIn) > balance) { throw new Error(`Insufficient USDT balance. Have: ${ethers.formatUnits(balance, 6)}, need: ${ethers.formatUnits(amountIn, 6)}`); } console.log('USDT balance:', ethers.formatUnits(balance, 6)); try { // Create swapping instance const swapping = symbiosis.bestPoolSwapping(); // Calculate route and fees console.log('Calculating best route...'); const { transactionRequest, fee, route, priceImpact } = await swapping.exactIn({ tokenAmountIn, tokenOut, from: wallet.address, to: wallet.address, revertableAddress: wallet.address, slippage: 100, // 1% slippage (100 basis points) deadline: Math.floor(Date.now() / 1000) + 1800, // 30 minutes }); console.log('Route:', route.map(t => t.symbol).join(' → ')); console.log('Fee:', fee.toSignificant(), fee.token.symbol); console.log('Price impact:', priceImpact.toSignificant(), '%'); // Validate price impact - warn if high, abort if excessive const impactPercent = parseFloat(priceImpact.toSignificant()); if (impactPercent > 5) { console.warn('⚠️ WARNING: High price impact detected! Consider smaller amount.'); } if (impactPercent > 10) { throw new Error(`Price impact too high (${impactPercent}%). Aborting to prevent losses.`); } // Check and approve USDT spending await approveToken(tokenIn, transactionRequest.to, amountIn); // Send bridge transaction console.log('\nSending bridge transaction...'); const tx = await wallet.sendTransaction({ to: transactionRequest.to, data: transactionRequest.data, value: transactionRequest.value || 0, gasLimit: 500000, }); console.log('Transaction hash:', tx.hash); console.log('Waiting for confirmation...'); const receipt = await tx.wait(); console.log('Confirmed in block:', receipt.blockNumber); // Wait for cross-chain completion console.log('\nWaiting for cross-chain transfer...'); const completionLog = await swapping.waitForComplete(receipt); console.log('Bridge complete! Destination tx:', completionLog.transactionHash); return completionLog; } catch (error) { console.error('Bridge failed:', error.message); throw error; } } ``` ## 5. Add token approval Add the approval helper: ```javascript theme={"system"} const ERC20_ABI = [ 'function approve(address spender, uint256 amount) returns (bool)', 'function allowance(address owner, address spender) view returns (uint256)', ]; async function approveToken(token, spender, amount) { const contract = new ethers.Contract(token.address, ERC20_ABI, wallet); // Check existing allowance const allowance = await contract.allowance(wallet.address, spender); if (allowance >= BigInt(amount)) { console.log('Sufficient allowance exists'); return; } // Approve spending console.log('Approving token spend...'); const approveTx = await contract.approve(spender, amount); await approveTx.wait(); console.log('Approval confirmed'); } ``` ## 6. Monitor transaction status Add status monitoring for pending transactions: ```javascript theme={"system"} async function checkBridgeStatus(txHash, sourceChainId) { console.log(`\nChecking status for ${txHash}...`); try { // Get pending requests from Symbiosis const pendingRequests = await symbiosis.getPendingRequests(wallet.address); for (const request of pendingRequests) { if (request.transactionHash === txHash) { console.log('Status:', request.status); console.log('Source chain:', request.chainIdFrom); console.log('Destination chain:', request.chainIdTo); return request; } } console.log('Transaction not found in pending requests (may be complete)'); return null; } catch (error) { console.error('Status check failed:', error.message); return null; } } ``` ## 7. Handle stuck transactions Add recovery logic for failed bridges: ```javascript theme={"system"} async function revertStuckTransaction(request) { console.log('\nAttempting to revert stuck transaction...'); try { const revertPending = symbiosis.newRevertPending(request); const { transactionRequest } = await revertPending.revert(); const tx = await wallet.sendTransaction({ to: transactionRequest.to, data: transactionRequest.data, value: transactionRequest.value || 0, }); console.log('Revert transaction:', tx.hash); const receipt = await tx.wait(); console.log('Revert confirmed in block:', receipt.blockNumber); // Wait for revert completion const log = await revertPending.waitForComplete(); console.log('Funds returned! Tx:', log.transactionHash); return log; } catch (error) { console.error('Revert failed:', error.message); throw error; } } ``` ## 8. Add retry logic Wrap the bridge with retry handling: ```javascript theme={"system"} async function bridgeWithRetry(maxAttempts = 3) { let lastError; for (let attempt = 1; attempt <= maxAttempts; attempt++) { console.log(`\n=== Attempt ${attempt} of ${maxAttempts} ===`); try { const result = await bridgeToPlasma(); return result; } catch (error) { lastError = error; console.error(`Attempt ${attempt} failed:`, error.message); if (attempt < maxAttempts) { const delay = Math.pow(2, attempt) * 1000; // Exponential backoff console.log(`Waiting ${delay / 1000}s before retry...`); await new Promise(resolve => setTimeout(resolve, delay)); } } } throw new Error(`Bridge failed after ${maxAttempts} attempts: ${lastError.message}`); } ``` ## 9. Run the bridge Add the main execution: ```javascript theme={"system"} async function main() { // Check balances before const ethBalance = await ethProvider.getBalance(wallet.address); console.log('ETH balance:', ethers.formatEther(ethBalance)); // Execute bridge with retry try { await bridgeWithRetry(3); console.log('\n✓ Bridge completed successfully!'); } catch (error) { console.error('\n✗ Bridge failed:', error.message); process.exit(1); } // Verify arrival on Plasma const usdtContract = new ethers.Contract( USDT_PLASMA, ['function balanceOf(address) view returns (uint256)'], plasmaProvider ); const plasmaBalance = await usdtContract.balanceOf(wallet.address); console.log('\nUSDT0 balance on Plasma:', ethers.formatUnits(plasmaBalance, 6)); } main(); ``` ## 10. Run the script Update `package.json` to enable ES modules: ```json theme={"system"} { "type": "module" } ``` Run the bridge: ```bash theme={"system"} node bridge.js ``` Expected output: ```text theme={"system"} Wallet address: 0x1234...5678 ETH balance: 0.5 === Attempt 1 of 3 === Bridging 10 USDT to Plasma... Calculating best route... Route: USDT → USDC → USDT0 Fee: 0.5 USDT Price impact: 0.3 % Approving token spend... Approval confirmed Sending bridge transaction... Transaction hash: 0xabcd...ef01 Waiting for confirmation... Confirmed in block: 18500000 Waiting for cross-chain transfer... Bridge complete! Destination tx: 0x9876...5432 ✓ Bridge completed successfully! USDT0 balance on Plasma: 9.50 ``` ## Alternative: Using the Symbiosis API For production applications, Symbiosis recommends using their API instead of the SDK for better stability: ```javascript theme={"system"} async function bridgeViaApi(fromChainId, toChainId, tokenIn, tokenOut, amount, recipient) { const response = await fetch('https://api.symbiosis.finance/crosschain/v1/swap', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tokenAmountIn: { chainId: fromChainId, address: tokenIn, amount: amount, decimals: 6, }, tokenOut: { chainId: toChainId, address: tokenOut, decimals: 6, }, from: recipient, to: recipient, revertableAddress: recipient, slippage: 100, }), }); return response.json(); } ``` ## Bridge alternatives ### deBridge For UI-based bridging or deBridge API integration: 1. Visit [app.debridge.finance](https://app.debridge.finance/) 2. Connect wallet and select source chain 3. Choose Plasma as destination 4. Enter amount and confirm ### Direct USDT0 transfers on Plasma Once you have USDT0 on Plasma, transfers are zero-fee using the Relayer API: ```javascript theme={"system"} // POST https://api.relayer.plasma.to/v1/submit // Requires EIP-3009 authorization signature ``` See the [Plasma zero-fee transfers documentation](https://plasma.to/docs/plasma-chain/stablecoin-native-contracts/zero-fee-usdt-transfers) for details. ## Troubleshooting ### Transaction stuck If your bridge transaction is pending for more than 10 minutes: 1. Check status using `checkBridgeStatus()` 2. If stuck, use `revertStuckTransaction()` to recover funds 3. Contact Symbiosis support with your transaction hash ### Insufficient gas Ensure your wallet has enough ETH for: * Token approval transaction * Bridge transaction (typically 0.01-0.05 ETH) ### Slippage errors Increase slippage tolerance for volatile conditions: ```javascript theme={"system"} slippage: 300, // 3% instead of 1% ``` ## Conclusion You now have working code to bridge assets to Plasma programmatically. The Symbiosis SDK handles routing, fee calculation, and cross-chain monitoring automatically. For production use, consider: * Using the Symbiosis API for better stability * Adding comprehensive error handling * Implementing transaction logging * Setting up monitoring for stuck transactions ## Resources * [Symbiosis documentation](https://docs.symbiosis.finance/) * [Symbiosis JS SDK](https://github.com/symbiosis-finance/js-sdk) * [deBridge Plasma integration](https://debridge.com/learn/blog/plasma-is-live-on-debridge/) * [Plasma bridge documentation](https://plasma.to/docs) # Plasma: Bridging from HyperEVM with deBridge Source: https://docs.chainstack.com/docs/plasma-tutorial-bridging-from-hyperevm-with-debridge Bridge assets between HyperEVM and Plasma using the deBridge DLN API with JavaScript, including transaction creation and status tracking via Chainstack. ## Overview This tutorial shows how to bridge assets between HyperEVM (Hyperliquid's EVM layer) and Plasma using the deBridge Liquidity Network (DLN) API. You will build a JavaScript application that creates cross-chain swap orders, executes bridge transactions, and monitors their status. By the end, you will have working code that: * Quotes bridge fees and estimated output amounts * Executes cross-chain transfers from HyperEVM to Plasma * Monitors order status until completion * Handles the reverse direction (Plasma to HyperEVM) ## Prerequisites * [Chainstack account](https://console.chainstack.com/) with Plasma and Hyperliquid nodes deployed * Node.js 18 or later * A wallet with assets on HyperEVM (HYPE, USDT, or other supported tokens) * Basic familiarity with ethers.js > [!NOTE] > > deBridge supports both directions: HyperEVM → Plasma and Plasma → HyperEVM. This tutorial covers both. ## Network parameters | Network | Chain ID | Currency | RPC endpoint | | - | - | - | - | | HyperEVM Mainnet | `999` | HYPE | `https://rpc.hyperliquid.xyz/evm` | | Plasma Mainnet | `9745` | XPL | `https://rpc.plasma.to` | ### Token addresses | Token | HyperEVM | Plasma | | - | - | - | | Native | HYPE (18 decimals) | XPL (18 decimals) | | USDT | Check deBridge | `0xB8CE59FC3717Ada4C02eadf9682A9e934F625ebb` | > [!TIP] > > Use your Chainstack endpoints for better reliability and higher rate limits compared to public RPCs. ## 1. Set up the project ```bash theme={"system"} mkdir hyperevm-plasma-bridge cd hyperevm-plasma-bridge npm init -y npm install ethers dotenv ``` Create a `.env` file: ```bash theme={"system"} PRIVATE_KEY=your_wallet_private_key CHAINSTACK_HYPEREVM_URL=YOUR_CHAINSTACK_HYPERLIQUID_ENDPOINT CHAINSTACK_PLASMA_URL=YOUR_CHAINSTACK_PLASMA_ENDPOINT ``` > [!WARNING] > > Never commit private keys to version control. Use environment variables or a secrets manager in production. ## 2. Initialize the bridge client Create `bridge.js`: ```javascript theme={"system"} import { ethers } from 'ethers'; import dotenv from 'dotenv'; dotenv.config(); // Standard chain IDs (for RPC connections) const HYPEREVM_ORIGINAL_CHAIN_ID = 999; const PLASMA_ORIGINAL_CHAIN_ID = 9745; // deBridge internal chain IDs (required for API calls) // IMPORTANT: deBridge uses different IDs than standard chain IDs // See: https://dln.debridge.finance/v1.0/supported-chains-info const DEBRIDGE_HYPEREVM_CHAIN_ID = 100000022; const DEBRIDGE_PLASMA_CHAIN_ID = 100000028; // deBridge DLN API const DLN_API_BASE = 'https://dln.debridge.finance/v1.0'; // Native token address (used for native assets like HYPE) const NATIVE_TOKEN = '0x0000000000000000000000000000000000000000'; // Plasma USDT0 address const PLASMA_USDT = '0xB8CE59FC3717Ada4C02eadf9682A9e934F625ebb'; // Set up providers const hyperevmProvider = new ethers.JsonRpcProvider( process.env.CHAINSTACK_HYPEREVM_URL || 'https://rpc.hyperliquid.xyz/evm' ); const plasmaProvider = new ethers.JsonRpcProvider( process.env.CHAINSTACK_PLASMA_URL || 'https://rpc.plasma.to' ); // Create wallet for source chain const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, hyperevmProvider); console.log('Wallet address:', wallet.address); ``` ## 3. Get a bridge quote Add the quote function to fetch estimated amounts and fees: ```javascript theme={"system"} async function getBridgeQuote({ srcChainId, dstChainId, srcTokenAddress, dstTokenAddress, amount, recipient, }) { const params = new URLSearchParams({ srcChainId: srcChainId.toString(), srcChainTokenIn: srcTokenAddress, srcChainTokenInAmount: amount, dstChainId: dstChainId.toString(), dstChainTokenOut: dstTokenAddress, dstChainTokenOutAmount: 'auto', dstChainTokenOutRecipient: recipient, srcChainOrderAuthorityAddress: recipient, dstChainOrderAuthorityAddress: recipient, }); const url = `${DLN_API_BASE}/dln/order/create-tx?${params}`; console.log('\nFetching quote from deBridge...'); const response = await fetch(url); if (!response.ok) { const error = await response.text(); throw new Error(`Quote failed: ${error}`); } const data = await response.json(); return data; } ``` ## 4. Execute the bridge transaction Add functions to handle token approval and transaction execution: ```javascript theme={"system"} const ERC20_ABI = [ 'function approve(address spender, uint256 amount) returns (bool)', 'function allowance(address owner, address spender) view returns (uint256)', 'function balanceOf(address account) view returns (uint256)', 'function decimals() view returns (uint8)', ]; async function approveToken(tokenAddress, spender, amount, signer) { // Skip approval for native token if (tokenAddress === NATIVE_TOKEN) { console.log('Native token - no approval needed'); return; } const contract = new ethers.Contract(tokenAddress, ERC20_ABI, signer); // Check existing allowance const allowance = await contract.allowance(signer.address, spender); if (allowance >= BigInt(amount)) { console.log('Sufficient allowance exists'); return; } // Approve spending console.log('Approving token spend...'); const tx = await contract.approve(spender, amount); const receipt = await tx.wait(); console.log('Approval confirmed in block:', receipt.blockNumber); } async function executeBridge({ srcChainId, dstChainId, srcTokenAddress, dstTokenAddress, amount, signer, }) { const recipient = signer.address; // Get quote with transaction data const quote = await getBridgeQuote({ srcChainId, dstChainId, srcTokenAddress, dstTokenAddress, amount, recipient, }); if (!quote.tx) { throw new Error('No transaction data in response. Check parameters.'); } // Log estimation details const estimation = quote.estimation; console.log('\n--- Bridge Quote ---'); console.log('Input:', estimation.srcChainTokenIn.amount, estimation.srcChainTokenIn.symbol); console.log('Output:', estimation.dstChainTokenOut.amount, estimation.dstChainTokenOut.symbol); console.log('Recommended slippage:', estimation.recommendedSlippage, '%'); console.log('Execution fee:', estimation.executionFee?.amount || 'included'); // Calculate and validate price impact const inputUsd = estimation.srcChainTokenIn.approximateUsdValue || 0; const outputUsd = estimation.dstChainTokenOut.approximateUsdValue || 0; if (inputUsd > 0 && outputUsd > 0) { const priceImpact = ((inputUsd - outputUsd) / inputUsd) * 100; console.log('Price impact:', priceImpact.toFixed(2), '%'); // Warn if price impact exceeds 5% if (priceImpact > 5) { console.warn('⚠️ WARNING: High price impact detected! Consider smaller amount.'); } // Abort if price impact exceeds 10% if (priceImpact > 10) { throw new Error(`Price impact too high (${priceImpact.toFixed(2)}%). Aborting to prevent losses.`); } } // Approve token if ERC20 await approveToken( srcTokenAddress, quote.tx.to, estimation.srcChainTokenIn.amount, signer ); // Execute bridge transaction console.log('\nSending bridge transaction...'); const tx = await signer.sendTransaction({ to: quote.tx.to, data: quote.tx.data, value: quote.tx.value || '0', gasLimit: 500000, }); console.log('Transaction hash:', tx.hash); console.log('Waiting for confirmation...'); const receipt = await tx.wait(); console.log('Confirmed in block:', receipt.blockNumber); return { txHash: tx.hash, orderId: extractOrderId(receipt), quote, }; } function extractOrderId(receipt) { // The order ID can be found in the transaction logs // For simplicity, we'll use the tx hash for tracking return receipt.hash; } ``` ## 5. Monitor order status Add status tracking to monitor the cross-chain transfer: ```javascript theme={"system"} async function getOrderStatus(txHash, srcChainId) { const url = `https://stats-api.dln.trade/api/Order/filteredList?fromAddress=${wallet.address}`; try { const response = await fetch(url); const data = await response.json(); // Find our order const order = data.orders?.find( (o) => o.createTx?.toLowerCase() === txHash.toLowerCase() ); if (order) { return { status: order.status, orderId: order.orderId, srcChainId: order.srcChainId, dstChainId: order.dstChainId, fulfilled: ['Fulfilled', 'SentUnlock', 'ClaimedUnlock'].includes(order.status), }; } return null; } catch (error) { console.error('Status check failed:', error.message); return null; } } async function waitForCompletion(txHash, srcChainId, maxWaitMs = 300000) { console.log('\nMonitoring cross-chain transfer...'); const startTime = Date.now(); const pollInterval = 10000; // 10 seconds while (Date.now() - startTime < maxWaitMs) { const status = await getOrderStatus(txHash, srcChainId); if (status) { console.log(`Status: ${status.status}`); if (status.fulfilled) { console.log('✓ Bridge transfer completed!'); return status; } } else { console.log('Order not yet indexed, waiting...'); } await new Promise((resolve) => setTimeout(resolve, pollInterval)); } throw new Error('Bridge transfer timed out'); } ``` ## 6. Bridge from HyperEVM to Plasma Add the main bridging function: ```javascript theme={"system"} async function bridgeHyperEVMToPlasma(tokenIn, tokenOut, amount) { console.log('\n=== Bridging from HyperEVM to Plasma ==='); // Check balance const balance = await hyperevmProvider.getBalance(wallet.address); console.log('HYPE balance:', ethers.formatEther(balance)); // Validate sufficient balance if (BigInt(amount) > balance) { throw new Error(`Insufficient balance. Have: ${ethers.formatEther(balance)}, need: ${ethers.formatEther(amount)}`); } try { const result = await executeBridge({ srcChainId: DEBRIDGE_HYPEREVM_CHAIN_ID, // Use deBridge internal ID dstChainId: DEBRIDGE_PLASMA_CHAIN_ID, // Use deBridge internal ID srcTokenAddress: tokenIn, dstTokenAddress: tokenOut, amount: amount, signer: wallet, }); // Wait for completion await waitForCompletion(result.txHash, DEBRIDGE_HYPEREVM_CHAIN_ID); return result; } catch (error) { console.error('Bridge failed:', error.message); throw error; } } ``` ## 7. Bridge from Plasma to HyperEVM Add the reverse direction: ```javascript theme={"system"} async function bridgePlasmaToHyperEVM(tokenIn, tokenOut, amount) { console.log('\n=== Bridging from Plasma to HyperEVM ==='); // Create wallet connected to Plasma const plasmaWallet = new ethers.Wallet(process.env.PRIVATE_KEY, plasmaProvider); // Check balance const balance = await plasmaProvider.getBalance(plasmaWallet.address); console.log('XPL balance:', ethers.formatEther(balance)); // Validate sufficient balance if (BigInt(amount) > balance) { throw new Error(`Insufficient balance. Have: ${ethers.formatEther(balance)}, need: ${ethers.formatEther(amount)}`); } try { const result = await executeBridge({ srcChainId: DEBRIDGE_PLASMA_CHAIN_ID, // Use deBridge internal ID dstChainId: DEBRIDGE_HYPEREVM_CHAIN_ID, // Use deBridge internal ID srcTokenAddress: tokenIn, dstTokenAddress: tokenOut, amount: amount, signer: plasmaWallet, }); // Wait for completion await waitForCompletion(result.txHash, DEBRIDGE_PLASMA_CHAIN_ID); return result; } catch (error) { console.error('Bridge failed:', error.message); throw error; } } ``` ## 8. Run the bridge Add the main execution and example usage: ```javascript theme={"system"} async function main() { const args = process.argv.slice(2); const direction = args[0] || 'to-plasma'; const amount = args[1] || '1000000000000000'; // 0.001 in 18 decimals console.log('Direction:', direction); console.log('Amount:', amount); try { if (direction === 'to-plasma') { // Bridge HYPE from HyperEVM to XPL on Plasma await bridgeHyperEVMToPlasma( NATIVE_TOKEN, // HYPE (native) NATIVE_TOKEN, // XPL (native) amount ); } else if (direction === 'to-hyperevm') { // Bridge XPL from Plasma to HYPE on HyperEVM await bridgePlasmaToHyperEVM( NATIVE_TOKEN, // XPL (native) NATIVE_TOKEN, // HYPE (native) amount ); } else { console.log('Usage: node bridge.js [to-plasma|to-hyperevm] [amount]'); return; } console.log('\n✓ Bridge completed successfully!'); } catch (error) { console.error('\n✗ Bridge failed:', error.message); process.exit(1); } } main(); ``` Update `package.json` for ES modules: ```json theme={"system"} { "type": "module" } ``` ## 9. Test the bridge Run the bridge in either direction: ```bash theme={"system"} # Bridge from HyperEVM to Plasma node bridge.js to-plasma 1000000000000000 # Bridge from Plasma to HyperEVM node bridge.js to-hyperevm 1000000000000000 ``` Expected output: ```text theme={"system"} Wallet address: 0x1234...5678 Direction: to-plasma Amount: 1000000000000000 === Bridging from HyperEVM to Plasma === HYPE balance: 0.5 Fetching quote from deBridge... --- Bridge Quote --- Input: 1000000000000000 HYPE Output: 980000000000000 XPL Recommended slippage: 0.5 % Execution fee: included Native token - no approval needed Sending bridge transaction... Transaction hash: 0xabcd...ef01 Waiting for confirmation... Confirmed in block: 1234567 Monitoring cross-chain transfer... Status: Created Status: Fulfilled ✓ Bridge transfer completed! ✓ Bridge completed successfully! ``` ## Alternative: Using the deBridge UI For manual transfers without code: 1. Visit [app.debridge.finance](https://app.debridge.finance/) 2. Connect your wallet 3. Select **HyperEVM** as source chain 4. Select **Plasma** as destination chain 5. Choose token and amount 6. Click **Swap** and confirm ## Troubleshooting ### "Quote failed" error Check that both chains are supported by deBridge and the token addresses are correct. Use the native token address (`0x0000...0000`) for HYPE and XPL. ### Transaction stuck Cross-chain transfers typically complete in 1-5 minutes. If stuck longer: 1. Check order status at [stats-api.dln.trade](https://stats-api.dln.trade/api/Order/filteredList?fromAddress=YOUR_ADDRESS) 2. Contact deBridge support with your transaction hash ### Insufficient balance Ensure you have enough: * Source tokens for the bridge amount * Native tokens for gas (HYPE on HyperEVM, XPL on Plasma) ### Rate limiting The public RPC endpoints have rate limits: * HyperEVM public: 100 requests/minute * Plasma public: varies Use Chainstack endpoints for higher limits and better reliability. ## Supported tokens deBridge supports bridging various tokens between HyperEVM and Plasma. Check the [deBridge app](https://app.debridge.finance/) for the current list of supported assets. Common routes: * HYPE ↔ XPL (native tokens) * USDT on HyperEVM ↔ USDT0 on Plasma * ETH variants ↔ wrapped versions ## Conclusion You now have working code to bridge assets between HyperEVM and Plasma using the deBridge DLN API. The same pattern works for bridging between any chains supported by deBridge. For production applications, consider: * Adding comprehensive error handling * Implementing retry logic with exponential backoff * Setting up monitoring for stuck transactions * Using webhooks for status notifications ## Resources * [deBridge documentation](https://docs.debridge.finance/) * [deBridge DLN API](https://dln.debridge.finance/v1.0) * [Hyperliquid HyperEVM docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm) * [Plasma documentation](https://plasma.to/docs) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Monitor Plasma USDT flows with web3.py Source: https://docs.chainstack.com/docs/plasma-tutorial-monitor-usdt-flows-with-web3py Inspect Plasma block activity and monitor USDT transfer flows using web3.py and a Chainstack endpoint without broadcasting any on-chain transactions. ## Overview This tutorial shows how to read Plasma Mainnet Beta data with Python. You will connect to your Chainstack endpoint, confirm network metadata, scan recent blocks, and decode USDT `Transfer` events—no write operations or private-key handling required. ## Prerequisites * Chainstack account with a Plasma node (chain ID `9745`, currency symbol `XPL`) * Python 3.10 or later * `pip` and `virtualenv` * Familiarity with `.env` files > [!TIP] > > Switch to a Plasma Testnet node (chain ID `9746`) by dropping the testnet endpoint from your Chainstack console into `PLASMA_RPC_URL`. ## 1. Set up the project ```bash theme={"system"} mkdir plasma-usdt-monitor cd plasma-usdt-monitor python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install web3 python-dotenv ``` Create a `.env` file and paste your Chainstack HTTPS URL from **Access and credentials**: ```bash theme={"system"} cat <<'EOF' > .env PLASMA_RPC_URL="_YOUR_CHAINSTACK_ENDPOINT" # Optional overrides BLOCK_WINDOW=40 MIN_USDT=0 EOF ``` > [!NOTE] > > Replace the placeholder endpoint with the URL shown in your Chainstack console. Leave `MIN_USDT` at `0` to list every transfer, or raise it to filter out dust. ## 2. Add a read-only monitor Create `monitor.py`: ```python theme={"system"} import os from dataclasses import dataclass from datetime import datetime, timezone from decimal import Decimal from typing import Iterable from dotenv import load_dotenv from web3 import Web3 from web3.middleware.proof_of_authority import ExtraDataToPOAMiddleware PLASMA_USDT_ADDRESS = Web3.to_checksum_address("0xB8CE59FC3717Ada4C02eadf9682A9e934F625ebb") ERC20_ABI = [ { "constant": True, "inputs": [], "name": "symbol", "outputs": [{"name": "", "type": "string"}], "stateMutability": "view", "type": "function", }, { "constant": True, "inputs": [], "name": "decimals", "outputs": [{"name": "", "type": "uint8"}], "stateMutability": "view", "type": "function", }, { "anonymous": False, "inputs": [ {"indexed": True, "name": "from", "type": "address"}, {"indexed": True, "name": "to", "type": "address"}, {"indexed": False, "name": "value", "type": "uint256"}, ], "name": "Transfer", "type": "event", }, ] @dataclass class TransferRecord: block_number: int tx_hash: str sender: str recipient: str amount: Decimal symbol: str timestamp: datetime def make_web3() -> Web3: load_dotenv() rpc_url = os.environ["PLASMA_RPC_URL"] w3 = Web3(Web3.HTTPProvider(rpc_url, request_kwargs={"timeout": 30})) # Plasma runs a HotStuff-style consensus and returns PoA-style extraData w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) if not w3.is_connected(): raise SystemExit("Unable to connect. Check PLASMA_RPC_URL or network status.") return w3 def describe_network(w3: Web3) -> None: latest = w3.eth.get_block("latest") print(f"Connected: {w3.client_version}") print(f"Chain ID : {w3.eth.chain_id}") print(f"Network ID : {w3.net.version}") print(f"Latest block : {latest.number} ({datetime.fromtimestamp(latest.timestamp, tz=timezone.utc)})") print(f"Gas used : {latest.gasUsed:,} / {latest.gasLimit:,}") def fetch_transfers(w3: Web3) -> Iterable[TransferRecord]: block_window = int(os.environ.get("BLOCK_WINDOW", "40")) min_usdt = Decimal(os.environ.get("MIN_USDT", "0")) latest = w3.eth.block_number start_block = max(latest - block_window, 0) contract = w3.eth.contract(address=PLASMA_USDT_ADDRESS, abi=ERC20_ABI) decimals = contract.functions.decimals().call() symbol = contract.functions.symbol().call() scale = Decimal(10) ** decimals logs = contract.events.Transfer().get_logs(from_block=start_block, to_block=latest) for event in logs: block = w3.eth.get_block(event["blockNumber"]) amount = Decimal(event["args"]["value"]) / scale if amount < min_usdt: continue yield TransferRecord( block_number=event["blockNumber"], tx_hash=event["transactionHash"].hex(), sender=event["args"]["from"], recipient=event["args"]["to"], amount=amount, symbol=symbol, timestamp=datetime.fromtimestamp(block.timestamp, tz=timezone.utc), ) def main() -> None: w3 = make_web3() describe_network(w3) print("\nRecent USDT transfers:") for transfer in fetch_transfers(w3): print( f"- Block {transfer.block_number} | {transfer.amount:,.6f} {transfer.symbol} | " f"{transfer.sender} → {transfer.recipient} | {transfer.timestamp.isoformat()} | " f"{transfer.tx_hash}" ) if __name__ == "__main__": main() ``` > [!NOTE] > > `PLASMA_USDT_ADDRESS` points to the USDT contract announced by HTX for Plasma Mainnet. Always cross-check the address on Plasmascan before running production monitors. ## 3. Run the monitor ```bash theme={"system"} python monitor.py ``` Expected console output: ```text theme={"system"} Connected: Plasma/v1.2.0-stable/linux-x86_64/go1.21.6 Chain ID : 9745 Network ID : 9745 Latest block : 3123456 (2025-10-19 17:24:02+00:00) Gas used : 3,210,000 / 30,000,000 Recent USDT transfers: - Block 3123438 | 4,500.000000 USDT | 0x5a... → 0x91... | 2025-10-19T17:22:11+00:00 | 0x0c5f... - Block 3123431 | 250.000000 USDT | 0x32... → 0x5e... | 2025-10-19T17:21:28+00:00 | 0x1639... ``` If you receive an empty list, increase `BLOCK_WINDOW` or switch to Plasma Testnet where transfers are more frequent during testing. ## 4. Extend the analysis * Pipe output into a CSV or database for dashboards. * Compare mainnet versus testnet activity by swapping the RPC URL. * Add token balance snapshots with `contract.functions.balanceOf(address).call()` to profile top holders. > [!TIP] > > Run the script on a schedule (for example, GitHub Actions or a lightweight cron job) to feed alerting pipelines whenever a large USDT transfer crosses the Plasma network. # Plasma: Network setup and configuration Source: https://docs.chainstack.com/docs/plasma-tutorial-network-setup-and-configuration Configure MetaMask, Hardhat, Foundry, and other tools to connect to Plasma mainnet and testnet networks using your Chainstack RPC node endpoints. **TLDR:** * Plasma is an EVM-compatible blockchain with \~1-second block times and PlasmaBFT consensus. * Mainnet chain ID is `9745`; testnet chain ID is `9746`. Currency symbol is `XPL`. * Configure MetaMask manually or use the Chainstack **Connect wallet** button for automatic injection. * Set up Hardhat or Foundry with proper network configurations to deploy smart contracts. * Get testnet tokens from gas.zip or through community support in Discord. ## Overview This tutorial walks through connecting your development tools to the Plasma network using a Chainstack node. You will configure MetaMask for wallet interactions, set up Hardhat and Foundry for smart contract deployment, and verify your connection is working correctly. By the end, you will have a fully configured development environment ready to deploy and interact with contracts on Plasma mainnet or testnet. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a Plasma node * [MetaMask](https://metamask.io/) browser extension * [Node.js](https://nodejs.org/) 18 or later (for Hardhat) * [Foundry](https://getfoundry.sh/) (optional, for Forge and Cast) ## Network parameters Use these values when configuring your tools: ### Mainnet | Parameter | Value | | - | - | | Network name | Plasma Mainnet | | Chain ID | `9745` | | Currency symbol | `XPL` | | Block explorer | `https://plasmascan.to` | ### Testnet | Parameter | Value | | - | - | | Network name | Plasma Testnet | | Chain ID | `9746` | | Currency symbol | `XPL` | | Block explorer | `https://testnet.plasmascan.to` | > [!NOTE] > > Replace `YOUR_CHAINSTACK_ENDPOINT` in all examples with your actual Chainstack HTTPS endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Configure MetaMask ### Automatic setup On Chainstack, navigate to your Plasma node and click **Connect wallet**. MetaMask will prompt you to add the network with all parameters pre-filled. ### Manual setup If you prefer to add the network manually: 1. Open MetaMask and click the network dropdown. 2. Click **Add network** > **Add a network manually**. 3. Enter the following values for mainnet: * Network name: `Plasma Mainnet` * New RPC URL: `YOUR_CHAINSTACK_ENDPOINT` * Chain ID: `9745` * Currency symbol: `XPL` * Block explorer URL: `https://plasmascan.to` 4. Click **Save**. For testnet, use chain ID `9746` and block explorer `https://testnet.plasmascan.to`. > [!TIP] > > Your Chainstack endpoint includes authentication. Never share it publicly or commit it to version control. ## Configure Hardhat 1. Create a new project or navigate to an existing one: ```shell Shell theme={"system"} mkdir plasma-project && cd plasma-project npm init -y npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox dotenv npx hardhat init ``` 2. Create a `.env` file with your credentials: ```shell .env theme={"system"} CHAINSTACK_PLASMA_MAINNET=YOUR_CHAINSTACK_ENDPOINT CHAINSTACK_PLASMA_TESTNET=YOUR_CHAINSTACK_ENDPOINT PRIVATE_KEY=your_wallet_private_key ``` 3. Update `hardhat.config.js`: ```javascript hardhat.config.js theme={"system"} require("@nomicfoundation/hardhat-toolbox"); require("dotenv").config(); module.exports = { solidity: "0.8.26", networks: { plasmaMainnet: { url: process.env.CHAINSTACK_PLASMA_MAINNET, chainId: 9745, accounts: [process.env.PRIVATE_KEY] }, plasmaTestnet: { url: process.env.CHAINSTACK_PLASMA_TESTNET, chainId: 9746, accounts: [process.env.PRIVATE_KEY] } }, etherscan: { apiKey: { plasmaMainnet: "not-needed", plasmaTestnet: "not-needed" }, customChains: [ { network: "plasmaMainnet", chainId: 9745, urls: { apiURL: "https://plasmascan.to/api", browserURL: "https://plasmascan.to" } }, { network: "plasmaTestnet", chainId: 9746, urls: { apiURL: "https://testnet.plasmascan.to/api", browserURL: "https://testnet.plasmascan.to" } } ] } }; ``` 4. Test the connection: ```shell Shell theme={"system"} npx hardhat console --network plasmaTestnet ``` ```javascript Hardhat console theme={"system"} > await ethers.provider.getBlockNumber() ``` > [!WARNING] > > Never commit your `.env` file. Add it to `.gitignore` to prevent exposing your private key and endpoint credentials. ## Configure Foundry 1. Install Foundry if you haven't already: ```shell Shell theme={"system"} curl -L https://foundry.paradigm.xyz | bash foundryup ``` 2. Create `foundry.toml` in your project root: ```toml foundry.toml theme={"system"} [profile.default] src = "src" out = "out" libs = ["lib"] solc = "0.8.26" [rpc_endpoints] plasma_mainnet = "${CHAINSTACK_PLASMA_MAINNET}" plasma_testnet = "${CHAINSTACK_PLASMA_TESTNET}" [etherscan] plasma_mainnet = { key = "", chain = 9745, url = "https://plasmascan.to/api" } plasma_testnet = { key = "", chain = 9746, url = "https://testnet.plasmascan.to/api" } ``` 3. Test the connection using Cast: ```shell Shell theme={"system"} cast chain-id --rpc-url $CHAINSTACK_PLASMA_TESTNET ``` Expected output: `9746` 4. Get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url $CHAINSTACK_PLASMA_TESTNET ``` ## Add network programmatically For dApps, you can prompt users to add the Plasma network automatically: ```javascript JavaScript theme={"system"} async function addPlasmaNetwork(isTestnet = false) { const chainId = isTestnet ? '0x2612' : '0x2611'; // 9746 or 9745 in hex const networkName = isTestnet ? 'Plasma Testnet' : 'Plasma Mainnet'; const explorerUrl = isTestnet ? 'https://testnet.plasmascan.to' : 'https://plasmascan.to'; const rpcUrl = 'YOUR_PUBLIC_RPC_OR_CHAINSTACK_ENDPOINT'; try { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId }], }); } catch (switchError) { if (switchError.code === 4902) { await window.ethereum.request({ method: 'wallet_addEthereumChain', params: [{ chainId, chainName: networkName, nativeCurrency: { name: 'XPL', symbol: 'XPL', decimals: 18 }, rpcUrls: [rpcUrl], blockExplorerUrls: [explorerUrl] }], }); } } } ``` ## Get testnet tokens Before deploying contracts on testnet, you need XPL for gas fees. ### Option 1: gas.zip faucet Visit [gas.zip/faucet/plasma](https://www.gas.zip/faucet/plasma) and connect your wallet. This faucet requires some mainnet wallet activity for eligibility. ### Option 2: Community support If the faucet doesn't work for your wallet, ask in the Plasma Discord `#developers` channel or DM the team members listed there. > [!TIP] > > For most testing, 0.5 XPL is sufficient to deploy multiple contracts and run hundreds of transactions. ## Verify your setup Run these checks to confirm everything is configured correctly. ### Check connection with curl ```shell Shell theme={"system"} curl -X POST YOUR_CHAINSTACK_ENDPOINT \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' ``` Expected response for testnet: ```json JSON theme={"system"} {"jsonrpc":"2.0","id":1,"result":"0x2612"} ``` The result `0x2612` is the hexadecimal representation of chain ID `9746`. ### Check connection with web3.py ```python Python theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(f"Connected: {w3.is_connected()}") print(f"Chain ID: {w3.eth.chain_id}") print(f"Latest block: {w3.eth.block_number}") ``` ### Check connection with ethers.js ```javascript JavaScript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT", { chainId: 9746, name: "plasma-testnet" }); async function checkConnection() { const blockNumber = await provider.getBlockNumber(); const network = await provider.getNetwork(); console.log(`Chain ID: ${network.chainId}`); console.log(`Latest block: ${blockNumber}`); } checkConnection(); ``` ## Troubleshooting ### Cannot connect to RPC * Verify the endpoint URL has no trailing slash. * Check that your Chainstack node is running and synced. * Confirm the URL includes your authentication credentials. ### Chain ID mismatch error * Mainnet chain ID is `9745`, testnet is `9746`. * Remove the network from MetaMask and re-add it with correct values. * Clear MetaMask activity data: **Settings** > **Advanced** > **Clear activity tab data**. ### Nonce errors after switching networks MetaMask can cache nonce data from other networks. To fix: 1. Open MetaMask and go to **Settings** > **Advanced**. 2. Click **Clear activity tab data**. 3. Check **Also reset my local nonce data**. 4. Confirm and retry your transaction. ### Transaction stuck as pending * Click the pending transaction in MetaMask and select **Speed up** to increase gas. * Alternatively, select **Cancel** to drop the transaction. * If multiple transactions are stuck, clear activity data to reset. ### Hardhat deployment fails with verification error If your deployment script includes automatic verification and fails: 1. Separate deployment from verification—deploy first, verify later. 2. Remove or comment out the verification section in your deploy script. 3. See [Plasma tooling](/docs/plasma-tooling) for verification commands. ## Next steps Now that your environment is configured: * [Deploy a contract](/docs/plasma-tooling#hardhat) using Hardhat or Foundry * [Monitor USDT flows](/docs/plasma-tutorial-monitor-usdt-flows-with-web3py) with web3.py * Explore the [Plasma RPC methods](/docs/plasma-methods) reference # Plasma: RPC failover with eRPC Source: https://docs.chainstack.com/docs/plasma-tutorial-rpc-failover-with-erpc Set up fault-tolerant RPC access for Plasma using eRPC proxy with Chainstack as primary and public fallback endpoints for production reliability. ## Overview This tutorial shows how to configure eRPC as a fault-tolerant proxy for Plasma, combining your Chainstack endpoint with public RPCs for automatic failover. When your primary endpoint experiences issues, eRPC seamlessly routes requests to backup providers. By the end, you will have a local proxy that: * Routes Plasma RPC calls through Chainstack as the primary provider * Falls back to public endpoints when the primary is unavailable * Caches responses to reduce latency and RPC calls * Retries failed requests with exponential backoff ## Prerequisites * [Chainstack account](https://console.chainstack.com/) with a Plasma node deployed * Docker installed * Basic familiarity with JSON-RPC > [!TIP] > > For general eRPC setup and Chainstack Platform API auto-discovery, see [Using eRPC with Chainstack: Quickstart](/docs/using-erpc-with-chainstack-quickstart). ## Network parameters | Network | Chain ID | Currency | | - | - | - | | Plasma Mainnet | `9745` | XPL | | Plasma Testnet | `9746` | XPL | ## 1. Get your endpoints ### Chainstack endpoint 1. Navigate to your Plasma node in the [Chainstack console](https://console.chainstack.com/). 2. Copy your HTTPS endpoint from **Access and credentials**. ### Public endpoints Plasma provides public RPC endpoints as backup: * `https://rpc.plasma.to` — public mainnet * `https://rpc-testnet.plasma.to` — public testnet > [!NOTE] > > Public endpoints have rate limits and no SLA. Use them as fallback only, not as primary. ## 2. Create the configuration Create a file named `erpc.yaml`: ```yaml theme={"system"} logLevel: info server: httpHostV4: 0.0.0.0 httpPortV4: 4000 maxTimeout: 30s projects: - id: plasma networks: - architecture: evm evm: chainId: 9745 failsafe: timeout: duration: 30s retry: maxAttempts: 3 delay: 500ms backoffMaxDelay: 5s backoffFactor: 2 jitter: 200ms hedge: delay: 2s maxCount: 2 upstreams: # Primary: Chainstack (higher priority) - id: chainstack-plasma type: evm endpoint: YOUR_CHAINSTACK_PLASMA_ENDPOINT rateLimitBudget: chainstack-budget evm: chainId: 9745 failsafe: timeout: duration: 15s retry: maxAttempts: 2 delay: 300ms backoffMaxDelay: 3s backoffFactor: 1.5 jitter: 100ms # Fallback: Public RPC (lower priority) - id: plasma-public type: evm endpoint: https://rpc.plasma.to rateLimitBudget: public-budget evm: chainId: 9745 failsafe: timeout: duration: 20s retry: maxAttempts: 2 delay: 500ms backoffMaxDelay: 5s backoffFactor: 2 jitter: 200ms rateLimiters: budgets: - id: chainstack-budget rules: - method: '*' maxCount: 25 period: 1s - method: 'eth_getLogs' maxCount: 10 period: 1s - id: public-budget rules: - method: '*' maxCount: 5 period: 1s ``` Replace `YOUR_CHAINSTACK_PLASMA_ENDPOINT` with your actual Chainstack endpoint URL. > [!WARNING] > > Never commit configuration files with real endpoint URLs to version control. Use environment variables in production. ## 3. Run eRPC Start the proxy with Docker: ```bash theme={"system"} docker run --rm \ -v $(pwd)/erpc.yaml:/erpc.yaml \ -p 4000:4000 \ -p 4001:4001 \ ghcr.io/erpc/erpc:latest ``` You should see output indicating both upstreams are configured: ```text theme={"system"} INF starting eRPC server... INF registered upstream id=chainstack-plasma network=evm:9745 INF registered upstream id=plasma-public network=evm:9745 INF http server started addr=0.0.0.0:4000 ``` ## 4. Test the setup ### Basic connectivity ```bash theme={"system"} curl -X POST http://localhost:4000/plasma/evm/9745 \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' ``` Expected response: ```json theme={"system"} {"jsonrpc":"2.0","id":1,"result":"0x2611"} ``` The hex value `0x2611` equals `9745` (Plasma Mainnet chain ID). ### Get latest block ```bash theme={"system"} curl -X POST http://localhost:4000/plasma/evm/9745 \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' ``` ### Check USDT balance ```bash theme={"system"} curl -X POST http://localhost:4000/plasma/evm/9745 \ -H "Content-Type: application/json" \ -d '{ "jsonrpc":"2.0", "method":"eth_call", "params":[{ "to":"0xB8CE59FC3717Ada4C02eadf9682A9e934F625ebb", "data":"0x70a08231000000000000000000000000YOUR_ADDRESS_HERE" },"latest"], "id":1 }' ``` ## 5. Use with your application Point your application to the eRPC proxy instead of directly to an RPC endpoint. ### ethers.js ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("http://localhost:4000/plasma/evm/9745", { chainId: 9745, name: "plasma" }); const blockNumber = await provider.getBlockNumber(); console.log("Current block:", blockNumber); ``` ### web3.py ```python theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("http://localhost:4000/plasma/evm/9745")) print("Connected:", w3.is_connected()) print("Block:", w3.eth.block_number) ``` ### Hardhat In `hardhat.config.js`: ```javascript theme={"system"} module.exports = { networks: { plasma: { url: "http://localhost:4000/plasma/evm/9745", chainId: 9745, accounts: [process.env.PRIVATE_KEY] } } }; ``` ## 6. Add testnet support To also proxy Plasma Testnet, add another network to the configuration: ```yaml theme={"system"} projects: - id: plasma networks: - architecture: evm evm: chainId: 9745 # ... mainnet failsafe config - architecture: evm evm: chainId: 9746 failsafe: timeout: duration: 30s retry: maxAttempts: 3 delay: 500ms backoffMaxDelay: 5s backoffFactor: 2 jitter: 200ms upstreams: # Mainnet upstreams - id: chainstack-plasma-mainnet type: evm endpoint: YOUR_CHAINSTACK_PLASMA_MAINNET_ENDPOINT evm: chainId: 9745 rateLimitBudget: chainstack-budget # ... failsafe config - id: plasma-public-mainnet type: evm endpoint: https://rpc.plasma.to evm: chainId: 9745 rateLimitBudget: public-budget # ... failsafe config # Testnet upstreams - id: chainstack-plasma-testnet type: evm endpoint: YOUR_CHAINSTACK_PLASMA_TESTNET_ENDPOINT evm: chainId: 9746 rateLimitBudget: chainstack-budget # ... failsafe config - id: plasma-public-testnet type: evm endpoint: https://rpc-testnet.plasma.to evm: chainId: 9746 rateLimitBudget: public-budget # ... failsafe config ``` Access testnet at `http://localhost:4000/plasma/evm/9746`. ## How failover works eRPC handles failures automatically: 1. **Primary attempt** — Request goes to Chainstack first 2. **Timeout/error** — If Chainstack fails or times out, eRPC retries based on `failsafe` config 3. **Fallback** — After exhausting retries, request routes to the public endpoint 4. **Hedging** — For latency-sensitive calls, eRPC can send parallel requests to multiple upstreams The `hedge` configuration sends a backup request after 2 seconds if the primary hasn't responded, using whichever completes first. ## Production deployment For production, consider: * **Environment variables** — Use `${ENV_VAR}` syntax in eRPC config * **Docker Compose** — Run eRPC alongside your application * **Health checks** — Monitor eRPC metrics on port 4001 * **Multiple Chainstack nodes** — Add nodes in different regions for geographic redundancy Example with environment variables: ```yaml theme={"system"} upstreams: - id: chainstack-plasma endpoint: ${CHAINSTACK_PLASMA_URL} ``` Run with: ```bash theme={"system"} docker run --rm \ -e CHAINSTACK_PLASMA_URL="https://your-endpoint..." \ -v $(pwd)/erpc.yaml:/erpc.yaml \ -p 4000:4000 \ ghcr.io/erpc/erpc:latest ``` ## Conclusion You now have fault-tolerant RPC access to Plasma. When your Chainstack endpoint experiences issues, eRPC automatically fails over to public endpoints, ensuring your application stays connected. For advanced configuration options, see the [eRPC documentation](https://docs.erpc.cloud/). # Chainstack platform overview: nodes, APIs, and billing Source: https://docs.chainstack.com/docs/platform-introduction Get started with Chainstack, the blockchain infrastructure platform supporting 70+ networks with managed RPC nodes, APIs, and self-hosted deployment. Chainstack gives you RPC access to 70+ blockchain networks. Deploy a node, get an endpoint, and call it — or run the same infrastructure on hardware you control. There are two products, and they have separate documentation: * Cloud — managed nodes that Chainstack runs for you. Deploy one from the [Chainstack console](https://console.chainstack.com/) and get an endpoint in seconds. * Chainstack Self-Hosted — the same node stack deployed on your own Kubernetes cluster, managed through the Control Panel. ## Start here > [card] [Node types](/docs/global-elastic-node) > [card] [Pricing and request units](/docs/pricing-introduction) > [card] [Supported protocols and networks](/docs/protocols-networks) > [card] [Clouds, regions, and locations](/docs/nodes-clouds-regions-and-locations) ## Build * [Blockchain APIs](/reference/blockchain-apis) — the RPC method reference for every supported protocol * [Web3 libraries](/reference/web3-libraries) — connecting web3.js, ethers.js, web3.py, and others to your endpoint * [Authentication methods](/docs/authentication-methods-for-different-scenarios) — auth tokens, basic auth, and when to use each * [Multichain API](/docs/multichain-api) — one auth token across the Global Nodes and Dedicated Nodes you attach to a multichain endpoint * [Chainstack MCP server](/docs/chainstack-mcp-server) — deploy and manage nodes from an AI agent ## Run it yourself * [Chainstack Self-Hosted](/docs/self-hosted/introduction) — run the node stack on your own Kubernetes cluster * [Quick start](/docs/self-hosted/quick-start) — a first Self-Hosted deployment end to end ## Move an existing workload Coming from another provider? See [Migrate to Chainstack with AI](/docs/migrate-to-chainstack-with-ai), which walks an agent through swapping your endpoints, along with the provider-specific guides alongside it. ## Testnet tokens Request testnet funds from the [Chainstack faucet](https://faucet.chainstack.com/), or through the [Chainstack MCP server](/docs/chainstack-mcp-server) if you would rather stay in your editor. See [Faucet](/docs/faucets) for the supported networks. ## Use these docs from an agent Every page is available as plain text: append `.md` to any documentation URL to get its markdown source, or use one of the whole-portal endpoints. * [llms.txt](https://docs.chainstack.com/llms.txt) — a compact index of every section, to discover what exists before fetching pages * [llms-full.txt](https://docs.chainstack.com/llms-full.txt) — the entire portal in a single file * [skill.md](https://docs.chainstack.com/skill.md) — the portal packaged as an agent skill The [Chainstack MCP server](/docs/chainstack-mcp-server) at `https://mcp.chainstack.com/mcp` searches these docs as well, alongside deploying and managing nodes. ## Billing and support Pay by card, or top up with crypto at [Billing](https://console.chainstack.com/user/settings/billing) — 150+ cryptocurrencies are supported. For plan comparisons and quotas, see [Pricing](/docs/pricing-introduction). To request a protocol, a capability, or any other improvement, use the [ideas portal](https://ideas.chainstack.com/). For anything else, [Chainstack support](https://support.chainstack.com) can help. # Polkadot: Real-time network health monitoring dashboard Source: https://docs.chainstack.com/docs/polkadot-network-health-monitoring Learn how to build a real-time network health monitoring dashboard for Polkadot using Python and the Substrate interface through Chainstack RPC endpoints. **TLDR:** * Demonstrates building a real-time network health monitoring dashboard for Polkadot mainnet. * Shows how to work with Substrate interface to query network statistics, validators, staking, and governance data. * Includes practical examples of data visualization and continuous monitoring with graceful error handling. ## Overview Polkadot is a multi-chain blockchain protocol that enables different blockchains to transfer messages and value in a trust-free fashion. Polkadot relies on validators to secure the network and produce blocks. This tutorial guides you through creating a sample real-time network health monitoring dashboard for Polkadot. You'll learn how to connect to the Polkadot network using the Substrate interface, fetch critical network metrics including block production, validator statistics, staking information, and governance data, and create an interactive dashboard with live updates. ## Prerequisites * [substrate-interface](https://github.com/polkascan/py-substrate-interface) * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Polkadot RPC endpoint](https://chainstack.com/build-better-with-polkadot/) ## Network health monitoring dashboard Now let's create the dashboard. The script connects to the Polkadot network over a Chainstack node WebSocket endpoint, then continuously queries various pallets to gather network statistics, validator information, staking metrics, and governance data. The dashboard displays live information about block production, finalization lag, validator counts, staking eras, and governance activity in an organized terminal interface. Here's the full script and the comments: ```python theme={"system"} import time from datetime import datetime from typing import Dict, Optional import signal import sys from substrateinterface import SubstrateInterface from rich.console import Console from rich.panel import Panel from rich.layout import Layout from rich.live import Live from rich.align import Align import click console = Console() class PolkadotHealthMonitor: def __init__(self, endpoint: str): self.endpoint = endpoint self.substrate = None self.running = False self.start_time = datetime.now() self.blocks_processed = 0 def connect(self) -> bool: """Connect to Polkadot network""" try: with console.status("[bold green]Connecting..."): self.substrate = SubstrateInterface( url=self.endpoint, ss58_format=0, type_registry_preset='polkadot' ) chain_info = self.substrate.get_chain_finalised_head() console.print(f"✅ Connected to {self.substrate.chain}") console.print(f"🔗 Latest finalized: {chain_info}") return True except Exception as e: console.print(f"❌ Connection failed: {e}", style="bold red") return False def safe_query(self, pallet: str, method: str, params: list = None) -> Optional[any]: """Safely execute substrate queries with error handling""" try: result = self.substrate.query(pallet, method, params or []) return result.value if result else None except: return None def get_all_data(self) -> Dict: """Fetch all network data in one consolidated method""" data = {'timestamp': datetime.now()} try: # Network info latest_hash = self.substrate.get_chain_head() finalized_hash = self.substrate.get_chain_finalised_head() latest_block = self.substrate.get_block(latest_hash) finalized_block = self.substrate.get_block(finalized_hash) latest_num = latest_block['header']['number'] if latest_block and 'header' in latest_block else 0 finalized_num = finalized_block['header']['number'] if finalized_block and 'header' in finalized_block else 0 data.update({ 'chain': str(self.substrate.chain), 'latest_block': latest_num, 'finalized_block': finalized_num, 'finalization_lag': latest_num - finalized_num, 'block_hash': str(latest_hash)[:12] + '...' if latest_hash else 'N/A' }) # Runtime version try: runtime = self.substrate.get_runtime_version() data.update({ 'runtime_version': runtime.get('specVersion', 'N/A') if runtime else 'N/A', 'runtime_name': runtime.get('specName', 'N/A') if runtime else 'N/A' }) except Exception as e: # Fallback: try to get from metadata try: metadata = self.substrate.get_metadata() runtime_version = getattr(metadata, 'runtime_version', None) if runtime_version: data.update({ 'runtime_version': str(runtime_version.get('spec_version', 'N/A')), 'runtime_name': str(runtime_version.get('spec_name', 'Polkadot')) }) else: # Fallback: show we have connection data.update({'runtime_version': 'Connected', 'runtime_name': 'Polkadot'}) except: data.update({'runtime_version': 'Connected', 'runtime_name': 'Polkadot'}) # Validators and staking validators = self.safe_query('Session', 'Validators') or [] validator_count = len(validators) current_era = self.safe_query('Staking', 'CurrentEra') or 0 total_issuance = self.safe_query('Balances', 'TotalIssuance') or 0 active_validators = self.safe_query('Staking', 'CounterForValidators') or validator_count data.update({ 'validators': validator_count, 'active_validators': active_validators, 'current_era': current_era, 'total_supply': f"{total_issuance / 10**10:,.0f} DOT" if total_issuance else "N/A" }) # Governance referendum_count = (self.safe_query('Referenda', 'ReferendumCount') or self.safe_query('Democracy', 'ReferendumCount') or 0) council_members = self.safe_query('Council', 'Members') or [] fellowship = self.safe_query('FellowshipCollective', 'Members') or [] proposals = self.safe_query('Democracy', 'PublicProps') or [] data.update({ 'referendums': referendum_count, 'council_size': len(council_members) or len(fellowship), 'proposals': len(proposals), 'governance': 'OpenGov v2' if referendum_count > 0 else 'Legacy' }) self.blocks_processed += 1 except Exception as e: console.print(f"❌ Data fetch error: {e}") return data def create_panel(self, title: str, content: str, style: str = "green") -> Panel: """Create a formatted panel""" return Panel(content.strip(), title=title, border_style=style) def create_dashboard(self) -> Layout: """Create compact dashboard layout""" data = self.get_all_data() # Network panel network_content = f"""[bold green]Chain:[/bold green] {data.get('chain', 'N/A')} [bold blue]Latest:[/bold blue] #{data.get('latest_block', 'N/A')} [bold yellow]Finalized:[/bold yellow] #{data.get('finalized_block', 'N/A')} [bold red]Lag:[/bold red] {data.get('finalization_lag', 'N/A')} blocks [bold magenta]Runtime:[/bold magenta] {data.get('runtime_version', 'N/A')}""" # Validators panel validator_content = f"""[bold green]Total:[/bold green] {data.get('validators', 'N/A')} [bold blue]Active:[/bold blue] {data.get('active_validators', 'N/A')} [bold yellow]Supply:[/bold yellow] {data.get('total_supply', 'N/A')}""" # Staking panel staking_content = f"""[bold green]Era:[/bold green] {data.get('current_era', 'N/A')} [bold blue]Duration:[/bold blue] 24 hours [bold yellow]Validators:[/bold yellow] {data.get('validators', 'N/A')}""" # Governance panel gov_content = f"""[bold green]Referendums:[/bold green] {data.get('referendums', 'N/A')} [bold blue]Council:[/bold blue] {data.get('council_size', 'N/A')} [bold yellow]Proposals:[/bold yellow] {data.get('proposals', 'N/A')} [bold magenta]System:[/bold magenta] {data.get('governance', 'Unknown')}""" # Status panel uptime = datetime.now() - self.start_time status_content = f"""[bold green]Status:[/bold green] {'🟢 Running' if self.running else '🔴 Stopped'} [bold blue]Uptime:[/bold blue] {str(uptime).split('.')[0]} [bold yellow]Blocks:[/bold yellow] {self.blocks_processed} [bold magenta]Updated:[/bold magenta] {datetime.now().strftime('%H:%M:%S')}""" # Create layout layout = Layout() layout.split_column( Layout(Panel(Align.center("🟡 Polkadot Network Health Monitor 📊", style="bold yellow"), style="bold blue"), size=3), Layout(name="body"), Layout(self.create_panel("📊 Status", status_content, "cyan"), size=7) ) layout["body"].split_row( Layout(name="left"), Layout(name="right") ) layout["left"].split_column( self.create_panel("🌐 Network", network_content), self.create_panel("👥 Validators", validator_content, "blue") ) layout["right"].split_column( self.create_panel("🏦 Staking", staking_content, "yellow"), self.create_panel("🗳️ Governance", gov_content, "magenta") ) return layout def start_monitoring(self, refresh_interval: int = 10): """Start monitoring with graceful shutdown""" if not self.connect(): return self.running = True signal.signal(signal.SIGINT, lambda s, f: self._shutdown()) console.print(f"🚀 Starting monitor (refresh: {refresh_interval}s)") console.print("💡 Press Ctrl+C to stop\n") try: with Live(self.create_dashboard(), refresh_per_second=1/refresh_interval, screen=True) as live: while self.running: live.update(self.create_dashboard()) time.sleep(refresh_interval) except KeyboardInterrupt: self._shutdown() except Exception as e: console.print(f"\n❌ Error: {e}") finally: self.running = False def _shutdown(self): """Graceful shutdown""" self.running = False console.print("\n👋 Monitor stopped") sys.exit(0) @click.command() @click.option('--endpoint', '-e', default='YOUR_CHAINSTACK_WSS_ENDPOINT', help='Chainstack endpoint') @click.option('--refresh', '-r', default=10, help='Refresh interval (seconds)') def main(endpoint: str, refresh: int): """🟡 Polkadot Network Health Monitor 📊""" console.print(Panel.fit( """🟡 [bold yellow]Polkadot Network Health Monitor[/bold yellow] 📊 Real-time monitoring of: • Network statistics & block production • Validator performance metrics • Staking analysis & governance activity [italic]Powered by Chainstack infrastructure[/italic]""", border_style="bold blue" )) monitor = PolkadotHealthMonitor(endpoint) monitor.start_monitoring(refresh_interval=refresh) if __name__ == "__main__": main() ``` where `YOUR_CHAINSTACK_WSS_ENDPOINT` is your Chainstack WebSocket endpoint. ## Conclusion The monitoring approach demonstrated here can be easily extended to track additional metrics, implement alerting systems, or integrate into larger network monitoring solutions requiring detailed Polkadot network insights. > [!NOTE] > > ### See also > > > [card] [Polkadot documentation](https://docs.polkadot.com/) > > > [card] [Substrate Interface documentation](https://github.com/polkascan/py-substrate-interface) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Polkadot tooling Source: https://docs.chainstack.com/docs/polkadot-tooling Complete Polkadot tooling guide for Chainstack nodes. Use @polkadot/api, py-substrate-interface, subxt, GSRPC, and Substrate sidecar for Polkadot development. Get started with a [reliable Polkadot RPC endpoint](https://chainstack.com/build-better-with-polkadot/) to use the tools below. ## @polkadot/api (JavaScript/TypeScript) Build dApps using the canonical JavaScript/TypeScript client maintained by Parity. 1. Install the Polkadot API package: ```bash theme={"system"} npm install @polkadot/api ``` 2. Connect over WebSocket: Use the `ApiPromise` to connect to your Chainstack node endpoint: ```javascript theme={"system"} import { ApiPromise, WsProvider } from '@polkadot/api'; async function main() { // Create a new instance of the API const wsProvider = new WsProvider('YOUR_CHAINSTACK_WSS_ENDPOINT'); const api = await ApiPromise.create({ provider: wsProvider }); // Get chain info console.log('Connected to chain:', await api.rpc.system.chain()); console.log('Current block number:', await api.query.system.number()); // Subscribe to new blocks const unsubscribe = await api.rpc.chain.subscribeNewHeads((header) => { console.log(`New block #${header.number} has hash ${header.hash}`); }); } main().catch(console.error); ``` where `YOUR_CHAINSTACK_WSS_ENDPOINT` is your Chainstack WebSocket endpoint. ## py-substrate-interface (Python) Pythonic wrapper for Substrate JSON-RPC with SCALE codec helpers. 1. Install py-substrate-interface: ```bash theme={"system"} pip install substrate-interface ``` 2. Connect and query: ```python theme={"system"} from substrateinterface import SubstrateInterface # Create connection substrate = SubstrateInterface( url="YOUR_CHAINSTACK_WSS_ENDPOINT", ss58_format=0, # Polkadot format type_registry_preset='polkadot' ) # Get chain info print(f"Chain: {substrate.chain}") # Get current block number latest_block = substrate.get_block() print(f"Current block: {latest_block['header']['number']}") # Query account balance account_info = substrate.query( module='System', storage_function='Account', params=['YOUR_POLKADOT_ADDRESS'] # Example with balance https://polkadot.statescan.io/#/accounts/15rqj6yN29bxzbjFWxYGNB1TaRdqrESkg7SZNDDHTcSsjNXq ) balance = account_info.value['data']['free'] print(f"Account balance: {balance / 10**10} DOT") # Convert from planck ``` where `YOUR_CHAINSTACK_WSS_ENDPOINT` is your Chainstack WebSocket endpoint. ## subxt (Rust) Generate type-safe Rust bindings from chain metadata for robust substrate interactions. 1. Add subxt to your `Cargo.toml`: ```toml theme={"system"} [package] name = "polkadot-connection" version = "0.1.0" edition = "2024" [dependencies] subxt = "0.42.1" tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } ``` 2. Generate metadata and create client: ```rust theme={"system"} use subxt::{OnlineClient, PolkadotConfig}; #[tokio::main] async fn main() -> Result<(), Box> { // Create client - using WebSocket endpoint let api = OnlineClient::::from_url("YOUR_CHAINSTACK_WSS_ENDPOINT").await?; // Get chain info let genesis_hash = api.genesis_hash(); let runtime_version = api.runtime_version(); println!("Genesis hash: {:?}", genesis_hash); println!("Runtime version: {:?}", runtime_version); // Subscribe to blocks let mut blocks_sub = api.blocks().subscribe_finalized().await?; println!("Subscribing to finalized blocks..."); let mut count = 0; while let Some(block) = blocks_sub.next().await { let block = block?; println!("Block #{}", block.header().number); // Stop after 5 blocks to avoid infinite loop count += 1; if count >= 5 { break; } } println!("Subscription complete!"); Ok(()) } ``` where `YOUR_CHAINSTACK_WSS_ENDPOINT` is your Chainstack WebSocket endpoint. ## GSRPC (Go) Go Substrate RPC client that mirrors @polkadot/api features. 1. Install GSRPC: ```bash theme={"system"} go mod init your-project go get github.com/centrifuge/go-substrate-rpc-client/v4 ``` 2. Connect and query: ```go theme={"system"} package main import ( "fmt" "log" gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" ) func main() { // Create API instance api, err := gsrpc.NewSubstrateAPI("YOUR_CHAINSTACK_HTTPS_ENDPOINT") if err != nil { log.Fatal(err) } // Get chain info chain, err := api.RPC.System.Chain() if err != nil { log.Fatal(err) } fmt.Printf("Connected to chain: %s\n", chain) // Get latest block hash, err := api.RPC.Chain.GetFinalizedHead() if err != nil { log.Fatal(err) } fmt.Printf("Latest finalized block: %s\n", hash.Hex()) // Get block details block, err := api.RPC.Chain.GetBlock(hash) if err != nil { log.Fatal(err) } fmt.Printf("Block number: %d\n", block.Block.Header.Number) fmt.Printf("Number of extrinsics: %d\n", len(block.Block.Extrinsics)) } ``` where `YOUR_CHAINSTACK_HTTPS_ENDPOINT` is your Chainstack HTTP endpoint. ## Substrate sidecar REST gateway that exposes common chain data without running a full indexer. Chainstack runs the sidecar for you, so all you need is to get the endpoint on your [node details](/docs/manage-your-node#view-node-access-and-credentials) and query it. Example: ```shell theme={"system"} curl YOUR_CHAINSTACK_SIDECAR_ENDPOINT/blocks/head ``` where `YOUR_CHAINSTACK_SIDECAR_ENDPOINT` is your Chainstack sidecar endpoint. # Polygon: Creating a Polymarket trading OpenClaw skill Source: https://docs.chainstack.com/docs/polygon-creating-a-polymarket-trading-openclaw-skill A Python-based OpenClaw skill that integrates Polymarket prediction markets with Claude Code via split + CLOB execution on Polygon > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). [PolyClaw - Polymarket Trading Skill for OpenClaw](https://www.youtube.com/embed/s_uP802NVTE) **TLDR** * This Python-based OpenClaw skill enables Claude Code to browse, trade, and manage Polymarket prediction market positions on Polygon. * Trading uses a split + CLOB execution strategy: split USDC.e into YES/NO tokens via the CTF contract, then sell the unwanted side on the CLOB order book. * LLM-powered hedge discovery finds covering portfolios via contrapositive logic—pairs of market positions that hedge each other. * The skill tracks positions with entry prices, current prices, and profit/loss calculations. [Polymarket](https://polymarket.com/) is a decentralized prediction market platform built on Polygon that enables trading on the outcomes of real-world events. It has generated significant volume and fees as evidenced by the [Polymarket DefiLlama page](https://defillama.com/protocol/polymarket). This project creates a trading skill for [OpenClaw](https://openclaw.ai/) (formerly known as Clawdbot and Moltbot)—the skill system for [Claude Code](https://claude.ai/code). Skills extend Claude Code's capabilities with specialized tools and workflows. With this skill installed, Claude Code can browse markets, execute trades, track positions, and discover hedging opportunities through natural language commands. See the full project code in the [GitHub repository](https://github.com/chainstacklabs/polyclaw). ## Overview The skill interacts with Polymarket's contracts on Polygon mainnet and the CLOB (Central Limit Order Book) API. The core Polymarket contracts are: * **USDC.e** — `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174` — the bridged USDC used for trading * **CTF (Conditional Tokens)** — `0x4D97DCd97eC945f40cF65F87097ACe5EA0476045` — the conditional token framework that creates outcome tokens * **CTF Exchange** — `0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` — the exchange contract for splitting and merging positions The trading flow uses a split + sell strategy: 1. **Split** — USDC.e is split into YES + NO tokens via the CTF contract 2. **Sell unwanted** — the unwanted side is sold on the CLOB order book 3. **Result** — you hold the wanted position, having recovered partial cost from selling the unwanted side Example: buying YES at \$0.70: * Split \$100 USDC.e → 100 YES + 100 NO tokens * Sell 100 NO tokens at \~\$0.30 → recover \~\$27 USDC.e * Net cost: \~\$73 for 100 YES tokens (entry price: \$0.73) ## Prerequisites * [Chainstack account](https://console.chainstack.com/) with a Polygon node deployed * Python 3.11+ with [uv](https://github.com/astral-sh/uv) package manager * A wallet with USDC.e on Polygon (for trading) * OpenRouter API key (for hedge discovery) ## Dependencies Install dependencies using uv: ```bash theme={"system"} git clone https://github.com/chainstacklabs/polyclaw cd polyclaw uv sync ``` ## Environment setup Set the required environment variables: ```bash theme={"system"} # Required for trading export CHAINSTACK_NODE="https://polygon-mainnet.core.chainstack.com/YOUR_API_KEY" export POLYCLAW_PRIVATE_KEY="0x..." # Your wallet private key # Required for hedge discovery export OPENROUTER_API_KEY="sk-or-..." # Optional: residential proxy for CLOB (recommended) export HTTPS_PROXY="http://user:pass@geo.iproyal.com:12321" ``` > [!WARNING] > > **Security:** Keep only small amounts in the trading wallet. The private key in an environment variable is convenient for automation but less secure than encrypted storage. Withdraw profits regularly to a secure wallet. ## Implementation The skill consists of five core modules: * **Markets module** — browse and search Polymarket markets * **Wallet module** — manage wallet status and contract approvals * **Trading module** — execute buy orders via split + CLOB * **Positions module** — track positions with P\&L calculations * **Hedge module** — discover hedging opportunities via LLM analysis ### First-time setup Before trading, set Polymarket contract approvals (one-time, costs \~0.01 POL in gas): ```bash theme={"system"} uv run python scripts/polyclaw.py wallet approve ``` This submits 6 approval transactions to Polygon. You only need to do this once per wallet. ### Markets module The markets module fetches data from the Polymarket API: ```bash theme={"system"} # Trending markets by volume uv run python scripts/polyclaw.py markets trending # Search markets uv run python scripts/polyclaw.py markets search "election" # Market details uv run python scripts/polyclaw.py market ``` Output options: * Default output is a formatted table * Use `--full` for full question text without truncation * Use `--json` for structured JSON output (useful for agent parsing) ### Wallet module The wallet module manages wallet status and approvals: ```bash theme={"system"} # Check wallet status (address, balances) uv run python scripts/polyclaw.py wallet status # Set contract approvals (one-time) uv run python scripts/polyclaw.py wallet approve ``` The wallet is configured via the `POLYCLAW_PRIVATE_KEY` environment variable, supporting hex format with or without the `0x` prefix. ### Trading module The trading module executes buy orders using the split + CLOB strategy: ```bash theme={"system"} # Buy YES position for $50 uv run python scripts/polyclaw.py buy YES 50 # Buy NO position for $25 uv run python scripts/polyclaw.py buy NO 25 ``` The trading flow: 1. Fetch market data and validate the position side 2. Split USDC.e into YES + NO tokens via CTF contract 3. Sell the unwanted tokens on the CLOB order book 4. Record the position with entry price > [!NOTE] > > If the CLOB sell fails (due to liquidity or IP blocking), your split still succeeded. You can sell the unwanted tokens manually on polymarket.com or use the `--skip-sell` flag to keep both sides. ### Positions module The positions module tracks all positions with profit/loss calculations: ```bash theme={"system"} # List all positions with P&L uv run python scripts/polyclaw.py positions ``` This displays: * Market question and position side (YES/NO) * Entry price and current price * Position size and current value * Profit/loss in USD and percentage ### Hedge module The hedge module uses LLM analysis to find covering portfolios—pairs of market positions that hedge each other via contrapositive logic. ```bash theme={"system"} # Scan trending markets for hedges uv run python scripts/polyclaw.py hedge scan # Scan markets matching a query uv run python scripts/polyclaw.py hedge scan --query "election" # Analyze specific markets for hedging relationship uv run python scripts/polyclaw.py hedge analyze ``` Coverage tiers: * **Tier 1 (HIGH):** 95%+ coverage — near-arbitrage opportunities * **Tier 2 (GOOD):** 90-95% — strong hedges * **Tier 3 (MODERATE):** 85-90% — decent but noticeable risk * **Tier 4 (LOW):** less than 85% — speculative (filtered by default) The LLM analyzes market questions for logical implications. For example: * If "X wins election" implies "Y loses election", then YES on X + NO on Y forms a covering portfolio * The contrapositive also holds: NO on X + YES on Y forms another hedge > [!NOTE] > > The hedge scanner uses `nvidia/nemotron-nano-9b-v2:free` via OpenRouter by default. Model selection matters—some models find spurious correlations while others have output format issues. Override with `--model ` if needed. ## Example prompts Once installed, you can use natural language prompts with Claude Code: ### Browse trending markets ``` What's trending on Polymarket? ``` Returns market IDs, questions, prices, and volume. ### Get market details ``` Show me details for market ``` Use the market ID from the Polymarket URL or from the trending markets response. ### Check wallet status ``` What's my PolyClaw wallet balance? ``` Shows address, POL balance (for gas), and USDC.e balance. ### Execute a trade ``` Buy $50 YES on market ``` Executes the split + CLOB flow and records the position. ### Discover hedges ``` Find me some hedging opportunities on Polymarket ``` Or more specifically: ``` Run hedge scan limit 10 ``` > [!NOTE] > > Hedge scanning takes a few minutes. The skill fetches open markets and sends pairs to the LLM for logical implication analysis. ### Check positions ``` Show my PolyClaw positions ``` Lists open positions with entry price, current price, and P\&L. ### Full trading flow 1. **"What's trending on Polymarket?"** — get market IDs 2. **"Run hedge scan limit 10"** — wait for LLM analysis 3. Review hedge opportunities with coverage tiers 4. **"Buy \$25 YES on market abc123"** — take position on target market 5. **"Buy \$25 NO on market xyz789"** — take position on covering market 6. **"Show my PolyClaw positions"** — verify entries and track P\&L ## Configuration options | Variable | Required | Description | | - | - | - | | `CHAINSTACK_NODE` | Yes (trading) | Polygon RPC URL | | `POLYCLAW_PRIVATE_KEY` | Yes (trading) | EVM private key (hex, with or without 0x prefix) | | `OPENROUTER_API_KEY` | Yes (hedge) | OpenRouter API key for LLM hedge discovery | | `HTTPS_PROXY` | Recommended | Rotating residential proxy for CLOB (e.g., IPRoyal) | | `CLOB_MAX_RETRIES` | No | Max CLOB retries with IP rotation (default: 5) | ## CLOB Cloudflare blocking Polymarket's CLOB API uses Cloudflare protection that blocks POST requests from many IPs, including datacenter IPs and some residential ISPs. This affects the "sell unwanted tokens" step. **Solution: Residential proxy with retry logic** The recommended setup uses a rotating residential proxy (e.g., IPRoyal, BrightData). The CLOB client automatically retries with new IPs until one works: ```bash theme={"system"} export HTTPS_PROXY="http://user:pass@geo.iproyal.com:12321" export CLOB_MAX_RETRIES=10 # Default is 5 ``` With this setup, CLOB orders typically succeed within 5–10 retries as the proxy rotates through IPs until finding an unblocked one. **Alternative workarounds:** 1. **Use `--skip-sell`** — keep both YES and NO tokens, sell manually on polymarket.com 2. **No proxy** — split still works; only CLOB sell is affected ## Troubleshooting ### "No wallet available" Set the `POLYCLAW_PRIVATE_KEY` environment variable: ```bash theme={"system"} export POLYCLAW_PRIVATE_KEY="0x..." ``` ### "Insufficient USDC.e" Check balance with `uv run python scripts/polyclaw.py wallet status`. You need USDC.e (bridged USDC) on Polygon, not native USDC. ### "CLOB order failed" The CLOB sell may fail due to: * Insufficient liquidity at the sell price * IP blocked by Cloudflare (try residential proxy) Your split still succeeded—you have the tokens, just couldn't sell the unwanted side automatically. ### "Approvals not set" First trade requires contract approvals. Run: ```bash theme={"system"} uv run python scripts/polyclaw.py wallet approve ``` > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Polygon methods Source: https://docs.chainstack.com/docs/polygon-methods Complete reference of supported Polygon PoS JSON-RPC methods on Chainstack, including eth, debug, trace, and bor namespace availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Polygon API call examples](/reference/polygon-getting-started). Polygon runs on the Bor client, which does not implement the Erigon-only Parity `trace_*` or `erigon_*` namespaces. Migrate the `trace_*` methods to their `debug_*` equivalents: | Parity `trace_*` method | Replacement on Bor | | - | - | | `trace_transaction` | [`debug_traceTransaction`](/reference/polygon-tracetransaction) with `flatCallTracer` | | `trace_block` | [`debug_traceBlockByNumber`](/reference/polygon-traceblockbynumber) with `flatCallTracer` | | `trace_call` | [`debug_traceCall`](/reference/polygon-tracecall) with `flatCallTracer` | | `trace_filter`, `trace_replayBlockTransactions`, `trace_replayTransaction` | [`debug_traceBlockByNumber`](/reference/polygon-traceblockbynumber) with `flatCallTracer` per block, or [`eth_getLogs`](/docs/understanding-eth-getlogs-limitations) for event ranges | On Bor, `debug_trace*` with `flatCallTracer` returns traces in the Parity-style flat format, matching the shape of the Erigon `trace_*` output, so you can reuse your existing trace-parsing logic. The `erigon_*` convenience methods have no Bor equivalent — use the standard `eth_*` methods instead. On Bor, `eth_getProof` returns proofs only for blocks less than 127 behind the tip; historical proof queries are not available. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | Blocks less than 127 behind the tip only | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | txpool\_content | | | | txpool\_inspect | | | | txpool\_contentFrom | | | | txpool\_status | | | | web3\_clientVersion | | | | web3\_sha3 | | | | erigon\_blockNumber | | Use `eth_*` | | erigon\_forks | | Use `eth_*` | | erigon\_getBlockByTimestamp | | Use `eth_*` | | erigon\_getBlockReceiptsByBlockHash | | Use `eth_*` | | erigon\_getHeaderByHash | | Use `eth_*` | | erigon\_getHeaderByNumber | | Use `eth_*` | | erigon\_getLatestLogs | | Use `eth_*` | | erigon\_getLogsByHash | | Use `eth_*` | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_getTrieFlushInterval | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | trace\_block | | Use `debug_trace*` | | trace\_call | | Use `debug_trace*` | | trace\_callMany | | Use `debug_trace*` | | trace\_filter | | Use `debug_trace*` | | trace\_rawTransaction | | Use `debug_trace*` | | trace\_replayBlockTransactions | | Use `debug_trace*` | | trace\_replayTransaction | | Use `debug_trace*` | | trace\_transaction | | Use `debug_trace*` | | bor\_getAuthor | | | | bor\_getCurrentProposer | | | | bor\_getCurrentValidators | | | | bor\_getRootHash | | | | bor\_getSignersAtHash | | | | eth\_getRootHash | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Polygon tooling Source: https://docs.chainstack.com/docs/polygon-tooling Set up MetaMask, Hardhat, Foundry, and development tools to build and deploy smart contracts on Polygon PoS through your Chainstack RPC node endpoints. Get started with a [reliable Polygon RPC endpoint](https://chainstack.com/build-better-with-polygon/) to use the tools below. ## Bor Interact with your Polygon node using [Bor](https://github.com/maticnetwork/bor). 1. Install [Bor](https://github.com/maticnetwork/bor). 2. Use `geth attach` command with the node endpoint: ```shell theme={"system"} bor attach YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). 3. Invoke any methods from [Web3 JavaScript API](https://web3js.readthedocs.io/). Example below demonstrates how to get the balance of an address in wei value and convert it to ether value: ```js theme={"system"} > web3.fromWei(web3.eth.getBalance("0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae")) 642538.078574759898951277 ``` You can also use the [Bor client Docker container](https://hub.docker.com/r/maticnetwork/bor). ## GraphQL You can use GraphQL on a Dedicated Node on the [paid plans](https://chainstack.com/pricing/). ### UI You can query data using the graphical interface. 1. On Chainstack, navigate to your dedicated Polygon node. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). 2. Hover over **GraphQL IDE URL** and click **Open**. 3. In the graphical interface that opens, run a GraphQL query. Example to get the latest block number: ```graphql GraphQL theme={"system"} { block { number } } ``` ### Node.js You can build a web app to query data using node.js and [axios](https://www.npmjs.com/package/axios): ```javascript Javascript theme={"system"} const axios = require('axios'); const main = async () => { try { const result = await axios.post( 'YOUR_CHAINSTACK_ENDPOINT', { query: ` { block { number } } ` } ); console.log(result.data); } catch(error) { console.error(error); } } main(); ``` * YOUR\_CHAINSTACK\_ENDPOINT — your node GraphQL endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * `query` — your GraphQL query. In this case, to get the latest block number. See also [Using GraphQL with EVM-compatible nodes](https://support.chainstack.com/hc/en-us/articles/4409604331161-Using-GraphQL-with-EVM-compatible-nodes). ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Polygon nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Polygon nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3.php Build DApps using [web3.php](https://github.com/web3p/web3.php) and Polygon nodes deployed with Chainstack. 1. Install [web3.php](https://github.com/web3p/web3.php). 2. Connect over HTTP: ```php theme={"system"} ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password 3. Use [JSON-RPC methods](https://eth.wiki/json-rpc/API) to interact with the node. Example to get the latest block number: ```php theme={"system"} eth; $eth->blockNumber(function ($err, $data) { print "$data \n"; }); ?> ``` ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Polygon nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Polygon nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Polygon network ID: * Mainnet: `137` * Amoy testnet: `80002` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — Polygon network ID: * Mainnet: `137` * Amoy testnet: `80002` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Brownie 1. Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). 2. Use the `brownie networks add` command with the node endpoint: ```shell theme={"system"} brownie networks add Polygon ID name="NETWORK_NAME" host=YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `chainstack-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password * NETWORK\_ID — Polygon network ID: * Mainnet: `137` * Amoy testnet: `80002` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Polygon: Bridging ERC-20 from Ethereum to Polygon Source: https://docs.chainstack.com/docs/polygon-tutorial-bridging-erc20-from-ethereum-to-polygon Bridge an ERC-20 token from Ethereum to Polygon PoS using the FxPortal smart contracts and Chainstack RPC endpoints. Uses deprecated Goerli testnet. > [!WARNING] > > ## Deprecation notice > > As Goerli & Mumbai testnets have been deprecated, this guide is for historical reference. The Polygon mainnet is an L2 commit chain to the Ethereum mainnet. Bridging your existing Ethereum smart contract to the Polygon commit chain allows network users to move their assets based on your contract between an Ethereum network and a Polygon commit chain. In this tutorial, you will: * Deploy an ERC-20 smart contract on the Ethereum [Goerli testnet](/docs/protocols-networks). * Deploy a compatible smart contract on the Polygon [Mumbai testnet](/docs/protocols-networks). * Map the Ethereum smart contract to the Polygon smart contract. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Polygon RPC endpoint](https://chainstack.com/build-better-with-polygon/) and an Ethereum node. * [Truffle Suite](https://trufflesuite.com/) to create and deploy contracts. * [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts/4.x/) to use the audited [ERC-20 libraries](https://docs.openzeppelin.com/contracts/4.x/erc20) to create your ERC-20 contract. ## Overview To get from zero to a deployed ERC-20 contract on the Ethereum Goerli testnet and bridge it to the Polygon Mumbai testnet, do the following: 1. With Chainstack, create a public chain project (public chain project- a project to join public networks). 2. With Chainstack, join the Ethereum Goerli testnet. 3. In the same project, join the Polygon Mumbai testnet. 4. With Chainstack, access your Ethereum node and Polygon node endpoints. 5. With OpenZeppelin, create an ERC-20 contract. 6. With Truffle, compile and deploy the contract through your Ethereum node. 7. With Truffle, compile and deploy a Polygon contract through your Polygon node. 8. Submit a mapping request to bridge the deployed Ethereum contract to the deployed Polygon contract. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Ethereum Goerli testnet and the Polygon Mumbai testnet See [Join a public network](/docs/manage-your-networks). ### Get your Ethereum node and Polygon node access and credentials See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Install OpenZeppelin Contracts See [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts/4.x/). ### Install Truffle Suite See [Truffle Suite: Installation](https://trufflesuite.com/docs/truffle/how-to/install/). ### Create the root Ethereum ERC-20 contract 1. On your machine, in the contract directory, initialize Truffle: ```bash theme={"system"} truffle init ``` This will generate the Truffle boilerplate structure: ``` . ├── contracts │ └── .gitkeep ├── migrations │ └── .gitkeep ├── test │ └── .gitkeep └── truffle-config.js ``` 2. Go to the `contracts` directory. In the directory, create your ERC-20 contract: `myL2token.sol`. ```solidity theme={"system"} //SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract myL2token is ERC20 { constructor(uint256 initialSupply) ERC20("myL2token", "ML2T") { _mint(msg.sender, initialSupply); } } ``` This is a standard [OpenZeppelin ERC-20 preset contract](https://docs.openzeppelin.com/contracts/4.x/erc20). 3. Create `2_deploy_contracts.js` in the `migrations` directory. ```javascript theme={"system"} var myL2token = artifacts.require("./myL2token.sol"); module.exports = function(deployer) { deployer.deploy(myL2token, 100); }; ``` This will create the instructions for Truffle to deploy the contract with the supply of `100 ML2T` tokens. ### Compile and deploy the root Ethereum ERC-20 contract 1. Install `HDWalletProvider`. [HDWalletProvider](https://github.com/trufflesuite/truffle/tree/develop/packages/hdwallet-provider) is Truffle's separate npm package used to sign transactions. Run: ```bash theme={"system"} npm install @truffle/hdwallet-provider ``` 2. Edit `truffle-config.js` to add: * `HDWalletProvider` * Your Ethereum node access and credentials * Your Ethereum account that you will use to deploy the contract ```javascript theme={"system"} const HDWalletProvider = require("@truffle/hdwallet-provider"); const private_key = 'PRIVATE_KEY'; module.exports = { networks: { goerli: { provider: () => new HDWalletProvider(private_key, "YOUR_CHAINSTACK_ENDPOINT"), network_id: 5 } }, compilers: { solc: { version: "0.8.1", } } }; ``` where * `goerli` — any network name that you will pass to the `truffle migrate --network` command. * `HDWalletProvider` — Truffle's custom provider to sign transactions. * PRIVATE\_KEY — the private key of your Ethereum account that will deploy the contract. * YOUR\_CHAINSTACK\_ENDPOINT — your Chainstack node endpoint. See also [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) and [Ethereum tooling](/docs/ethereum-tooling). * `network_id` — the network ID of the Ethereum Goerli testnet: `5`. * `solc` — the Solidity compiler version that Truffle must use. 3. Run: ```bash theme={"system"} truffle migrate --network goerli ``` This will engage `2_deploy_contracts.js` and deploy the contract to the Ethereum Goerli testnet as specified in `truffle-config.js`. ### Verify your root Ethereum ERC-20 contract on Etherscan Once your contract is deployed, you can view it online at [Etherscan](https://goerli.etherscan.io/). Before you submit a mapping request to bridge your root Ethereum ERC-20 contract to the Polygon commit chain, you must verify the contract on Etherscan. 1. Flatten your contract code Since your ERC-20 contract uses imported OpenZeppelin libraries, you must put all the imports into one `.sol` file to make Etherscan be able to verify it. Install [Truffle Flattener](https://www.npmjs.com/package/truffle-flattener). In the `contracts` directory, run: ```bash theme={"system"} npx truffle-flattener myL2token.sol > flatmyL2token.sol ``` 2. Clean up the licensing information. The flattened contract will have the same licensing note imported from each of the files. Multiple licensing notes in one file break the Etherscan verification, so you have to leave one licensing note for the entirety of the flattened contract. The easiest way to clean up is to search for the `SPDX` mentions in the file and remove all of them except for the very first one. 3. Verify the deployed contract on Etherscan. At this point, you have your flattened and cleaned-up contract ready for the Etherscan verification. 4. Go to [Etherscan](https://goerli.etherscan.io/). 5. Find your deployed contract. The address of your contract should have been printed by Truffle at the end of the deployment in the contract address field. 6. On the contract page on Etherscan, click **Contract** > **Verify and Publish**. 7. In **Compiler Type**, select **Solidity (Single file)**. 8. In **Compiler Version**, select **v0.8.1**. This is the version this tutorial used to compile the contract. 9. In **Open Source License Type**, select **MIT License (MIT)**. 10. Click **Continue**. 11. Keep the **Optimization** option set to **No** as Truffle does not use optimization by default. 12. Paste the entirety of your flattened `.sol` contract in the **Enter the Solidity Contract Code below** field. 13. Click **Verify and Publish**. Etherscan will take a few seconds to compile your contract, verify, and publish it. ### Create the child Polygon ERC-20 contract 1. Go to the `contracts` directory. In the directory, put the default [child ERC-20 contract](https://github.com/maticnetwork/pos-portal/blob/master/flat/ChildERC20.sol) provided by Polygon. 2. Create `2_deploy_contracts.js` in the `migrations` directory. ```javascript theme={"system"} var ChildERC20 = artifacts.require("./ChildERC20.sol"); module.exports = function(deployer) { deployer.deploy(ChildERC20, 'myL2tokenChild', 'ML2T', 18, '0x2e5e27d50EFa501D90Ad3638ff8441a0C0C0d75e'); }; ``` where * `myL2tokenChild` — the name of your ERC-20 token * `ML2T` — the symbol of your ERC-20 token * `18` — the default decimals number as used by the [OpenZeppelin ERC-20 preset contract](https://docs.openzeppelin.com/contracts/4.x/erc20) * `0x2e5e27d50EFa501D90Ad3638ff8441a0C0C0d75e` — the [ChildChainmanager](https://wiki.polygon.technology/docs/develop/ethereum-polygon/pos/getting-started/#steps-to-use-the-pos-bridge) address on the Polygon Mumbai testnet. For the ChildChainManager contract addresses, look online for the addresses provided by Polygon: * [Mainnet addresses](https://github.com/maticnetwork/static/blob/master/network/mainnet/v1/index.json) ### Compile and deploy the child Polygon ERC-20 contract Clean up the environment by moving `myL2token.sol` and `flatmyL2token.sol` to a backup directory so that Truffle does not pick them up for deployment. 1. Edit `truffle-config.js` to change to: * Your Polygon node access and credentials * Your Polygon account that you will use to deploy the contract. * The Solidity compiler version used by the default [child ERC-20 contract template](https://github.com/maticnetwork/pos-portal/blob/master/flat/ChildERC20.sol) provided by Polygon. ```js JavaScript theme={"system"} const HDWalletProvider = require("@truffle/hdwallet-provider"); const private_key = 'PRIVATE_KEY'; module.exports = { networks: { mumbai: { provider: () => new HDWalletProvider(private_key, "YOUR_CHAINSTACK_ENDPOINT"), network_id: 80001 } }, compilers: { solc: { version: "0.6.6", } } }; ``` where * `mumbai` — any network name that you will pass to the `truffle migrate --network` command. * `HDWalletProvider` — Truffle's custom provider to sign transactions. * PRIVATE\_KEY — the private key of your Polygon account that will deploy the contract. * YOUR\_CHAINSTACK\_ENDPOINT — your Chainstack node endpoint. See also [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) and [Polygon tooling](/docs/polygon-tooling). * `network_id` — the network ID of the Polygon network: testnet is `80001`, mainnet is `137`. * `solc` — the Solidity compiler version that Truffle must use. OpenZeppelin contracts have a higher version Solidity compiler requirement than the default Truffle installation, hence you must provide a specific compiler version. 2. Run: ```bash Shell theme={"system"} truffle migrate --network Mumbai ``` ### Verify your child Polygon ERC-20 contract on the Polygon explorer Once your contract is deployed, you can view it online at the [Polygon Mumbai explorer](https://mumbai.polygonscan.com/). 1. Go to the [Polygon Mumbai explorer](https://mumbai.polygonscan.com/). 2. Find your deployed contract. The address of your contract should have been printed by Truffle at the end of the deployment in the `contract address` field. 3. On the contract page on the explorer, click **Contract** > **Verify and Publish**. 4. In **Compiler Type**, select **Solidity (Single file)**. 5. In **Compiler Version**, select **v0.6.6**. This is the compiler version the default child contract uses as provided by Polygon. 6. In **Open Source License Type**, select **MIT License (MIT)**. 7. Click **Continue**. 8. Keep the **Optimization** option set to **No** as Truffle does not use optimization by default. 9. Paste the entirety of your `ChildERC20.sol` contract in the **Enter the Solidity Contract Code below** field. 10. Click **Verify and Publish**. > [!NOTE] > > ### ABI data > > If on the verification attempt you get a message that the explorer cannot get the ABI data for the contract verification, do the following: > > 1. Go to the [online ABI encoding service](https://abi.hashex.org/). > > 2. In the service, provide the [ChildERC20.json](https://github.com/maticnetwork/pos-portal/blob/master/artifacts/ChildERC20.json) ABI data as (ticne). Remove the first and last curly bracket `{}` and `"abi":` or the code will not parse. > > 3. Click **Parse**. > > 4. Put your constructor data by adding arguments with the data type: > > * name\_: `myL2tokenChild` > * symbol\_: `ML2T` > * decimals\_: `18` > * childChainManager: `0x2e5e27d50EFa501D90Ad3638ff8441a0C0C0d75e` > > 5. Copy the encoded data. > > 6. Paste the encoded data in the ABI constructor arguments field on the explorer. The explorer will take a few seconds to compile your contract, verify, and publish it. ### Map your Ethereum ERC-20 contract to the Polygon contract 1. Go to the [token mapper](https://mapper.polygon.technology/map). 2. Select **ERC20** and **Goerli Testnet - Mumbai Testnet**. 3. Provide the address of your contract on the Ethereum Goerli testnet and on the Polygon Mumbai testnet. 4. Provide an email address to be notified of when the mapping is done. 5. Click **Submit**. ### Bridge the mapped tokens When your token is mapped, bridge your token from Ethereum to Polygon and back: > [card] [Polygon Mumbai testnet bridge](https://wallet.polygon.technology/polygon/bridge/deposit) > [card] [Polygon mainnet bridge](https://wallet.polygon.technology/polygon/bridge/deposit) ## Conclusion This tutorial guided you through the basics of bridging an ERC-20 contract from the Ethereum Goerli testnet to the Polygon Mumbai testnet. The same instructions will work for the Ethereum mainnet and the Polygon mainnet. Polygon has public L2 contract templates and a network of deployed contracts monitored by Heimdall nodes, all of which make it easy to bridge assets from the Ethereum mainnet to the Polygon commit chain. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Ponder: Blockchain indexing backends with Chainstack Source: https://docs.chainstack.com/docs/ponder-tutorial-building-blockchain-application-backends-with-chainstack Build a blockchain application backend using Ponder indexing framework with a Chainstack RPC endpoint for real-time smart contract event processing. **TLDR:** * You'll build a complete blockchain indexing backend using Ponder and Chainstack nodes. * You'll create a Ponder project that indexes ERC-20 token transfers and balances across multiple chains. * You'll configure Ponder to use Chainstack RPC endpoints for reliable data fetching. * By the end, you'll have a production-ready GraphQL API for querying blockchain data with hot reloading and type safety. ## Main article In this tutorial, you will: * Create a Ponder project that indexes blockchain data from multiple networks. * Configure Ponder to use Chainstack nodes for reliable, high-performance data access. * Build a schema to track ERC-20 token transfers, balances, and holder statistics. * Deploy indexing functions that process blockchain events in real-time. * Query your data through Ponder's auto-generated GraphQL API. Ponder is an open-source framework for building blockchain application backends. Unlike traditional indexing solutions, Ponder provides end-to-end type safety, hot reloading during development, and automatic GraphQL API generation based on your schema. ## Prerequisites * [Chainstack account](https://console.chainstack.com/user/login) to deploy blockchain nodes * [Node.js](https://nodejs.org/) version 18.14 or higher * [pnpm](https://pnpm.io/installation) package manager (recommended) ## Dependencies * ponder: ^0.11.0 * viem: ^2.30.0 * typescript: ^5.0.0 ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join blockchain networks For this tutorial, we'll index data from multiple chains. Join the following networks: * **Ethereum** - for established DeFi protocols * **Polygon** - for high-volume, low-cost transactions * **Base** - for modern L2 applications See [Join a public network](/docs/manage-your-networks). ### Get your node access and credentials See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). You'll need the RPC endpoints for each network you joined. ### Create a new Ponder project 1. Create a new Ponder project using the CLI: ```bash theme={"system"} pnpm create ponder@latest my-ponder-indexer ``` This will prompt you to choose a template. For this tutorial, select **"ERC-20"** as it provides a good starting point for token indexing. 2. Navigate to your project directory: ```bash theme={"system"} cd my-ponder-indexer ``` 3. Install dependencies: ```bash theme={"system"} pnpm install ``` ### Configure environment variables 1. Create a `.env.local` file in your project root: ```sh theme={"system"} # Chainstack RPC endpoints PONDER_RPC_URL_1="YOUR_ETHEREUM_CHAINSTACK_ENDPOINT" PONDER_RPC_URL_137="YOUR_POLYGON_CHAINSTACK_ENDPOINT" PONDER_RPC_URL_8453="YOUR_BASE_CHAINSTACK_ENDPOINT" # Optional: Database URL for production # DATABASE_URL="postgresql://user:password@localhost:5432/ponder" ``` Replace the placeholder values with your actual Chainstack endpoints. > [!NOTE] > > ### Environment variable naming > > Ponder uses the pattern `PONDER_RPC_URL_{CHAIN_ID}` for RPC endpoints. The chain IDs are: > > * Ethereum: 1 > * Polygon: 137 > * Base: 8453 ### Configure Ponder for multiple chains 1. Edit `ponder.config.ts` to configure your chains and contracts: ```ts theme={"system"} import { createConfig } from "ponder"; import { http } from "viem"; // Import ABIs for the contracts you want to index import { erc20Abi } from "viem"; export default createConfig({ chains: { ethereum: { id: 1, rpc: http(process.env.PONDER_RPC_URL_1!), }, polygon: { id: 137, rpc: http(process.env.PONDER_RPC_URL_137!), }, base: { id: 8453, rpc: http(process.env.PONDER_RPC_URL_8453!), }, }, contracts: { // USDC on multiple chains UsdcMainnet: { chain: "ethereum", abi: erc20Abi, address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", startBlock: 22842789, }, UsdcPolygon: { chain: "polygon", abi: erc20Abi, address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", startBlock: 73549831, }, UsdcBase: { chain: "base", abi: erc20Abi, address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", startBlock: 32402921, }, }, }); ``` This configuration sets up Ponder to index USDC transfers across three different chains using your Chainstack nodes. > [!NOTE] > > ### Start block optimization > > Setting appropriate start blocks is crucial for performance. Choose blocks close to when your contracts were deployed or when interesting activity began. ### Define your database schema 1. Edit `ponder.schema.ts` to define the data structure you want to track: ```ts theme={"system"} import { onchainTable } from "ponder"; // Track individual token transfers export const transfer = onchainTable("transfer", (t) => ({ id: t.text().primaryKey(), // tx_hash:log_index from: t.text().notNull(), to: t.text().notNull(), amount: t.bigint().notNull(), contract: t.text().notNull(), chain: t.text().notNull(), blockNumber: t.bigint().notNull(), timestamp: t.integer().notNull(), })); // Track account balances per contract export const balance = onchainTable("balance", (t) => ({ id: t.text().primaryKey(), // account:contract:chain account: t.text().notNull(), contract: t.text().notNull(), chain: t.text().notNull(), balance: t.bigint().notNull(), lastUpdated: t.integer().notNull(), })); // Track holder statistics per contract export const tokenStats = onchainTable("token_stats", (t) => ({ id: t.text().primaryKey(), // contract:chain contract: t.text().notNull(), chain: t.text().notNull(), totalHolders: t.integer().notNull().default(0), totalTransfers: t.bigint().notNull().default(0n), totalVolume: t.bigint().notNull().default(0n), lastUpdated: t.integer().notNull(), })); ``` This schema defines three tables: * **transfers**: Individual transfer events * **balances**: Current token balances per account * **tokenStats**: Aggregate statistics per token contract ### Write indexing functions 1. Create indexing functions to process Transfer events. Replace the contents of `src/index.ts`: ```ts theme={"system"} import { ponder } from "ponder:registry"; import schema from "ponder:schema"; // Helper function to create unique IDs function createTransferId(event: any) { return `${event.transaction.hash}:${event.log.logIndex}`; } function createBalanceId(account: string, contract: string, chain: string) { return `${account}:${contract}:${chain}`; } function createStatsId(contract: string, chain: string) { return `${contract}:${chain}`; } // Index USDC transfers on Ethereum ponder.on("UsdcMainnet:Transfer", async ({ event, context }) => { const { from, to, value } = event.args; const transferId = createTransferId(event); // Insert transfer record await context.db.insert(schema.transfer).values({ id: transferId, from, to, amount: value, contract: event.log.address, chain: "ethereum", blockNumber: event.block.number, timestamp: Number(event.block.timestamp), }); // Update balances for both accounts (if not mint/burn) const zeroAddress = "0x0000000000000000000000000000000000000000"; if (from !== zeroAddress) { await updateBalance(context, from, event.log.address, "ethereum", -value, event.block.timestamp); } if (to !== zeroAddress) { await updateBalance(context, to, event.log.address, "ethereum", value, event.block.timestamp); } // Update token statistics await updateTokenStats(context, event.log.address, "ethereum", value, event.block.timestamp); }); // Index USDC transfers on Polygon ponder.on("UsdcPolygon:Transfer", async ({ event, context }) => { const { from, to, value } = event.args; const transferId = createTransferId(event); await context.db.insert(schema.transfer).values({ id: transferId, from, to, amount: value, contract: event.log.address, chain: "polygon", blockNumber: event.block.number, timestamp: Number(event.block.timestamp), }); const zeroAddress = "0x0000000000000000000000000000000000000000"; if (from !== zeroAddress) { await updateBalance(context, from, event.log.address, "polygon", -value, event.block.timestamp); } if (to !== zeroAddress) { await updateBalance(context, to, event.log.address, "polygon", value, event.block.timestamp); } await updateTokenStats(context, event.log.address, "polygon", value, event.block.timestamp); }); // Index USDC transfers on Base ponder.on("UsdcBase:Transfer", async ({ event, context }) => { const { from, to, value } = event.args; const transferId = createTransferId(event); await context.db.insert(schema.transfer).values({ id: transferId, from, to, amount: value, contract: event.log.address, chain: "base", blockNumber: event.block.number, timestamp: Number(event.block.timestamp), }); const zeroAddress = "0x0000000000000000000000000000000000000000"; if (from !== zeroAddress) { await updateBalance(context, from, event.log.address, "base", -value, event.block.timestamp); } if (to !== zeroAddress) { await updateBalance(context, to, event.log.address, "base", value, event.block.timestamp); } await updateTokenStats(context, event.log.address, "base", value, event.block.timestamp); }); // Helper function to update account balances async function updateBalance( context: any, account: string, contract: string, chain: string, change: bigint, blockTimestamp: bigint ) { const balanceId = createBalanceId(account, contract, chain); // Try to get existing balance const existingBalance = await context.db.find(schema.balance, { id: balanceId }); if (existingBalance) { // Update existing balance const oldBalance = existingBalance.balance; const newBalance = oldBalance + change; // Handle cases where we're processing historical data from a start block // and don't have the complete balance history if (newBalance < 0n && change < 0n) { // This suggests the account had a balance before our start block // Initialize with the absolute value of the change (minimum needed balance) const initializedBalance = -change; await context.db.update(schema.balance, { id: balanceId }).set({ balance: initializedBalance + change, // This will equal initializedBalance - abs(change) = 0 lastUpdated: Number(blockTimestamp), }); return; // Exit early after handling this case } else if (newBalance < 0n) { throw new Error(`Balance would become negative for account ${account}. Old: ${oldBalance}, Change: ${change}`); } await context.db.update(schema.balance, { id: balanceId }).set({ balance: newBalance, lastUpdated: Number(blockTimestamp), }); // Update holder count based on balance changes if (oldBalance === 0n && newBalance > 0n) { // New holder (balance went from 0 to positive) await updateHolderCount(context, contract, chain, 1, blockTimestamp); } else if (oldBalance > 0n && newBalance === 0n) { // Holder exit (balance went from positive to 0) await updateHolderCount(context, contract, chain, -1, blockTimestamp); } } else { // Create new balance record if (change < 0n) { // Account is sending tokens but has no balance record // This means they had tokens before our start block // Initialize with the absolute value of the change, then subtract it const initializedBalance = -change; await context.db.insert(schema.balance).values({ id: balanceId, account, contract, chain, balance: 0n, // After the transfer, balance will be 0 lastUpdated: Number(blockTimestamp), }); // Account just exited its position – adjust holder count await updateHolderCount(context, contract, chain, -1, blockTimestamp); } else { // Normal case: receiving tokens to a new account await context.db.insert(schema.balance).values({ id: balanceId, account, contract, chain, balance: change, lastUpdated: Number(blockTimestamp), }); // If this is a new holder with positive balance, increment holder count if (change > 0n) { await updateHolderCount(context, contract, chain, 1, blockTimestamp); } } } } // Helper function to update holder count async function updateHolderCount( context: any, contract: string, chain: string, change: number, blockTimestamp: bigint ) { const statsId = createStatsId(contract, chain); const existingStats = await context.db.find(schema.tokenStats, { id: statsId }); if (existingStats) { await context.db.update(schema.tokenStats, { id: statsId }).set({ totalHolders: Math.max(0, existingStats.totalHolders + change), lastUpdated: Number(blockTimestamp), }); } else { // Create new token stats record for the first holder event await context.db.insert(schema.tokenStats).values({ id: statsId, contract, chain, totalHolders: Math.max(0, change), totalTransfers: 0n, totalVolume: 0n, lastUpdated: Number(blockTimestamp), }); } } // Helper function to update token statistics async function updateTokenStats( context: any, contract: string, chain: string, volume: bigint, blockTimestamp: bigint ) { const statsId = createStatsId(contract, chain); const existingStats = await context.db.find(schema.tokenStats, { id: statsId }); if (existingStats) { await context.db.update(schema.tokenStats, { id: statsId }).set({ totalTransfers: existingStats.totalTransfers + 1n, totalVolume: existingStats.totalVolume + volume, lastUpdated: Number(blockTimestamp), }); } else { await context.db.insert(schema.tokenStats).values({ id: statsId, contract, chain, totalHolders: 0, totalTransfers: 1n, totalVolume: volume, lastUpdated: Number(blockTimestamp), }); } } ``` This indexing logic: * Records every transfer event across all three chains * Maintains real-time balance tracking for all accounts * Handles historical data gaps when starting from an arbitrary block (accounts may have balances from before the start block) * Tracks holder counts by detecting when balances go from zero to positive (new holder) or positive to zero (holder exit) * Calculates aggregate statistics including transfer volume and holder statistics per token contract ### Start the development server 1. Start the Ponder development server: ```bash theme={"system"} pnpm dev ``` You should see output similar to: ``` 09:14:25.156 INFO server Started listening on port 42069 09:14:25.341 INFO build Hot reloaded config for update 09:14:25.445 INFO indexing Started indexing from blocks ethereum: 22842789, polygon: 73549831, base: 32402921 09:14:26.123 INFO indexing Processed 100 UsdcMainnet:Transfer events (mainnet) ``` > [!NOTE] > > ### Development features > > Ponder's development server includes: > > * Hot reloading when you change config, schema, or indexing functions > * Real-time progress monitoring and error reporting > * Built-in GraphQL playground at [http://localhost:42069](http://localhost:42069) 2. Open your browser to [http://localhost:42069](http://localhost:42069) to access the GraphQL playground. ### Query your indexed data 1. In the GraphQL playground, try these example queries: **Get recent transfers across all chains:** ```graphql theme={"system"} { transfers( limit: 10 orderBy: "timestamp" orderDirection: "desc" ) { items { id from to amount contract chain timestamp } } } ``` **Get top holders for a specific token:** ```graphql theme={"system"} { balances( where: { chain: "ethereum" } limit: 10 orderBy: "balance" orderDirection: "desc" ) { items { account balance contract chain lastUpdated } } } ``` ### Deploy to production 1. For production deployment, you'll need a PostgreSQL database. Update your `.env.local`: ```sh theme={"system"} # Add your production database URL DATABASE_URL="postgresql://user:password@localhost:5432/ponder" # Keep your Chainstack endpoints PONDER_RPC_URL_1="YOUR_ETHEREUM_CHAINSTACK_ENDPOINT" PONDER_RPC_URL_137="YOUR_POLYGON_CHAINSTACK_ENDPOINT" PONDER_RPC_URL_8453="YOUR_BASE_CHAINSTACK_ENDPOINT" ``` 2. Build your Ponder application: ```bash theme={"system"} pnpm build ``` 3. Start the production server: ```bash theme={"system"} pnpm start ``` Your Ponder application will be available at the configured port with a production-ready GraphQL API. ## Conclusion This tutorial guided you through building a comprehensive blockchain indexing backend using Ponder and Chainstack. You've created a system that: * Indexes data from multiple blockchain networks simultaneously * Provides type-safe, real-time data processing * Offers a production-ready GraphQL API * Scales efficiently with Chainstack's reliable infrastructure The combination of Ponder's developer experience and Chainstack's enterprise-grade blockchain infrastructure provides a robust foundation for building modern Web3 applications. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Chainstack pricing: plans, request units, and quotas Source: https://docs.chainstack.com/docs/pricing-introduction Understand Chainstack pricing plans, request unit billing, node costs, and subscription tiers to choose the right plan for your blockchain project. ## Pricing principles Each Chainstack’s subscription plan has its own quota that represents the allocated resources and usage limits that users can spend within their chosen plan. In addition to the allocated quota, Chainstack also offers an extra usage setting. With it enabled, if you exceed your allocated quota, you can continue using Chainstack's services seamlessly, with any additional usage being automatically charged at the applicable rates. Visit Chainstack’s [pricing page](https://chainstack.com/pricing/) to get familiar with a quota for each plan and extra usage costs. Quota spending can vary depending on the combination of services that you use. Service cost is reflected in [request units](/docs/request-units#what-an-ru-is). In addition to basic services, you can purchase Chainstack’s add-ons that help you to enrich your experience with the platform and improve your DApp performance. Available add-ons and their cost can be found in the [pricing page](https://chainstack.com/pricing/). ## Coupons & promos Follow the Chainstack [twitter account](https://twitter.com/chainstackhq) & sign up for the [newsletter](https://chainstack.com/newsletter/) to never miss a promo or get a coupon code. We run the promos very often. ## What you can be billed for Chainstack has the following chargeable items: * Request units — for request-based services. The cost of a request can vary depending on the service. * [Warp transactions](/docs/warp-transactions) — due to the partnership with bloXroute, Warp transactions are charged separately. * Compute hours and hourly rated storage — for Dedicated Nodes and indexers. * Management fee — for hybrid hosting. [Contact us](https://chainstack.com/contact/) to learn more. > [!NOTE] > > Learn how requests are metered and priced in [request units](/docs/request-units). ## Payment mechanics Your internal balance serves as a primary payment source to cover any charges incurred from using the Chainstack services. If your balance has sufficient funds, the charges are automatically deducted from it. If the balance is insufficient, Chainstack then proceeds to charge the attached credit card. Currently, the following payment methods can be used to top up your balance: * Credit card * Crypto payment (powered by NOWPayments) * Wire transfer * Direct crypto payment If your request volume is extra high or you prefer specific payment methods, [contact us](https://chainstack.com/contact/) for a special offer. > [!NOTE] > > ### Dedicated Nodes > > You can also order [Dedicated Nodes](/docs/dedicated-node) and pay for the node resource consumption only instead of per-request billing. ## Limits For rate limits, range limits, and other operational thresholds, see [Limits](/docs/limits). # Clients Source: https://docs.chainstack.com/docs/protocols-clients The node client Chainstack runs for each protocol — Geth, Reth, Erigon, Nethermind, Bor, Nitro, op-reth, AvalancheGo, Agave, Pathfinder, and more — and why the client determines which RPC methods are available. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Chainstack selects the **node client** for each protocol, and often per **mode** (full vs archive) and **node type** (Global Node, Trader Node, Dedicated Node). Clients evolve as protocols upgrade — so treat this page as the current picture, not a frozen contract. > [!NOTE] > > The client determines which **RPC namespaces** you can call. For example, Geth exposes `debug_*` while Erigon and Reth add the Parity `trace_*` namespace, and Bor (Polygon) does not implement `trace_*` at all. See [Debug and trace APIs](/docs/debug-and-trace-apis) for the per-protocol breakdown and [Request units](/docs/request-units) for how calls are billed. This page lists every protocol you can deploy on Chainstack. For a protocol available only on request, see [Dedicated Nodes on request](#dedicated-nodes-on-request) or the [Networks](/docs/protocols-networks) page. You can confirm the client at runtime with `web3_clientVersion` (EVM) or the chain's equivalent. ## Ethereum **Execution layer:** * Geth — the [Go Ethereum](https://github.com/ethereum/go-ethereum) implementation, used for full nodes; exposes the `eth_*` and `debug_*` namespaces. * Reth — the [Reth](https://github.com/paradigmxyz/reth) implementation, used for archive requests on Mainnet Global Nodes, and for all requests on Sepolia and Hoodi Global Nodes. * Erigon — the [Erigon](https://github.com/erigontech/erigon) implementation, used for archive [Dedicated Nodes](/docs/dedicated-node); adds the Parity `trace_*` namespace on top of `debug_*`. **Consensus layer:** * Lighthouse — the [Rust](https://github.com/sigp/lighthouse) implementation of the Ethereum Beacon Chain client. See [Debug and trace APIs](/docs/debug-and-trace-apis#ethereum) for how the execution client affects available tracing methods. ## Polygon Polygon runs the **Bor** execution client — the [native Polygon client](https://github.com/0xPolygon/bor) — with [Heimdall v2](https://github.com/0xPolygon/heimdall-v2) for consensus. Bor is interacted with using the [standard Ethereum JSON-RPC methods](https://ethereum.org/en/developers/docs/apis/json-rpc/). > [!WARNING] > > Chainstack has **retired the Erigon client for Polygon** — archive nodes now run Bor. Because Bor does not implement the Erigon-only Parity `trace_*` or `erigon_*` namespaces, use the `debug_*` methods (with `flatCallTracer`) instead. See [Polygon methods](/docs/polygon-methods) and [Debug and trace APIs](/docs/debug-and-trace-apis). ## BNB Smart Chain BNB Smart Chain nodes run a different client for each mode: * BSC Geth — the [BNB Chain fork of Go Ethereum](https://github.com/bnb-chain/bsc), used for full nodes. * Reth — the [`reth-bsc`](https://github.com/bnb-chain/reth-bsc) implementation, used for archive nodes. Chainstack migrated BNB Smart Chain archive nodes from Erigon to Reth. BNB Smart Chain Testnet Global Nodes run Reth for both modes. Both clients are interacted with using the [standard Ethereum JSON-RPC methods](https://ethereum.org/en/developers/docs/apis/json-rpc/). ## opBNB opBNB runs [op-geth](https://github.com/bnb-chain/op-geth), BNB Chain's fork of the OP Stack execution client. ## Avalanche [AvalancheGo](https://github.com/ava-labs/avalanchego) is the Go implementation of an Avalanche node. It exposes a Geth-compatible C-Chain API with a limited set of `debug_trace*` methods. See the [Chainstack Avalanche API reference](/reference/avalanche-getting-started). ## Arbitrum > [!NOTE] > > ### Stylus is supported > > Arbitrum nodes on Chainstack support Stylus. [Nitro](https://github.com/OffchainLabs/nitro) is the official Arbitrum node — a fully integrated layer-2 optimistic rollup stack (fraud proofs, sequencer, token bridges, calldata compression). Blocks from before the Nitro upgrade are served by an Arbitrum Classic node, so Arbitrum uses `arbtrace_*` for pre-Nitro blocks and `debug_*` for post-Nitro blocks. See the [Chainstack Arbitrum API reference](/reference/arbitrum-getting-started) and the [official Arbitrum docs](https://developer.arbitrum.io/). ## Robinhood Chain Robinhood Chain runs [Nitro](https://github.com/OffchainLabs/nitro), the Arbitrum client. Tracing is served through the `debug_*` namespace; the `arbtrace_*` and `trace_*` namespaces are not exposed. See [Robinhood Chain methods](/docs/robinhood-methods). ## Elysium Elysium is an Arbitrum Orbit chain that settles to HyperEVM, and it runs [Nitro](https://github.com/OffchainLabs/nitro), the Arbitrum client. Tracing is served through the `debug_*` namespace; the `arbtrace_*` and `trace_*` namespaces are not exposed. See [Elysium methods](/docs/elysium-methods) and [Elysium tooling](/docs/elysium-tooling). ## Base The execution client is [`base-reth-node`](https://github.com/base/node) (built on [Reth](https://github.com/paradigmxyz/reth)) and the consensus client is [`base-consensus`](https://github.com/base/node). Base introduced this native stack with the Azul upgrade (mainnet May 28, 2026), replacing op-geth and op-node. ## Optimism, Unichain, and Zora [Global Nodes](/docs/global-elastic-node) and [Trader Nodes](/docs/trader-node) run **op-reth** — the OP Stack execution client built on [Reth](https://github.com/paradigmxyz/reth) — with op-node for consensus. See the [Chainstack Optimism API reference](/reference/optimism-api-reference). ## Celo Celo runs op-reth in Celo's build, with op-node for consensus. ## Mantle Mantle runs Mantle's Geth-based op-geth execution client. ## Linea Linea runs [Geth](https://github.com/ethereum/go-ethereum). ## Scroll The client is [Geth-based](https://github.com/scroll-tech/go-ethereum) (`l2geth`), accessed via the [standard Ethereum JSON-RPC methods](https://ethereum.org/en/developers/docs/apis/json-rpc/). ## zkSync Era The client is [zkSync Era](https://github.com/matter-labs/zksync-era). Tracing is served through the `debug_*` namespace on archive nodes. ## Starknet Starknet runs [Pathfinder](https://github.com/software-mansion/pathfinder). The default path and `/rpc/v0_9` serve the [Starknet JSON-RPC specification](https://github.com/starkware-libs/starknet-specs) 0.9, and `/rpc/v0_10` serves 0.10. Confirm the version on any path with `starknet_specVersion`. ## Arc Arc runs Circle's [arc-node](https://github.com/circlefin/arc-node): a Reth-based execution client with Malachite consensus. See [Arc tooling](/docs/arc-tooling). ## Tempo Tempo runs the [tempo](https://github.com/tempoxyz/tempo) client, built on [Reth](https://github.com/paradigmxyz/reth). ## Plasma Plasma runs a Reth-based client. See [Plasma methods](/docs/plasma-methods). ## Stable Stable runs Stable's Cosmos SDK-based node with an EVM module. See [Stable methods](/docs/stable-methods). ## MegaETH MegaETH runs mega-reth, a Reth-based client. See [MegaETH methods](/docs/megaeth-methods). ## Monad Monad runs the official [Monad](https://github.com/category-labs/monad) client. See [Monad tooling](/docs/monad-tooling). ## Berachain Berachain runs [bera-reth](https://github.com/berachain/bera-reth) for execution and [BeaconKit](https://github.com/berachain/beacon-kit) for consensus. ## Ink Ink runs op-reth, the OP Stack execution client built on [Reth](https://github.com/paradigmxyz/reth), on [Global Nodes](/docs/global-elastic-node). See [Ink tooling](/docs/ink-tooling). ## X Layer X Layer runs [xlayer-reth](https://github.com/okx/xlayer-reth), OKX's OP Stack execution client built on [Reth](https://github.com/paradigmxyz/reth), on [Global Nodes](/docs/global-elastic-node). See [X Layer tooling](/docs/xlayer-tooling). ## World Chain World Chain runs [world-chain](https://github.com/worldcoin/world-chain), a Reth-based OP Stack client. ## Hyperliquid HyperEVM JSON-RPC is served by nanoreth, a Reth-based client, on the `/evm` and `/nanoreth` paths. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration). ## Kaia Kaia runs the official [Kaia](https://github.com/kaiachain/kaia) client. `web3_clientVersion` reports it as `Klaytn`. ## Gnosis Chain * **Execution layer** — [Nethermind](https://github.com/NethermindEth/nethermind) (.NET implementation), [Erigon](https://github.com/erigontech/erigon), and [Reth](https://github.com/paradigmxyz/reth), depending on the node type. Gnosis Chain Mainnet Global Nodes run Erigon, and Chiado Testnet Global Nodes run Nethermind. [Trader Nodes](/docs/trader-node) run Nethermind in the full mode and Erigon in the archive mode. [Dedicated Nodes](/docs/dedicated-node) on Gnosis Chain Mainnet run Nethermind or Reth in both modes. See the JSON-RPC reference in the [Nethermind](https://docs.nethermind.io/), [Erigon](https://docs.erigon.tech/), and [Reth](https://reth.rs/jsonrpc/intro) documentation. * **Consensus layer** — [Lighthouse](https://github.com/sigp/lighthouse). ## Cronos [Cronos](https://github.com/crypto-org-chain/cronos) is a fully EVM-compatible chain. Connect via the [standard Ethereum JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/) or libraries like ethers.js, web3.js, and web3.py. ## Harmony The client is the [Harmony](https://github.com/harmony-one/harmony) Go implementation, interacted with using [Geth-style JSON-RPC methods](https://ethereum.org/en/developers/docs/apis/json-rpc/). ## Fantom and Sonic Both Fantom and Sonic run the **[Sonic](https://github.com/0xsoniclabs/sonic)** client (Sonic is the successor network to Fantom Opera). It is EVM-compatible and accessed via the [standard Ethereum JSON-RPC methods](https://ethereum.org/en/developers/docs/apis/json-rpc/). ## Ronin Ronin runs `conduit-op-reth`, Conduit's build of the OP Stack op-reth client, interacted with using the [standard Ethereum JSON-RPC methods](https://ethereum.org/en/developers/docs/apis/json-rpc/). On Ronin Saigon Testnet, early history is served by the legacy [Ronin](https://github.com/axieinfinity/ronin-archive) client, a Go Ethereum fork. ## Solana Solana runs **[Agave](https://github.com/anza-xyz/agave)** (the Anza validator client; reports as `solana-core` via `getVersion`). JSON-RPC methods are documented in the [Solana docs](https://solana.com/docs/rpc). For clients, use [`@solana/kit`](https://github.com/anza-xyz/kit) or the classic [`@solana/web3.js`](https://github.com/solana-labs/solana-web3.js) — both are maintained. ## TON TON runs the official [TON node](https://github.com/ton-blockchain/ton). Chainstack exposes the v2 and v3 APIs, and on Global Nodes also TON API v4, served by [ton-api-v4](https://github.com/ton-community/ton-api-v4) over the node's lite server — see [Choosing TON API v2, v3, or v4](/docs/ton-choosing-v2-or-v3). ## Sui Sui runs the official [Sui full node](https://github.com/MystenLabs/sui). Chainstack serves Sui over gRPC — see the [Sui gRPC endpoint](/docs/sui-grpc-endpoint). Sui Mainnet Global Nodes also serve GraphQL — see the [Sui GraphQL endpoint](/docs/sui-graphql-endpoint). ## Aptos [Aptos-core](https://github.com/aptos-labs/aptos-core) is the official Aptos client, accessed through its [REST API](https://aptos.dev/en/build/apis) and SDKs (TypeScript, Python, Rust). ## TRON [java-tron](https://github.com/tronprotocol/java-tron) is the client. See the [TRON API reference](https://developers.tron.network/reference/background). ## Polkadot Polkadot runs Parity's [Polkadot node](https://github.com/paritytech/polkadot-sdk), with [Substrate API Sidecar](/docs/polkadot-tooling#substrate-sidecar) for the REST interface. ## XRP Ledger XRP Ledger runs [xrpld](https://github.com/XRPLF/rippled) (formerly rippled), the reference XRP Ledger server. See [XRP Ledger tooling](/docs/xrpl-tooling). ## Stellar Stellar runs [Stellar RPC](https://github.com/stellar/stellar-rpc) (`stellar-rpc`) with a captive [Stellar Core](https://github.com/stellar/stellar-core). Horizon is not served. See [Stellar tooling](/docs/stellar-tooling). ## Bitcoin [Bitcoin Core](https://bitcoincore.org/) connects to the Bitcoin peer-to-peer network to download and validate blocks and transactions. See the available methods in the [Bitcoin RPC methods reference](/reference/bitcoin-rpc-methods-postman-collection). ## Zcash Zcash runs [Zebra](https://github.com/ZcashFoundation/zebra) (`zebrad`), the Zcash Foundation's Rust node, on [Global Nodes](/docs/global-elastic-node). Zebra serves a Bitcoin-style JSON-RPC API with Zcash additions for address indexes and shielded pool state. [Dedicated Nodes](/docs/dedicated-node) can also run [lightwalletd](https://github.com/zcash/lightwalletd) for light wallet clients. See [Zcash tooling](/docs/zcash-tooling). ## Dedicated Nodes on request Some protocols are available only as [Dedicated Nodes](/docs/dedicated-node) deployed on request — [contact us](https://chainstack.com/contact/) to set one up. Clients for the ones running on Chainstack today: * Cardano — [cardano-node](https://github.com/IntersectMBO/cardano-node), with [Ogmios](https://github.com/CardanoSolutions/ogmios) * Litecoin — [Litecoin Core](https://github.com/litecoin-project/litecoin) # Configurations Source: https://docs.chainstack.com/docs/protocols-configurations Reference for node configuration options across Chainstack-supported blockchain protocols, including node types, client settings, and network modes. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). > [card] [Global Node](/docs/global-elastic-node) > [card] [Unlimited Node](/docs/unlimited-node) > [card] [Trader Node](/docs/trader-node) > [card] [Dedicated Node](/docs/dedicated-node) > [!NOTE] > > ### Advanced options on paid plans > > Dedicated Nodes, archive nodes, debug & trace APIs are available on the [paid plans](https://chainstack.com/pricing/). # Modes and types Source: https://docs.chainstack.com/docs/protocols-modes-and-types Understand Chainstack node deployment modes including full, archive, Global Node, Dedicated Node, and Trader Node types with their capabilities and trade-offs. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). ## Types Chainstack supports 2 types of nodes for public chains: Global Node and Dedicated Node. Global Nodes are available for all networks by default. To get a Dedicated Node, you'll have to contact us directly. ## Modes For most of the available public chains, Chainstack supports deploying nodes in the following modes: * Full — a node that stores full blockchain data. However, it has limitations to how many blocks are available for querying. * Extended — a full node that keeps more history than a standard full node, but not a complete archive. See [Extended history](/docs/extended-nodes). * Archive — a node that stores full blockchain data and an archive of historical states, which makes it possible to query any block since the chain genesis. * Trader — a node that propagates transactions to the global mempool at high speed. See [Trader Nodes](/docs/warp-transactions). > [!NOTE] > > On smart-contract-enabled chains, a Full node keeps recent state; an Archive node adds all historical state from genesis. Bitcoin has no per-block historical state — chain state is the UTXO set, derived from the chain itself — so there is no separate Archive class. Chainstack Bitcoin nodes are non-pruned with `txindex` enabled: every block and every transaction from genesis is queryable. Full does not mean pruned. > > Historical address queries (transaction history, balance at a past block) need an indexer like [electrs](https://github.com/Blockstream/electrs) — available as a [Bitcoin Dedicated Node customization](/docs/features-availability-across-subscription-plans#node-customization). [Contact us](https://chainstack.com/contact/) to enable it. > > Archive semantics can also vary by protocol — for example, TRON archive mode serves the complete block and transaction history but not historical state, because java-tron has no state-at-block queries. See [TRON historical data availability](/docs/tron-tooling#historical-data-availability). > > Node mode describes data retention, not pricing. Requests are billed by request class — on some protocols every request bills as full even on archive-mode nodes. See [Request units](/docs/request-units). > > Subscribing to pending transactions works in either mode, and the archive mode does not enable the `txpool_content`, `txpool_contentFrom`, and `txpool_inspect` pool-inspection methods — the node type decides those. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). > [!NOTE] > > ### Advanced options on paid plans > > Dedicated Nodes, archive nodes, debug & trace APIs are available on [paid plans](https://chainstack.com/pricing/). # Networks Source: https://docs.chainstack.com/docs/protocols-networks Browse all supported blockchain networks on Chainstack including Ethereum, Solana, Bitcoin, Polygon, and 70+ mainnet and testnet protocol networks. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). > [!NOTE] > > On smart-contract-enabled chains, a Full node keeps recent state; an Archive node adds all historical state from genesis. Bitcoin has no per-block historical state — chain state is the UTXO set, derived from the chain itself — so there is no separate Archive class. Chainstack Bitcoin nodes are non-pruned with `txindex` enabled: every block and every transaction from genesis is queryable. Full does not mean pruned. > > Historical address queries (transaction history, balance at a past block) need an indexer like [electrs](https://github.com/Blockstream/electrs) — available as a [Bitcoin Dedicated Node customization](/docs/features-availability-across-subscription-plans#node-customization). [Contact us](https://chainstack.com/contact/) to enable it. > > Node mode describes data retention, not pricing. Requests are billed by request class — on some protocols every request bills as full even on archive-mode nodes. See [Request units](/docs/request-units). | Chain | Support | Global Node Mainnet Support | Global Node Testnet Support | Testnet networks | Debug and Trace | Deploy node | | - | - | - | - | - | - | - | | AB Chain | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | ADI Chain | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Apechain | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-apechain/#request) | | Aptos | Global Node, Dedicated | Full, Archive | Full | Testnet | No | [Deploy through platform](https://console.chainstack.com) | | Arbitrum | Global Node, Dedicated | Full, Archive | Full, Archive | Arbitrum Sepolia Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Arc | Global Node, Dedicated | Full, Archive | Full, Archive | Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Astar | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-astar/#request) | | Avalanche | Global Node, Dedicated | Full, Archive | Full, Archive | Fuji Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | B^2 | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-b2/#request) | | Base | Global Node, Dedicated | Full, Archive | Full, Archive | Sepolia Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Berachain | Global Node, Dedicated | Full, Archive | Full, Archive | Bepolia Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Bitcoin | Global Node, Dedicated | Full | Full | Testnet, Signet Testnet, Testnet4 | No | [Deploy through platform](https://console.chainstack.com) | | Bitlayer | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-bitlayer/#request) | | Bittensor | Dedicated | - | - | - | No | [Request deployment](https://chainstack.com/dedicated-nodes/) | | BNB Smart Chain | Global Node, Dedicated | Full, Archive | Full, Archive | Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Cardano | Dedicated | - | - | - | No | [Request deployment](https://chainstack.com/build-better-with-cardano/#request) | | Celestia | Dedicated | - | - | - | No | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Celo | Global Node, Dedicated | Full, Archive | - | - | Yes | [Deploy through platform](https://console.chainstack.com) | | Centrifuge | Dedicated | - | - | - | No | [Request deployment](https://chainstack.com/build-better-with-centrifuge/#request) | | Core | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-core/#request) | | Corn | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-corn/#request) | | Cronos | Global Node, Dedicated | Full, Archive | - | - | Yes | [Deploy through platform](https://console.chainstack.com) | | Cronos zkEVM | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Dogecoin | Dedicated | - | - | - | No | [Request deployment](https://chainstack.com/build-better-with-dogecoin/#request) | | dYdX | Dedicated | - | - | - | No | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Elysium | Global Node, Dedicated | - | Full, Archive | Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Etherlink | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-etherlink/#request) | | Ethereum | Global Node, Dedicated | Full, Archive | Full, Archive | Sepolia Testnet, Hoodi Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Everclear | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Fantom | Global Node, Dedicated | Full, Archive | - | - | Yes | [Deploy through platform](https://console.chainstack.com) | | Filecoin | Dedicated | - | - | - | No | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Fluent | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Fraxchain | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-fraxchain/#request) | | Gnosis Chain | Global Node, Dedicated | Full, Archive | Full | Chiado Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Harmony | Trader Node, Dedicated | - | - | - | No | [Deploy through platform](https://console.chainstack.com) | | Hashkey | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-hashkey/#request) | | Hedera | Dedicated | - | - | - | No | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Hemi | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Hyperliquid | Global Node, Dedicated | Full, Archive | Full, Archive | Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Ink | Global Node, Dedicated | Full, Archive | - | - | Yes | [Deploy through platform](https://console.chainstack.com) | | Jovay | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Kaia | Global Node, Dedicated | Full | Full | Kairos Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Katana | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-katana/#request) | | Kroma | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-kroma/#request) | | L3X | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Lens | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-lens/#request) | | Linea | Global Node, Dedicated | Full, Archive | - | - | Yes | [Deploy through platform](https://console.chainstack.com) | | Litecoin | Dedicated | - | - | - | No | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Mantle | Global Node, Dedicated | Full, Archive | - | - | Yes | [Deploy through platform](https://console.chainstack.com) | | MegaETH | Global Node, Dedicated | Archive | - | Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Merlin | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-merlin/#request) | | Metis | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-metis/#request) | | Mind | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-mind/#request) | | Mint | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-mint/#request) | | Monad (see note below) | Global Node, Dedicated | Full, Archive | Full | Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | NeoX | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | opBNB | Global Node | Full | - | - | Yes | - | | Optimism | Global Node, Dedicated | Full, Archive | Full, Archive | Optimism Sepolia Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Pharos | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Plasma | Global Node, Dedicated | Archive | Archive | Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Plume | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-plume/#request) | | Polkadot | Global Node, Dedicated | Full, Archive | - | - | No | [Deploy through platform](https://console.chainstack.com) | | Polygon | Global Node, Dedicated | Full, Archive | Full, Archive | Amoy Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Robinhood Chain | Global Node, Dedicated | Full, Archive | Full, Archive | Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Ronin | Global Node, Dedicated | Full, Archive | Full, Archive | Saigon Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Scroll | Global Node, Dedicated | Full, Archive | - | - | Yes | [Deploy through platform](https://console.chainstack.com) | | Shibarium | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-shibarium/#request) | | Solana | Global Node, Dedicated | Full, Archive | Full, Archive | Devnet | No | [Deploy through platform](https://console.chainstack.com) | | Soneium | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-soneium/#request) | | Sonic | Global Node, Dedicated | Full, Archive | Full, Archive | Blaze Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Stable | Global Node, Dedicated | Full, Archive | - | - | Yes | [Deploy through platform](https://console.chainstack.com) | | Starknet | Global Node, Dedicated | Full, Archive | Full, Archive | Sepolia Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Stellar | Global Node, Dedicated | Extended | Extended | Testnet | No | [Deploy through platform](https://console.chainstack.com) | | Sui (see note below) | Global Node, Dedicated | Extended | Extended | Testnet | No | [Deploy through platform](https://console.chainstack.com) | | Superseed | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | TAC | Dedicated | - | - | - | No | [Request deployment](https://chainstack.com/dedicated-nodes/) | | Taiko | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-taiko/#request) | | Tempo | Global Node, Dedicated | Full, Archive | Full, Archive | Moderato Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | TON | Global Node, Dedicated | Archive | Archive | Testnet | No | [Deploy through platform](https://console.chainstack.com) | | Treasure | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | TRON | Global Node, Dedicated | Full, Archive | Full | Nile Testnet | No | [Deploy through platform](https://console.chainstack.com) | | Unichain | Global Node, Dedicated | Full, Archive | Full, Archive | Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Vana | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | WEMIX | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-wemix/#request) | | World Chain | Global Node, Dedicated | Full, Archive | - | - | Yes | [Deploy through platform](https://console.chainstack.com) | | X Layer | Global Node, Dedicated | Full, Archive | Full, Archive | Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | XDC | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/dedicated-nodes/) | | XRP Ledger | Global Node, Dedicated | Extended | Extended | Testnet | No | [Deploy through platform](https://console.chainstack.com) | | Zcash | Global Node, Dedicated | Full | Full | Testnet | No | [Deploy through platform](https://console.chainstack.com) | | Zircuit | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-zircuit/#request) | | zkLink | Dedicated | - | - | - | Yes | [Request deployment](https://chainstack.com/build-better-with-zklink/#request) | | ZKsync Era | Global Node, Dedicated | Full, Archive | Full, Archive | zkSync Era Sepolia Testnet | Yes | [Deploy through platform](https://console.chainstack.com) | | Zora | Global Node, Dedicated | Full, Archive | - | - | Yes | [Deploy through platform](https://console.chainstack.com) | Monad Archive on Global Nodes serves the full history of blocks, transactions, receipts, and logs, and `callTracer` traces through `debug_traceTransaction` and `debug_traceBlockByNumber`. State reads are served only for a rolling window of recent blocks, several days long, bounded by the size of the node's state database: `eth_call`, `eth_estimateGas`, `eth_createAccessList`, `eth_getBalance`, `eth_getCode`, `eth_getStorageAt`, `eth_getTransactionCount`, `debug_traceCall`, and `prestateTracer` traces. A state read for an older block returns `Block requested not found`. See Monad's [Historical data](https://docs.monad.xyz/developer-essentials/historical-data). Sui Global Nodes are [Extended](/docs/extended-nodes) full nodes: they hold a window of checkpoints starting at `lowestAvailableCheckpoint`, not history from genesis. See [Checkpoint retention](/docs/sui-grpc-endpoint#checkpoint-retention). Every Sui request is billed as full. > [!NOTE] > > Chainstack also deploys bespoke Dedicated Nodes for chains not listed here. [Contact us](https://chainstack.com/contact/) to discuss your chain. # Protocols and tooling: Web3 libraries and frameworks Source: https://docs.chainstack.com/docs/protocols-tooling-introduction Overview of development tools compatible with Chainstack nodes, including Web3 libraries, CLI tools, IDEs, and framework integrations for each protocol. Each protocol's guide is designed to offer step-by-step instructions for each tool, including installation, configuration, and usage examples, such as how to get the balance of an address, query data using GraphQL, or deploy smart contracts using Truffle or Hardhat. It also provides details on how to connect to blockchain nodes using different protocols and endpoints, and how to secure these connections using keys or passwords. > [!NOTE] > > ### Run nodes on Chainstack > > [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required. You can sign up with your GitHub, X, Google, or Microsoft account. # Protocols Source: https://docs.chainstack.com/docs/protocols-tutorials These tutorials provide a basic introduction to the protocols supported by Chainstack, enabling you to instantly dive into the world of Web3 development. Check the guides on the left pane. # Quotas Source: https://docs.chainstack.com/docs/quotas View resource quotas for Chainstack subscription plans including maximum nodes, networks, API request limits, and concurrent WebSocket connection counts. ## Quotas and notifications Each Chainstack’s subscription plan has its own quota that represents the allocated resources and usage limits that users can spend within their chosen plan. In addition to the allocated quota, Chainstack also offers an extra usage setting. With it enabled, if you exceed your allocated quota, you can continue using Chainstack's services seamlessly, with any additional usage being automatically charged at the applicable rates. Visit Chainstack’s [pricing page](https://chainstack.com/pricing/) to get familiar with a quota for each plan and extra usage costs. See [Manage your billing](/docs/manage-your-billing#manage-the-extra-usage-setting) on how to enable or disable the extra usage setting. Quota spending can vary depending on the combination of services that you use. Service cost is reflected in [request units](/docs/request-units#what-an-ru-is). You will receive at least two email notifications: * On using up 80% of your plan's quota * On using up 100% ## Inactivity deactivation and notifications To optimize platform performance and maintain efficient resource allocation, unused resources are automatically managed according to specific inactivity periods. ### Nodes * Inactivity period: 30 days * Notification schedule: * First notice: Day 23 * Second notice: Day 28 * Final notice: Day 30 * Action: Automatic deletion after 30 days of inactivity ### Organizations * Inactivity criteria: No active assets within the organization * Suspension timeline: 60 days * Notification schedule: * First notice: Day 53 * Second notice: Day 58 * Final notice: Day 60 * Action: Organization suspension after 60 days # Reading EVM traces: callTracer and prestateTracer Source: https://docs.chainstack.com/docs/reading-evm-traces-with-calltracer-and-prestatetracer Read Geth's built-in callTracer and prestateTracer output — the call tree, reverts, gas, and pre/post state — with tested debug_traceTransaction examples on Chainstack. Geth ships two built-in tracers that answer the two questions you actually have about a transaction: **what did it call** (`callTracer`) and **what state did it read or change** (`prestateTracer`). Both run inside the node's EVM as it replays the transaction, and both are passed the same way — as a `tracer` field to `debug_traceTransaction`, `debug_traceCall`, `debug_traceBlockByNumber`, or `debug_traceBlockByHash`. This page explains how to read their output. For which chains expose debug and trace APIs and how to enable them, see [Debug and Trace APIs](/docs/debug-and-trace-apis). For writing your own tracer, see [Mastering custom JavaScript tracing](/docs/mastering-custom-javascript-tracing-for-ethereum-virtual-machine). ## Which tracer answers which question | You want to know… | Use | Output shape | | - | - | - | | What contracts a transaction called, in what order, and why it reverted | `callTracer` | A nested tree of call frames | | Where the gas went, per call | `callTracer` | `gasUsed` on each frame | | What accounts and storage slots a transaction reads (to replay or simulate it) | `prestateTracer` (default mode) | Account state *before* execution | | What state a transaction changed | `prestateTracer` with `diffMode: true` | `pre`/`post` of only what changed | Both tracers accept an optional `tracerConfig` object, shown per tracer below. ## callTracer: the call tree `callTracer` reconstructs the transaction as a tree of call frames — the top-level call plus every `CALL`, `DELEGATECALL`, `STATICCALL`, `CREATE`, `CREATE2`, and `SELFDESTRUCT` it made. ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -X POST \ -H 'Content-Type: application/json' \ -d '{ "id": 1, "jsonrpc": "2.0", "method": "debug_traceTransaction", "params": ["0xTRANSACTION_HASH", {"tracer": "callTracer"}] }' ``` The result is a single top-level frame whose `calls` array holds its subcalls, each of which may have its own `calls`. Here is a complete trace of a straightforward ERC-20 (USDT) transfer — one frame, no subcalls: ```json JSON theme={"system"} { "from": "0x18e296053cbdf986196903e889b7dca7a73882f6", "gas": "0x15f90", "gasUsed": "0xb41d", "to": "0xdac17f958d2ee523a2206206994597c13d831ec7", "input": "0xa9059cbb0000000000000000000000006e258cde1f8dd20e59f9d22be3ad6a1730a287ae0000000000000000000000000000000000000000000000000000000003e66254", "value": "0x0", "type": "CALL" } ``` Read each frame like this: * **`type`** — the opcode that created the frame. `DELEGATECALL` runs another contract's code in the caller's context (proxies), so a frame's `from`/`to` being equal usually means a proxy delegating to its implementation. * **`from` / `to`** — caller and callee. `to` is absent on `CREATE`/`CREATE2` frames (the address doesn't exist yet). * **`value`** — wei transferred with the call. * **`gas` / `gasUsed`** — gas supplied to the frame and gas actually consumed by it (both hex). Summing `gasUsed` down a branch shows where a transaction spent its gas. * **`input` / `output`** — calldata sent and data returned. The first 4 bytes of `input` are the function selector. * **`calls`** — the subcalls this frame made, in execution order. Absent if the frame made none. When a call makes subcalls, they nest under `calls`. This trace is an ETH transfer into a smart-contract wallet that `DELEGATECALL`s its implementation — note the top frame's empty `input` (`0x`, a plain value transfer), the `value` carried on both frames, and the `DELEGATECALL` type on the subcall: ```json JSON theme={"system"} { "from": "0xfb74767c1ce1aada0a0e114441173b57f8c1571b", "gas": "0x6ac1", "gasUsed": "0x6ac1", "to": "0x3391aade96c1b96e23ef7f910b58b5c708b96ce5", "input": "0x", "calls": [ { "from": "0x3391aade96c1b96e23ef7f910b58b5c708b96ce5", "gas": "0x5f2", "gasUsed": "0x5e0", "to": "0x41675c099f32341bf84bfc5382af534df5c7461a", "input": "0x", "value": "0xa32d2f312d12", "type": "DELEGATECALL" } ], "value": "0xa32d2f312d12", "type": "CALL" } ``` ### Reverts When a call reverts, its frame carries an `error` field, and — when the revert returned a reason string — a decoded `revertReason` plus the raw ABI-encoded `output`. This is a USDC transfer that reverts on insufficient balance; USDC is a proxy, so the revert propagates from the implementation (`DELEGATECALL`) up to the top frame: ```json JSON theme={"system"} { "from": "0x000000000000000000000000000000000000dead", "gas": "0x20c85580", "gasUsed": "0x8e21", "to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "input": "0xa9059cbb0000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002645524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63650000000000000000000000000000000000000000000000000000", "error": "execution reverted", "revertReason": "ERC20: transfer amount exceeds balance", "calls": [ { "from": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "gas": "0x2044c4ce", "gasUsed": "0x1cc1", "to": "0x43506849d7c04f9138d1a2050bbf3a0c054402dd", "input": "0xa9059cbb0000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002645524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63650000000000000000000000000000000000000000000000000000", "error": "execution reverted", "revertReason": "ERC20: transfer amount exceeds balance", "value": "0x0", "type": "DELEGATECALL" } ], "value": "0x0", "type": "CALL" } ``` The failing frame is the deepest one with an `error`; its parents show `execution reverted` too as the failure propagates up. This is the fastest way to find *which* internal call failed rather than just seeing the top-level transaction fail. (`revertReason` is only present when the contract reverts with a reason string — some contracts, like USDT, revert with the `INVALID` opcode instead, which shows as `"error": "invalid opcode: INVALID"` with no `revertReason`.) ### callTracer config Pass `tracerConfig` alongside the tracer: ```json JSON theme={"system"} {"tracer": "callTracer", "tracerConfig": {"onlyTopCall": true, "withLog": true}} ``` * **`onlyTopCall`** (default `false`) — when `true`, traces only the top-level call and skips all subcalls. Cheaper when you only need the entry call. * **`withLog`** (default `false`) — when `true`, adds a `logs` array (the events emitted) to each frame that emitted any. Useful for tying emitted events to the exact call that produced them. ## prestateTracer: the state a transaction touches `prestateTracer` has two modes, selected by `diffMode`. ### Default mode: what the transaction reads Called without config, `prestateTracer` returns every account the transaction touched, with its state **as it was before** the transaction ran — keyed by address: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -X POST \ -H 'Content-Type: application/json' \ -d '{ "id": 1, "jsonrpc": "2.0", "method": "debug_traceTransaction", "params": ["0xTRANSACTION_HASH", {"tracer": "prestateTracer"}] }' ``` This is a plain ETH transfer, so every account it touched is an externally owned account: ```json JSON theme={"system"} { "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97": { "balance": "0x67b6ea94a12c5602", "code": "0xef010063c0c19a282a1b52b07dd5a65b58948a07dae32b", "nonce": 5422022 }, "0xa9ac43f5b5e38155a288d1a01d2cbc4478e14573": { "balance": "0xe367b1b974b4f1d4f22", "nonce": 1095285 }, "0xe5198ab9cf8252a7756c2e8f370270506af8929c": { "balance": "0x0" } } ``` Each account carries only the fields the transaction actually accessed: `balance` (hex wei), `nonce` (a number), `code` (present for accounts that have code — the first account here carries a short [EIP-7702](/docs/ethereum-eip-7702-tutorial) delegation), and, for contract accounts, a `storage` map of the exact slots read. This is the state needed to replay the transaction off-chain — feed it to a local EVM or an `eth_call` [state override](/docs/debug-and-trace-apis) to reproduce execution without an archive node. ### diffMode: what the transaction changed With `tracerConfig: {"diffMode": true}`, the tracer returns two maps, `pre` and `post`, containing **only the accounts and fields that changed** — `pre` holds the old values, `post` the new ones: Here is the same ETH transfer from above, in `diffMode`: ```json JSON theme={"system"} { "post": { "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97": { "balance": "0x67b6f42159f17e02" }, "0xa9ac43f5b5e38155a288d1a01d2cbc4478e14573": { "balance": "0xe366cf3b3ec3709007a", "nonce": 1095286 }, "0xe5198ab9cf8252a7756c2e8f370270506af8929c": { "balance": "0xe27d87a346ee800" } }, "pre": { "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97": { "balance": "0x67b6ea94a12c5602", "code": "0xef010063c0c19a282a1b52b07dd5a65b58948a07dae32b", "nonce": 5422022 }, "0xa9ac43f5b5e38155a288d1a01d2cbc4478e14573": { "balance": "0xe367b1b974b4f1d4f22", "nonce": 1095285 }, "0xe5198ab9cf8252a7756c2e8f370270506af8929c": { "balance": "0x0" } } } ``` An account appears in `post` only for the fields that changed: the sender (`0xa9ac43…`) spent value and its `nonce` incremented; the recipient (`0xe5198a…`) and the fee recipient (`0x4838b1…`) only gained balance, so their `nonce` and `code` are omitted from `post`. An account created by the transaction appears only in `post`; one destroyed appears only in `pre`. This mode is the precise answer to "what did this transaction modify." When a transaction changes contract storage, the changed slots appear under `storage` in `post`. For example, an ERC-20 transfer's `post` includes the token contract with the two balance slots it rewrote: ```json JSON theme={"system"} "0xdac17f958d2ee523a2206206994597c13d831ec7": { "storage": { "0x091db9d9a9c6e118727ad07f6cd0cfe3b3277fe2b643c6b0eff07704f324c69c": "0x000000000000000000000000000000000000000000000000000062997b365c58", "0x442d4b34f47df890efc6cba33e36ec1bfd065cbc44cfe8d4fd12344487cfcb9b": "0x00000000000000000000000000000000000000000000000000000000049884ac" } } ``` ## Applying tracers to other methods The same `{tracer, tracerConfig}` object works across the debug namespace: * **[`debug_traceCall`](/reference/ethereum-tracecall)** — trace a hypothetical, unmined call (great for "why would this revert" before sending). Takes a call object and a block tag, then the tracer object. * **`debug_traceBlockByNumber`** / **`debug_traceBlockByHash`** — apply the tracer to every transaction in a block; the result is an array of traces. ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -X POST \ -H 'Content-Type: application/json' \ -d '{ "id": 1, "jsonrpc": "2.0", "method": "debug_traceBlockByNumber", "params": ["latest", {"tracer": "callTracer", "tracerConfig": {"onlyTopCall": true}}] }' ``` > [!NOTE] > > Tracing replays historical execution, so it needs the state at the traced block. Recent blocks work on a [Global Node](/docs/global-elastic-node); tracing older history needs the state retained by an archive node. See [Debug and Trace APIs](/docs/debug-and-trace-apis) for per-protocol availability and how to enable the namespaces. ## FAQ **What is the difference between callTracer and prestateTracer?** `callTracer` shows control flow — the tree of calls a transaction made, their gas, and any reverts. `prestateTracer` shows data — the account balances, nonces, code, and storage the transaction read (default mode) or changed (`diffMode`). Use `callTracer` to understand *what happened*, `prestateTracer` to understand *what state was involved*. **How do I find which internal call reverted?** Trace with `callTracer` and walk the tree to the deepest frame carrying an `error` field. If the contract returned a reason string, the frame also has a decoded `revertReason`. See [Reverts](#reverts). **How do I get the state needed to replay a transaction locally?** Trace it with `prestateTracer` in default mode. The result is every account and storage slot the transaction reads, in its pre-execution state — the exact input for a local EVM or an [`eth_call`](/reference/ethereum-ethcall) state override. See [Default mode](#default-mode-what-the-transaction-reads). **Why is my trace request timing out or returning an error?** Tracing is state-heavy. Confirm your node has the debug/trace namespaces enabled and retains the state for the block you're tracing — recent blocks on a Global Node, older history on an archive node. Availability and enablement are per-protocol; see [Debug and Trace APIs](/docs/debug-and-trace-apis). **Can I use these tracers on chains other than Ethereum?** Yes, on any Geth-based chain that exposes the `debug_*` namespace (Base, BNB Smart Chain, Optimism, Polygon, Arbitrum via `debug_*` post-Nitro, Hyperliquid HyperEVM, and more). The [Debug and Trace APIs](/docs/debug-and-trace-apis) page lists coverage and client differences. **Do I need a custom JavaScript tracer instead?** Only if the built-in tracers don't capture what you need. `callTracer` and `prestateTracer` cover most debugging and state-inspection needs; for bespoke aggregation, write a [custom JavaScript tracer](/docs/mastering-custom-javascript-tracing-for-ethereum-virtual-machine). # Request units (RUs) Source: https://docs.chainstack.com/docs/request-units How Chainstack bills full and archive requests across all supported protocols, with exact thresholds and method rules. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). ## What an RU is A request unit (RU) is the metric Chainstack uses to measure each request against our request-based services. Different requests consume different resources, so pricing is tied to RUs rather than raw call counts. What counts as a full or an archive request depends on your node type as well as the request itself — see [How your node type affects this](#how-your-node-type-affects-this). See current service pricing on the [pricing page](https://chainstack.com/pricing/). ## Cost per request | Request | Cost | | - | - | | Full | 1 RU | | Archive | 2 RUs | ## How your node type affects this Node type decides whether requests are classified one by one or billed at a fixed class. Global Nodes classify each request; Trader Nodes bill every request according to the node's mode; Dedicated Nodes and the Unlimited Node add-on do not meter requests. [Extended](/docs/extended-nodes) Global Nodes are billed exactly like Full nodes — the extra history carries no request-unit change. | Node type | Billing | | - | - | | [Global Node](/docs/global-elastic-node) | Each request is classified on its own. Full requests are 1 RU and archive requests are 2 RUs, by the per-protocol rules on this page. Debug and trace requests are 2 RUs. | | [Trader Node](/docs/trader-node), full mode | Every request is 1 RU, including debug and trace methods where the node offers them. Archive requests are not served: a state query for a block outside the node's retained window returns `-32000` with the message `missing trie node`. See [EVM node returns "Missing trie node"](/docs/evm-node-returns-missing-trie-node). | | [Trader Node](/docs/trader-node), archive mode | Every request is 2 RUs, including requests for the latest block, requests with no block parameter, and each WebSocket subscription notification. The per-protocol full-versus-archive rules do not apply. | | [Dedicated Node](/docs/dedicated-node) | Not metered per request. Billed monthly for the node's compute and storage. | | [Unlimited Node](/docs/unlimited-node) add-on | Not metered per request. A flat monthly fee tied to an RPS tier. | The archive Trader Node rule applies on every protocol that offers Trader Nodes in archive mode, including protocols with their own full-versus-archive boundaries such as Aptos. Which regions offer each mode is listed in [Available clouds, regions, and locations](/docs/nodes-clouds-regions-and-locations). ## Full vs archive by protocol For each protocol, here's what counts as a **full request (1 RU)** and what counts as an **archive request (2 RUs)**: | Protocols | Classification | | - | - | | Ethereum, Polygon, BNB Smart Chain, Arbitrum, Arc, Base, Optimism, Avalanche, Linea, Mantle, Berachain, Scroll, Zora, Sonic, Unichain, Ink, Monad, Hyperliquid (HyperEVM), Plasma, Kaia, Cronos, Gnosis Chain, Celo, MegaETH, Ronin, Fantom, zkSync Era, Tempo, Robinhood Chain, World Chain, Stable, Elysium, X Layer | Less than 127 blocks behind the tip is **full**; 127 or more blocks behind is **archive** | | Solana | **Archive** billing applies to specific methods when fetching historical slots. See [Solana method scope](#solana-method-scope) below. | | TON | Most requests are **full**. `getTransactions`, `getTransactionsStd`, and the v4 account transaction routes are always **archive**; block/seqno methods are **archive** when the requested seqno is 128 or more behind the tip. See [TON method scope](#ton-method-scope). | | Aptos | Most requests are **full**. `/v1/accounts/{address}/transactions` is always **archive**; block-height methods are **archive** when the requested height is more than 128 behind the tip, and ledger-version methods when the requested version is more than 256 behind. See [Aptos method scope](#aptos-method-scope) below. | | Starknet | Most requests are **full**. Trace methods are always **2 RUs**; block-scoped methods are **archive** when the requested block is more than 128 behind the tip. See [Starknet method scope](#starknet-method-scope) below. | | Polkadot | Most requests are **full**. Sidecar trace endpoints are always **2 RUs**; `chain_getBlockHash` and block-scoped Sidecar endpoints are **archive** when the requested block is more than 128 behind the tip. See [Polkadot method scope](#polkadot-method-scope) below. | | Sui | No archive split — every gRPC and GraphQL request is billed as **full**. For the history window, see [Checkpoint retention](/docs/sui-grpc-endpoint#checkpoint-retention) | | Bitcoin, Zcash, TRON, opBNB, Harmony, XRP Ledger, Stellar | No archive split — every request is billed as full | ## Always 2 RUs On Global Nodes and archive Trader Nodes, these EVM methods are billed at 2 RUs regardless of block age. On a full Trader Node every request is 1 RU, including these methods. * All `debug_*`, `trace_*`, and `arbtrace_*` methods — including `debug_traceTransaction`, `debug_traceBlockByNumber`, and `trace_block`. * `eth_callMany` For per-protocol debug and trace capabilities, see [Debug and trace APIs](/docs/debug-and-trace-apis). ## EVM methods affected by block age The block-age rule in the [table above](#full-vs-archive-by-protocol) applies to these EVM methods: * `eth_call` * `eth_createAccessList` * `eth_estimateGas` * `eth_feeHistory` * `eth_getAccount` * `eth_getBalance` * `eth_getBlockByNumber` * `eth_getBlockReceipts` * `eth_getBlockTransactionCountByHash` * `eth_getBlockTransactionCountByNumber` * `eth_getCode` * `eth_getLogs` * `eth_getProof` * `eth_getStorageAt` * `eth_getTransactionByBlockNumberAndIndex` * `eth_getTransactionCount` * `eth_getUncleCountByBlockHash` * `eth_getUncleCountByBlockNumber` * `eth_newFilter` * `eth_simulateV1` For `eth_getLogs` and `eth_newFilter`, the block evaluated for billing is `fromBlock` if set, otherwise `toBlock` if set, otherwise `latest`. For example, `eth_getLogs({fromBlock: 1, toBlock: latest})` bills as archive because `fromBlock` is far below the tip — even though the range extends to `latest`. All other EVM methods are billed as full (1 RU) regardless of the block they target. ## Solana method scope Archive billing on Solana applies only to these methods: * `getTransaction` * `getBlock` * `getBlockTime` * `getBlocks` * `getBlocksWithLimit` * `getSignaturesForAddress` * `getFirstAvailableBlock` * `getSignatureStatuses` A request is archive (2 RUs) when its target slot is below `firstAvailable + 5,000`, where `firstAvailable` is the lowest slot our Solana full node still retains. Slots at or above this boundary are full (1 RU). [`getSignaturesForAddress`](/reference/solana-getsignaturesforaddress) and [`getFirstAvailableBlock`](/reference/solana-getfirstavailableblock) are always billed as archive, regardless of slot. All other [Solana methods](/docs/solana-methods) are billed as full (1 RU) regardless of slot. ### Two values shape the Solana archive boundary * **Ledger retention** — each Solana full node is configured with `--limit-ledger-size 400000000` (400 million shreds). [Shreds](https://github.com/solana-foundation/specs/blob/main/p2p/shred.md) are Solana's network-propagation units, and the number produced per slot varies with block density, so this caps disk usage rather than fixing the slot count retained. * **Safety buffer** — 5,000 slots immediately above the pruning floor. Requests targeting these slots are routed to the archive backend to avoid races where the full node prunes data mid-request. This sets the boundary that triggers archive billing. ### Putting it together A call's RU cost on Solana depends on the method and, for methods eligible for archive billing, the target slot. The ledger size (400M shreds) caps how far back the full node retains data; the 5,000-slot safety buffer above the pruning floor sets the actual billing boundary. For a sequence of calls: | Call | Why | Cost | | - | - | - | | `getBalance(account)` | Not eligible for archive billing | 1 RU | | `getTransaction(signature)` on a recent transaction | Eligible; target slot above the boundary | 1 RU | | `getTransaction(signature)` on a transaction from months ago | Eligible; target slot below the boundary | 2 RUs | | `getSignaturesForAddress(address)` | Always archive | 2 RUs | | `getBlock(slot=1)` | Eligible; target slot far below the boundary | 2 RUs | | **Total** | | **8 RUs** | See also [Solana archive methods availability](/docs/limits#solana-archive-methods-availability). ## TON method scope TON requests are billed as full (1 RU) by default. Archive billing (2 RUs) applies in two cases. **Always archive**, regardless of parameters: * `getTransactions` * `getTransactionsStd` * v4 `/account/{address}/tx/{lt}/{hash}` * v4 `/account/{address}/tx/parsed/{lt}/{hash}` These methods return an account's transaction history keyed by address (and logical time), not by block. The depth of the result cannot be determined before the request is served, so they are always classified as archive. **Archive only when targeting a historical block** — these methods take a block/seqno parameter and are billed as archive (2 RUs) when the requested seqno is 128 or more behind the chain tip, and full (1 RU) otherwise (recent block, or no block parameter): * v2: * `getBlockTransactions` * `getBlockTransactionsExt` * `getBlockHeader` * `lookupBlock` * `shards` / `getShards` * `getShardAccountCell` * `getShardBlockProof` * `getMasterchainBlockSignatures` * `getAddressState` * `getConfigParam` * `getConfigAll` * v3: * `transactions` * `transactionsByMasterchainBlock` * `blocks` * `traces` * `actions` * `masterchainBlockShards` * `masterchainBlockShardState` * v4 — the seqno is the path segment after `/block/`: * `/block/{seqno}` * `/block/{seqno}/config` and `/block/{seqno}/config/{ids}` * `/block/{seqno}/{address}` * `/block/{seqno}/{address}/lite` * `/block/{seqno}/{address}/changed/{lt}` * `/block/{seqno}/{address}/run/{method}`, with or without arguments The threshold is evaluated against the latest seqno of the workchain referenced by the request — the masterchain by default. v4 seqnos are masterchain seqnos and are always compared against the masterchain tip. All other TON methods — across v2 REST, v3 REST, the `/jsonRPC` envelope, and v4, including `/block/latest` and `/block/utime/{utime}` — are billed as full (1 RU). ## Aptos method scope Aptos requests are billed as full (1 RU) by default. Aptos sequences data two ways, and the archive threshold differs by which an endpoint takes: * **Ledger version** — a global counter that increments on every transaction, including the system transactions Aptos adds to each block. The threshold is **256** versions. * **Block height** — one increment per block. Because each block spans several versions, the threshold is **128** blocks — roughly the same depth in time as 256 versions. Archive billing (2 RUs) applies in three cases. **Always archive**, regardless of parameters: * `/v1/accounts/{address}/transactions` This endpoint returns an account's transaction history keyed by address, not by block or version. The depth of the result cannot be determined before the request is served, so it is always classified as archive. **Archive when targeting a historical block height** — billed as archive (2 RUs) when the requested height is more than 128 behind the chain tip, and full (1 RU) otherwise: * `/v1/blocks/by_height/{block_height}` **Archive when targeting a historical ledger version** — billed as archive (2 RUs) when the requested version is more than 256 behind the latest version, and full (1 RU) otherwise: * `/v1/blocks/by_version/{version}` * `/v1/transactions` — the `start` version * `/v1/transactions/by_version/{txn_version}` * `/v1/transactions/by_hash/{txn_hash}` — resolved to its ledger version * `/v1/accounts/{address}/resources` — the `ledger_version` parameter * `/v1/accounts/{address}/resource/{resource_type}` — the `ledger_version` parameter * `/v1/tables/{table_handle}/item` — the `ledger_version` parameter * `/v1/tables/{table_handle}/raw_item` — the `ledger_version` parameter * `/v1/view` — the `ledger_version` parameter A request that omits the block or version parameter, or targets one within the threshold, is billed as full (1 RU). All other Aptos endpoints are billed as full (1 RU). ## Starknet method scope Starknet requests are billed as full (1 RU) by default. Three cases are billed at 2 RUs. **Always 2 RUs** — these trace methods are billed at the trace rate (2 RUs) regardless of block age, the same as the EVM `debug_*`/`trace_*` rule: * `starknet_traceBlockTransactions` * `starknet_traceTransaction` **Archive when targeting a historical block** — these block-scoped methods are billed as archive (2 RUs) when the requested block is more than 128 behind the chain tip, and full (1 RU) otherwise (recent block, or a `latest`/`pending` block tag): * `starknet_getBlockWithTxHashes` * `starknet_getBlockWithTxs` * `starknet_getBlockWithReceipts` * `starknet_getStateUpdate` * `starknet_getBlockTransactionCount` * `starknet_getTransactionByBlockIdAndIndex` * `starknet_getClass` * `starknet_getNonce` * `starknet_getClassHashAt` * `starknet_getClassAt` * `starknet_call` * `starknet_getStorageAt` **Archive when the event range is historical** — `starknet_getEvents` is billed as archive (2 RUs) when its `from_block` or `to_block` is more than 128 behind the tip, and full (1 RU) otherwise. This parallels the `eth_getLogs` rule in the EVM section. All other [Starknet methods](/docs/starknet-methods) are billed as full (1 RU) regardless of the block they target. ## Polkadot method scope Polkadot is served over two transports — the Substrate node JSON-RPC and the [Sidecar](https://github.com/paritytech/substrate-api-sidecar) REST API. Both are billed as full (1 RU) by default. Three cases are billed at 2 RUs. **Always 2 RUs** — these Sidecar trace endpoints are billed at the trace rate (2 RUs) regardless of block age, the same as the EVM `debug_*`/`trace_*` rule: * `/experimental/blocks/head/traces` * `/experimental/blocks/{number}/traces` * `/experimental/blocks/{number}/traces/operations` * `/experimental/blocks/head/traces/operations` **Archive when targeting a historical block (JSON-RPC)** — billed as archive (2 RUs) when the requested block is more than 128 behind the chain tip, and full (1 RU) otherwise (recent block, or no block parameter): * `chain_getBlockHash` **Archive when targeting a historical block (Sidecar REST)** — a Sidecar request that reads a historical block is billed as archive (2 RUs) when that block is more than 128 behind the chain tip, and full (1 RU) otherwise (recent block, the chain head, or no block specified). The block is taken from the path on these endpoints: * `/blocks/{number}` * `/blocks/{number}/header` * `/blocks/{blockId}/extrinsics/{extrinsicIndex}` * `/blocks/{blockId}/extrinsics-raw` * `/blocks/{blockId}/para-inclusions` …or from the `at` query parameter on any other Sidecar endpoint. All other Polkadot methods and Sidecar endpoints — including requests that target the chain head or omit a block — are billed as full (1 RU). ## HTTP requests vs WebSocket subscriptions A "request" is anything the node returns to you over the wire. The transport changes how those individual returns are counted. ### HTTP Each JSON-RPC call counts as one request, regardless of how much data the response contains. * `eth_blockNumber` — 1 request. * `eth_getLogs` across 2,000 blocks returning 5,000 events — still 1 request. * `eth_call` simulating a complex contract — 1 request. The RU multiplier (1 RU for full, 2 RUs for archive) is applied on top. ### WebSocket subscriptions Subscriptions over WebSocket flip the counting model. Setting up the subscription counts as one request — and then each push notification the node sends to you counts as one more request. * `eth_subscribe newHeads` on Ethereum mainnet (≈one new block every 12 s) — \~7,200 requests/day. * `eth_subscribe logs` filtered to one contract emitting 5 events per block — \~36,000 requests/day. * `eth_subscribe newPendingTransactions` on a high-throughput chain — easily millions of requests/day. > [!NOTE] > > If you're running sustained subscriptions, consider an [Unlimited Node](/docs/unlimited-node) — it uses RPS-tiered flat-fee pricing instead of per-request billing, so subscription throughput stops mapping to RU cost. The same rule applies to legacy HTTP filter polling (`eth_newPendingTransactionFilter` + `eth_getFilterChanges`): each poll that returns events is one request per call, not per event — but the call cadence is yours to control, unlike a WebSocket push. ## Predicting whether an EVM call is full or archive For any EVM protocol in the [table above](#full-vs-archive-by-protocol): 1. Read the chain tip with `eth_blockNumber`. 2. Compute `tip − your_block`. 3. If the result is `127` or greater, the call is archive (2 RUs). Otherwise it's full (1 RU). Calls without a block parameter — `eth_blockNumber`, `eth_chainId`, `net_version` — are always full. # Robinhood Chain methods Source: https://docs.chainstack.com/docs/robinhood-methods Complete reference of Robinhood Chain JSON-RPC methods supported on Chainstack, with per-method availability for the eth, debug, net, web3, and arb namespaces. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Robinhood Chain API call examples](/reference/robinhood-getting-started). Robinhood Chain runs the Arbitrum Nitro client. The availability below applies to Robinhood Chain Mainnet and Testnet on Global Nodes. The default native tracers — `callTracer`, `prestateTracer`, `4byteTracer`, and `noopTracer` — and the struct/opcode logger are supported on [`debug_traceTransaction`](/reference/robinhood-debug-tracetransaction), [`debug_traceBlockByNumber`](/reference/robinhood-debug-traceblockbynumber), [`debug_traceBlockByHash`](/reference/robinhood-debug-traceblockbyhash), and [`debug_traceCall`](/reference/robinhood-debug-tracecall). `flatCallTracer`, `muxTracer`, and `erc7562Tracer` are not enabled — see [Available native tracers](/docs/limits#available-native-tracers). The Parity-style `arbtrace_*` and `trace_*` namespaces are not exposed — use the `debug_*` tracers instead. | Method | Availability | Comment | | - | - | - | | arb\_getRawBlockMetadata | | returns `null` | | arbtrace\_block | | Parity-style trace not exposed; use debug\_\* tracers | | arbtrace\_call | | | | arbtrace\_callMany | | | | arbtrace\_filter | | | | arbtrace\_get | | | | arbtrace\_replayBlockTransactions | | | | arbtrace\_replayTransaction | | | | arbtrace\_transaction | | | | debug\_accountRange | | on Mainnet, recent blocks only; older blocks return `-32000 missing trie node` | | debug\_chaindbProperty | | not available on Global Nodes | | debug\_dumpBlock | | not available on Global Nodes | | debug\_getAccessibleState | | not available on Global Nodes | | debug\_getBadBlocks | | | | debug\_getModifiedAccountsByHash | | on Mainnet, recent blocks only; older blocks return `-32000 missing trie node` | | debug\_getModifiedAccountsByNumber | | on Mainnet, recent blocks only; older blocks return `-32000 missing trie node` | | debug\_getRawBlock | | | | debug\_getRawHeader | | | | debug\_getRawReceipts | | | | debug\_getRawTransaction | | | | debug\_intermediateRoots | | | | debug\_preimage | | returns `-32000 unknown preimage` for keys the node has not recorded | | debug\_printBlock | | | | debug\_storageRangeAt | | returns `-32603 method handler crashed`; use eth\_getStorageAt | | debug\_traceBadBlock | | returns `-32000 bad block not found` when the chain has no bad blocks | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | eth\_accounts | | | | eth\_blobBaseFee | | returns `null` | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_createAccessList | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | use eth\_getBalance / eth\_getCode / eth\_getTransactionCount | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getHeaderByHash | | | | eth\_getHeaderByNumber | | | | eth\_getLogs | | | | eth\_getProof | | on Mainnet, about the latest 128 blocks — see [eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits) | | eth\_getRawTransactionByBlockHashAndIndex | | | | eth\_getRawTransactionByBlockNumberAndIndex | | | | eth\_getRawTransactionByHash | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionBySenderAndNonce | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleByBlockHashAndIndex | | | | eth\_getUncleByBlockNumberAndIndex | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_hashrate | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_protocolVersion | | | | eth\_sendRawTransaction | | | | eth\_sendRawTransactionSync | | | | eth\_simulateV1 | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | txpool\_content | | | | txpool\_inspect | | | | txpool\_status | | | | web3\_clientVersion | | | | web3\_sha3 | | | # Robinhood Chain tooling Source: https://docs.chainstack.com/docs/robinhood-tooling Robinhood Chain tooling guide for Chainstack nodes. Use MetaMask, ethers.js, viem, web3.py, Hardhat, Foundry, and Remix with Robinhood Chain mainnet and testnet. Robinhood Chain is an Arbitrum Orbit layer-2 with full EVM compatibility, so standard Ethereum tooling works against a Chainstack endpoint unchanged. Point any library or framework at your node URL and set the chain ID — `4663` for mainnet, `46630` for testnet. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet** to inject your Chainstack endpoint automatically. If you need to add the network manually, use: * Network name: `Robinhood Chain Mainnet` * RPC URL: your Chainstack HTTPS endpoint * Chain ID: `4663` * Currency symbol: `ETH` * Block explorer URL: `https://robinhoodchain.blockscout.com` For the testnet, use: * Network name: `Robinhood Chain Testnet` * RPC URL: your Chainstack HTTPS endpoint * Chain ID: `46630` * Currency symbol: `ETH` * Block explorer URL: `https://explorer.testnet.chain.robinhood.com` Refer to [node access details](/docs/manage-your-node#view-node-access-and-credentials) for your endpoint URL format and credentials. ## ethers.js Build dapps using [ethers.js](https://github.com/ethers-io/ethers.js/) v6 and Robinhood Chain nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket to get the latest block number. ```javascript HTTP theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); provider.getBlockNumber().then(console.log); ``` ```javascript WebSocket theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.WebSocketProvider("YOUR_CHAINSTACK_WSS_ENDPOINT"); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_CHAINSTACK\_WSS\_ENDPOINT — your node WSS endpoint ## viem Build dapps using [viem](https://github.com/wevm/viem) and Robinhood Chain nodes deployed with Chainstack. 1. Install [viem](https://www.npmjs.com/package/viem). 2. Create a public client over HTTP and get the latest block number. ```javascript Javascript theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blockNumber = await client.getBlockNumber(); console.log(blockNumber); ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## web3.py Build dapps using [web3.py](https://github.com/ethereum/web3.py) and Robinhood Chain nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP to get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider("https://%s:%s@%s" % ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — the username to your node if you use a password-protected endpoint * PASSWORD — the password to your node if you use a password-protected endpoint * HOSTNAME — the host name of your node ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Robinhood Chain nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Add the network to `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomicfoundation/hardhat-toolbox"); module.exports = { solidity: "0.8.24", networks: { robinhood: { url: "YOUR_CHAINSTACK_ENDPOINT", chainId: 4663, accounts: ["YOUR_PRIVATE_KEY"], }, }, }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network robinhood` and Hardhat will deploy through Chainstack. For the testnet, set `chainId: 46630`. ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password. ## Remix IDE To make Remix IDE interact with Robinhood Chain through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [MetaMask](/docs/robinhood-tooling#metamask). 2. In Remix IDE, navigate to the **Deploy & run transactions** tab. Select **Injected Provider - MetaMask** in **Environment**. This engages MetaMask and makes Remix IDE interact with Robinhood Chain through a Chainstack node. ## Benchmarks Independent latency and reliability measurements for Chainstack's Robinhood Chain RPC endpoints are available at [OpenChainBench: Robinhood Chain keyed RPC benchmark](https://openchainbench.com/benchmarks/keyed-rpc-robinhood). # Ronin: Consensus algorithm Source: https://docs.chainstack.com/docs/ronin-consensus-algorithm Understand Ronin's Delegated Proof of Stake consensus mechanism, validator selection, slashing conditions, and finality guarantees for Web3 game chains. Ronin on a mix of Proof of Authority (PoA) and Delegated Proof of Stake (DPoS) to gradually become more decentralized with more PoA and less of DPoS. ## Consensus mechanism ### Initial setup: Proof of Authority (PoA) Ronin initially employed PoA where transactions are verified by pre-selected validators, providing fast but centrally controlled validation. ### Transition to Delegated Proof of Stake (DPoS) To advance decentralization, Ronin has integrated DPoS, allowing token holders to delegate their stakes to validators, enhancing the democratic and decentralized validation process. #### DPoS features: * **Validator selection**: Validators are chosen through token holder votes, increasing their stakes and potential to be selected as validators. * **Validator roles**: Consists of Governing Validators (selected through PoA) and Standard Validators (chosen through DPoS by token stake). * **Block production**: Validators take turns producing blocks, maintaining elements of PoA for continuity. ### Validator dynamics * **22 Validator slots**: 12 are Governing Validators, and 10 are open to any qualifying Standard Validators. * **Daily updates**: Validator stakes are updated daily, with the top 10 by stake becoming Standard Validators. * **Delegation**: Token holders can delegate stakes to influence validator selection. ### Staking and rewards * **Token**: RON. * **Minimum stake**: 250,000 RON to become a Validator Candidate. * **Reward distribution**: Validators earn rewards from transaction fees and a portion of the staking rewards, distributed daily via smart contracts. ### Security measures * **Clone attack prevention**: Requires a supermajority to confirm transactions, minimizing the risk of block height duplication attacks. * **Slashing mechanisms**: Penalties for validators engaged in malicious activities, enhancing network integrity. ### Governance and operations * **Bridge operators**: Manage asset transfers between Ronin and other EVM-compatible chains, with specific rewards and penalties. * **Maintenance and unavailability**: Systems in place for maintenance modes and slashing for non-performance, ensuring operational continuity. ### Transition to Ethereum L2 Ronin is migrating from an independent sidechain to an Ethereum Layer 2 using Optimism's OP Stack with EigenDA for data availability and RON as the custom gas token. The Saigon testnet completed its L2 migration on February 5, 2026, at block 45,528,550. The testnet chain ID changed from `2021` to `202601`. The mainnet migration is planned for Q1–Q2 2026, with the mainnet chain ID remaining `2020`. For details on how the migration affects RPC queries on Chainstack nodes, see the [Ronin tooling](/docs/ronin-tooling) page. ### Final remarks Ronin's hybrid consensus approach, combining PoA and DPoS, aims to balance efficiency with increased decentralization, reflecting the evolving landscape of blockchain governance. # Ronin gaming: Overview of Axie & Pixels Source: https://docs.chainstack.com/docs/ronin-gaming-overview-of-axie-pixels Explore how Ronin powers Web3 gaming through Axie Infinity and the Pixels ecosystem, with on-chain game mechanics and player-owned digital assets. The two flagship games currently on Ronin are: Pixels and Axie. ## Ronin: A scalable blockchain for Web3 gaming Ronin is an Ethereum-based sidechain developed specifically to support the needs of blockchain games and decentralized applications. Designed to address the scalability and efficiency challenges often associated with Ethereum, Ronin offers a high-performance infrastructure that is tailor-made for the demands of the gaming industry. One of the flagship games on the Ronin's network is the Pixels game, a unique blockchain-based title that allows players to purchase and own individual pixels on a virtual canvas. Pixels leverages Ronin's Ethereum Virtual Machine (EVM) compatibility and efficient transaction processing to provide a seamless and cost-effective gameplay experience. ## What the Pixels game brings to the table The Pixels game is a testament to the capabilities of the Ronin blockchain. Employing a play-to-earn model, the game enables players to earn rewards in the form of the native PIXEL token by engaging in various in-game activities, such as exploring the virtual world, building their own environments, and participating in the thriving community. One of the key mechanics of Pixels is its use of non-fungible tokens (NFTs) to represent the individual pixels on the game's canvas. These Pixel NFTs can be bought, sold, and traded by players, creating a vibrant secondary market and empowering users with true digital ownership. The game's decentralized governance model, powered by a Decentralized Autonomous Organization (DAO), further reinforces the principles of Web3 by allowing the community to shape the game's development and future direction. The Pixels game has already made a significant impact on the broader blockchain gaming ecosystem. Its success has demonstrated the potential of Web3 gaming to create engaging and innovative user experiences, while also showcasing the capabilities of the Ronin blockchain. ## What Axie is and what Axie does Axie Infinity has emerged as the leading blockchain-based game, built on the custom-designed Ronin network. Developed by Sky Mavis, Axie Infinity is a play-to-earn game where players breed, battle, and trade digital pets called Axies, earning the game's native token, Smooth Love Potion (SLP), which can be exchanged for other cryptocurrencies or fiat currency. The success of Axie Infinity has been a driving force behind the adoption and growth of the Ronin blockchain. As of 2022, Axie Infinity boasts over 2 million active players, making it one of the top blockchain games by user activity and transaction volume on the Ronin network. The growth, performance, and the longevity of Axie Infinity on the Ronin blockchain have been impressive. At the start, In Q1 2022, the Ronin network saw over \$2.6 billion in total transaction volume, with more than 560,000 daily active wallets. And Axie has been running ever since. The success of Axie Infinity on the Ronin blockchain is a testament to the potential of blockchain technology in transforming the gaming industry. By providing scalability, cost-efficiency, and true player ownership of in-game assets, Axie Infinity and the Ronin network have paved the way for a new era of blockchain-powered gaming experiences. In terms of performance, the Ronin network has consistently delivered on its promise of scalability and efficiency. The blockchain's high transaction throughput and low gas fees have enabled the Pixels and the Axie games to handle the significant user demand without compromising the user experience. ## Ronin key features: 1. EVM compatibility: Ronin is fully EVM-compatible, allowing developers to easily build and deploy Solidity-based decentralized applications on the network. 2. High throughput: Ronin is designed to scale to handle millions of daily active users and transactions, processing over \$4 billion in NFT volumes to date. 3. Near-instant transactions: With fast block times and minimal fees, Ronin enables near-instant transactions, an essential requirement for real-time gaming applications. 4. Optimized for gaming: Ronin's architecture and design are tailored for the unique needs of Web3 games, providing a smooth, frictionless experience for both players and developers. 5. Decentralized governance: The Ronin network is governed by a Decentralized Autonomous Organization (DAO), allowing the community to participate in important decisions regarding the network's development and evolution. ## Conclusion As evidenced by the user base of Axie and Pixels, the Ronin blockchain ecosystem presents an exciting opportunity for developers to create innovative, blockchain-based games and applications. By understanding the technical features, developer resources, and practical examples, developers can make informed decisions about whether Ronin is the right fit for their project needs. The platform's growing user base, low-cost transactions, and cross-chain compatibility make it a promising option for Web3 developers looking to build the next generation of decentralized applications. # Ronin methods Source: https://docs.chainstack.com/docs/ronin-methods Complete Ronin RPC methods reference for Chainstack nodes. Access Ethereum-compatible JSON-RPC and debug methods with availability status on Ronin blockchain. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Ronin API call examples](/reference/getting-started-ronin). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | txpool\_content | | | | txpool\_inspect | | | | txpool\_contentFrom | | | | txpool\_status | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Ronin: on-chain meta racing game Source: https://docs.chainstack.com/docs/ronin-on-chain-meta-racing-game Build an on-chain meta racing game on Ronin with Chainstack. Learn block hash predictions, $RON staking, Foundry deployment, and Web3 gaming mechanics. **TLDR:** * Ronin is an EVM protocol tailored for Web3 gaming, here used to build a meta racing game called *Race the Ronin Chain*. * Players deposit \$RON, predict parts of block hashes, and win if their predictions match the actual chain blocks over a 50-block race. * Computation for determining winners is done off-chain for efficiency, with final results submitted on-chain by the contract owner. * The mechanics showcase a blend of on-chain transparency and off-chain checks, making for a fun yet cost-effective Web3 game experiment. ## Introduction Ronin is an EVM protocol purpose-built to serve the unique needs of the gaming industry. Developed by Sky Mavis, the creators of Axie Infinity, Ronin stands out for its ability to support online games, specifically in the realm of Web3 gaming. A purpose-built blockchain protocol needs a purpose built blockchain game — and that's what we created here. The game is called *Race the Ronin Chain*. Here is the [full game repo on Chainstacklabs](https://github.com/akegaviar/race-ronin-chain). > [!WARNING] > > ### NFP > > Not for production (NFP) obviously. Feel free to take the source, modify to your needs, and boost the Web3 gaming ecosystem. > > We assume no responsibility for the code. Moreover, this is a very rough unaudited contract. **TLDR of the game** It's a *meta* racing game because you run the game on the system you race on. The premise is very simple: * Anyone can create a race on the contract * A race is a range of 50 blocks * A number of players can enter the created race * When entering a race, the players commit a up to 50 of 3-character hash values or slots called *predictions* * The game mechanics is that if there's a match of one of the committed 3-character values with a 64-character block hash in the range of 50 blocks race range, the player advances one step forward * The player that advances the most by the end of the race (i.e. gets the most predictions right), wins * There's also other stuff like: when entering a race, each player pays the entrance fee of 1 $RON; the winner gets all of the players' $RON; and the treasury keeps 1% on each win Interested? Let's run through all the details. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a Ronin node. * [Foundry](https://getfoundry.sh/) to compile, test, and deploy the contract. * [web3.py](https://github.com/ethereum/web3.py) for the participation and the winner calculation & submission scripts. ## Quick start It's all in the repository: [race-ronin-chain](https://github.com/akegaviar/race-ronin-chain). > [!NOTE] > > ### Solidity 0.8.19 & PUSH0 > > Keep the Solidity compiler to 0.8.19 unless the Ronin chain supports PUSH0 when you are reading this. Otherwise you won't be able to deploy the contract. Assuming you have all the prerequisites satisfied. ### Generate the contract ABI The one in the repository will work, but if you modify the contract, by far the easiest way to generate the ABI is to run: ```shell Shell theme={"system"} forge build --silent && jq '.abi' ./out/RaceRoninChain.sol/RaceRoninChain.json > /root/race-ronin-chain/abi/RaceRoninChain.abi ``` ### Deploy the contract Ronin is not EIP-1559 compatible, so keep the transactions to legacy. ```shell Shell theme={"system"} forge create src/RaceRoninChain.sol:RaceRoninChain --private-key PRIVATE_KEY --constructor-args TREASURY_ADDRESS --legacy ``` where * PRIVATE\_KEY — your deployer private key. Important to remember that for the RaceRoninChain contract the message sender is the owner. This means that your will be submitting the race winner from this deployer account (see later in the article). * TREASURY\_ADDRESS — the house keeps 1% on each prize distribution, so this where the \$RON will go from every race in your house fees. ### Interact with the contract There are two scripts in the repository in the `python_scripts` directory: * `enter_race.py` — generates 50 random 3-character predictions for each of the players, starts a race, and enrols the four players in the race * `compute_stats_and_submit_winner.py` — does the winner calculation and submits the winner In each of the scripts, check the commented lines and make sure you provide all the necessary variables like keys, addresses, and Chainstack endpoints for Ronin, of course. ## Game mechanics & other important considerations There's quite a bit of nuance to this seemingly simple game, so let's do a run-down. Pretty sure there's a lot of uncharted territory too. The game runs over a course of 50 blocks on the Ronin chain. Players try predicting parts of block hashes that will appear during this span. Each Ronin block comes with a unique hash—a 64-character hexadecimal string. The crux of the game is in forecasting these hash segments correctly. To participate, players commit to the game by executing a transaction to the contract's `enterRace` function at a specified block number. This commitment not only signifies the start of a new race but also allows for a transparent view of the number of participants, the stakes involved in terms of total \$RON, and the kickoff block. When entering, players also submit their wager—the entry fee in \$RON with up to 50 three-character predictions of the block hashes that will appear over the race duration. As the race progresses, players' positions are determined by how accurately their predictions match the actual block hashes within the race's 50-block duration. A correct prediction, matching a segment of a block's hash, propels a player forward in the race. Conversely, incorrect guesses leave a player lagging. At the race's conclusion, the game tallies each participant's successful predictions against the block hashes that appeared during the race. The player whose predictions align most closely with the actual block hashes wins. In the event of a tie, the game honors the principle of "first come, first served"—the player who first submitted their predictions is declared the winner, rewarding promptness and deterring mimicry (but not front-running — more on that later). ### Off-chain computation & other considerations #### Off-chain compute As I'm sure you noticed, the off-chain winner computation & submission is the most glaring thing about this game, so let's explore it. Doing an on-chain computation is prohibitively expensive and is straight impossible at scale on any EVM network. This leaves us with the only option to move the winner compute to off-chain. Here's the current implementation: * the `compute_stats_and_submit_winner.py` script runs and checks whether the latest race is completed * if completed, the script retrieves the block hashes for all 50 blocks from the start block to the end block of the race * then the script calculates the winner based on the entered predictions and the actual block hashes * the script submits the winner from the contract owner (the deployer) * the contract does an on-chain verification of the submitted results and accepts or reverts the transaction. The verification is a check whether the submitted winning address & the full string of the submitted predictions are a part of the player and of this race id. In the future, it should be worth it moving the off-chain compute to something like [Space and Time's Verifiable Web3 Off-Chain Compute Layer](https://www.spaceandtime.io/blog/verifiable-off-chain-compute-for-smart-contracts) when it's fully available. For now, however, the bet is on keeping the compute component trusted (as opposed to trustless) in a way that if the players and the community detect foul play, they will abandon the project. #### Front-running & copy-trading While copy-trading has a bit of a protection through the "first come, first served" model (discussed earlier), the front-running would still be an issue. On the other hand, these and all the other unexplored mechanics are a part of what makes the game fun. #### High gas cost The cost of participation scales with the number of predictions made due to gas fees, which can significantly affect a player's strategy regarding the number of predictions to submit. For example, a 10 slot prediction will cost you ballpark 350k gas, while a full 50 slot prediction will cost you about 1,300k gas. Submitting more slots increases your chances to win the race, though. ## Conclusion Explore both the creator and user gaming ideas in the Ronin ecosystem and hopefully your project will end up in the [top Ronin games](https://chainplay.gg/chain/ronin/). There might be a lot of opportunity in that segment. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Ronin tooling Source: https://docs.chainstack.com/docs/ronin-tooling Complete Ronin tooling guide for Chainstack nodes. Use web3.py, ethers.js with HTTP and WebSocket connections to build Web3 games on Ronin blockchain. Get started with a [reliable Ronin RPC endpoint](https://chainstack.com/build-better-with-ronin/) to use the tools below. > [!WARNING] > > ### Saigon testnet L1 to L2 migration > > On February 5, 2026, the Ronin Saigon testnet migrated from L1 to an Ethereum L2 using the OP Stack at block 45,528,550. The chain ID changed from `2021` to `202601`. > > **Block range sharding on Chainstack Saigon testnet nodes:** > > * Blocks 0–45,528,549 are served by the old L1 backend (chain ID `2021`) > * Blocks 45,528,550 onward are served by the new L2 backend (chain ID `202601`) > > There is no block overlap between the two backends. Range-based methods like `eth_getLogs` cannot span both ranges in a single call—you must explicitly target either the L1 range or the L2 range. > > **Block explorers:** > > * Current (L2): [saigon-explorer.roninchain.com](https://saigon-explorer.roninchain.com/) > * Legacy (L1): [legacy-saigon-explorer.roninchain.com](https://legacy-saigon-explorer.roninchain.com/) > > The Ronin mainnet is also planned to migrate to an Ethereum L2 in Q1–Q2 2026. The mainnet chain ID will remain `2020`. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Ronin nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Ronin nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Ronin network ID: * Ronin Mainnet: `2020` * Saigon Testnet: `202601` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — Ronin network ID: * Ronin Mainnet: `2020` * Saigon Testnet: `202601` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). # Ronin: Make a game's smart contract Source: https://docs.chainstack.com/docs/ronin-tutorial-making-a-game-contract Deploy a blockchain game contract on Ronin Saigon testnet using Hardhat with Chainstack, featuring item management and player reward mechanics. **TLDR:** * Ronin is an EVM-compatible chain optimized for large-scale Web3 gaming, offering streamlined gameplay and robust security. * This tutorial walks you through deploying a Ronin node on the Saigon Testnet and creating a Hardhat-based smart contract for a simple blockchain game. * The contract accepts deposits, verifies if players can participate, and pays out winners while retaining funds when players lose—illustrating a basic on-chain gaming model. * You’ll learn to configure Hardhat, manage environment variables, and deploy your contract to Ronin for full transparency and fair gameplay. ## Introduction to Ronin Ronin, an Ethereum Virtual Machine (EVM) compatible blockchain, is purpose-built to serve the unique needs of the gaming industry. Developed by Sky Mavis, the creators of Axie Infinity, Ronin stands out for its ability to support large-scale online games, specifically in the realm of Web3 gaming. ### Key technical features of Ronin * **Optimized for gaming**: Ronin is designed to streamline the gaming experience by removing the complexities commonly found in other blockchains. This results in a platform that is efficient, with minimal spam and optimized uptime for games. * **Security enhancements**: in response to security challenges faced in the past, Ronin has undergone extensive security overhauls. These include rigorous internal security protocols, comprehensive code reviews, and architecture audits to ensure robust security measures are in place. ### Consensus mechanisms in Ronin * **Proof-of-authority (PoA)** — Ronin initially utilized the PoA consensus mechanism. In this system, a select group of validators, trusted for their expertise and reputation, were responsible for maintaining the network. This approach facilitated faster transaction speeds and lower fees due to its energy-efficient design. * **Transition to delegated-proof-of-stake (DPoS)** — Ronin integrated the DPoS consensus mechanism to advance decentralization. This allowed broader participation in the network's maintenance, where anyone holding enough RON tokens could become a validator. While retaining the benefits of PoA, such as efficiency and low costs, this shift markedly improved the blockchain's decentralization. > [!NOTE] > > Check out the [Ronin docs](https://docs.roninchain.com/basics/introduction) to learn more. Since Ronin is designed to develop games, today, we'll make a smart contract that can handle for games. In this tutorial, you will: * Deploy a Ronin node on the Saigon Testnet. * Create the game smart contract with Hardhat. * Deploy the smart contract with Hardhat ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Ronin RPC endpoint](https://chainstack.com/build-better-with-ronin/). * [node.js](https://nodejs.org/en/) as the JavaScript framework. * [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#overview) to create, deploy, and interact with contracts. ## Overview This tutorial guides you through the process of developing a smart contract for a blockchain-based game specifically tailored for deployment on the Ronin Saigon Testnet. We aim to develop a versatile and robust smart contract capable of managing the core game logic on the blockchain. The game itself is designed to be played on the client side, typically within a web browser. Players will have the ability to connect their Ronin wallet and deposit a specified fee to begin gameplay. The smart contract plays a pivotal role in the gaming experience: it securely handles the deposit and, depending on the game's outcome, executes the payout. To get from zero to a working game, do the following: 1. With Chainstack, create a public chain project. 2. With Chainstack, join the Ronin Saigon Testnet. 3. With Chainstack, access your Ronin node endpoint. 4. With Hardhat, create and set up the project. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Ronin Saigon testnet See [Join a public network](/docs/manage-your-networks). ### Get your Ronin node endpoint See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Fund your wallet Before diving into the game project, make sure to top up your wallet with testnet RON. * Install the [Ronin wallet](https://wallet.skymavis.com/). * Use the [Ronin faucet](https://faucet.roninchain.com/). ### Install Hardhat See [Installing Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#installation). ### Install dotenv Install the `dotenv` package to securely manage environment variables. ```shell Shell theme={"system"} npm i dotenv ``` ### Create a Hardhat project Create a new directory for your project, then run the following from a terminal: ```shell Shell theme={"system"} npx hardhat ``` This will launch the Hardhat CLI, prompting you to choose a starter project. For this project, answer **yes** to the following: * `Create a JavaScript project` * `Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)?` ### Edit the Hardhat configuration file You will find a file named `hardhat.config.js` in the root directory. This file configures various settings for your Hardhat projects, such as the network you want to deploy your contracts on, the compilers you want to use, and the plugins you want to enable. Delete the default code in the file and replace it with the following: ```javascript hardhat.config.js theme={"system"} require("@nomicfoundation/hardhat-toolbox"); require("dotenv").config(); const RONIN_SAIGON_CHAINSTACK = process.env.RONIN_SAIGON_CHAINSTACK; const PRIVATE_KEY = process.env.RONIN_PRIVATE_KEY; /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { networks: { saigon: { url: RONIN_SAIGON_CHAINSTACK, accounts: [PRIVATE_KEY], }, }, solidity: "0.8.23", }; ``` Let's break down what each part of the file does: * `require("@nomicfoundation/hardhat-toolbox");` imports the Hardhat Toolbox plugin, which provides several useful tools and utilities for Hardhat projects. * `require("dotenv").config();` loads environment variables from a `.env` file using the `dotenv` package. * `module.exports = { ... }` exports a JavaScript object containing the configuration for the Hardhat project. * `solidity: "0.8.23",` sets the Solidity compiler version to 0.8.23. * `networks: { ... }` defines the network configurations for the Hardhat project. In this case, it defines a network called `saigon` that connects to the Ronin Saigon blockchain network. * `saigon: { ... }` defines the configuration for the `saigon` network. * `url: RONIN_SAIGON_CHAINSTACK,` sets the URL for the Saigon network using the `RONIN_SAIGON_CHAINSTACK` environment variable. * `accounts: [PRIVATE_KEY],` sets the accounts for the `saigon` network using the `PRIVATE_KEY` environment variable. This will allow the Hardhat project to deploy contracts and interact with the Saigon network using the specified private key. ### Create the game-handling smart contract In the root directory, you will find a directory named `contracts` with a sample contract in it. Rename this contract to `Game.sol` and replace its code with the following: ```sol Game.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /// @title Game Contract /// @notice This contract allows players to deposit ETH to play a game and handles game results and payouts. contract Game { /// @notice Minimum deposit amount required to play the game uint256 public constant MINIMUM_DEPOSIT = 1 ether; /// @notice Maximum deposit amount allowed for the game uint256 public constant MAXIMUM_DEPOSIT = 2 ether; /// @notice Mapping to track user deposits mapping(address => uint256) public deposits; /// @notice Owner address address private owner; /// @notice Sets the contract deployer as the owner constructor() payable { owner = msg.sender; } /// @notice Ensures that only the owner can call the function modifier onlyOwner() { require(msg.sender == owner, "Only the owner can call this function"); _; } /// @dev Deposits ETH to the contract to play the game /// @notice Deposits must be between 1 and 2 Ethers /// @notice Contract must have enough funds to pay potential winnings function deposit() public payable { require(msg.value >= MINIMUM_DEPOSIT && msg.value <= MAXIMUM_DEPOSIT, "Deposit must be between 1 and 2 Ethers"); require(address(this).balance + msg.value >= MAXIMUM_DEPOSIT * 2, "Contract does not have enough funds to cover potential winnings"); deposits[msg.sender] += msg.value; } /// @notice Checks if the user has enough deposit to play /// @param user The address of the user to check /// @return True if the user has enough deposit, false otherwise function canPlay(address user) public view returns (bool) { return deposits[user] >= MINIMUM_DEPOSIT; } /// @notice Handles the game result and processes payouts or deposit retention /// @dev Only callable by the owner /// @param player The address of the player /// @param userWon Indicates whether the player won or not function gameResult(address player, bool userWon) public onlyOwner { require(canPlay(player), "Player did not deposit enough to play"); uint256 depositAmount = deposits[player]; if (userWon) { require(address(this).balance >= depositAmount * 2, "Contract does not have enough funds"); payable(player).transfer(depositAmount * 2); } else { // Keep the deposit in the contract if AI wins } deposits[player] = 0; } /// @notice Allows the owner to withdraw all funds from the contract /// @dev Only callable by the owner function withdraw() public onlyOwner { uint256 balance = address(this).balance; require(balance > 0, "No funds to withdraw"); payable(owner).transfer(balance); } // Additional functions can be added here } ``` > [!NOTE] > > ### Default logic > > Keep in mind that this smart contract is a proof of concept and it should not be used in production as is. A security audit is higly recomended. ## Understanding the smart contract This smart contract, designed for a blockchain-based game, operates on a simple yet effective mechanism. It allows players to deposit RON (or the native currency of the chain you are using) within a specified range—a minimum of 1 ether and a maximum of 2 ethers—to participate in the game. This range ensures fair play and manages the contract's ability to pay winnings. The contract tracks these deposits against each player's address, maintaining a balance reflecting their current game stake. Let's break down each element and function of the smart contract. ### Contract overview * **Contract name** — `Game` * **Purpose** — this contract allows players to deposit ETH to play a game, and it manages the game results and payouts. ### Constants 1. **MINIMUM\_DEPOSIT** * **Type** — `uint256` (unsigned integer) * **Purpose** — specifies the minimum amount of ether a player must deposit to play the game. * **Value** — 1 ether 2. **MAXIMUM\_DEPOSIT** * **Type** — `uint256` * **Purpose** — indicates the maximum amount of ether a player can deposit. * **Value** — 2 ethers ### State variables 1. **deposits** * **Type** — `mapping(address => uint256)` * **Purpose** — keeps track of the amount of ether each player (address) has deposited. 2. **owner** * **Type** — `address` * **Purpose** — stores the address of the contract owner, who has special privileges (like executing the `gameResult` and `withdraw` functions). ### Constructor * **Functionality** — sets the deployer of the contract as the `owner`. ### Modifiers 1. **onlyOwner** * **Purpose** — restricts the execution of certain functions to only the contract owner. ### Functions 1. **deposit** * **Access** — public * **Payment Type** — payable (can receive ether) * **Purpose** — allows players to deposit ETH within the allowed range (1 to 2 ethers). It also ensures the contract has enough funds to cover potential winnings. * **Logic** — updates the `deposits` mapping with the player's deposit amount. 2. **canPlay** * **Access** — public * **Purpose** — checks if a user has deposited enough ETH to play the game. * **Parameters** — `user` (address of the player) * **Returns** — `bool` (`True` if the player has enough deposit, `False` otherwise) 3. **gameResult** * **Access** — public, but restricted to `onlyOwner` * **Purpose** — processes the outcome of the game. It either pays out double the deposit to the player if they win or retains the deposit in the contract if they lose. * **Parameters**: * `player` — address of the player * `userWon` — boolean indicating whether the player won or not * **Logic** — if the player wins, it transfers double the deposit amount to them and resets their deposit to zero. If the player loses, just resets their deposit. 4. **withdraw** * **Access** — public, but restricted to `onlyOwner` * **Purpose** — allows the owner to withdraw all ETH stored in the contract. * **Logic** — transfers the entire contract balance to the owner's address. ### TL;DR * Players can participate in the game by depositing a certain amount of ether (between 1 and 2 ethers). * The contract ensures fairness and readiness for payouts before accepting deposits. * After the game ends, the result is communicated to the contract. Winners receive double their stake, while the stakes of those who lose remain with the contract. * Only the contract owner can process game results and withdraw funds from the contract, ensuring controlled and secure operations. ### Environment variables In the root directory of the Hardat project, create a `.env` file for your endpoint and private keys: ``` RONIN_SAIGON_CHAINSTACK="YOUR_CHAINSTACK_RONIN_ENDPOINT" RONIN_PRIVATE_KEY="YOUR_RONIN_WALLET_PRIVATE_KEY" ``` ### Create the deploying script In the `scripts` directory inside the root of your project, you will find a file named `deploy.js`. Replace its content with the following: ```javascript deploy.js theme={"system"} const hre = require("hardhat"); async function main() { console.log("Deploying contract..."); const GameContract = await hre.ethers.deployContract("Game"); // Deploy the contract. await GameContract.waitForDeployment(); console.log("Contract deployed to:", GameContract.target); const roninAddress = GameContract.target.substring(2); console.log( `Find the contract at https://saigon-app.roninchain.com/address/ronin:${roninAddress}` ); } // We recommend this pattern to be able to use async/await everywhere // and properly handle errors. main().catch((error) => { console.error(error); process.exitCode = 1; }); ``` This code is a script that deploys a `Game` smart contract. Here's a breakdown of what each part of the script does: 1. **Import Hardhat runtime environment (HRE)**: ```javascript Javascript theme={"system"} const hre = require("hardhat"); ``` This line imports the Hardhat runtime environment, which provides various utilities for working with Ethereum, such as deploying contracts. 2. **Main function**: ```javascript Javascript theme={"system"} async function main() { console.log("Deploying contract..."); const GameContract = await hre.ethers.deployContract("Game"); // Deploy the contract. await GameContract.waitForDeployment(); console.log("Contract deployed to:", GameContract.target); const roninAddress = GameContract.target.substring(2); console.log( `Find the contract at https://saigon-app.roninchain.com/address/ronin:${roninAddress}` ); } ``` The `main` function is an asynchronous function where the main logic of the script is executed. 3. **Starting deployment process**: ```javascript Javascript theme={"system"} console.log("Deploying contract..."); ``` This line prints a message to the console indicating that the deployment process is starting. 4. **Deploying the contract**: ```javascript Javascript theme={"system"} const GameContract = await hre.ethers.deployContract("Game"); ``` This line uses Hardhat's `ethers` plugin to deploy a contract named `Game`. The `await` keyword is used because `deployContract` is an asynchronous operation. 5. **Waiting for deployment completion**: ```javascript Javascript theme={"system"} await GameContract.waitForDeployment(); ``` This line waits for the contract deployment to be completed. It's important to wait for the deployment to finish before proceeding. 6. **Logging the deployed contract address**: ```javascript Javascript theme={"system"} console.log("Contract deployed to:", GameContract.target); ``` After the contract is successfully deployed, this line logs the address of the deployed contract to the console. 7. **Removing the `0x` prefix from the address**: ```javascript Javascript theme={"system"} const roninAddress = GameContract.target.substring(2); ``` This line removes the `0x` prefix from the Ethereum address using the `substring` method. As the Ronin explorer uses this format: `https://saigon-app.roninchain.com/address/ronin:49a1EA88e5F81850DE30Dc038c1d08028ecFc9b5`. 8. **Providing the contract address on Ronin explorer**: ```javascript Javascript theme={"system"} console.log( `Find the contract at https://saigon-app.roninchain.com/address/ronin:${roninAddress}` ); ``` This line constructs a URL to view the contract on the Ronin blockchain explorer and logs it to the console. It appends the modified address to the explorer's URL. 9. **Error handling**: ```javascript Javascript theme={"system"} main().catch((error) => { console.error(error); process.exitCode = 1; }); ``` This part of the script ensures that if any errors occur during the execution of the `main` function, they are caught and printed to the console, and the script exits with an error code. ### Deploy the smart contract To deploy the `Game` contract, run the following command in the terminal: ``` npx hardhat run --network saigon scripts/deploy.js ``` This will deploy the contract on Ronin Saigon Testnet displaying something similar to the following: ``` Deploying contract... Contract deployed to: 0x49a1EA88e5F81850DE30Dc038c1d08028ecFc9b5 Find the contract at https://saigon-app.roninchain.com/address/ronin:49a1EA88e5F81850DE30Dc038c1d08028ecFc9b5 ``` You can now find the contract on the [Saigon Explorer](https://saigon-app.roninchain.com/). You can also find the transactions from the Ronin wallet. ## Next steps Now you have a working smart contract deployed, the next step will be to build a front end with your game and wallet interaction. ## Conclusion In this comprehensive tutorial, we journeyed through the exciting world of blockchain-based game development on the Ronin blockchain, an EVM-compatible platform optimized for gaming. From setting up a node on the Ronin Saigon testnet to deploying a game-centric smart contract using Hardhat, we've laid down a robust foundation for blockchain game developers. The key takeaway from this tutorial is the seamless integration of blockchain technology into gaming. By deploying a smart contract on Ronin, we have created a system that enhances the gaming experience and ensures secure and fair gameplay. The ability to handle in-game financial transactions directly on the blockchain, including player deposits and payouts, showcases the power and versatility of smart contracts in gaming environments. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Requests per second (RPS) plan limits Source: https://docs.chainstack.com/docs/rps-plan-limits View requests-per-second rate limits for each Chainstack subscription plan to choose the right tier for your blockchain application traffic needs. ## Global plan RPS limits * Developer plan: 25 requests per second (RPS) * Growth plan: 250 requests per second (RPS) * Pro plan: 400 requests per second (RPS) * Business: 600 requests per second (RPS) * Enterprise: unlimited To upgrade a subscription plan, do the following: 1. In the left navigation bar, click **Billing**. 2. Next to **Plan**, click **Change**. 3. Select a new subscription plan and click **Next**. 4. Check the details of your new subscription plan and click **Confirm**. Your subscription plan changes immediately. > [!NOTE] > > ### Dedicated Nodes > > You can also order [Dedicated Nodes](/docs/dedicated-node) and pay for the node resource consumption only instead of per-request billing. > [!NOTE] > > See also [Throughput guidelines](/docs/limits) for additional protocol-specific limits and fine-tuning information. # RSS feeds Source: https://docs.chainstack.com/docs/rss-feeds Subscribe to Chainstack RSS feeds to get notified about platform updates, new protocol support, feature releases, and developer portal content changes. Stay up to date with Chainstack product updates and announcements by subscribing to our RSS feeds. ## Available RSS feeds ### Release notes RSS feed The [Chainstack release notes](/changelog) are available as an RSS feed, allowing you to track product updates, new features, and important announcements automatically. **RSS feed URL**: `https://docs.chainstack.com/changelog/rss.xml` ### Infastructure status RSS feed The [Chainstack infrastructure status](https://status.chainstack.com/) is available as an RSS feed. **RSS feed URL**: `https://status.chainstack.com/history.rss` ## How to subscribe to RSS feeds RSS feeds can integrate with various tools and services to notify you of product changes. Choose the integration method that works best for your workflow: ### Slack integration Add RSS feeds directly to your Slack workspace to receive updates in a dedicated channel. **1. Set up Slack RSS integration** Follow the [Slack RSS integration guide](https://slack.com/help/articles/218688467-Add-RSS-feeds-to-Slack) to add the Chainstack RSS feed to your workspace. **2. Configure notifications** Choose a channel for updates and customize notification preferences. ### Email notifications via Zapier Receive RSS updates directly in your inbox using Zapier's automation. **1. Create Zapier automation** Use [Zapier's RSS to Email integration](https://zapier.com/apps/email/integrations/rss/1441/send-new-rss-feed-entries-via-email) to set up email notifications. **2. Configure email settings** Set up filtering rules and email frequency based on your preferences. ### Discord integration Keep your Discord community informed with automatic RSS updates. **1. Choose a Discord bot** Select one of these popular Discord RSS bots: * [Readybot](https://readybot.io) — user-friendly RSS bot with rich formatting * [RSS Feeds to Discord Bot](https://rss.app/en/bots/rssfeeds-discord-bot) — simple and reliable RSS integration **2. Add RSS feed** Configure the bot with the Chainstack RSS feed URL and select the channel for updates. ### Other RSS readers You can use any standard RSS reader application or service to subscribe to Chainstack updates: * **Desktop applications** — Feedly, Inoreader, NewsBlur * **Browser extensions** — Feedbro, RSS Feed Reader * **Mobile apps** — Reeder, NetNewsWire, Feedly mobile Simply add the RSS feed URL to your preferred RSS reader to start receiving updates. ## Benefits of RSS subscriptions > [card] Real-time updates > > Get notified immediately when new features, updates, or important announcements are published. > [card] Centralized tracking > > Aggregate Chainstack updates with other important feeds in your preferred RSS reader. > [card] Team awareness > > Share updates with your entire team through Slack, Discord, or email integrations. > [card] No manual checking > > Eliminate the need to manually check for updates — let the RSS feed bring news to you. # Scroll methods Source: https://docs.chainstack.com/docs/scroll-methods Complete reference of supported Scroll JSON-RPC methods on Chainstack, including eth, debug, trace, and scroll namespace method availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | scroll\_estimateL1DataFee | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Scroll tooling Source: https://docs.chainstack.com/docs/scroll-tooling Set up MetaMask, Hardhat, Foundry, and development tools to build and deploy smart contracts on Scroll through your Chainstack RPC node endpoints. Get started with a [reliable Scroll RPC endpoint](https://chainstack.com/build-better-with-scroll/) to use the tools below. ## Geth Interact with your Scroll node using [Geth](https://geth.ethereum.org/docs/getting-started). 1. Install [Geth](https://github.com/ethereum/go-ethereum). 2. Use `geth attach` command with the node endpoint: ```bash theme={"system"} geth attach YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). 3. Invoke any methods from [Web3 JavaScript API](https://web3js.readthedocs.io/). Example below demonstrates how to get the balance of an address in wei value and convert it to ether value: ```js Javascript theme={"system"} > web3.fromWei(web3.eth.getBalance("0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae")) 642538.078574759898951277 ``` ## GraphQL You can use GraphQL on a Dedicated Node on the [paid plans](https://chainstack.com/pricing/). ### UI You can query data using the graphical interface. 1. On Chainstack, navigate to your dedicated Scroll node. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). 2. Hover over **GraphQL IDE URL** and click **Open**. 3. In the graphical interface that opens, run a GraphQL query. Example to get the latest block number: ```graphql GraphQL theme={"system"} { block { number } } ``` ### Node.js You can build a web app to query data using node.js and [axios](https://www.npmjs.com/package/axios): ```javascript Javascript theme={"system"} const axios = require('axios'); const main = async () => { try { const result = await axios.post( 'YOUR_CHAINSTACK_ENDPOINT', { query: ` { block { number } } ` } ); console.log(result.data); } catch(error) { console.error(error); } } main(); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node GraphQL endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * `query` — your GraphQL query. In this case, to get the latest block number. See also [Using GraphQL with EVM-compatible nodes](https://support.chainstack.com/hc/en-us/articles/4409604331161-Using-GraphQL-with-EVM-compatible-nodes). ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Scroll nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. For a detailed tutorial with Remix IDE, see [Trust fund account with Remix](/docs/ethereum-tutorial-trust-fund-account-with-remix). ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Scroll nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number: ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ## web3j Build DApps using [web3j](https://github.com/web3j/web3j) and Scroll nodes deployed with Chainstack. Use the `HttpService` object to connect to your node endpoint. Example to get the latest block number: ```java Java theme={"system"} package getLatestBlock; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.exceptions.ClientConnectionException; import org.web3j.protocol.http.HttpService; import okhttp3.Authenticator; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.Route; public final class App { private static final String USERNAME = "USERNAME"; private static final String PASSWORD = "PASSWORD"; private static final String ENDPOINT = "ENDPOINT"; public static void main(String[] args) { try { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credential = Credentials.basic(USERNAME, PASSWORD); return response.request().newBuilder().header("Authorization", credential).build(); } }); HttpService service = new HttpService(RPC_ENDPOINT, clientBuilder.build(), false); Web3j web3 = Web3j.build(service); EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false).send().getBlock(); System.out.println("Latest Block: #" + latestBlock.getNumber()); } catch (IOException | ClientConnectionException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } } ``` where * ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [the full code on GitHub](https://github.com/chainstack/web3j-getLatestBlock). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Scroll nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` ```javascript Password Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT', user: 'USERNAME', password: 'PASSWORD' }; var provider = new ethers.providers.JsonRpcProvider(urlInfo, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) * NETWORK\_ID — Scroll network ID: * Testnet: `534351` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.providers.WebSocketProvider('YOUR_CHAINSTACK_ENDPOINT', NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint endpoint protected either with the key or password * NETWORK\_ID — Scroll network ID: * Testnet: `534351` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Brownie 1. Install [Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html). 2. Use the `brownie networks add` command with the node endpoint. For example, Scroll mainnet: ```bash theme={"system"} brownie networks add Scroll ID name="NETWORK_NAME" host=YOUR_CHAINSTACK_ENDPOINT chainid=NETWORK_ID ``` where * ID — any name that you will use as the network tag to run a deployment. For example, `chainstack-mainnet`. * NETWORK\_NAME — any name that you want to identify the network by in the list of networks. For example, **Mainnet (Chainstack)**. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password * NETWORK\_ID — Scroll network ID: * Testnet: `534351` Example to run the deployment script: ```shell Shell theme={"system"} brownie run deploy.py --network chainstack-mainnet ``` ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract. * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Scroll: Deploy Uniswap V3 on Scroll Source: https://docs.chainstack.com/docs/scroll-tutorial-deploy-the-uniswap-v3-smart-contracts-on-scroll Deploy Uniswap V3 smart contracts on Scroll with Chainstack. Learn hierarchical ZKP optimization, factory deployment, and complete AMM suite setup tutorial. **TLDR:** * Shows how to deploy the entire Uniswap v3 suite onto Scroll Mainnet in a few steps * Guides through setting up the v3 deploy repository, configuring a Scroll RPC endpoint, and specifying WETH and admin settings * Explains core Uniswap v3 contracts (Router, Factory, NFT positions, migrator, etc.) and how they compose the AMM * Emphasizes the Scroll environment’s hierarchical ZKP optimizations, making this deployment straightforward and efficient ## What is Scroll? [Scroll](https://scroll.io/) network is a new scaling solution for Ethereum that uses zero-knowledge proofs to make transactions fast, secure, and cheap. It's fully compatible with the EVM (Ethereum Virtual Machine), so developers can easily port their DApps to Scroll without any changes. Chainstack supports Scroll Mainnet, which makes it even easier for developers to get started. In this tutorial, you’ll learn how to deploy the full Uniswap v3 AMM suite on Scroll in just a few easy steps. ## Why use Scroll? [Scroll's hierarchical ZKP](https://docs.scroll.io/en/technology/) system is optimized for both provers and verifiers, allowing it to achieve faster transaction finality and lower gas fees than other layer-2 scaling solutions. The first layer of Scroll's ZKP system is optimized for provers, using custom circuit optimization and hardware-friendly proving algorithms. This allows provers to generate proofs much faster, reducing the overall transaction latency. The second layer of Scroll's ZKP system is optimized for verifiers, using succinct proofs and verification algorithms compatible with the EVM. This allows verifiers to verify proofs much faster, reducing the overall transaction cost. As a result of its hierarchical ZKP system, Scroll is able to achieve much faster transaction finality and lower gas fees than other layer-2 scaling solutions. This makes it a great choice for applications that require high throughput and low latency, such as DeFi and NFT trading. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Scroll RPC endpoint](https://chainstack.com/build-better-with-scroll/) * [node.js](https://nodejs.org/en/) as the JavaScript framework * The [yarn](https://yarnpkg.com/) package manager * TypeScript V4.2.3 ## Overview This tutorial shows you how to deploy the Uniswap V3 smart contracts on Scroll Mainnet. To get from zero to a deployed instance of Uniswap V3, do the following: 1. With Chainstack, create a public chain project (A public chain project- a project to join public networks). 2. With Chainstack, join Scroll Mainnet. 3. With Chainstack, access your Scroll Mainnet node endpoint. 4. Fund your wallet with ETH on Scroll. 5. Deploy the Uniswap v3 suite. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join Scroll Mainnet See [Join a public network](/docs/manage-your-networks). ### Get your Scroll Mainnet node endpoint See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Fund your wallet Deploying on Scroll Mainnet spends real ETH. Fund your deployer wallet with ETH on Scroll by bridging from Ethereum Mainnet with the [Scroll Bridge](https://scroll.io/bridge), or by withdrawing to Scroll from an exchange that supports it. > [!WARNING] > > This deploys to mainnet and spends real funds. Budget roughly 0.5 ETH as a safe buffer for the full suite, and confirm the current Scroll gas price before you deploy. ### Deploy the Uniswap v3 suite In this step, we will deploy the full Uniswap v3 AMM suite on Scroll network. And we will leverage the [v3 deploy](https://github.com/Uniswap/deploy-v3) official repository to automate the process. The repository contains a CLI script for deploying the latest Uniswap v3 smart contracts to any EVM-compatible network. This repository collects almost all of the contracts that make up Uniswap v3 and provides scripts to deploy them easily in a single command. Uniswap is one of the most popular decentralized finance (DeFi) solutions in the Web3 space. Uniswap v3 is the third major version of Uniswap, and it allows users to swap ERC-20 tokens without a centralized intermediary. Since its launch in May 2021, Uniswap v3 has quickly become the most popular [DeX](/docs/web3-language-and-acronyms#dex) in the world, with over \$100 billion in [total value locked](/docs/smart-contracts-glossary#total-value-locked). #### Set up the CLI 1. Clone the repository: ```bash theme={"system"} git clone https://github.com/Uniswap/deploy-v3.git ``` 2. Make sure you install TypeScript V4.2.3: ```bash theme={"system"} yarn add typescript@4.2.3 --dev ``` 3. Move into the repository's directory: ```bash theme={"system"} cd deploy-v3 ``` 4. Install the dependencies and build: ```bash theme={"system"} yarn && yarn build ``` The console should have a similar output: ``` $ ncc build index.ts -o dist -m ncc: Version 0.36.1 ncc: Compiling file index.js into CJS ncc: Using typescript@4.2.3 (local user-provided) 1480kB dist/index.js 1480kB [3043ms] - ncc 0.36.1 $ cat shebang.txt dist/index.js > dist/index.cmd.js && mv dist/index.cmd.js dist/index.js ✨ Done in 3.76s. ``` #### Deploy the smart contracts For the next step, you will need the following: * The private key of the account deploying the smart contracts, starting with `0x` * The Chainstack Scroll Mainnet HTTPS endpoint * The address of the account that will manage the smart contracts In the console, run the following command, adding your private key, endpoint, and address: ```shell Shell theme={"system"} yarn start \ -pk YOUR_PRIVATE_KEY \ -j YOUR_CHAINSTACK_ENDPOINT \ -w9 0x5300000000000000000000000000000000000004 \ -ncl ETH -o YOUR_ADDRESS ``` Here is a list of what the flags mean: * `-pk` — your deployment account's private key. * `-j` — the JSON-RPC endpoint. * `-w9` — the address of the WETH9 contract. On Scroll Mainnet, WETH9 is deployed at `0x5300000000000000000000000000000000000004`. * `-ncl` — the native currency label. This is the symbol for the native token, which is `ETH`. * `-o` — the owner's address of the deployed administrator contract. Below is the full list of options: ```shell Shell theme={"system"} > npx @uniswap/deploy-v3 --help Usage: npx @uniswap/deploy-v3 [options] Options: -pk, --private-key Private key used to deploy all contracts -j, --json-rpc JSON RPC URL where the program should be deployed -w9, --weth9-address
Address of the WETH9 contract on this chain -ncl, --native-currency-label Native currency label, e.g. ETH -o, --owner-address
Contract address that will own the deployed artifacts after the script runs -s, --state Path to the JSON file containing the migrations state (optional) (default: "./state.json") -v2, --v2-core-factory-address
The V2 core factory address used in the swap router (optional) -g, --gas-price The gas price to pay in GWEI for each transaction (optional) -c, --confirmations How many confirmations to wait for after each transaction (optional) (default: "2") -V, --version output the version number -h, --help display help for command ``` Running this command will deploy the contracts one by one; the console will display each deployment like the following: ``` Step 1 complete [ { message: 'Contract UniswapV3Factory deployed', address: '0x0287f57A1a17a725428689dfD9E65ECA01d82510', hash: '0x9832d37fc03460326a4a17a77bf81ad0f7c4e75a4a5d495230bc49cb93f7d1d6' } ] Step 2 complete [ { message: 'UniswapV3Factory added a new fee tier 1 bps with tick spacing 1', hash: '0x8f9e1cf9fc09efbcad4e56885e041d1bbe9bae53432f798d53122fc3ceb9477b' } ] . . . ``` You can also find the details of the contracts in the `state.json` file. Congratulations, you deployed all of the Uniswap v3 smart contracts on Scroll Mainnet. > [!NOTE] > > Use the [block explorer](https://scrollscan.com/) to see the transactions. ## Understanding the smart contracts ### `UniswapV3Factory` [The UniswapV3Factory contract](https://docs.uniswap.org/protocol/reference/core/UniswapV3Factory) is the central hub for all Uniswap v3 pools. It allows users to create new pools, query information about existing pools, and add or remove liquidity from pools. The UniswapV3Factory contract is also responsible for managing the protocol fees for all Uniswap v3 pools. ### `UniswapInterfaceMulticall` [UniswapInterfaceMulticall](https://docs.uniswap.org/contracts/v3/reference/periphery/base/Multicall) is a helper contract allowing users to group multiple function calls into one call. This can be useful for reducing the number of transactions that need to be sent to the blockchain and ensuring that all calls are executed in the same block. ### `ProxyAdmin` and `TransparentUpgradeableProxy` A proxy gateway contract is a smart contract that allows for the easy upgrade of other smart contracts. This is essential for any project that wants to evolve over time without having to fork the blockchain. ### `TickLens` The [TickLens](https://docs.uniswap.org/protocol/reference/periphery/lens/TickLens) contract is a peripheral contract in the Uniswap v3 protocol. It provides functions for querying information about the ticks in a Uniswap v3 pool. The `TickLens` contract can be used to: * Find out how many ticks are in a pool. * Determine which ticks are initialized. * Identify which ticks have liquidity. ### `NonfungibleTokenPositionDescriptor` and `NonfungiblePositionManager` The [NonfungibleTokenPositionDescriptor](https://docs.uniswap.org/contracts/v3/reference/periphery/NonfungibleTokenPositionDescriptor) contract stores the ID of NFTs and their respective `NonFungiblePositionManger` contract address. The [NonfungiblePositionManager](https://docs.uniswap.org/contracts/v3/reference/periphery/NonfungiblePositionManager) manages the positions and liquidity for ERC-721 non-fungible tokens. ### `V3Migrator` The [V3Migrator](https://docs.uniswap.org/contracts/v3/reference/periphery/V3Migrator) contract is a tool that helps developers migrate their Uniswap v2 pools to Uniswap v3. ### `UniswapV3Staker` The [UniswapV3Staker](https://docs.uniswap.org/protocol/reference/periphery/staker/UniswapV3Staker) contract is a peripheral contract in the Uniswap v3 protocol. It allows users to stake their ERC-20 tokens in Uniswap v3 pools in order to earn rewards. The rewards are paid out as the pool's native token. The `UniswapV3Staker` contract is critical to the Uniswap v3 protocol because it incentivizes liquidity provision. By allowing users to earn rewards for staking their tokens, the `UniswapV3Staker` contract helps to ensure that there is always enough liquidity in Uniswap v3 pools to facilitate swaps. This makes the protocol more decentralized and efficient. ### `QuoterV2` and `SwapRouter02` The `Quoter` and `SwapRouter` contracts are essential for users who want to exchange tokens on Uniswap. [QuoterV2](https://docs.uniswap.org/protocol/reference/periphery/lens/QuoterV2) is a peripheral contract in the Uniswap v3 protocol. It allows users to get price quotes for swaps without executing the swap. `QuoterV2` uses the Uniswap v3 core, periphery contracts, and the `PoolTicksCounter` library. It includes functions to quote exact input or output amounts and handles reverts by counting initialized ticks crossed. [SwapRouter02](https://docs.uniswap.org/contracts/v3/reference/periphery/SwapRouter) executes swaps across different asset pools. It is a stateless contract, meaning it holds no token balances. This makes it more efficient and secure than other types of contracts. The `SwapRouter` contract considers the price of tokens, the liquidity in different pools, and the fees for swaps to calculate the best price for a swap. It then executes the swap, transferring tokens between pools and updating users' balances. ## Conclusion In conclusion, deploying Uniswap v3 on Scroll is a relatively straightforward process. Following the steps outlined in this tutorial, you can create a Uniswap v3 pool on Scroll in just a few minutes. Once your pool is deployed, you can start providing liquidity and earning fees. ### About the author > [card] Wuzhong Zhu > > ![Wuzhong Zhu](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1548860905064017921/xCKUgveS_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=075e8dc4db13d9384c63bf4904eb520d) > > Developer Advocate @ Chainstack > \ > BUIDLs on Ethereum, zkEVMs, and The Graph protocol > > [https://twitter.com/wuzhongzhu](https://twitter.com/wuzhongzhu) > > [https://www.linkedin.com/in/wuzhong-zhu-44563589/](https://www.linkedin.com/in/wuzhong-zhu-44563589/) > > [https://github.com/wuzhong-zhu](https://github.com/wuzhong-zhu) # Statistics Source: https://docs.chainstack.com/docs/see-statistics View your Chainstack organization's request unit consumption, protocol usage breakdown, and network traffic statistics on the Statistics dashboard page. The [Statistics](https://console.chainstack.com/statistics) page provides an organization-wide view of your request units (RUs) consumption across all your nodes. > [!NOTE] > > Data updates every few hours, not in real time. Today's figures are partial and continue to fill in through the day. > [!NOTE] > > For per-node metrics with granular request data, method calls, and response codes, see [View node requests metrics](/docs/manage-your-node#view-node-requests-metrics). For a per-node, per-day breakdown of compute hours, storage, and request units with CSV export, see [Node usage](/docs/node-usage). ## Access the Statistics page On Chainstack, click **Statistics** in the left sidebar. ## Node types The Statistics page organizes data by node type: * **Global Nodes** — usage statistics for your Global Nodes * **Dedicated Nodes** — usage statistics for your Dedicated Nodes Click the corresponding tab to view statistics for each node type. ## Filter your statistics Use the filters to narrow down the displayed data. ### Date filter The date filter lets you select a time period for viewing statistics: 1. Click the **Date Filter** dropdown. 2. Select the period type: * **Previous** — past completed periods * **Current** — the current period * **Next** — future periods (for projections) 3. Enter a number and select a time unit: **days**, **weeks**, **months**, **quarters**, or **years**. 4. Toggle **Include current** to include or exclude the current period. 5. Click **Update filter** to apply. > [!NOTE] > > The Statistics page displays cumulative totals for the selected period. For example, selecting "Past 1 day" shows the total RUs consumed during that day, not a breakdown of usage within the day. ### Protocol filter Filter statistics by specific blockchain protocols: 1. Click the **Protocol** dropdown. 2. Use the search box or scroll to find protocols. 3. Select one or more protocols, or click **Select all**. 4. Click **Update filter** to apply. ### Protocol network filter Filter statistics by specific networks within protocols: 1. Click the **Protocol network** dropdown. 2. Select the networks you want to view. 3. Click **Update filter** to apply. ## Statistics charts The Statistics page displays the following data visualizations: ### Usage by protocols and networks * **RUs by protocols** — a bar chart showing request units consumed per protocol * **RUs by protocol networks** — a breakdown of request units by specific networks within each protocol ### Usage by billing periods * **Number of RUs over billing periods** — shows your RU consumption across billing cycles ## Understanding request units Request units (RUs) measure the size and complexity of request processing. Different request types consume different amounts of RUs: * Full node requests: 1 RU * Archive node requests: 2 RUs For detailed RU pricing by protocol, see [Request units](/docs/request-units). # Advanced installation Source: https://docs.chainstack.com/docs/self-hosted/advanced-installation Customize your Chainstack Self-Hosted deployment with external PostgreSQL databases, custom Helm values, and a full cpctl flag reference. Advanced installation generates a values template with pre-filled passwords and RSA keys before deploying. Use this mode when you need to customize the deployment — for example, to connect an external PostgreSQL database or Temporal server, or to adjust resource limits. For a quick setup with auto-generated defaults, use the [basic installation](/docs/self-hosted/installation) instead. ## Advanced installation steps **1. Generate the values template** ```bash theme={"system"} cpctl install --advanced ``` This creates a `cp-values.yaml` file in your current directory with auto-generated passwords and keys. To specify a different output file, use the `-o` flag: ```bash theme={"system"} cpctl install --advanced -o my-values.yaml ``` > [!NOTE] > > By default, the installer uses the latest version. To pin a specific version, add `-v vX.Y.Z` — see [Release notes](/docs/self-hosted/release-notes) for available versions. **2. Edit the values file** Open the generated file and customize it for your environment. The default file name is `cp-values.yaml`, or the name you specified with `-o`: ```bash theme={"system"} nano cp-values.yaml ``` Common customizations: * External PostgreSQL — provide your database host, port, credentials, and disable the bundled PostgreSQL-HA * External Temporal — provide your Temporal server address and disable the bundled Temporal * Storage class — set a specific storage class for persistent volumes * Resource limits — adjust CPU and memory for Control Panel components **3. Install with the custom values** Run the installer with your edited values file (replace the file name if you used `-o`): ```bash theme={"system"} cpctl install -f cp-values.yaml -s STORAGE_CLASS_NAME ``` **4. Confirm and deploy** The installer shows a summary and asks you to confirm: ```text theme={"system"} ==> About to install Control Panel Version: v1.8.0 Namespace: control-panel Workload Namespace: control-panel-deployments Release: cp Timeout: 15m0s Do you want to proceed? [y/N]: y ``` Confirm with `y` to start the deployment. Generated credentials are saved the same way as with basic installation — see [Credentials storage](/docs/self-hosted/installation#credentials-storage). ## Installation options reference ### Install flags | Option | Description | Default | | - | - | - | | `-v, --version` | Chart version (required, format: `vX.Y.Z`) | — | | `-f, --values` | Path to custom values file | — | | `-s, --storage-class` | Storage class for persistent volumes | Cluster default | | `--workload-namespace` | Kubernetes namespace for blockchain node workloads | `-deployments` | | `--set key=value` | Set Helm values (repeatable) | — | | `--advanced` | Generate values template instead of installing | `false` | | `-o, --output` | Output file for `--advanced` mode | `cp-values.yaml` | | `--backend-url` | CP UI backend API URL | `http://-cp-deployments-api` | | `--disable-monitoring` | Skip monitoring stack installation | `false` | | `--reuse-vm-operator` | Use existing VictoriaMetrics operator; skip operator and CRD installation | `false` | | `--chart-registry` | OCI registry URL | Public CP registry | | `-y, --yes` | Auto-approve prompts (non-interactive) | `false` | | `--dry-run` | Print Helm command without executing | `false` | | `--timeout` | Installation timeout | `15m0s` | | `--poll-interval` | Status polling interval for progress display | `3s` | | `--config-dir` | Directory for credentials storage | `~/.config/cp-suite` | | `--clear-lines` | Clear lines for progress updates in non-TUI mode (requires TTY) | `true` | ### Global flags | Flag | Description | Default | | - | - | - | | `-n, --namespace` | Kubernetes namespace | `control-panel` | | `-r, --release` | Helm release name | `cp` | | `--kubeconfig` | Path to kubeconfig file | `~/.kube/config` | | `--no-color` | Disable colored output | `false` | | `--verbose` | Enable verbose output | `false` | ## Next steps 1. [First login](/docs/self-hosted/first-login) — First login and configuration 2. [Deploying nodes](/docs/self-hosted/deploying-nodes) — Deploy your first blockchain node # Chainstack Self-Hosted v1.0.0: January 28, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v1-0-0-january-28-2026 Chainstack Self-Hosted v1.0.0 initial beta release notes covering the first deployment of the self-hosted blockchain infrastructure management platform. **Status:** Beta This release includes the full deployment stack for Ethereum full nodes on your own Kubernetes infrastructure. ## Features * **Ethereum Mainnet support** — deploy full nodes using Reth execution client and Prysm consensus client * **Ethereum testnets support** — Sepolia and Hoodi testnets * **Web-based control panel** — intuitive interface for node deployment and management * **Kubernetes-native architecture** — Helm-based deployment on any Kubernetes distribution (k3s, k8s, EKS, GKE, AKS) ## Known limitations * Single protocol support (Ethereum only) * Full sync only (no snapshot-based deployment) * No built-in monitoring dashboards * No archive node support * No validator support # Chainstack Self-Hosted v1.4.6: April 6, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v1-4-6-april-6-2026 Chainstack Self-Hosted v1.4.6 release notes covering snapshot bootstrapping, integrated monitoring, cpctl improvements, and platform stability updates. This release builds on the v1.0.0 foundation with major additions across installation tooling, node operations, and observability. ## Snapshot-based node bootstrapping Nodes can now be deployed from a pre-downloaded chain snapshot rather than syncing from genesis. This significantly reduces time-to-live for new nodes on supported networks—from days to hours or minutes depending on chain and snapshot availability. ## Integrated monitoring stack The Control Panel now ships with an optional, fully integrated observability stack: * VictoriaMetrics — metrics storage * Grafana — dashboards * Chainstack blockchain-node-exporter — surfaces chain-specific health signals alongside standard Kubernetes infrastructure metrics The stack is enabled at install time and requires no additional configuration. ## cpctl improvements The `cpctl` CLI has been substantially extended: * **Interactive TUI shell** — `cpctl` now runs as a persistent interactive shell with streaming output, live status tables, progress bars, and health indicators. * **One-line install scripts** — available for Linux, macOS, and Windows. Multi-platform binaries (amd64/arm64) are published and installable with a single command. * **Shell completion** — `cpctl completion` generates completion scripts for bash, zsh, fish, and PowerShell. * **Upgrade command** — `cpctl upgrade` handles in-place Control Panel upgrades with a plan preview, configuration diff, values backup, and an upgrade journal for auditability. # Chainstack Self-Hosted v1.5.0: April 10, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v1-5-0-april-10-2026 Chainstack Self-Hosted v1.5.0 release with unified deployment progress, dark mode for Control Panel, smaller frontend bundle, and Reth v1.11.3 support. ## Unified deployment progress The node deployment progress display has been redesigned with a unified, flat structure. Bootstrap, configuration, and sync stages now flow into one timeline, giving you a single consistent view of what is happening during a deployment. Previously, status updates from different subsystems appeared separately and out of sequence. This is the first step in a broader effort to make node lifecycle visibility cleaner and more actionable. ## Dark mode The Control Panel now supports dark mode. Switch between light and dark themes from the interface settings. The dark theme applies across all pages and panels in the Control Panel. ## UI performance improvements The frontend vendor bundle has been reduced from roughly 1,900 KiB to around 57 KiB. This results in noticeably faster initial load times and lower bandwidth usage, especially on slower connections or remote deployments. ## Reth v1.11.3 Reth, the Ethereum execution client, is now supported at version 1.11.3. Upgrade existing Reth nodes to this version using the standard [one-click upgrade workflow](/docs/self-hosted/upgrade). ## Component versions | Component | Version | | - | - | | cp-ui | v2.2.0 | | cp-deployments-api | v0.30.0 | | cp-workflows | v1.11.0 | | cp-auth | v0.4.2 | | cp-bolt | v1.8.0 | # Chainstack Self-Hosted v1.6.0: April 10, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v1-6-0-april-10-2026 Named deployments let you assign a human-readable label to each node deployment, making multi-node installations easier to manage. ## Named deployments Node deployments now support a human-readable name. In installations running many nodes across different networks or environments, the name gives you a stable, recognizable label to distinguish deployments beyond their auto-generated identifiers. The name is stored with the deployment and surfaced through the API, and forms the groundwork for richer labeling and filtering in future releases. ## Component versions | Component | Version | | - | - | | cp-ui | v2.2.0 | | cp-deployments-api | v0.31.0 | | cp-workflows | v1.11.0 | | cp-auth | v0.4.2 | | cp-bolt | v1.8.0 | # Chainstack Self-Hosted v1.7.1: April 15, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v1-7-1-april-15-2026 Built-in telemetry management via a new cp-tracer component, plus reliability improvements to the upgrade workflow and node deployment operations. ## Telemetry management A new `cp-tracer` component is deployed alongside the Control Panel to collect and export operational telemetry from your installation. Telemetry export is opt-in and fully operator-controlled: * `cpctl telemetry enable` — enables telemetry export * `cpctl telemetry disable` — disables telemetry export * `cpctl telemetry status` — displays the current telemetry state * `cpctl telemetry send` — triggers an immediate export on demand The installation identifier is automatically propagated to the tracer at install and upgrade time, so each export is correctly attributed to your deployment. ## Upgrade reliability The `cpctl upgrade` command now validates input more strictly, catching invalid version strings and conflicting flags before the upgrade begins. Helm diff parsing has been refactored to decouple manifest evaluation from hook filtering, eliminating a class of false-positive diff results that could cause the upgrade to stall or misreport changes. Preflight checks are more resilient: * `--preflight-timeout` — configures how long preflight checks wait before timing out * `--ignore-preflight-errors` — allows the upgrade to continue past non-critical preflight errors ## Node deployment stability Volume population polling during node deployment no longer uses a hard-coded timeout. Previously, this timeout could expire before a slow volume snapshot finished populating, marking a deployment as failed when it would have succeeded given more time. The workflow now relies on the Temporal activity context for deadline management, which is configured at the workflow level and respects actual deployment conditions. ## Component versions | Component | Version | | - | - | | cp-ui | v2.2.0 | | cp-deployments-api | v0.31.3 | | cp-workflows | v1.13.1 | | cp-auth | v0.4.2 | | cp-bolt | v1.8.0 | | cp-tracer | v0.1.1 | # Chainstack Self-Hosted v1.8.0: May 4, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v1-8-0-may-4-2026 First non-beta release: new Ethereum evaluation presets, default telemetry, simpler installs and upgrades, Postgres resource limits, and UI refinements. **Chainstack Self-Hosted is now generally available.** With v1.8.0, the product exits beta and becomes the first public release. This version expands Ethereum coverage with new evaluation presets, makes telemetry on by default, and smooths out install, upgrade, and resource defaults. ## Ethereum evaluation presets Three new lightweight Ethereum presets are available in the deployment flow: Mainnet, Sepolia, and Hoodi. The deployment Control Panel now lists every preset configuration available for a given network instead of surfacing only one, so you can pick the client combination and footprint that matches your evaluation or production target without leaving the wizard. ## Simpler installs and upgrades `cpctl install` now defaults to the latest available chart version when you omit `-v`, so first-time installs no longer require looking up a version string. Error messages from the version resolver are clearer about what went wrong — empty tag lists, dev-only registries, and unsupported version jumps are now reported separately. Upgrades also run without the previous Helm hook step, which removes a class of upgrade-time failures on clusters with restrictive RBAC. ## Resource limit defaults The bundled Postgres component now ships with explicit CPU, memory, and storage limits instead of relying on cluster defaults. This makes capacity planning predictable on small or shared clusters and avoids the noisy-neighbor scenarios reported by early adopters running the control plane alongside other workloads. ## UI refinements The Control Panel now uses the shared UIKit side menu, aligning navigation with the rest of the Chainstack product surface. The Node Summary panel was repolished so resource readouts line up with the surrounding layout. ## Component versions | Component | Version | | - | - | | cp-ui | v3.0.0 | | cp-deployments-api | v0.32.0 | | cp-workflows | v1.13.1 | | cp-auth | v0.4.2 | | cp-bolt | v1.8.0 | | cp-tracer | v0.1.1 | # Chainstack Self-Hosted v1.9.0: May 11, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v1-9-0-may-11-2026 Failed deployments now recover via a new revision, revisions track the initiating user, cp-bolt clears stuck PVCs, and cpctl patches single containers. This release adds recovery paths for failed deployments, an audit trail for who changed which deployment revision, a self-healing storage layer for stuck PersistentVolumeClaims, and finer-grained container patching during upgrades. ## Recover from failed deployments Failed deployments are no longer terminal. Submitting a new revision against a deployment in the `failed` state transitions it back to `pending` and re-runs the deployment with the updated configuration. Operators can adjust the node spec and retry without manual database surgery or recreating the deployment from scratch. ## Revision audit trail Each deployment revision now records who initiated it. The revision payload and history view in the Control Panel expose the initiating user, giving teams a clear audit trail of who changed which node configuration and when. ## Self-healing stuck volumes cp-bolt now ships a reaper that detects PersistentVolumeClaims stuck on finalizers after pod deletion and clears them automatically. Stuck PVCs were a common cause of node restarts that never completed on clusters where the CSI driver crashed or was reinstalled mid-operation. The reaper closes that failure mode and exposes metrics so operators can see when it has had to intervene. ## Container-level upgrade patching `cpctl upgrade` gains an option to patch individual containers as part of the upgrade pipeline, instead of treating every release as an all-or-nothing chart rollout. This shortens the change footprint for targeted image bumps and reduces the blast radius of routine maintenance upgrades. ## Component versions | Component | Version | | - | - | | cp-ui | v3.0.0 | | cp-deployments-api | v0.34.0 | | cp-workflows | v1.14.0 | | cp-auth | v0.4.2 | | cp-bolt | v1.9.0 | | cp-tracer | v0.1.1 | # Chainstack Self-Hosted v2.0.0: May 13, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-0-0-may-13-2026 Helm is retired for a typed in-process provisioning pipeline, and bootstrap polling is bounded by an explicit presence state machine preventing worker hangs. This release retires Helm from the deployment path, replacing it with a typed in-process provisioning pipeline, and hardens the bootstrap loop against a class of hangs that could stall the worker. > [!WARNING] > > **Breaking change for existing deployments**. The provisioning pipeline switch from Helm to typed Server-Side Apply changes how resources are owned and reconciled. Nodes deployed on v1.9.0 or earlier may misbehave after the upgrade because their resources were created under Helm release ownership and are not tracked by the new label-scoped reconciler. > > Before upgrading to v2.0.0, remove existing node deployments from the Control Panel. After the upgrade completes, redeploy the nodes so they are created under the new pipeline. ## A new provisioning pipeline Node deployments no longer flow through Helm. The control plane now assembles each deployment from typed components, composes them into a typed Blueprint, and applies the result directly to Kubernetes as typed objects through Server-Side Apply. Manifests are checked at build time rather than at render time, reconciliation is per-resource with label-scoped pruning instead of release-level, and the provisioner now sits behind an environment-agnostic seam — a prerequisite for running the same deployment definitions outside Kubernetes in future releases. ## Bounded bootstrap polling Deployments that lost their storage volumes mid-bootstrap could previously leave the workflow polling indefinitely, and three hung workflows were enough to stop the worker from accepting new tasks. Bootstrap polling is now bounded by an explicit presence state machine: if the expected volumes never appear, or appear and then disappear, the workflow fails fast and frees the worker slot. Two new environment variables let operators tune the bounds for slower clusters: * `BOLT_INITIAL_PRESENCE_TIMEOUT` — how long the workflow waits for the expected volumes to first appear. Default `5m`. * `BOLT_DISAPPEARANCE_TIMEOUT` — how long the workflow tolerates volumes disappearing after they were observed. Default `1m`. ## Sharper reconciliation behavior Because Server-Side Apply now runs on typed configurations instead of rendered YAML, ownership and pruning are tracked per field and per resource rather than per Helm release. Drift between the desired Blueprint and the live cluster is corrected resource-by-resource, and resources that fall out of a Blueprint are pruned by label scope rather than by chart membership. In practice this means fewer surprising phantom objects left behind after a node spec changes, and clearer attribution of which controller owns which field on a shared object. ## Component versions | Component | Version | | - | - | | cp-ui | v3.0.0 | | cp-deployments-api | v0.34.2 | | cp-workflows | v2.0.0 | | cp-auth | v0.4.2 | | cp-bolt | v1.9.0 | | cp-tracer | v0.1.1 | # Chainstack Self-Hosted v2.10.0: July 27, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-10-0-july-27-2026 Arbitrum One and Arbitrum Sepolia are now available as deployment targets. This release adds Arbitrum One and Arbitrum Sepolia as deployment targets and changes how Ethereum reth nodes handle pruning. ## Arbitrum support Arbitrum One mainnet and Arbitrum Sepolia are now available as deployment targets, running the Arbitrum Nitro client. You can deploy and manage Arbitrum nodes from the Control Panel with the same workflows as other supported networks, including snapshot-accelerated initial sync. * **Arbitrum One** — mainnet, chain ID 42161 * **Arbitrum Sepolia** — testnet, chain ID 421614 Nitro deployments read their chain directory from the network preset, so snapshots packaged in the canonical Nitro layout restore and start correctly. ## Ethereum reth pruning mode Ethereum mainnet and Sepolia reth deployments now run in full mode rather than archive mode, matching the pruned snapshots they restore from. * **Affected presets** — Ethereum mainnet, Ethereum mainnet light, Sepolia, and Sepolia light * **Receipt history** — full mode retains roughly the most recent 10,000 blocks of receipts * **Hoodi** — unaffected, and continues to run in archive mode ## Component versions | Component | Version | | - | - | | cp-ui | v3.7.0 | | cp-deployments-api | v0.47.0 | | cp-workflows | v3.13.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | # Chainstack Self-Hosted v2.12.0: July 28, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-12-0-july-28-2026 Sui mainnet and Sui testnet are now available as deployment targets. This release adds Sui mainnet and Sui testnet as deployment targets, and lets you choose whether a deployment syncs from a snapshot or from genesis. ## Sui support Sui mainnet and Sui testnet are now available as deployment targets, running the official Sui full node. You can deploy and manage Sui nodes from the Control Panel with the same workflows as other supported networks, including snapshot-accelerated initial sync. * **Sui mainnet** — deployed against the mainnet genesis and peer set * **Sui testnet** — deployed against the testnet genesis and peer set * **Endpoints** — Sui serves gRPC and JSON-RPC multiplexed on a single port, and the node overview now shows the gRPC endpoint ## Snapshot or genesis You can now switch between deploying from a snapshot and syncing from genesis for each deployment. Clients that ship a snapshot default to it, and the deployment summary shows the choice. ## Component versions | Component | Version | | - | - | | cp-ui | v3.9.0 | | cp-deployments-api | v0.49.0 | | cp-workflows | v3.15.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | # Chainstack Self-Hosted v2.14.0: July 29, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-14-0-july-29-2026 Tempo Mainnet and Tempo Testnet are now available as deployment targets. This release adds Tempo Mainnet and Tempo Testnet as deployment targets, running as non-validator nodes in archive mode. ## Tempo support Tempo Mainnet and Tempo Testnet are now available as deployment targets, running as non-validator nodes in archive mode. * Tempo Mainnet — chain ID 4217, explorer at [explore.tempo.xyz](https://explore.tempo.xyz) * Tempo Testnet — chain ID 42431, explorer at [explore.testnet.tempo.xyz](https://explore.testnet.tempo.xyz) * Endpoints — HTTP and WebSocket JSON-RPC, with the `eth`, `net`, `web3`, `debug`, and `trace` namespaces enabled Testnet carries considerably more history than mainnet — provision about 1,100 GiB of storage for Testnet against 70 GiB for Mainnet. See [System requirements](/docs/self-hosted/requirements) for the full specification and [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the deployment catalog. ## Snapshot bootstrap Tempo deployments bootstrap from a snapshot provided by the client's own software. ## Component versions | Component | Version | | - | - | | cp-ui | v3.9.0 | | cp-deployments-api | v0.51.0 | | cp-workflows | v3.17.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | # Chainstack Self-Hosted v2.16.0: July 30, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-16-0-july-30-2026 Plasma Mainnet and Plasma Testnet are now available as deployment targets. This release adds Plasma Mainnet and Plasma Testnet as deployment targets, running Reth for execution alongside the PlasmaBFT consensus client as non-validator nodes in full mode. ## Plasma support Plasma Mainnet and Plasma Testnet are now available as deployment targets. Each deployment runs two components — Reth for execution and the PlasmaBFT consensus client — as non-validator nodes in full mode. * Plasma Mainnet — chain ID 9745, explorer at [plasmascan.to](https://plasmascan.to) * Plasma Testnet — chain ID 9746, explorer at [testnet.plasmascan.to](https://testnet.plasmascan.to) * Endpoints — HTTP and WebSocket JSON-RPC, with the `eth`, `net`, `web3`, `txpool`, and `debug` namespaces enabled Both networks sync from genesis over the network's public peers rather than restoring from a snapshot. Provision about 350 GiB of storage for Mainnet and 250 GiB for Testnet across the two components. See [System requirements](/docs/self-hosted/requirements) for the full specification and [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the deployment catalog. ## Component versions | Component | Version | | - | - | | cp-ui | v3.9.0 | | cp-deployments-api | v0.53.0 | | cp-workflows | v3.19.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | | blockchain-node-exporter | v1.3.0 | # Chainstack Self-Hosted v2.2.0: June 4, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-2-0-june-4-2026 Node configuration management with tracked revisions, zero-downtime disk growth, a rebuilt workflow-based orchestration engine, and built-in monitoring metrics. This release brings node configuration management into the Control Panel, makes disk growth a zero-downtime operation, and rebuilds the orchestration engine for more reliable, observable deployments. ## Node configuration and revisions Every node now has a configuration tab where you can review its current settings, change client and resource options, and apply those changes as a tracked revision. * **Configuration tab** — review a node's current settings and change client and resource options in one place. * **Tracked revisions** — each apply is recorded with its author and a diff of what changed, giving you a full history of how a node's configuration evolved over time. * **Live status** — revision status updates as a change rolls out, so you can watch progress instead of guessing. * **Up-front validation** — the Control Panel surfaces clear errors before a change is applied, for example when you attempt to shrink storage below its current size. ## More reliable deployment operations The engine that drives deployments, upgrades, and deletions has been rebuilt to run each operation as a first-class workflow. * **Automatic retries** — failed steps retry automatically rather than leaving an operation stuck. * **Clean cancellation** — in-flight operations can be cancelled cleanly. * **Progress reporting** — long-running actions report progress instead of going dark. The result is fewer stuck deployments and clearer visibility into what the platform is doing on your behalf. ## Built-in monitoring metrics The Control Panel now exposes its own metrics and ships scrapers for the components that matter to operators — PostgreSQL, the Control Panel services, Temporal, and the blockchain nodes themselves. This makes it easier to wire the platform into your existing observability stack without assembling per-component exporters by hand. ## Refreshed deployment experience The node list, node details tabs, and resource selectors have been rebuilt on the latest design system for a cleaner, more consistent interface. * **Full preset coverage** — the deployment flow now shows every available preset configuration for a network rather than just the first match. * **Kubernetes-standard units** — capacity is expressed in Kubernetes-standard units (Gi/Ti) throughout. ## Component versions | Component | Version | | - | - | | cp-ui | v3.4.2 | | cp-deployments-api | v0.36.0 | | cp-workflows | v3.0.0 | | cp-auth | v0.4.2 | | cp-bolt | v1.9.0 | # Chainstack Self-Hosted v2.21.0: August 4, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-21-0-august-4-2026 Avalanche, Arc, and World Chain are now available as deployment targets, and the bundled Grafana dashboards render correctly again. This release adds three protocols as deployment targets — Avalanche, Arc, and World Chain — and fixes the bundled Kubernetes Grafana dashboards. ## Avalanche support Avalanche Mainnet and Fuji Testnet are now available as deployment targets, running a single AvalancheGo node as a non-validator in full mode. * Avalanche Mainnet — chain ID 43114, explorer at [snowscan.xyz](https://snowscan.xyz) * Avalanche Fuji — chain ID 43113, explorer at [testnet.snowscan.xyz](https://testnet.snowscan.xyz) * Endpoints — one node serves the whole Primary Network, so the node overview lists four endpoints that share a single port and differ only by path: C-Chain HTTP and WebSocket JSON-RPC, X-Chain HTTP, and P-Chain HTTP Sync lag and peer count are reported for Avalanche nodes the same way as for any other EVM node. Mainnet restores from a snapshot, so provision about 400 GiB of storage; Fuji syncs from genesis over the network's public peers and needs about 300 GiB. ## Arc support Arc Testnet is now available as a deployment target. Each deployment runs two components — an execution client and a consensus client — as a non-validator node in full mode, following the network's public endpoints. * Arc Testnet — chain ID 5042002, explorer at [testnet.arcscan.app](https://testnet.arcscan.app) * Endpoints — HTTP and WebSocket JSON-RPC, with the `eth`, `net`, `web3`, `txpool`, `trace`, and `debug` namespaces enabled The deployment restores from a snapshot. Provision about 314 GiB of storage across the two components. ## World Chain support World Chain Mainnet and World Chain Sepolia are now available as OP Stack deployment targets, running a World Chain execution client alongside OP-Node as the rollup client. * World Chain Mainnet — chain ID 480, explorer at [worldscan.org](https://worldscan.org) * World Chain Sepolia — chain ID 4801, explorer at [worldchain-sepolia.explorer.alchemy.com](https://worldchain-sepolia.explorer.alchemy.com) * Endpoints — HTTP and WebSocket JSON-RPC on the execution client, plus the rollup HTTP RPC on OP-Node Like the other OP Stack deployments, both networks require an Ethereum Layer 1 RPC endpoint and a Beacon API endpoint that you supply. Both restore from a snapshot — provision about 1300 GiB of storage for Mainnet and 100 GiB for Sepolia. See [System requirements](/docs/self-hosted/requirements) for the full specification and [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the deployment catalog. ## Monitoring dashboards The bundled Kubernetes node and pod Grafana dashboards render correctly again, and kube-state-metrics is now scraped with the labels those dashboards expect. Panels that previously came up empty are populated. See [Monitoring](/docs/self-hosted/monitoring) for how to reach the dashboards. ## Component versions | Component | Version | | - | - | | cp-ui | v3.10.1 | | cp-deployments-api | v1.1.0 | | cp-workflows | v3.23.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | | blockchain-node-exporter | v1.4.0 | # Chainstack Self-Hosted v2.23.0: August 6, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-23-0-august-6-2026 XRP Ledger Mainnet and Testnet are now available as deployment targets. This release adds XRP Ledger as a deployment target, covering both Mainnet and Testnet. ## XRP Ledger support XRP Ledger Mainnet and Testnet are now available as deployment targets, each running a single xrpld node as a non-validator in full mode. * XRP Ledger Mainnet — explorer at [livenet.xrpl.org](https://livenet.xrpl.org) * XRP Ledger Testnet — explorer at [testnet.xrpl.org](https://testnet.xrpl.org) * Endpoints — HTTP JSON-RPC and the WebSocket API. The XRP Ledger subscription methods are available over WebSocket only, so both endpoints are exposed See [System requirements](/docs/self-hosted/requirements) for the full specification and [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the deployment catalog. ## Component versions | Component | Version | | - | - | | cp-ui | v3.10.1 | | cp-deployments-api | v1.3.0 | | cp-workflows | v3.24.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | | blockchain-node-exporter | v1.4.0 | # Chainstack Self-Hosted v2.27.0: August 10, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-27-0-august-10-2026 Sonic and Stable are now available as deployment targets, covering both Mainnet and Testnet on each protocol. This release adds Sonic and Stable as deployment targets, covering Mainnet and Testnet on both protocols. Node workloads also get a client-appropriate shutdown window. ## Sonic support Sonic Mainnet and Testnet are now available as deployment targets, each running a single sonicd node from Sonic Labs. * Sonic Mainnet — explorer at [sonicscan.org](https://sonicscan.org) * Sonic Testnet — explorer at [testnet.sonicscan.org](https://testnet.sonicscan.org) * Sync — Mainnet bootstraps from a pre-built chain snapshot, while Testnet starts from a pruned genesis export and syncs the remainder from the network ## Stable support Stable Mainnet and Testnet are now available as deployment targets, each running a single stabled node. * Stable Mainnet — explorer at [stablescan.xyz](https://stablescan.xyz) * Stable Testnet — explorer at [testnet.stablescan.xyz](https://testnet.stablescan.xyz) * Architecture — one stabled process carries both the CometBFT consensus engine and an embedded EVM, and the node exposes the EVM JSON-RPC while its CometBFT RPC and Cosmos gRPC stay internal * Consensus timing — tuned per network, so Mainnet and Testnet each run on their own vendor-recommended values * Sync — both networks bootstrap from a snapshot ## Graceful node shutdown Node workloads now shut down with a client-appropriate grace period instead of a fixed timeout. Clients that need time to flush state to disk get it, which reduces the chance of a corrupted database after a restart, a rescale, or a node upgrade. See [System requirements](/docs/self-hosted/requirements) for the full specification and [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the deployment catalog. ## Component versions | Component | Version | | - | - | | cp-ui | v3.10.1 | | cp-deployments-api | v1.7.0 | | cp-workflows | v3.27.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | | blockchain-node-exporter | v1.4.0 | # Chainstack Self-Hosted v2.29.0: August 11, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-29-0-august-11-2026 opBNB is now available as a deployment target, covering both Mainnet and Testnet. This release adds opBNB as a deployment target, covering both Mainnet and Testnet. Each deployment pairs an op-geth execution node with an opbnb-node consensus client. ## opBNB support opBNB Mainnet and Testnet are now available as deployment targets, each running a paired op-geth execution node and opbnb-node consensus client. * opBNB Mainnet — explorer at [opbnbscan.com](https://opbnbscan.com) * opBNB Testnet — explorer at [testnet.opbnbscan.com](https://testnet.opbnbscan.com) * Architecture — opBNB is an OP Stack chain settling to BNB Smart Chain, so op-geth serves the EVM JSON-RPC and WebSocket APIs while opbnb-node derives the chain from the layer 1 * Sync — both networks bootstrap from a pre-built chain snapshot, then follow the sequencer See [System requirements](/docs/self-hosted/requirements) for the full specification and [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the deployment catalog. ## Component versions | Component | Version | | - | - | | cp-ui | v3.10.1 | | cp-deployments-api | v1.9.0 | | cp-workflows | v3.28.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | | blockchain-node-exporter | v1.4.0 | # Chainstack Self-Hosted v2.3.0: June 22, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-3-0-june-22-2026 New Starknet, Bitcoin, and TRON presets, Base and Optimism on Reth, in-console protocol logs, and clearer node configuration. This release broadens protocol coverage and makes day-to-day node operations clearer in the Control Panel. ## More supported networks You can now deploy several new networks straight from the new-node flow. Each network appears in node selection with its own label and logo, so picking the right chain is a single click. * **Starknet mainnet** — available through a Pathfinder-based preset. * **Bitcoin** — available through a core `bitcoind` preset. * **TRON** — available through a java-tron preset family. ## Base and Optimism on Reth Base and Optimism deployments now ship as dedicated presets built on Reth execution clients paired with their respective consensus and node components. Base supports both archive and full (pruned) sync modes, letting you trade storage footprint against history depth depending on what your workloads need. ## Protocol logs in the Control Panel Node logs are now surfaced directly in the node list and node overview pages. You can check what a node is doing without dropping to `kubectl`, which speeds up diagnosis when a node is syncing, restarting, or behaving unexpectedly. ## Clearer node configuration The configuration page now hides settings that a preset doesn't expose, so you only see options that apply to the node you're running. * **Static environment variables** — workload definitions support static environment variables for cases where a client needs fixed configuration baked in. * **Corrected access URL labels** — node access URL labels have been corrected for consistency. ## Component versions | Component | Version | | - | - | | cp-ui | v3.4.5 | | cp-deployments-api | v0.37.0 | | cp-workflows | v3.3.1 | | cp-auth | v0.4.2 | | cp-bolt | v1.9.0 | # Chainstack Self-Hosted v2.32.0: August 12, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-32-0-august-12-2026 Berachain and Cronos are now available as deployment targets, covering Berachain Mainnet, Berachain Bepolia, and Cronos Testnet. This release adds two protocols as deployment targets. Berachain covers Mainnet and the Bepolia testnet, each pairing a Bera-Reth execution node with a Beacond consensus node, and Cronos covers Testnet with a single cronosd node. ## Berachain support Berachain Mainnet and Berachain Bepolia are now available as deployment targets, each running a paired Bera-Reth execution node and Beacond consensus node. * Berachain Mainnet — chain ID 80094, explorer at [berascan.com](https://berascan.com) * Berachain Bepolia — chain ID 80069, explorer at [testnet.berascan.com](https://testnet.berascan.com) * Architecture — Bera-Reth is a Reth-based execution client that serves the EVM JSON-RPC and WebSocket APIs, while Beacond, Berachain's BeaconKit consensus client, provides consensus over CometBFT * Mode — the node runs as a non-validator in full mode, with block building disabled on the consensus client * Sync — both networks bootstrap from a pre-built chain snapshot, then follow the network's peers ## Cronos support Cronos Testnet is now available as a deployment target, running a single cronosd node. * Cronos Testnet — chain ID 338, explorer at [explorer.cronos.org/testnet](https://explorer.cronos.org/testnet) * Architecture — Cronos is a Cosmos SDK chain with an embedded EVM, so one cronosd node carries both CometBFT consensus and the EVM JSON-RPC and WebSocket APIs * Storage — the node uses the RocksDB backend with versioned state enabled and default pruning * Sync — the network bootstraps from a pre-built chain snapshot, then follows the network's peers See [System requirements](/docs/self-hosted/requirements) for the full specification and [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the deployment catalog. ## Component versions | Component | Version | | - | - | | cp-ui | v3.10.1 | | cp-deployments-api | v1.12.0 | | cp-workflows | v3.30.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | | blockchain-node-exporter | v1.4.0 | # Chainstack Self-Hosted v2.33.0: August 12, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-33-0-august-12-2026 Cronos Mainnet is now available as a deployment target, completing Cronos coverage alongside Testnet. This release adds Cronos Mainnet as a deployment target, completing Cronos coverage after Testnet shipped in the previous release. ## Cronos Mainnet support Cronos Mainnet is now available as a deployment target, running a single cronosd node. * Cronos Mainnet — chain ID 25, explorer at [explorer.cronos.org](https://explorer.cronos.org) * Architecture — Cronos is a Cosmos SDK chain with an embedded EVM, so one cronosd node carries both CometBFT consensus and the EVM JSON-RPC and WebSocket APIs * Storage — the node uses the RocksDB backend with default pruning * Sync — the network bootstraps from a pre-built chain snapshot, then follows the network's peers See [System requirements](/docs/self-hosted/requirements) for the full specification and [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the deployment catalog. ## Component versions | Component | Version | | - | - | | cp-ui | v3.10.1 | | cp-deployments-api | v1.13.0 | | cp-workflows | v3.30.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | | blockchain-node-exporter | v1.4.0 | # Chainstack Self-Hosted v2.37.0: August 13, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-37-0-august-13-2026 Blast and Mantle are now available as deployment targets, each covering Mainnet and Sepolia with an execution and rollup node pair. This release adds Blast and Mantle as deployment targets, each covering Mainnet and Sepolia. Both are OP-Stack rollups, so every deployment pairs an execution node with a rollup node that derives the chain from Ethereum. ## Blast support Blast Mainnet and Blast Sepolia are now available as deployment targets, pairing a blast-geth execution node with a blast-node rollup node. * Blast Mainnet — chain ID 81457, explorer at [blastscan.io](https://blastscan.io) * Blast Sepolia — chain ID 168587773, explorer at [testnet.blastscan.io](https://testnet.blastscan.io) * Architecture — blast-geth serves the EVM JSON-RPC and WebSocket APIs, while blast-node derives the rollup from Ethereum and drives the execution node over the engine API * Storage — blast-geth uses the Pebble backend with path-based state and full sync * Sync — Mainnet bootstraps from a pre-built chain snapshot, and Sepolia syncs from genesis ## Mantle support Mantle Mainnet and Mantle Sepolia are now available as deployment targets, pairing a mantle-geth execution node with a mantle-node rollup node. * Mantle Mainnet — chain ID 5000, explorer at [mantlescan.xyz](https://mantlescan.xyz) * Mantle Sepolia — chain ID 5003, explorer at [sepolia.mantlescan.xyz](https://sepolia.mantlescan.xyz) * Architecture — mantle-geth serves the EVM JSON-RPC and WebSocket APIs, while mantle-node derives the rollup from Ethereum and drives the execution node over the engine API * Peering — Mantle publishes no execution-layer bootnodes, so payloads reach mantle-geth over the engine API and mantle-node connects to the network's static peers * Sync — both networks bootstrap from a pre-built chain snapshot See [System requirements](/docs/self-hosted/requirements) for the full specification and [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the deployment catalog. ## Component versions | Component | Version | | - | - | | cp-ui | v3.10.1 | | cp-deployments-api | v1.17.0 | | cp-workflows | v3.32.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | | blockchain-node-exporter | v1.4.0 | # Chainstack Self-Hosted v2.4.0: July 6, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-4-0-july-6-2026 Polygon support and node renaming. This release adds Polygon support, rounds out node management with renaming and has several small display fixes. ## Polygon support Polygon is now available as a deployment preset, running Bor and Heimdall together as a matched pair. It shows up in the new-node flow like any other supported network, so bringing up a Polygon node follows the same one-click experience as other chains. ## Node management * **Node renaming** — nodes can now be renamed after deployment instead of having to be recreated to fix a name. * **Cleaner protocol and network names** — network labels in the console no longer repeat the protocol name. * **Logo fixes** — logos now resolve correctly for protocols with multi-word names. ## Fixes * Fixed an issue where `cpctl` could report an unknown command in certain cases. ## Component versions | Component | Version | | - | - | | cp-ui | v3.4.6 | | cp-deployments-api | v0.38.0 | | cp-workflows | v3.4.0 | | cp-auth | v0.4.2 | | cp-bolt | v1.9.0 | # Chainstack Self-Hosted v2.43.0: August 18, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-43-0-august-18-2026 BNB Smart Chain, Kaia, Ronin Saigon, and Hyperliquid Testnet are now available as deployment targets, and the Control Panel can serve the monitoring dashboards. This release adds four protocols to the deployment catalog — BNB Smart Chain, Kaia, Ronin, and Hyperliquid — covering six networks in total. The Control Panel can now also serve the Grafana monitoring dashboards, so node metrics open in the same interface as the deployments they describe. ## BNB Smart Chain support BNB Smart Chain (BSC) Mainnet and Testnet are now available as deployment targets, running a single reth-bsc node. * BNB Smart Chain Mainnet — chain ID 56, explorer at [bscscan.com](https://bscscan.com) * BNB Smart Chain Testnet — chain ID 97, explorer at [testnet.bscscan.com](https://testnet.bscscan.com) * Architecture — a single reth-bsc node serves the EVM JSON-RPC and WebSocket APIs, exposing the eth, net, web3, txpool, and debug namespaces * Storage — the node runs in pruned mode, keeping recent state rather than the full history * Sync — both networks bootstrap from a pre-built chain snapshot ## Kaia support Kaia Mainnet and the Kairos testnet are now available as deployment targets, running a single kend node. * Kaia Mainnet — chain ID 8217, explorer at [kaiascan.io](https://kaiascan.io) * Kaia Kairos — chain ID 1001, explorer at [kairos.kaiascan.io](https://kairos.kaiascan.io) * Architecture — a single kend node serves both JSON-RPC and WebSocket, exposing Kaia's native klay namespace alongside the standard eth, net, web3, and debug namespaces * Storage — the node runs full sync with full garbage collection, so it retains the state needed to serve current-block queries * Sync — both networks sync from genesis ## Ronin Saigon support The Ronin Saigon testnet is now available as a deployment target, and is the first deployment to run an OP-Stack rollup on an alternative data-availability layer. * Ronin Saigon — chain ID 202601, explorer at [saigon-explorer.roninchain.com](https://saigon-explorer.roninchain.com) * Architecture — a conduit-reth execution node serves the EVM JSON-RPC and WebSocket APIs, an op-node rollup node derives the chain from Ethereum and drives the execution node over the engine API, and an EigenDA proxy retrieves the rollup's batch data * Data availability — Ronin Saigon posts batches to EigenDA instead of Ethereum calldata, so the rollup node reads generic commitments and resolves the underlying blobs through the proxy * Sync — the execution node bootstraps from a published state export, reaching the chain head without replaying history ## Hyperliquid Testnet support The Hyperliquid testnet is now available as a deployment target, running a single node. * Hyperliquid Testnet — chain ID 998, explorer at [app.hyperliquid-testnet.xyz/explorer](https://app.hyperliquid-testnet.xyz/explorer) * Architecture — a single node serves the EVM JSON-RPC API * Storage — the node prunes its logs on a rolling 48-hour window, keeping disk use flat over long runs ## Monitoring dashboards in the Control Panel When you install the monitoring stack, the Control Panel now proxies Grafana at `/monitoring/`, so node dashboards open in the same interface as the deployments they describe. Installations that skip monitoring are unaffected — the proxy route only renders when Grafana is deployed. See [System requirements](/docs/self-hosted/requirements) for the full specification and [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the deployment catalog. ## Component versions | Component | Version | | - | - | | cp-ui | v3.13.0 | | cp-deployments-api | v1.23.0 | | cp-workflows | v3.36.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | | blockchain-node-exporter | v1.4.0 | # Chainstack Self-Hosted v2.53.0: August 20, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-53-0-august-20-2026 Ten new networks join the deployment catalog, including Hyperliquid Mainnet, and snapshot bootstrapping now accepts pre-extracted archives. This release broadens protocol coverage and improves snapshot-based node bootstrapping. ## More supported networks You can now deploy ten new networks straight from the new-node flow: * Base Sepolia, Optimism Sepolia, Zora Sepolia, and Unichain Sepolia — OP-Stack rollup testnets * Polygon Amoy — Bor and Heimdall testnet * Starknet Sepolia — Pathfinder-based testnet * TRON Nile — java-tron testnet * Bitcoin Signet and Bitcoin Testnet4 — core bitcoind testnets * Hyperliquid Mainnet — completes mainnet coverage alongside the existing Hyperliquid Testnet See [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the full deployment catalog. ## Snapshot bootstrapping Snapshot-based bootstrapping now accepts an already-unpacked archive as a data source, so nodes that have unpacked snapshot data available can skip the extract step. ## Component versions | Component | Version | | - | - | | cp-ui | v3.13.0 | | cp-deployments-api | v1.33.0 | | cp-workflows | v3.41.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.13.0 | # Chainstack Self-Hosted v2.59.0: August 24, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-59-0-august-24-2026 Solana joins the deployment catalog on mainnet and devnet, alongside reliability fixes for OP-Stack and Ronin Saigon nodes. ## Solana support Solana is now available in the new-node flow, on both mainnet and devnet, running on the agave client. See [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the full deployment catalog. ## Reliability fixes * OP-Stack execution clients — transaction pool admission no longer depends on the sequencer HTTP endpoint being reachable * Ronin Saigon — the execution client now gets a trusted peer, removing a source of sync stalls ## Component versions | Component | Version | | - | - | | cp-ui | v3.13.0 | | cp-deployments-api | v1.39.0 | | cp-workflows | v3.43.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.13.0 | # Chainstack Self-Hosted v2.62.0: August 25, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-62-0-august-25-2026 Stellar joins the deployment catalog on mainnet and testnet. ## Stellar support Stellar is now available in the new-node flow, on both mainnet and testnet, running on the stellar-core and stellar-rpc clients. See [Supported clients and protocols](/docs/self-hosted/supported-clients-and-protocols) for the full deployment catalog. ## Component versions | Component | Version | | - | - | | cp-ui | v3.13.0 | | cp-deployments-api | v1.42.0 | | cp-workflows | v3.45.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.13.0 | # Chainstack Self-Hosted v2.63.0: August 31, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-63-0-august-31-2026 Updates blockchain client images across Sui, Arc, Polygon, Tempo, and Stellar deployments for protocol compatibility, upcoming hardfork readiness, and reliability. Updates blockchain client images across Sui, Arc, Polygon, Tempo, and Stellar deployments for protocol compatibility, upcoming hardfork readiness, and reliability. ## Improvements and fixes * Updates the Sui Mainnet and Sui Testnet node images to support the current on-chain protocol version. * Updates the Arc Testnet execution and consensus node images ahead of the Zero8 hardfork on September 3, 2026. * Updates the Polygon (Mainnet and Amoy) and Tempo (Mainnet and Testnet) node images for reliability and bug fixes. * Updates the Stellar RPC node image on Mainnet and Testnet. ## Component versions | Component | Version | | - | - | | cp-ui | v3.13.0 | | cp-deployments-api | v1.43.0 | | cp-workflows | v3.45.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.13.0 | # Chainstack Self-Hosted v2.64.0: September 22, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-64-0-september-22-2026 Prepares Avalanche, World Chain, and Mantle deployments for mandatory mainnet hardforks, adds Plasma Aquila upgrade support ahead of its mainnet activation, and updates client images with a security fix and compatibility improvements across the catalog. Prepares Avalanche, World Chain, and Mantle deployments for mandatory mainnet hardforks, adds Plasma Aquila upgrade support ahead of its mainnet activation, and updates client images with a security fix and compatibility improvements across the catalog. ## Mandatory hardfork readiness Avalanche, World Chain, and Mantle deployments now run client versions required for the Helicon, Karst, and Elysium hardforks. Avalanche's Helicon hardfork activates on mainnet today, September 22, 2026, at 15:00 UTC, and nodes not upgraded beforehand will diverge from the network. ## Plasma Aquila upgrade Plasma deployments now configure the Aquila protocol upgrade automatically. Aquila has already activated on Plasma testnet and activates on Plasma mainnet at block 33,300,000. ## Breaking RPC changes Avalanche removes the admin, warp, and personal JSON-RPC namespaces and several debug methods, OP Stack's optimism\_syncStatus drops cross\_unsafe\_l2, and Starknet's pathfinder now disconnects WebSocket clients that stop answering keepalive pings. ## Improvements and fixes * Fixes a deposit-transaction security issue on OP Stack reth clients. * Updates Sui, Arbitrum, Starknet, TRON, Sonic, Tempo, and Solana client images for compatibility and reliability. ## Component versions | Component | Version | | - | - | | cp-ui | v3.13.0 | | cp-deployments-api | v1.44.0 | | cp-workflows | v3.46.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.13.0 | # Chainstack Self-Hosted v2.7.1: July 17, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-7-1-july-17-2026 In-console update notifications, custom node sizing in the deployment wizard, server-side session revocation, and network and snapshot-restore reliability work. This release focuses on keeping operators informed and in control: in-console update notifications, custom node sizing at deployment time, stronger session security, and a round of network-support and snapshot-restore reliability work. ## Update notifications and version awareness The Control Panel now has a notification center. A bell icon in the header shows an unread badge, opens a slide-in drawer, and links to a dedicated notifications page grouped by date. When a newer version of a supported client or of the Control Panel itself becomes available, you get notified in-console instead of tracking releases elsewhere. * **Notification center** — a header bell with an unread badge, a slide-in drawer, and a dedicated notifications page grouped by date. * **Version awareness** — notifications when a newer client or Control Panel version becomes available. * **Session-time fetch** — notifications are fetched when your session starts and can be dismissed individually or cleared all at once. ## Custom node configuration in the deployment wizard The deployment wizard now includes a custom configuration option. Instead of accepting a preset as-is, you can tailor a node before you deploy it. * **Per-client resources** — edit CPU, RAM, and storage per client, prefilled from the preset you started with. * **Client-specific settings** — adjust client-specific settings alongside the resource values. * **Detailed summary** — the summary step shows a per-component breakdown of everything you changed. * **Preserved edits** — your changes are kept if you navigate back from the summary to make further adjustments. ## Stronger session security Signing out now revokes the session's tokens server-side rather than only clearing them from the browser. Revoked tokens are tracked in a dedicated store and rejected on subsequent requests, closing the window where a previously issued token could still be used after logout. ## Broader and more reliable network support * **TRON snapshot startup** — TRON nodes restored from a pruned RocksDB snapshot no longer crash-loop on startup. * **TRON logs** — TRON logs now stream to standard output, so `kubectl logs` shows what the node is doing. * **Polygon Heimdall retention** — Polygon Heimdall nodes ship with state pruning and block retention configured out of the box, keeping the blockstore bounded instead of growing unbounded after a snapshot restore. * **Polygon preset** — a Polygon deployment preset is included. ## More reliable snapshot restores * **Single-file compressed snapshots** — single-file compressed snapshots are now extracted correctly during download. * **Filename preservation** — restored filenames are preserved end to end. * **Automatic cleanup** — orphaned artifacts left behind by interrupted restores are cleaned up automatically. * **Per-source placement** — snapshot data sources can target specific placement per source entry, giving you finer control over where restore data lands. ## Component versions | Component | Version | | - | - | | cp-ui | v3.6.0 | | cp-deployments-api | v0.41.0 | | cp-workflows | v3.9.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | # Chainstack Self-Hosted v2.8.2: July 22, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-8-2-july-22-2026 Robinhood Chain support, zero-downtime control-plane upgrades, cpctl update notifications, and node creation fixes. This release adds Robinhood Chain support and makes control-plane upgrades safer with zero-downtime rollouts. It also brings update notifications to `cpctl` and fixes an invalid-configuration case in the new-node wizard. ## Robinhood Chain support You can now deploy Robinhood Chain nodes. Support is built on a config-driven Arbitrum Nitro component, so the same foundation extends cleanly to other Nitro-based networks over time. ## Zero-downtime control-plane upgrades Upgrading the Control Panel no longer interrupts running operations. The deployments API, workflow engine, and tracer now roll out one instance at a time, so an upgrade to a newer version completes without a service gap. This makes routine platform maintenance safe to run during business hours. ## Update notifications in cpctl `cpctl` now checks whether a newer control-panel version is available and surfaces it when you install, check status, or run an upgrade. You get a clear heads-up when your installation is behind, so staying current no longer depends on manually tracking releases. ## Node creation improvements The new-node wizard now correctly disables custom settings when they don't apply to the selected configuration, preventing invalid combinations before a deployment starts. ## Component versions | Component | Version | | - | - | | cp-ui | v3.7.0 | | cp-deployments-api | v0.42.0 | | cp-workflows | v3.10.0 | | cp-tracer | v0.2.1 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | # Chainstack Self-Hosted v2.9.0: July 22, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-9-0-july-22-2026 Robinhood Chain Sepolia is now available as a deployment target. This release adds Robinhood Chain Sepolia as a new supported test network. ## Robinhood Chain Sepolia support Robinhood Chain Sepolia is now available as a deployment target, built on the Arbitrum Nitro preset. You can deploy and manage Robinhood Chain Sepolia nodes from the Control Panel with the same workflows as other supported networks. ## Component versions | Component | Version | | - | - | | cp-ui | v3.7.0 | | cp-deployments-api | v0.44.0 | | cp-workflows | v3.11.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | # Chainstack Self-Hosted v2.9.1: July 23, 2026 Source: https://docs.chainstack.com/docs/self-hosted/changelog/chainstack-self-hosted-v2-9-1-july-23-2026 Robinhood Chain's test network is now labeled Testnet instead of Sepolia across the network and preset catalog. This maintenance release refines the network naming for Robinhood Chain. ## Improvements and fixes * **Robinhood Chain network naming** — the Robinhood Chain test network is now labeled "Testnet" instead of "Sepolia" across the network and deployment preset catalog. This brings the name in line with Robinhood's own naming. ## Component versions | Component | Version | | - | - | | cp-ui | v3.7.0 | | cp-deployments-api | v0.45.0 | | cp-workflows | v3.11.0 | | cp-auth | v0.5.1 | | cp-bolt | v1.12.0 | # Deploying nodes Source: https://docs.chainstack.com/docs/self-hosted/deploying-nodes Step-by-step guide to deploying blockchain nodes using Chainstack Self-Hosted, including protocol selection, client configuration, and network options. This guide explains how to deploy blockchain nodes using Chainstack Self-Hosted. ## Supported deployments For the full catalog of protocols, networks, clients, client versions, and resource requirements, see [Supported deployments](/docs/self-hosted/supported-clients-and-protocols). It covers Ethereum, Optimism, Base, Unichain, Zora, World Chain, Blast, Mantle, opBNB, Arbitrum, Robinhood Chain, Polygon, Avalanche, Arc, Sonic, Stable, Berachain, Cronos, BNB Smart Chain, Kaia, Ronin, Hyperliquid, Starknet, TRON, Sui, Tempo, Plasma, Solana, Stellar, XRP Ledger, and Bitcoin, with more protocols on the roadmap. ## Resource requirements Before deploying a node, ensure your infrastructure has sufficient resources available. See [System requirements](/docs/self-hosted/requirements) for detailed specifications. > [!NOTE] > > The Control Panel itself consumes system resources. When planning your infrastructure, account for both the Control Panel overhead and the resources required by the nodes you intend to deploy. ## Deployment wizard **1. Access the deployment wizard** 1. Log in to the Control Panel 2. Navigate to **Nodes** 3. Click **Create node** or click the **+** button next to a preset **2. Select protocol and network** 1. Select a protocol 2. Select a network **3. Configure node settings** Each network ships with a preset that fixes the clients, versions, and resource allocation. Ethereum networks additionally offer a **Light** preset that uses half the CPU and RAM of the standard preset with the same storage, so you can move between tiers without resizing disks. Pick the preset that matches your goal — the standard preset for production, the Light preset for evaluation on a smaller server. For the exact clients, versions, and resource figures per network, see [Supported deployments](/docs/self-hosted/supported-clients-and-protocols), and size your host per [System requirements](/docs/self-hosted/requirements). > [!WARNING] > > Snapshot-bootstrapped deployments (Ethereum, Optimism Mainnet, Arbitrum, Robinhood Chain, and Sui) temporarily require **2× the steady-state storage**. The node first downloads the snapshot archive, then extracts it into the chain data directory, so both copies coexist on disk until extraction completes. Provision the node's persistent volume at this peak; the volume can be reclaimed or stay sized for headroom once the snapshot archive is removed. See [Initial sync times](#initial-sync-times) below. Tempo also bootstraps from a snapshot, but its client handles the download and extraction within the storage its presets already allocate, so no extra headroom is needed. **4. Review and deploy** 1. Review your configuration 2. Click **Create node** to start the deployment ![Chainstack Self-Hosted node creation page](https://mintcdn.com/chainstack/X00JVqSBsO9dEVg8/images/self-hosted/node-create.png?fit=max\&auto=format\&n=X00JVqSBsO9dEVg8\&q=85\&s=1221a6a41a74bea86e0fef4a9a6e975f) ## Deployment process After initiating deployment, the node goes through several phases: | Phase | Description | Duration | | - | - | - | | **Bootstrapping** | Resources are allocated, client software is configured | 2–5 minutes | | **Running** | Node is deployed and synchronizing with the network | Continuous | > [!NOTE] > > The node shows **Running** status once deployed. Initial bootstrap from the snapshot completes in minutes to hours; the node then catches up to the chain head in the background. ### Initial sync times Sync times depend on your hardware and network conditions: | Phase | Approximate time | | - | - | | Snapshot bootstrap | Minutes to hours (network dependent) | | Catch-up to chain head | Continuous | See [System requirements](/docs/self-hosted/requirements) for specifications and a community-maintained list of hardware recommendations. ## After deployment Once your node reaches **Running** status: 1. Access the node details to view connection information 2. Start using the node for your applications ## Next steps * [Managing nodes](/docs/self-hosted/managing-nodes) — Learn about node operations * [Troubleshooting](/docs/self-hosted/troubleshooting) — Resolve deployment issues # Download Self-Hosted CLI Source: https://docs.chainstack.com/docs/self-hosted/download-installer Install cpctl, the Chainstack Self-Hosted command-line tool, on Linux, macOS, or Windows using the Chainstack bootstrap script for setup. **`cpctl`** is the command-line tool for installing and upgrading Chainstack Self-Hosted. This page covers getting it onto your local machine. > [!NOTE] > > Installing `cpctl` only puts it on your machine. It does not deploy the Control Panel itself — for that, see [Installation](/docs/self-hosted/installation). To install `cpctl`, run the script for your operating system: > [tab] Linux / macOS > > ```bash theme={"system"} > curl -sSL https://install.chainstack.com/cpctl.sh | sh > ``` > [tab] Windows > > ```powershell theme={"system"} > irm https://install.chainstack.com/cpctl.ps1 | iex > ``` This downloads and runs the `cpctl` installer script, which sets up the Chainstack Self-Hosted command-line tool on your system. ## Supported platforms The installer automatically detects your operating system and architecture. | Operating system | Architectures | | - | - | | Linux | amd64 (x86\_64), arm64 (aarch64) | | macOS | amd64 (x86\_64), arm64 (aarch64) | | Windows | amd64 (x86\_64), arm64 | > [!NOTE] > > 32-bit systems (x86, ARM) are not supported. ## Install a specific version > [tab] Linux / macOS > > Pass the version as an argument: > > ```bash theme={"system"} > curl -sSL https://install.chainstack.com/cpctl.sh | sh -s v1.8.0 > ``` > > Or set the `CPCTL_VERSION` environment variable: > > ```bash theme={"system"} > CPCTL_VERSION=v1.8.0 curl -sSL https://install.chainstack.com/cpctl.sh | sh > ``` > [tab] Windows > > ```powershell theme={"system"} > iwr https://install.chainstack.com/cpctl.ps1 -OutFile cpctl.ps1 > .\cpctl.ps1 -Version v1.8.0 > ``` > > Or set the `CPCTL_VERSION` environment variable: > > ```powershell theme={"system"} > $env:CPCTL_VERSION = "v1.8.0" > irm https://install.chainstack.com/cpctl.ps1 | iex > ``` ## Custom install directory > [tab] Linux / macOS > > By default, `cpctl` installs to `/usr/local/bin/`. To override the install directory, set the `CPCTL_INSTALL_DIR` environment variable: > > ```bash theme={"system"} > CPCTL_INSTALL_DIR=/opt/bin curl -sSL https://install.chainstack.com/cpctl.sh | sh > ``` > [tab] Windows > > By default, `cpctl` installs to `%LOCALAPPDATA%\cpctl\` and is automatically added to the user `PATH`. > [!NOTE] > > Make sure your server meets the [system requirements](/docs/self-hosted/requirements) before running the installer. ## Verify the cpctl binary After the installer completes, confirm that `cpctl` is available and check its version: ```bash theme={"system"} cpctl version ``` ## Shell completion `cpctl` can generate shell completion scripts for bash, zsh, fish, and PowerShell. > [tab] bash > > Load completions for the current session: > > ```bash theme={"system"} > source <(cpctl completion bash) > ``` > > Load for every new session: > > ```bash theme={"system"} > # Linux > cpctl completion bash > /etc/bash_completion.d/cpctl > > # macOS > cpctl completion bash > $(brew --prefix)/etc/bash_completion.d/cpctl > ``` > [tab] zsh > > Load completions for the current session: > > ```zsh theme={"system"} > source <(cpctl completion zsh) > ``` > > Load for every new session: > > ```zsh theme={"system"} > cpctl completion zsh > "${fpath[1]}/_cpctl" > ``` > [tab] fish > > Load completions for the current session: > > ```fish theme={"system"} > cpctl completion fish | source > ``` > > Load for every new session: > > ```fish theme={"system"} > cpctl completion fish > ~/.config/fish/completions/cpctl.fish > ``` > [tab] PowerShell > > Load completions for the current session: > > ```powershell theme={"system"} > cpctl completion powershell | Out-String | Invoke-Expression > ``` > > To persist, add that line to your PowerShell profile (`$PROFILE`). ## Next steps With `cpctl` on your machine, you're ready to set up the cluster and deploy the Control Panel: 1. [Environment setup](/docs/self-hosted/environment-setup) — Set up your Kubernetes cluster and required tools, if not already done 2. [Installation](/docs/self-hosted/installation) — Deploy the Control Panel using `cpctl install` 3. [Quick start](/docs/self-hosted/quick-start) — End-to-end walkthrough from a fresh server # Environment setup Source: https://docs.chainstack.com/docs/self-hosted/environment-setup Prepare your Kubernetes cluster and install required tools for a Chainstack Self-Hosted deployment, including kubectl, Helm, and storage configuration. This page walks you through preparing a Linux server for Chainstack Self-Hosted. By the end, you will have a running Kubernetes cluster and all required command-line tools installed. For hardware and software specifications, see [System requirements](/docs/self-hosted/requirements). ## Install Kubernetes (k3s) k3s is a lightweight Kubernetes distribution suitable for single-server deployments. Other distributions (k8s, EKS, GKE, AKS) are also supported, but k3s is what's tested and documented. ```bash theme={"system"} # Install k3s curl -sfL https://get.k3s.io | sh - # Configure kubectl to use k3s export KUBECONFIG=/etc/rancher/k3s/k3s.yaml # Make the configuration persistent echo 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' >> /etc/environment # Verify the cluster is running kubectl cluster-info kubectl get nodes ``` You should see output indicating the cluster is running and your node is in `Ready` state. ## Install required tools ### Helm Helm is the package manager used to deploy Chainstack Self-Hosted. ```bash theme={"system"} # Install prerequisites apt update apt install curl gpg wget apt-transport-https --yes # Add the Helm repository and install curl -fsSL https://packages.buildkite.com/helm-linux/helm-debian/gpgkey | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/helm.gpg] https://packages.buildkite.com/helm-linux/helm-debian/any/ any main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list apt update apt install helm --yes # Verify helm version ``` ### yq The installer uses yq (mikefarah version) to process YAML configuration files. ```bash theme={"system"} # Download and install yq wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq chmod +x /usr/local/bin/yq # Verify (v4+ required) yq --version ``` ### openssl openssl is used during installation to generate certificates and RSA keys. It is pre-installed on most Linux distributions. ```bash theme={"system"} # Verify openssl version ``` If openssl is not installed, install it with your package manager: ```bash theme={"system"} apt install openssl --yes ``` ## Configure storage ### Default storage (single disk) If your server uses a single disk, the default k3s `local-path` storage class works out of the box. No additional configuration is needed. ```bash theme={"system"} # Verify the storage class is available kubectl get storageclass ``` ### Multi-disk storage with TopoLVM If you have multiple disks available for blockchain node data, set up LVM and TopoLVM for dynamic storage provisioning. #### Set up LVM ```bash theme={"system"} # Install LVM tools apt install lvm2 --yes # Find your disk devices lsblk # Create physical volumes (replace with your actual disk devices) pvcreate /dev/sdb /dev/sdc /dev/sdd # Create volume group vgcreate myvg1 /dev/sdb /dev/sdc /dev/sdd ``` > [!NOTE] > > Device paths vary by provider. The paths `/dev/sdb`, `/dev/sdc`, `/dev/sdd` are examples. On DigitalOcean, volumes appear under `/dev/disk/by-id/`. On AWS, they may be `/dev/nvme1n1`, `/dev/nvme2n1`, etc. Always verify your actual device paths with `lsblk` before creating physical volumes. #### Install TopoLVM ```bash theme={"system"} # Add the TopoLVM Helm repository helm repo add topolvm https://topolvm.github.io/topolvm helm repo update # Create namespace and install cert-manager (required by TopoLVM) kubectl create namespace topolvm-system CERT_MANAGER_VERSION=v1.17.4 kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/${CERT_MANAGER_VERSION}/cert-manager.crds.yaml # Install TopoLVM helm install --namespace=topolvm-system topolvm topolvm/topolvm --set cert-manager.enabled=true # Check storage classes kubectl get storageclass ``` #### Set TopoLVM as the default storage class ```bash theme={"system"} # Remove default from local-path kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}' # Set TopoLVM as default kubectl patch storageclass topolvm-provisioner -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' # Verify kubectl get storageclass ``` ## Verify your environment Run these commands to confirm everything is in place before proceeding to [Installation](/docs/self-hosted/installation): ```bash theme={"system"} # Kubernetes cluster kubectl cluster-info kubectl get nodes # Required tools helm version yq --version openssl version # Storage class kubectl get storageclass ``` All commands should complete without errors. Your node should show `Ready` status, and at least one storage class should be listed. # Evaluation setup Source: https://docs.chainstack.com/docs/self-hosted/evaluation-setup Smallest viable hardware to install and try Chainstack Self-Hosted before going to production: CPU, RAM, disk, and Kubernetes pod resource requirements. This page covers the smallest hardware that lets you install the Chainstack Self-Hosted Control Panel end-to-end and click around. It is not "laptop-tier" — the Control Panel runs \~22 pods on Kubernetes, and the cumulative pod resource requests set a real floor. ## What you need The Control Panel is the management interface where you deploy, monitor, and manage blockchain nodes. The minimum that lets a fresh `cpctl install` reach `Healthy` status: | Resource | Minimum | | - | - | | CPU | 5 cores | | RAM | 6 GiB | | Storage | 15 GB | These requirements are for the Control Panel only. Blockchain nodes require additional resources — see [Optional — deploy a testnet node](#optional-deploy-a-testnet-node) below or [System requirements](/docs/self-hosted/requirements) for the full picture. Any of these environments will work for the Control Panel: * A small dedicated server * A virtual machine with at least 5 vCPUs and 6 GiB RAM * A cloud instance such as DigitalOcean `s-8vcpu-16gb` or equivalent on AWS/GCP/Azure ## Software | Component | Requirement | | - | - | | Operating system | Ubuntu 22.04 or 24.04 LTS | | Kubernetes | k3s (installed during setup) | > [!NOTE] > > The [quick start guide](/docs/self-hosted/quick-start) walks you through installing k3s and all other required tools. No prior Kubernetes experience is needed. ## What you can evaluate With just the Control Panel running, you can: * Explore the Chainstack Self-Hosted web interface and deployment workflow * See the available protocols and node configurations * Understand how Chainstack Self-Hosted manages infrastructure * Plan your production deployment based on hands-on experience with the product ## Get started 1. [Download the installer](/docs/self-hosted/download-installer) — get the cpctl installer for your operating system 2. [Quick start](/docs/self-hosted/quick-start) — end-to-end walkthrough from a fresh Ubuntu server to a running Control Panel 3. [System requirements](/docs/self-hosted/requirements) — full hardware specifications for production planning ## Optional — deploy a testnet node If you want to go a step further and deploy an actual blockchain node, the Ethereum Hoodi testnet is the lightest option available. The Light preset uses half the CPU and RAM of the standard preset but the same storage. | Resource | Control Panel + Hoodi node (Light preset) | | - | - | | CPU | 9 cores | | RAM | 22 GiB | | Storage (steady state) | 265 GB NVMe SSD | | Storage (during deploy) | 515 GB NVMe SSD | The numbers above sum the Control Panel minimum (5 cores / 6 GiB / 15 GB) and the Hoodi Light preset (4 cores / 16 GiB / 250 GB steady state, 500 GB during deploy). > [!WARNING] > > Snapshot bootstrap temporarily requires **2× the node storage**: the snapshot archive and the extracted chain data coexist on disk until extraction completes. Provision the node's persistent volume at the during-deploy figure. > [!NOTE] > > Hoodi is an Ethereum testnet that uses test ETH with no real value, making it ideal for evaluation. Initial bootstrap from the snapshot completes in minutes to hours depending on network conditions; the node then catches up to the chain head in the background. # FAQ Source: https://docs.chainstack.com/docs/self-hosted/faq Answers to frequently asked questions about Chainstack Self-Hosted including licensing, supported protocols, Kubernetes requirements, and troubleshooting. ## General ### What is Chainstack Self-Hosted? Chainstack Self-Hosted brings the power of Chainstack's blockchain infrastructure platform to your own infrastructure. Deploy, manage, and monitor blockchain nodes on your own hardware or cloud environment while maintaining complete control over your data and infrastructure. ### Is Chainstack Self-Hosted ready for production? Yes. Chainstack Self-Hosted is generally available and supports production deployments across Ethereum, Optimism, Base, Unichain, Zora, World Chain, Blast, Mantle, opBNB, Arbitrum, Robinhood Chain, Polygon, Avalanche, Arc, Sonic, Stable, Berachain, Cronos, BNB Smart Chain, Kaia, Ronin, Hyperliquid, Starknet, TRON, Sui, Tempo, Plasma, Solana, Stellar, XRP Ledger, and Bitcoin. See [Supported deployments](/docs/self-hosted/supported-clients-and-protocols) for the full catalog. ## Features and roadmap ### Is snapshot-based deployment available? Yes — Ethereum (all networks), Optimism Mainnet, World Chain (both networks), Blast Mainnet, Mantle (both networks), opBNB (both networks), Arbitrum (both networks), Robinhood Chain (both networks), Sui (both networks), Tempo (both networks), Avalanche Mainnet, Arc Testnet, Sonic Mainnet, Stable (both networks), Berachain (both networks), Cronos (both networks), and BNB Smart Chain (both networks) presets ship with snapshot bootstrap pre-configured. Those nodes deploy from a chain snapshot rather than syncing from genesis, reducing initial sync from days to hours. Tempo restores using its own client tooling rather than a pre-built Chainstack snapshot, so its storage figures already cover the bootstrap peak. Other deployments sync without a snapshot. See [Deploying nodes](/docs/self-hosted/deploying-nodes) for details. ### When will you support more protocols? We will consistently add more deployment presets throughout 2026. See [Supported deployments](/docs/self-hosted/supported-clients-and-protocols) for the current list. Planned additions include additional EVM-compatible chains and Layer 2 networks. **Interested in a specific protocol?** Contact us at [Chainstack Feedback](https://ideas.chainstack.com/en?b=6968ecf76c93cb94db0422c8) with your requirements. ### Is monitoring and observability available? Yes. The Control Panel ships with an optional integrated observability stack: VictoriaMetrics for metrics storage, Grafana for dashboards, and the Chainstack blockchain-node-exporter for chain-specific health signals. The stack is enabled at install time and requires no additional configuration, and its dashboards are embedded on the Control Panel's **Monitoring** page. See [Monitoring](/docs/self-hosted/monitoring). ### Do you plan to support validator nodes? Validator node support is on the roadmap but is **not expected before late 2026**. Running validators requires additional considerations around uptime guarantees, slashing protection, and key management that we want to address properly. **Need validator support sooner?** Contact us at [Chainstack Feedback](https://ideas.chainstack.com/en?b=6968ecf76c93cb94db0422c8) to discuss your use case. ### Will you support multiple endpoints per node? Support for multiple RPC endpoints and load balancing across nodes is planned for future releases. Check the [release notes](/docs/self-hosted/release-notes) for updates. ## Installation and requirements ### What Kubernetes distributions are supported? Chainstack Self-Hosted works with any recent Kubernetes distribution: * k3s (tested and documented) * Standard Kubernetes (k8s) * Amazon EKS * Google GKE * Azure AKS * Other Kubernetes-compatible platforms ### What are the minimum hardware requirements? **Control Panel only:** * 6 CPU cores, 8 GiB RAM, 15 GB storage **Control Panel + Ethereum Mainnet node (Light preset):** * 10 CPU cores, 24 GiB RAM, 2+ TB NVMe SSD See [System requirements](/docs/self-hosted/requirements) for detailed specifications. ### Can I run multiple blockchain nodes on one server? Yes, provided you have sufficient resources. Each Ethereum full node requires approximately 4 CPU cores, 16 GB RAM, and 2 TB storage. Plan your infrastructure accordingly. ### Do I need a dedicated server or can I use a VM? Either works, as long as storage performance is adequate. For blockchain nodes, NVMe SSD storage is strongly recommended due to the high I/O requirements. > [!WARNING] > > For VMs, ensure the virtual disk is backed by SSD storage, not HDD. ## Operations ### How long does initial node sync take? With standard sync from genesis, expect 2–5 days for Ethereum Mainnet, depending on your hardware and network. With [snapshot-based deployment](/docs/self-hosted/deploying-nodes), this can be reduced to hours or even minutes. See [System requirements](/docs/self-hosted/requirements) for specifications and a community-maintained list of hardware recommendations. ### How to find out the status of the initial node sync? At the moment, the only option is to check the corresponding pod logs. For example, if the Blockchain Node ID on **Chainstack Self-Hosted > Nodes > Node Details** is `6244046a-f344-4453-abd5-5bbbc0377422`, then you can check the logs in the following way: ```bash theme={"system"} # List all pods and find the pods related to the node ID kubectl get pods -n control-panel-deployments | grep 6244046a-f344-4453-abd5-5bbbc0377422 cp-6244046a-f344-4453-abd5-5bbbc0377422-prysm-cb87b4dcc-l9rvp 1/1 Running 2 (4h51m ago) 2d18h cp-6244046a-f344-4453-abd5-5bbbc0377422-reth-776c6954fc-p6htz 1/1 Running 107 (127m ago) 2d18h # Check the log files for the corresponding pod kubectl logs -f cp-6244046a-f344-4453-abd5-5bbbc0377422-reth-776c6954fc-p6htz -n control-panel-deployments ``` ### What happens if my node crashes? The Kubernetes deployment will automatically restart failed pods. If the node data is intact on the persistent volume, the node will resume from its last state. ### How do I resize the disk if storage runs out? As blockchain storage constantly grows, you may need to allocate more disk space to your node. This involves finding the right PVC and using `kubectl patch`. **1. Find the node ID** Go to **Chainstack Self-Hosted > Nodes > Node details** and copy the Node ID, for example `6244046a-f344-4453-abd5-5bbbc0377422`. **2. Find the pods and PVC** ```bash theme={"system"} # Find the pods associated with the deployment kubectl get pods -n control-panel-deployments | grep 6244046a-f344-4453-abd5-5bbbc0377422 cp-6244046a-f344-4453-abd5-5bbbc0377422-prysm-cb87b4dcc-l9rvp 1/1 Running 2 (26h ago) 3d15h cp-6244046a-f344-4453-abd5-5bbbc0377422-reth-776c6954fc-p6htz 1/1 Running 125 (61m ago) 3d15h # Find the corresponding PVC kubectl get pvc -n control-panel-deployments | grep 6244046a-f344-4453-abd5-5bbbc0377422 cp-6244046a-f344-4453-abd5-5bbbc0377422-prysm Bound pvc-b27f6710-8b8b-42ec-a559-6def88edc1d0 100Gi RWO topolvm-provisioner 3d15h cp-6244046a-f344-4453-abd5-5bbbc0377422-reth Bound pvc-6c396c66-ad08-48ce-a60c-b9d188f03228 1300Gi RWO topolvm-provisioner 3d15h ``` **3. Check current disk usage** ```bash theme={"system"} df -h | grep pvc-6c396c66-ad08-48ce-a60c-b9d188f03228 /dev/dm-4 1.2T 1.2T 58G 96% /var/lib/kubelet/pods/.../mount ``` **4. Increase the storage** ```bash theme={"system"} kubectl patch pvc -n control-panel-deployments cp-6244046a-f344-4453-abd5-5bbbc0377422-reth \ -p '{"spec": {"resources": {"requests": {"storage": "1500Gi"}}}}' ``` **5. Verify the storage extension** ```bash theme={"system"} df -h | grep pvc-6c396c66-ad08-48ce-a60c-b9d188f03228 /dev/dm-4 1.5T 1.2T 300G 80% /var/lib/kubelet/pods/.../mount ``` > [!NOTE] > > Your storage class must support volume expansion. Most cloud providers and modern storage provisioners support this by default. ## Support ### How do I get technical support? * **Support Center:** [support.chainstack.com](https://support.chainstack.com/hc/en-us) * **Documentation issues:** [Chainstack Feedback](https://ideas.chainstack.com/en?b=6968ecf76c93cb94db0422c8) ### How do I provide feedback or request features? Submit your feedback and feature requests to: [Chainstack Feedback](https://ideas.chainstack.com/en?b=6968ecf76c93cb94db0422c8) We actively review all feedback and use it to prioritize the roadmap. ### Where can I report bugs? * For urgent issues: [Chainstack Support Center](https://support.chainstack.com/hc/en-us) * For general bugs and feedback: [Chainstack Feedback](https://ideas.chainstack.com/en?b=6968ecf76c93cb94db0422c8) When reporting bugs, please include: * Version of Chainstack Self-Hosted * Steps to reproduce the issue * Relevant logs and error messages * Your environment details (K8s distribution, OS, etc.) # First login Source: https://docs.chainstack.com/docs/self-hosted/first-login Complete your first login to the Chainstack Self-Hosted Control Panel and configure initial settings including admin credentials and node deployment. This guide walks you through your first login to Chainstack Self-Hosted and initial configuration. ## First login ### Finding your credentials During installation, the bootstrap credentials were generated and saved to a values file. Run the `yq` command from the installer output to retrieve your password: ```bash theme={"system"} yq '.cp-auth.env.CP_AUTH_BOOTSTRAP_PASSWORD' /root/.config/cp-suite/values/cp-control-panel-*.yaml ``` Your credentials: | Setting | Default value | | - | - | | Username | `admin` | | Password | Auto-generated (in values file) | ### Accessing the login page Open your browser and navigate to the Control Panel URL. If you haven't exposed the services yet, see [Expose the UI and deployments API](/docs/self-hosted/installation#expose-the-ui-and-deployments-api) for instructions. The URL depends on how you exposed the services: | Exposure method | URL | | - | - | | LoadBalancer | `http://` | | NodePort | `http://:` | | Ingress | `http://your-configured-hostname` | | Port forward | `http://:8080` | You'll see the Chainstack Self-Hosted login page: ![Chainstack Self-Hosted login page](https://mintcdn.com/chainstack/X00JVqSBsO9dEVg8/images/self-hosted/login-page.png?fit=max\&auto=format\&n=X00JVqSBsO9dEVg8\&q=85\&s=7d27c46763b28cea020ff4f604dd325f) ### Logging in 1. Enter `admin` in the **Username** field 2. Enter the generated password from your values file in the **Password** field 3. Click **Log in** ## Initial configuration After your first login, we recommend completing these configuration steps. ### Update admin profile For security, update your admin credentials immediately after first login. 1. Click **Settings** in the left sidebar 2. Under **Personal Information**, update the following: | Field | Recommendation | | - | - | | Username | Change from default `admin` to a unique username | | Password | Set a strong password | ![Chainstack Self-Hosted settings page](https://mintcdn.com/chainstack/X00JVqSBsO9dEVg8/images/self-hosted/settings-page.png?fit=max\&auto=format\&n=X00JVqSBsO9dEVg8\&q=85\&s=b696d671a4b88244ff5afaf579221c05) > [!WARNING] > > After changing your password, the original password in the values file is no longer valid. Store your new credentials securely. ## Understanding the interface The Chainstack Self-Hosted interface provides access to all node management functions. ### Main sections | Section | Purpose | | - | - | | **Nodes** | List, deploy, and manage blockchain nodes | | **Docs** | Link to Chainstack Self-Hosted documentation | | **Settings** | User profile and credentials | ### Node status indicators When viewing nodes, you'll see status indicators: | Status | Description | | - | - | | **Bootstrapping** | Node resources are being allocated and client software is being configured | | **Running** | Node is deployed and operational | | **Error** | Node encountered an issue | ## Next steps With your admin account configured, you're ready to: 1. [Deploy your first node](/docs/self-hosted/deploying-nodes) — Deploy a blockchain node 2. [Manage nodes](/docs/self-hosted/managing-nodes) — Learn node operations 3. [Monitoring](/docs/self-hosted/monitoring) — Track node health and sync status on the **Monitoring** page ## Troubleshooting If you run into issues logging in or accessing the Control Panel, see the [Troubleshooting guide](/docs/self-hosted/troubleshooting#access-troubleshooting) for solutions to common problems including login failures, backend URL configuration, and credential issues. # Installation Source: https://docs.chainstack.com/docs/self-hosted/installation Install Chainstack Self-Hosted using cpctl with auto-generated defaults for a streamlined deployment of your self-managed blockchain node infrastructure. This guide walks through a standard installation with auto-generated passwords and default settings. For custom deployments — external databases, adjusted resource limits, or manual values — see [Advanced installation](/docs/self-hosted/advanced-installation). ## Prerequisites Before installation, make sure you have a running Kubernetes cluster with kubectl, Helm, yq, and openssl installed. See [Environment setup](/docs/self-hosted/environment-setup) for instructions and a verification checklist. ## Install **1. Run the installer** Find your storage class name: ```bash theme={"system"} kubectl get storageclass ``` Common storage classes: * `local-path` — default k3s storage class (single-disk setups) * `topolvm-provisioner` — TopoLVM (multi-disk setups) * Cloud providers use their own defaults (`gp2`/`gp3` on AWS, `standard` on GCP, `managed-premium` on Azure) Run the installer with your storage class: ```bash theme={"system"} cpctl install -s STORAGE_CLASS_NAME ``` For example: ```bash theme={"system"} cpctl install -s topolvm-provisioner ``` > [!NOTE] > > By default, the installer uses the latest version. To pin a specific version, add `-v vX.Y.Z` — see [Release notes](/docs/self-hosted/release-notes) for available versions. To get the installer, see [Download Chainstack Self-Hosted](/docs/self-hosted/download-installer). **2. Confirm auto-generation** When prompted, confirm that you want to auto-generate passwords: ```text theme={"system"} No values file provided. Generate passwords automatically? [y/N]: y ``` **3. Specify the URL for the backend** The installer will ask for the backend API URL. This is the URL the Control Panel UI uses to reach the deployments API. **The browser must be able to resolve and reach this URL** — an in-cluster hostname will not work for browser access. ```text theme={"system"} Enter CP UI backend API URL [http://cp-cp-deployments-api]: ``` * **Single server, browser access (recommended)** — enter `http://:8081`. You will expose the deployments API on port 8081 in [Post-installation](#expose-the-ui-and-deployments-api). Use port 80 (or another free port) instead of 8081 if it suits your environment, but match it during exposure. * **In-cluster only (rare)** — press Enter to keep `http://cp-cp-deployments-api`. This is reachable only from inside the cluster, so the UI will fail to log in from a browser outside. > [!TIP] > > You can skip this prompt by passing `--backend-url http://:8081` to `cpctl install`. **4. Specify the workload namespace** The installer will ask for the Kubernetes namespace where blockchain node pods will run: ```text theme={"system"} Enter workload namespace for blockchain nodes [control-panel-deployments]: ``` Press Enter to accept the default `control-panel-deployments`. Change it only if you need a custom namespace layout. **5. Choose monitoring configuration** The installer will prompt you to configure the monitoring stack: ```text theme={"system"} Monitoring stack: [1] Install monitoring stack (clean cluster, no existing VictoriaMetrics) [2] Install monitoring using existing VictoriaMetrics operator [3] Skip monitoring Choice [1]: ``` * **Option 1** — installs the full monitoring stack (Grafana, VictoriaMetrics, exporters). Use this for a clean cluster. * **Option 2** — reuses an existing VictoriaMetrics operator already deployed on the cluster. * **Option 3** — skips monitoring entirely. **6. Confirm and deploy** The installer will show a summary and ask you to confirm: ```text theme={"system"} ==> About to install Control Panel Version: v1.8.0 Namespace: control-panel Workload Namespace: control-panel-deployments Release: cp Timeout: 15m0s Do you want to proceed? [y/N]: y ``` Confirm with `y`. The installer will then deploy the full stack (PostgreSQL, Temporal, Keycloak, Control Panel services). ### Credentials storage Generated credentials are saved to: ```text theme={"system"} ~/.config/cp-suite/values/cp-control-panel-.yaml ``` > [!WARNING] > > This file contains sensitive passwords and keys. Keep it secure and backed up. ## Post-installation ### Monitoring stack The Control Panel includes an optional integrated observability stack (Grafana, VictoriaMetrics, and the Chainstack blockchain-node-exporter). During installation, you are prompted to install it, reuse an existing VictoriaMetrics operator, or skip it. See [Monitoring](/docs/self-hosted/monitoring) for access instructions and available dashboards. ### Verify deployment status ```bash theme={"system"} cpctl status ``` Look for the healthy status at the bottom of the output: ```text theme={"system"} Overall Status: ✓ Healthy Reason: All components ready ``` Useful flags: | Flag | Description | | - | - | | `-w, --watch` | Watch status with continuous updates | | `--wide` | Show full resource names without truncation | | `--no-pods` | Hide pod details for a cleaner view | | `--unhealthy-only` | Show only unhealthy or degraded components | | `-o json` / `-o yaml` | Machine-readable output for automation | | `--log-file ` | Write the status output to a file | ### Expose the UI and deployments API For browser access to work, you need to expose **two** services to the network: the UI (`cp-cp-ui`) and the deployments API (`cp-cp-deployments-api`). The UI page is served by the first; the JavaScript in the page calls the URL you set in the [backend URL step](#specify-the-url-for-the-backend) — by default, that's `http://:8081`, which routes to the deployments API. #### Option 1: LoadBalancer (recommended for production) ```bash theme={"system"} kubectl expose service cp-cp-ui --type=LoadBalancer --name=cp-ui-external -n control-panel kubectl expose service cp-cp-deployments-api --type=LoadBalancer --name=cp-deployments-api-external -n control-panel --port=8081 --target-port=8080 ``` #### Option 2: NodePort ```bash theme={"system"} kubectl expose service cp-cp-ui --type=NodePort --name=cp-ui-nodeport -n control-panel kubectl expose service cp-cp-deployments-api --type=NodePort --name=cp-deployments-api-nodeport -n control-panel --port=8081 --target-port=8080 ``` #### Option 3: Ingress Create an Ingress that exposes the UI on port 80. The deployments API is exposed separately on port 8081 via a LoadBalancer or port-forward (see Options 1 or 4): ```yaml theme={"system"} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: cp-ui-ingress namespace: control-panel spec: rules: - host: cp.yourdomain.com http: paths: - path: / pathType: Prefix backend: service: name: cp-cp-ui port: number: 80 ``` #### Option 4: Port forward (testing only) ```bash theme={"system"} kubectl port-forward svc/cp-cp-ui 8080:80 -n control-panel --address 0.0.0.0 & kubectl port-forward svc/cp-cp-deployments-api 8081:8080 -n control-panel --address 0.0.0.0 & ``` ## Next steps 1. [First login](/docs/self-hosted/first-login) — First login and configuration 2. [Deploying nodes](/docs/self-hosted/deploying-nodes) — Deploy your first blockchain node 3. [Advanced installation](/docs/self-hosted/advanced-installation) — Customize your deployment # Introduction to Chainstack Self-Hosted Source: https://docs.chainstack.com/docs/self-hosted/introduction Deploy and manage blockchain nodes in your own environment with Chainstack Self-Hosted, giving you full control over data, security, and infrastructure. Welcome to the documentation for Chainstack Self-Hosted, a control plane for deploying and managing blockchain infrastructure in your own environment. ## What is Chainstack Self-Hosted? Chainstack Self-Hosted brings the power of Chainstack's blockchain infrastructure platform to your own infrastructure. Deploy, manage, and monitor blockchain nodes on your own hardware or cloud environment while maintaining complete control over your data and infrastructure. ### Key features * Full infrastructure control — run blockchain nodes on your own servers, whether on premises, in a private cloud, or on dedicated servers * Simplified deployment — deploy blockchain nodes through an intuitive web interface without complex manual configuration * Enterprise-grade architecture — built on Kubernetes for reliability, scalability, and ease of operations * Multi-protocol support — deploy full nodes across Ethereum, Optimism, Base, Unichain, Zora, World Chain, Blast, Mantle, opBNB, Arbitrum, Robinhood Chain, Polygon, Avalanche, Arc, Sonic, Stable, Berachain, Cronos, BNB Smart Chain, Kaia, Ronin, Hyperliquid, Starknet, TRON, Sui, Tempo, Plasma, Solana, Stellar, XRP Ledger, and Bitcoin. See [Supported deployments](/docs/self-hosted/supported-clients-and-protocols) for the full catalog To see what you need to try it, see [Evaluation setup](/docs/self-hosted/evaluation-setup). You install and upgrade Chainstack Self-Hosted using `cpctl`, our command-line tool — see [Install the Self-Hosted CLI](/docs/self-hosted/download-installer) to get it. ## Getting help * Technical support — [Chainstack Support Center](https://support.chainstack.com/hc/en-us) * Feedback and feature requests — [Chainstack Feedback](https://ideas.chainstack.com/en?b=6968ecf76c93cb94db0422c8) # Managing nodes Source: https://docs.chainstack.com/docs/self-hosted/managing-nodes Manage blockchain node operations in Chainstack Self-Hosted including starting, stopping, monitoring sync status, and accessing node credentials. This guide covers the operations available for managing deployed blockchain nodes. ## Node list ![Chainstack Self-Hosted node list](https://mintcdn.com/chainstack/X00JVqSBsO9dEVg8/images/self-hosted/node-list.png?fit=max\&auto=format\&n=X00JVqSBsO9dEVg8\&q=85\&s=2886098d12c6c0721538fac557efa589) The node list displays all deployed nodes with: * Node name * Protocol and network * Date created * Date updated * Current status ### Filtering and sorting Use the controls to: * Filter by status — All statuses, Running, Bootstrapping, Error * Search for specific nodes ## Node details Click on any node to view its details page, which includes: ![Chainstack Self-Hosted node details](https://mintcdn.com/chainstack/X00JVqSBsO9dEVg8/images/self-hosted/node-details.png?fit=max\&auto=format\&n=X00JVqSBsO9dEVg8\&q=85\&s=2ba08ed5ddcf12df0ad2952ff9aaafd6) ### Overview tab | Information | Description | | - | - | | Protocol | Blockchain protocol (e.g., Ethereum) | | Network | Network name (e.g., Mainnet, Hoodi) | | Date created | When the node was deployed | | Deployment ID | Unique identifier for the deployment | | Revision ID | Current revision of the deployment | | Date updated | Last modification timestamp | **Access and credentials for your node:** | Endpoint | Description | | - | - | | Execution client HTTP endpoint | JSON-RPC HTTP endpoint (port 8545) | | Execution client WSS endpoint | JSON-RPC WebSocket endpoint (port 8546) | | Consensus client HTTP endpoint | Beacon API endpoint (port 5052) | > [!NOTE] > > The endpoint URLs are internal Kubernetes service addresses. To access nodes from outside the cluster, you'll need to expose these services via LoadBalancer, NodePort, or Ingress. ### Settings tab The Settings tab provides additional actions for the deployed node. Currently available: | Action | Purpose | | - | - | | Delete node | Permanently remove the node and its data | ## Monitoring Click **Monitoring** in the Control Panel sidebar to open pre-built Grafana dashboards for node health, sync status, and Kubernetes infrastructure metrics, embedded in the Control Panel. See [Monitoring](/docs/self-hosted/monitoring) for what each dashboard covers and for how to reach full Grafana. ## Next steps * [Troubleshooting](/docs/self-hosted/troubleshooting) — Resolve common issues * [FAQ](/docs/self-hosted/faq) — Answers to common questions # Monitoring Source: https://docs.chainstack.com/docs/self-hosted/monitoring View node health, sync status, and resource utilization on the Monitoring page of the Chainstack Self-Hosted Control Panel, backed by an integrated Grafana and VictoriaMetrics observability stack. Chainstack Self-Hosted ships with an optional observability stack — Grafana, VictoriaMetrics, and a set of exporters — deployed in the `control-panel` namespace alongside the Control Panel itself. Once the stack is installed, three of its dashboards appear on the Control Panel's **Monitoring** page with no additional configuration. Reaching the rest of Grafana takes one extra step, because the Grafana service is internal to the cluster until you expose it. ## Viewing dashboards in the Control Panel Click **Monitoring** in the Control Panel sidebar. The page embeds three Grafana dashboards as tabs and follows the Control Panel's light or dark theme, so you never leave the interface to check a node. It is a fixed set of dashboards rather than all of Grafana — to reach the rest, see [Accessing full Grafana](/docs/self-hosted/monitoring#accessing-full-grafana). The tabs are: * **Nodes** — sync status, peer count, and client-specific health signals for every deployed node, from the Chainstack blockchain-node-exporter * **Cluster Overview** — cluster-wide resource utilization, from kube-state-metrics and the Prometheus node exporter * **Pods** — per-pod status, restarts, and resource consumption Grafana prompts you to log in the first time you open the page. Use the credentials described in [Grafana credentials](/docs/self-hosted/monitoring#grafana-credentials). If the installation skipped the monitoring stack, the **Monitoring** page reports that monitoring is not enabled. Re-run the installer without the `--disable-monitoring` flag to add the stack. ## Grafana credentials Grafana credentials are auto-generated during installation. At the end of the installation, `cpctl` displays the retrieval command. The Grafana username is `admin`. To retrieve the password: ```bash theme={"system"} yq '.grafana.adminPassword' ~/.config/cp-suite/values/cp-control-panel-*.yaml ``` ## Accessing full Grafana The Control Panel's **Monitoring** page carries three dashboards. To use everything else Grafana offers — the full dashboard library, Explore and ad-hoc queries, and the alerting configuration — expose the `cp-grafana` service yourself. It is a ClusterIP service on port 80, reachable only inside the cluster until you do. Two rules apply to every method: * Include `/monitoring/` in the URL — Grafana is configured to serve from that sub-path, so a URL without it returns a redirect or a 404 * Use an address other than the Control Panel's — the Control Panel reserves `/monitoring` on its own address for the embedded page, so a typed URL there returns the three-dashboard view instead of Grafana ### Option 1: LoadBalancer (recommended for production) ```bash theme={"system"} kubectl expose service cp-grafana --type=LoadBalancer --name=cp-grafana-external -n control-panel kubectl get svc cp-grafana-external -n control-panel ``` Open `http:///monitoring/` once the external IP is assigned. ### Option 2: NodePort ```bash theme={"system"} kubectl expose service cp-grafana --type=NodePort --name=cp-grafana-nodeport -n control-panel kubectl get svc cp-grafana-nodeport -n control-panel ``` Open `http://:/monitoring/`, taking the assigned port from the command output. ### Option 3: Ingress Give Grafana its own hostname. Keep the path at `/monitoring` so that Grafana's own links and redirects resolve: ```yaml theme={"system"} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: cp-grafana-ingress namespace: control-panel spec: rules: - host: grafana.yourdomain.com http: paths: - path: /monitoring pathType: Prefix backend: service: name: cp-grafana port: number: 80 ``` Open `http://grafana.yourdomain.com/monitoring/`. ### Option 4: Port forward (testing only) ```bash theme={"system"} kubectl port-forward svc/cp-grafana 3000:80 -n control-panel --address 0.0.0.0 ``` Open `http://:3000/monitoring/`. ## Stack components | Component | Pod prefix | Purpose | | - | - | - | | Grafana | `cp-grafana` | Dashboards and visualization | | VictoriaMetrics (single-node) | `vmsingle-cp-victoria-metrics-k8s-stack` | Metrics storage | | VMAgent | `vmagent-cp-victoria-metrics-k8s-stack` | Metrics scraping | | VMAlertmanager | `vmalertmanager-cp-victoria-metrics-k8s-stack` | Alerting | | VictoriaMetrics operator | `cp-victoria-metrics-operator` | Manages VictoriaMetrics custom resources | | Chainstack blockchain-node-exporter | `cp-blockchain-node-exporter` | Chain-specific health signals | | Prometheus node exporter | `cp-prometheus-node-exporter` | Host-level metrics | | kube-state-metrics | `cp-kube-state-metrics` | Kubernetes resource metrics | ## Disabling monitoring During installation, the installer prompts you to configure the monitoring stack. Select option **\[3] Skip monitoring** to skip it. You can also skip it non-interactively with `--disable-monitoring`: ```bash theme={"system"} cpctl install -s STORAGE_CLASS_NAME --disable-monitoring ``` Skipping monitoring removes all observability components, and the Control Panel's **Monitoring** page reports that monitoring is not enabled. The rest of the Control Panel is unaffected. # Quick start Source: https://docs.chainstack.com/docs/self-hosted/quick-start Complete walkthrough from a fresh Ubuntu installation to a running Chainstack Self-Hosted Control Panel with your first blockchain node deployed. This guide walks you through a complete installation of Chainstack Self-Hosted on a dedicated server, from a fresh Ubuntu installation to a running Control Panel. ## Overview By the end of this guide, you will have: * A Kubernetes cluster running on your server * The Chainstack Self-Hosted Control Panel deployed and accessible * The ability to deploy blockchain nodes through the web interface ## Prerequisites Before starting, ensure you have: * A dedicated server or virtual machine meeting the [system requirements](/docs/self-hosted/requirements) * Root or sudo access to the server * A stable internet connection ## End-to-end example This example uses a dedicated server from Contabo running Ubuntu 22.04, but the steps apply to any compatible server. **1. Install required tools** Connect to your server via SSH and install the required dependencies. For detailed instructions, see [Environment setup](/docs/self-hosted/environment-setup). ```bash theme={"system"} # Update package lists apt update # Install prerequisites apt install curl gpg wget apt-transport-https --yes # Install Helm curl -fsSL https://packages.buildkite.com/helm-linux/helm-debian/gpgkey | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/helm.gpg] https://packages.buildkite.com/helm-linux/helm-debian/any/ any main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list apt update apt install helm --yes # Install yq (mikefarah version) wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq chmod +x /usr/local/bin/yq # Verify installations helm version yq --version ``` **2. Install Kubernetes (k3s)** k3s is a lightweight Kubernetes distribution that's easy to install and suitable for single-server deployments. For detailed instructions, see [Environment setup](/docs/self-hosted/environment-setup). ```bash theme={"system"} # Install k3s curl -sfL https://get.k3s.io | sh - # Configure kubectl to use k3s export KUBECONFIG=/etc/rancher/k3s/k3s.yaml # Make the configuration persistent echo 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' >> /etc/environment # Verify the cluster is running kubectl cluster-info kubectl get nodes ``` You should see output indicating the cluster is running and your node is in Ready state. **3. Configure storage (optional but recommended)** If you have multiple disks available for blockchain node data, set up LVM and TopoLVM for dynamic storage provisioning. For detailed instructions, see [Environment setup](/docs/self-hosted/environment-setup). ```bash theme={"system"} # Install LVM tools apt install lvm2 # Find your disk devices (paths vary by provider) lsblk # Create physical volumes (replace with your actual disk devices) pvcreate /dev/sdb /dev/sdc /dev/sdd # Create volume group vgcreate myvg1 /dev/sdb /dev/sdc /dev/sdd ``` > [!NOTE] > > Device paths vary by provider. The paths `/dev/sdb`, `/dev/sdc`, `/dev/sdd` are examples from Contabo. On DigitalOcean, volumes appear under `/dev/disk/by-id/`. On AWS, they may be `/dev/nvme1n1`, `/dev/nvme2n1`, etc. Always verify your actual device paths with `lsblk` before creating physical volumes. ```bash theme={"system"} # Install TopoLVM for Kubernetes storage provisioning helm repo add topolvm https://topolvm.github.io/topolvm helm repo update # Create namespace and install cert-manager (required by TopoLVM) kubectl create namespace topolvm-system CERT_MANAGER_VERSION=v1.17.4 kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/${CERT_MANAGER_VERSION}/cert-manager.crds.yaml # Install TopoLVM helm install --namespace=topolvm-system topolvm topolvm/topolvm --set cert-manager.enabled=true # Check storage classes kubectl get storageclass ``` If using TopoLVM, set it as the default storage class: ```bash theme={"system"} # Remove default from local-path kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}' # Set TopoLVM as default kubectl patch storageclass topolvm-provisioner -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' # Verify kubectl get storageclass ``` > [!NOTE] > > If you're using the default k3s local-path storage class (single disk), you can skip this step. **4. Get the installer** [Download the installer](/docs/self-hosted/download-installer) for your operating system. **5. Run the installation** Run the installer with your storage class and your server's public IP. Replace `` with the actual IP. This example uses `topolvm-provisioner` — the storage class set up in the previous step. ```bash theme={"system"} cpctl install -s topolvm-provisioner --backend-url http://:8081 ``` > [!NOTE] > > The `--backend-url` is the URL the browser will use to reach the deployments API. We expose the deployments API on port 8081 in the next steps. By default, the installer uses the latest version — to pin one, add `-v vX.Y.Z` (see [Release notes](/docs/self-hosted/release-notes) for available versions). The installer will: 1. Check prerequisites (kubectl, helm, yq, openssl, cluster access) 2. Generate secure passwords for all services 3. Save the credentials to `~/.config/cp-suite/values/` 4. Prompt for the workload namespace where blockchain node pods will run (default: `control-panel-deployments`). 5. Prompt to configure the monitoring stack — install fresh, reuse an existing VictoriaMetrics operator, or skip. 6. Show a summary and ask you to confirm before deploying. The installation typically takes 5–10 minutes. You'll see output similar to: ```text theme={"system"} → Validating prerequisites... ✓ Checking kubectl installation ✓ Checking helm installation ✓ Checking helm version (v3+ required) ✓ Checking Kubernetes cluster connectivity ✓ All prerequisites validated successfully ==> Generating credentials for basic installation → Generating secure passwords... → Generating RSA keys for JWT authentication... → Saving credentials to config directory... ✓ Credentials saved: /root/.config/cp-suite/values/cp-control-panel-.yaml ✓ Storage class "topolvm-provisioner" validated ==> About to install Control Panel Version: v1.8.0 Namespace: control-panel Workload Namespace: control-panel-deployments Release: cp Timeout: 15m0s Do you want to proceed? [y/N]: y ``` **6. Verify the installation** The installer shows a live progress table during deployment. Once complete, you should see: ```text theme={"system"} ✓ Installation completed successfully in 6m5s ✓ Installation completed successfully ``` You can also check the status at any time: ```bash theme={"system"} cpctl status ``` Look for the healthy status at the bottom of the output: ```text theme={"system"} Overall Status: ✓ Healthy Reason: All components ready ``` **7. Expose the web interface and deployments API** Browser access requires exposing both the Control Panel and the deployments API. The Ingress puts the Control Panel on port 80, which also serves the Grafana dashboards embedded on its **Monitoring** page; the deployments API is exposed separately on port 8081 to match the `--backend-url` you set during installation. Apply the Ingress for the Control Panel. The example uses Traefik, which k3s ships with by default: ```bash theme={"system"} kubectl apply -f - < [!NOTE] > > The `kubectl port-forward` process must keep running for the deployments API to remain reachable. For production deployments, expose `cp-cp-deployments-api` via a LoadBalancer or NodePort instead — see [Expose the UI and deployments API](/docs/self-hosted/installation#expose-the-ui-and-deployments-api). **8. Access the Control Panel** Open your browser and navigate to: * **Ingress:** `http://` — Control Panel * **Port forward:** `http://:8080` — Control Panel You should see the Chainstack Self-Hosted login page. Node and cluster dashboards are on the Control Panel's **Monitoring** page, so this one address covers them too. Reaching full Grafana takes an extra step — see [Monitoring](/docs/self-hosted/monitoring). **9. Find your login credentials** The installer displays the username and a command to retrieve the password at the end of the installation: ```text theme={"system"} ==> Retrieving Bootstrap Admin Credentials → Username: admin → To retrieve the bootstrap password, run: yq '.cp-auth.env.CP_AUTH_BOOTSTRAP_PASSWORD' "/root/.config/cp-suite/values/cp-control-panel-.yaml" ``` Run the `yq` command from the output to get your password: ```bash theme={"system"} yq '.cp-auth.env.CP_AUTH_BOOTSTRAP_PASSWORD' /root/.config/cp-suite/values/cp-control-panel-*.yaml ``` ## Next steps Congratulations! You now have Chainstack Self-Hosted running. Continue with: 1. [First login](/docs/self-hosted/first-login) — First login and initial configuration 2. [Deploying nodes](/docs/self-hosted/deploying-nodes) — Deploy your first blockchain node 3. [Troubleshooting](/docs/self-hosted/troubleshooting) — If you encounter any issues ## Useful kubectl commands Set the default namespace to avoid typing `-n control-panel` every time: ```bash theme={"system"} kubectl config set-context --current --namespace=control-panel ``` Common commands for managing your installation: ```bash theme={"system"} # View all pods kubectl get pods # View all services kubectl get svc # View persistent volume claims kubectl get pvc # View logs for a specific pod kubectl logs # Describe a deployment for troubleshooting kubectl describe deployment cp-cp-deployments-api # Restart a deployment kubectl rollout restart deployment cp-cp-ui ``` # Chainstack Self-Hosted release notes and version history Source: https://docs.chainstack.com/docs/self-hosted/release-notes Product updates, bug fixes, and new features released for Chainstack Self-Hosted, including version history and upgrade instructions for each release. **Chainstack Self-Hosted v2.64.0: September 22, 2026** Prepares Avalanche, World Chain, and Mantle deployments for mandatory mainnet hardforks, adds Plasma Aquila upgrade support ahead of its mainnet activation, and updates client images with a security fix and compatibility improvements across the catalog. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-64-0-september-22-2026) **Chainstack Self-Hosted v2.63.0: August 31, 2026** Updates blockchain client images across Sui, Arc, Polygon, Tempo, and Stellar deployments for protocol compatibility, upcoming hardfork readiness, and reliability. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-63-0-august-31-2026) **Chainstack Self-Hosted v2.62.0: August 25, 2026** This release adds Stellar Mainnet and Testnet to the deployment catalog, running on the stellar-core and stellar-rpc clients. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-62-0-august-25-2026) **Chainstack Self-Hosted v2.59.0: August 24, 2026** This release adds Solana Mainnet and Devnet to the deployment catalog, running on the agave client, and includes reliability fixes for OP-Stack transaction pool admission and Ronin Saigon peer connectivity. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-59-0-august-24-2026) **Chainstack Self-Hosted v2.53.0: August 20, 2026** This release adds ten new networks to the deployment catalog — Base Sepolia, Optimism Sepolia, Zora Sepolia, Unichain Sepolia, Polygon Amoy, Starknet Sepolia, TRON Nile, Bitcoin Signet, Bitcoin Testnet4, and Hyperliquid Mainnet. Snapshot-based bootstrapping now also accepts an already-unpacked archive as a data source. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-53-0-august-20-2026) **Chainstack Self-Hosted v2.43.0: August 18, 2026** This release adds BNB Smart Chain, Kaia, Ronin, and Hyperliquid as deployment targets, covering six networks in total. BNB Smart Chain and Kaia each run a single execution node across mainnet and testnet, Ronin Saigon runs an OP-Stack rollup that reads its batch data from EigenDA, and Hyperliquid Testnet runs a single node. The Control Panel can now also serve the Grafana monitoring dashboards. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-43-0-august-18-2026) **Chainstack Self-Hosted v2.37.0: August 13, 2026** This release adds Blast and Mantle as deployment targets, each covering Mainnet and Sepolia. Both are OP-Stack rollups, so every deployment pairs an execution node with a rollup node that derives the chain from Ethereum and serves the EVM JSON-RPC and WebSocket APIs. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-37-0-august-13-2026) **Chainstack Self-Hosted v2.33.0: August 12, 2026** This release adds Cronos Mainnet as a deployment target, completing Cronos coverage after Testnet shipped in the previous release. A single cronosd node carries both CometBFT consensus and the embedded EVM, and bootstraps from a pre-built chain snapshot. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-33-0-august-12-2026) **Chainstack Self-Hosted v2.32.0: August 12, 2026** This release adds Berachain and Cronos as deployment targets. Berachain covers Mainnet and the Bepolia testnet, each pairing a Bera-Reth execution node with a Beacond consensus node over CometBFT, and Cronos Testnet runs a single cronosd node that carries both consensus and an embedded EVM. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-32-0-august-12-2026) **Chainstack Self-Hosted v2.29.0: August 11, 2026** This release adds opBNB as a deployment target, covering both Mainnet and Testnet. Each deployment pairs an op-geth execution node with an opbnb-node consensus client, serving the EVM JSON-RPC and WebSocket APIs. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-29-0-august-11-2026) **Chainstack Self-Hosted v2.27.0: August 10, 2026** This release adds Sonic and Stable as deployment targets, covering Mainnet and Testnet on both protocols. Sonic runs the sonicd client, and Stable runs a single stabled node that carries both CometBFT consensus and an embedded EVM. Node workloads now also shut down with a client-appropriate grace period instead of a fixed timeout. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-27-0-august-10-2026) **Chainstack Self-Hosted v2.23.0: August 6, 2026** This release adds XRP Ledger as a deployment target, covering both Mainnet and Testnet. Each deployment runs a single xrpld node as a non-validator in full mode, serving HTTP JSON-RPC and the WebSocket API. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-23-0-august-6-2026) **Chainstack Self-Hosted v2.21.0: August 4, 2026** This release adds Avalanche, Arc, and World Chain as deployment targets. Avalanche runs a single node that serves the whole Primary Network, Arc runs a paired execution and consensus node, and World Chain joins the OP Stack deployments. The bundled Kubernetes Grafana dashboards also render correctly again. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-21-0-august-4-2026) **Chainstack Self-Hosted v2.16.0: July 30, 2026** This release adds Plasma Mainnet and Plasma Testnet as deployment targets, running Reth for execution alongside the PlasmaBFT consensus client as non-validator nodes in full mode. Both networks sync from genesis over the network's public peers. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-16-0-july-30-2026) **Chainstack Self-Hosted v2.14.0: July 29, 2026** This release adds Tempo Mainnet and Tempo Testnet as deployment targets, running as non-validator nodes in archive mode. Tempo deployments bootstrap from a snapshot provided by the client's own software. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-14-0-july-29-2026) **Chainstack Self-Hosted v2.12.0: July 28, 2026** This release adds Sui mainnet and Sui testnet as deployment targets, running the official Sui full node. You can now switch between deploying from a snapshot and syncing from genesis for each deployment. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-12-0-july-28-2026) **Chainstack Self-Hosted v2.10.0: July 27, 2026** This release adds Arbitrum One and Arbitrum Sepolia as deployment targets, running the Arbitrum Nitro client. Ethereum mainnet and Sepolia reth deployments now run in full mode rather than archive mode, matching the pruned snapshots they restore from. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-10-0-july-27-2026) **Chainstack Self-Hosted v2.9.1: July 23, 2026** This maintenance release renames the Robinhood Chain test network from Sepolia to Testnet across the network and deployment preset catalog. Existing deployments are unaffected. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-9-1-july-23-2026) **Chainstack Self-Hosted v2.9.0: July 22, 2026** This release adds Robinhood Chain Sepolia as a new supported test network, deployable with the same workflows as other supported networks. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-9-0-july-22-2026) **Chainstack Self-Hosted v2.8.2: July 22, 2026** This release adds Robinhood Chain support and makes control-plane upgrades safer with zero-downtime rollouts. It also brings update notifications to cpctl and fixes an invalid-configuration case in the new-node wizard. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-8-2-july-22-2026) **Chainstack Self-Hosted v2.7.1: July 17, 2026** This release adds an in-console notification center for update and version alerts, custom node sizing in the deployment wizard, and server-side session revocation on sign-out. It also improves TRON and Polygon network support and makes snapshot restores more reliable. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-7-1-july-17-2026) **Chainstack Self-Hosted v2.4.0: July 6, 2026** This release adds Polygon support, and adds node renaming along with small display fixes. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-4-0-july-6-2026) **Chainstack Self-Hosted v2.3.0: June 22, 2026** This release adds Starknet, Bitcoin, and TRON presets, ships Base and Optimism on Reth, and surfaces protocol logs directly in the Control Panel. It also makes node configuration clearer by hiding settings a preset doesn't expose and supporting static environment variables. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-3-0-june-22-2026) **Chainstack Self-Hosted v2.2.0: June 4, 2026** This release brings node configuration management into the Control Panel, makes disk growth a zero-downtime operation, and rebuilds the orchestration engine for more reliable, observable deployments. It also adds built-in monitoring metrics and a refreshed deployment experience built on the latest design system. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-2-0-june-4-2026) **Chainstack Self-Hosted v2.0.0: May 13, 2026** This release retires Helm from the deployment path in favor of a typed in-process provisioning pipeline backed by Server-Side Apply, and bounds bootstrap polling with an explicit presence state machine so lost storage volumes can no longer hang the worker. Breaking change: remove existing node deployments before upgrading and redeploy them afterwards, otherwise nodes created on v1.9.0 or earlier may misbehave under the new reconciler. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v2-0-0-may-13-2026) **Chainstack Self-Hosted v1.9.0: May 11, 2026** This release adds recovery for failed deployments, a revision audit trail that records who initiated each change, a cp-bolt reaper that clears stuck PersistentVolumeClaims, and a cpctl upgrade option for patching individual containers. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v1-9-0-may-11-2026) **Chainstack Self-Hosted v1.8.0: May 4, 2026** **General availability**. Chainstack Self-Hosted exits beta with this first public release. v1.8.0 also adds three lightweight Ethereum evaluation presets (Mainnet, Sepolia, Hoodi), turns telemetry on by default, simplifies installs and upgrades via `cpctl`, sets explicit Postgres resource limits, and refines the Control Panel side menu and Node Summary layout. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v1-8-0-may-4-2026) **Chainstack Self-Hosted v1.7.1: April 15, 2026** This release adds built-in telemetry management via a new cp-tracer component, improves upgrade reliability with stricter input validation and more resilient preflight checks, and fixes a hard-coded timeout issue in node deployment volume population. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v1-7-1-april-15-2026) **Chainstack Self-Hosted v1.6.0: April 10, 2026** This release adds a human-readable name field to node deployments, making it easier to identify and manage nodes in multi-node installations. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v1-6-0-april-10-2026) **Chainstack Self-Hosted v1.5.0: April 10, 2026** This release redesigns node deployment progress into a unified timeline, adds dark mode to the Control Panel, reduces the frontend bundle size from roughly 1,900 KiB to 57 KiB, and adds support for Reth v1.11.3. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v1-5-0-april-10-2026) **Chainstack Self-Hosted v1.4.6: April 6, 2026** Snapshot-based node bootstrapping, integrated monitoring stack (VictoriaMetrics, Grafana, blockchain-node-exporter), cpctl interactive shell and upgrade command. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v1-4-6-april-6-2026) **Chainstack Self-Hosted v1.0.0: January 28, 2026** **Initial beta release**. Deploy Ethereum Mainnet, Sepolia, and Hoodi full nodes on your own Kubernetes infrastructure using Reth execution client and Prysm consensus client. [Read more](/docs/self-hosted/changelog/chainstack-self-hosted-v1-0-0-january-28-2026) # System requirements Source: https://docs.chainstack.com/docs/self-hosted/requirements Hardware, software, and network requirements for running Chainstack Self-Hosted including CPU, RAM, storage, Kubernetes versions, and port configurations. This page outlines the hardware, software, and network requirements for running Chainstack Self-Hosted. > [!TIP] > > Looking to evaluate the software without dedicated hardware? See [Evaluation setup](/docs/self-hosted/evaluation-setup) for minimal requirements. ## Overview Chainstack Self-Hosted consists of two main components with different resource requirements: 1. **Control Panel** — the management platform that handles deployments, authentication, and orchestration 2. **Blockchain nodes** — the actual blockchain clients that you deploy and manage ## Control Panel requirements The Control Panel runs inside a Kubernetes cluster and includes the web interface, API services, database, and workflow orchestration. ### Hardware requirements | Resource | Minimum | Recommended | | - | - | - | | CPU | 5 cores | 8 cores | | RAM | 6 GiB | 16 GiB | | Storage | 15 GB | 20 GB | > [!NOTE] > > These requirements are for the Control Panel only. Blockchain nodes require additional resources as specified in [Blockchain node requirements](#blockchain-node-requirements). ### Software requirements | Component | Requirement | Notes | | - | - | - | | Kubernetes | Any recent version | k3s, k8s, EKS, GKE, AKS supported | | Helm | v3.x or later | Required for installation | | yq | v4.x or later | mikefarah/yq required | | openssl | Any recent version | Required for certificate generation | | kubectl | Compatible with cluster | Required for cluster management | ### Operating systems The Control Panel is distributed as an umbrella Helm chart and runs on any Kubernetes-compatible environment. The underlying host operating system should be a modern Linux distribution capable of running Kubernetes. Tested configurations: * Ubuntu 22.04 LTS with k3s * Ubuntu 24.04 LTS with k3s ### Kubernetes storage requirements A storage class with dynamic provisioning is required for persistent volumes. The installer creates PersistentVolumeClaims for the PostgreSQL database. Tips for storage classes: * **k3s** — local-path (default) or TopoLVM for multi-disk setups * **Cloud providers** — use the default storage class (gp2/gp3 for AWS, standard for GCP, managed-premium for Azure) * **On-premises** — any CSI-compatible storage provisioner ## Blockchain node requirements Resource requirements vary by protocol, network, and client. The full per-deployment breakdown — clients, versions, vCPU, RAM, storage, and exposed ports — lives in [Supported deployments](/docs/self-hosted/supported-clients-and-protocols). This table summarizes the standard preset totals by architecture family. Use it to size a host, then see the catalog for the exact figures per network. | Family | Example deployments | vCPU | RAM | Storage (steady state) | | - | - | - | - | - | | EVM Layer 1 (execution + consensus) | Ethereum Mainnet, Sepolia, Hoodi | 8 | 32 GiB | 250 GB–2 TB | | EVM Layer 2 — OP Stack | Optimism, Base, Unichain, Zora, World Chain, Blast, Mantle | 6–12 | 24–48 GiB | 100 GB–3.5 TB | | EVM Layer 2 — opBNB | opBNB Mainnet, Testnet | 6–12 | 24–48 GiB | 500 GB–2 TB | | EVM Layer 2 — Arbitrum Nitro | Arbitrum One, Arbitrum Sepolia, Robinhood Chain | 4–8 | 32–64 GiB | 300 GB–2.5 TB | | Polygon PoS | Polygon Mainnet, Amoy | 6–12 | 24–96 GiB | 170 GB–6.65 TB | | Starknet | Starknet Mainnet, Sepolia | 4–8 | 16–32 GiB | 400 GB–1 TB | | TRON | TRON Mainnet, Nile | 4–8 | 16–32 GiB | 200 GB–3 TB | | Bitcoin | Bitcoin Mainnet, Signet, Testnet4 | 4 | 8 GiB | 100 GB–1 TB | | Sui | Sui Mainnet, Testnet | 16 | 64 GiB | 400 GB–2.2 TB | | Tempo | Tempo Mainnet, Testnet | 4 | 16 GiB | 70 GB–1.1 TB | | Plasma | Plasma Mainnet, Testnet | 5 | 20 GiB | 250–350 GB | | Avalanche | Avalanche Mainnet, Fuji | 4 | 16 GiB | 300–400 GB | | Arc | Arc Testnet | 8 | 32 GiB | \~315 GB | | XRP Ledger | XRP Ledger Mainnet, Testnet | 4–8 | 16–32 GiB | 50 GB | | Sonic | Sonic Mainnet, Testnet | 4–8 | 32–64 GiB | 800 GB–2.5 TB | | Stable | Stable Mainnet, Testnet | 4 | 16 GiB | 50–200 GB | | Berachain | Berachain Mainnet, Bepolia | 6–8 | 24–36 GiB | \~165–250 GB | | Cronos | Cronos Mainnet, Testnet | 4–8 | 16–32 GiB | 50–100 GB | | BNB Smart Chain | BNB Smart Chain Mainnet, Testnet | 4 | 16 GiB | 900 GB–5.5 TB | | Kaia | Kaia Mainnet, Kairos | 4–8 | 16–32 GiB | \~1.2–2.75 TB | | Ronin | Ronin Saigon | 7 | 28 GiB | 220 GB | | Hyperliquid | Hyperliquid Testnet, Mainnet | 4–8 | 16–32 GiB | 250–300 GB | | Solana | Solana Mainnet, Devnet | 8–16 | 64–256 GiB | \~2.5–5 TB | | Stellar | Stellar Mainnet, Testnet | 4 | 16 GiB | 64–400 GB | > [!NOTE] > > vCPU and RAM are split across the clients in multi-client families. Ethereum networks also ship a Light preset at half the CPU and RAM with the same storage. See [Supported deployments](/docs/self-hosted/supported-clients-and-protocols) for per-network figures. ### Snapshot bootstrap and storage headroom > [!WARNING] > > Deployments that bootstrap from a pre-built snapshot — currently Ethereum (all networks), Optimism (both networks), Base Sepolia, Unichain Sepolia, World Chain (both networks), Blast Mainnet, Mantle (both networks), opBNB (both networks), Arbitrum (both networks), Robinhood Chain (both networks), Polygon Amoy, Starknet Sepolia, Bitcoin Signet, Sui (both networks), Avalanche Mainnet, Arc Testnet, Sonic Mainnet, Stable (both networks), Berachain (both networks), Cronos (both networks), BNB Smart Chain (both networks), and Solana (both networks) — temporarily require about **2× the steady-state storage**. The node downloads the snapshot archive and then extracts it into the chain data directory, so both copies coexist on disk until extraction completes. Provision the node's persistent volume at this peak; you can keep it sized for headroom afterward. Deployments that sync without a snapshot do not have this peak. Tempo (both networks) also bootstraps from a snapshot, but its client downloads and extracts the archive within the storage figures already listed in the catalog, so it needs no extra headroom for the bootstrap. ### Storage considerations * **SSD type** — TLC NVMe drives are strongly recommended. Avoid QLC drives due to lower write endurance and performance. * **Storage growth** — chain data grows over time, so provision headroom beyond the steady-state figure. Ethereum Mainnet, for example, grows approximately 1 TB per year. * **IOPS and throughput** — high random read/write IOPS are critical for block processing. Target NVMe drives with sustained write throughput of 1+ GB/s. For a deep dive on SSD selection for Ethereum nodes, see [yorickdowne's SSD guide](https://gist.github.com/yorickdowne/f3a3e79a573bf35767cd002cc977b038). ### CPU considerations * Clock speed matters more than core count for blockchain nodes * Higher single-thread performance improves block processing * AMD Ryzen 7000/9000 series or Intel Core 12th gen+ recommended ### Initial sync time Snapshot-bootstrapped deployments — listed under [Snapshot bootstrap and storage headroom](#snapshot-bootstrap-and-storage-headroom) — start from a chain snapshot rather than syncing from genesis. Initial bootstrap completes in minutes to hours depending on network conditions; the node then catches up to the chain head in the background. Other deployments sync conventionally and reach the chain head over a longer period. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times) for the breakdown. ## Network requirements ### Internal communication (within Kubernetes cluster) All Control Panel services communicate internally within the Kubernetes cluster. The following ports are used for internal service-to-service communication: | Service | Port | Protocol | Purpose | | - | - | - | - | | cp-ui | 80 | TCP | Web interface | | cp-auth | 8080, 9090 | TCP | Authentication service | | cp-deployments-api | 8080, 9090 | TCP | Deployment management API | | keycloak | 80, 8080 | TCP | Identity management | | PostgreSQL | 5432 | TCP | Database | | Temporal | 7233–7246 | TCP | Workflow orchestration | ### External access For browser-based UI access, you need to expose **both** the UI service (`cp-cp-ui`) and the deployments API service (`cp-cp-deployments-api`). The browser loads the UI page from `cp-cp-ui` and then calls `cp-cp-deployments-api` directly using the `--backend-url` you set at install time. | Service | Default external port | Purpose | | - | - | - | | `cp-cp-ui` | 80 | Web interface | | `cp-cp-deployments-api` | 8081 (mapped to internal 8080) | Backend API used by the UI | Either service can be exposed using: | Method | Use case | | - | - | | LoadBalancer | Production deployments with external load balancer | | NodePort | Direct access via node IP and port | | Ingress | Production deployments with ingress controller | | Port forward | Development and testing | ### Firewall rules The UI service port (default 80) and the deployments API port (default 8081) must both be reachable from clients that will access the Control Panel through a browser. All other Control Panel services communicate internally inside the cluster. For blockchain nodes, the exposed ports depend on the client. Each deployment's exposed ports are listed per family in [Supported deployments](/docs/self-hosted/supported-clients-and-protocols). For example, EVM execution clients expose 8545 (HTTP JSON-RPC) and 8546 (WS JSON-RPC), and Prysm exposes 5052 (Beacon API). Internal ports — engine auth, P2P, and gRPC — are not exposed. ## Combined infrastructure example Size a host by adding the Control Panel baseline to the deployment you pick from [Supported deployments](/docs/self-hosted/supported-clients-and-protocols). As a worked example, the Control Panel plus one Ethereum Mainnet full node: | Resource | Control Panel | Ethereum Mainnet (Light) | Ethereum Mainnet (standard) | | - | - | - | - | | CPU | 5 cores | + 4 cores = 9 cores | + 8 cores = 13 cores | | RAM | 6 GiB | + 16 GiB = 22 GiB | + 32 GiB = 38 GiB | | Storage (steady state) | 15 GB | + \~2 TB | + \~2 TB | | Storage (during deploy) | 15 GB | + \~4 TB | + \~4 TB | For other deployments, substitute the totals from the catalog and apply the 2× storage peak only to snapshot-bootstrapped deployments (see [Snapshot bootstrap and storage headroom](#snapshot-bootstrap-and-storage-headroom)). ### Example server configurations #### Budget configuration (single Ethereum Mainnet node) * **CPU** — AMD Ryzen 5 7600 or Intel Core i5-13400 * **RAM** — 32 GB DDR5 * **Storage** — 4 TB NVMe SSD #### Testnet configuration (2 Ethereum testnet nodes) * **CPU** — AMD Ryzen 5 7600 or Intel Core i5-13400 * **RAM** — 32 GB DDR5 * **Storage** — 3 TB NVMe SSDs ## Trusted infrastructure partners Chainstack Self-Hosted works on any compatible infrastructure you provision yourself. To simplify hardware procurement, you can use one of our trusted partners that offer servers meeting the requirements on this page. | Partner | | Promo | | - | - | - | | is\*hosting | [Deploy on is\*hosting](https://ishosting.com/en/dedicated/apps/chainstack?utm_source=chainstack\&utm_campaign=docs) | Promocode `CHAINSTACK20` — 20% off the first dedicated server purchase | | Vultr | [Deploy on Vultr](https://www.vultr.com/marketplace/apps/chainstack-self-hosted#general-information?utm_source=chainstack\&utm_campaign=docs) | — | | Hostkey | [Deploy on Hostkey](https://hostkey.com/apps/developer-tools/chainstack/?utm_source=chainstack\&utm_campaign=docs) | — | | Velia | [Deploy on Velia](https://checkout.velia.net/order/product/1e96d298-537d-4ed6-868c-84e120637085/?bcm=1\&utm_source=chainstack\&utm_campaign=docs) | Promocode `ChainstackSH80` — 80% off your first month on all Chainstack products | | Serverside | [Deploy on Serverside](https://serverside.com/en-gb/partners/chainstack?utm_source=chainstack\&utm_campaign=docs) | 10% off — applied automatically via the link | | BreezeHost | [Deploy on BreezeHost](https://www.breezehost.io/chainstack/?utm_source=chainstack\&utm_campaign=docs) | Promocode `CHAINSTACK50` — 50% off your first month on all Chainstack products | ## Next steps Once you've verified your system meets these requirements: 1. [Environment setup](/docs/self-hosted/environment-setup) — Install Kubernetes and required tools 2. [Quick start guide](/docs/self-hosted/quick-start) — Complete walkthrough from zero to running 3. [Installation guide](/docs/self-hosted/installation) — Detailed installation instructions # Support and feedback Source: https://docs.chainstack.com/docs/self-hosted/support Get technical support and submit feedback for Chainstack Self-Hosted to receive direct engineering help and influence the product roadmap. Learn how to get help and provide feedback on Chainstack Self-Hosted. ## Getting help ### Documentation Start with the documentation for answers to common questions: * [Quick start guide](/docs/self-hosted/quick-start) — Installation walkthrough * [Troubleshooting](/docs/self-hosted/troubleshooting) — Common issues and solutions * [FAQ](/docs/self-hosted/faq) — Frequently asked questions ### Technical support For technical issues and questions: **Chainstack Support Center** [https://support.chainstack.com/hc/en-us](https://support.chainstack.com/hc/en-us) When contacting support, please include: ```bash theme={"system"} ./cpctl status > diagnostic.txt kubectl get events -n control-panel >> diagnostic.txt kubectl get events -n control-panel-deployments >> diagnostic.txt timestamp=$(date +%s) touch debug-$timestamp.txt debug=debug-$timestamp.txt for pod in $(kubectl get pods -n control-panel -o name); do echo "=== $pod ===" >> $debug kubectl logs $pod -n control-panel --since=1h 2>/dev/null >> $debug done for pod in $(kubectl get pods -n control-panel-deployments -o name); do echo "=== $pod ===" >> $debug kubectl logs $pod -n control-panel-deployments --since=1h 2>/dev/null >> $debug done ``` 1. **Description of the issue** * What you were trying to do * What happened * What you expected to happen * Steps to reproduce ### Community resources Additional resources for blockchain node operators: * Ethereum documentation: [ethereum.org/developers](https://ethereum.org/developers) * Reth documentation: [reth.rs](https://reth.rs/) * Prysm documentation: [docs.prylabs.network](https://docs.prylabs.network/) ## Providing feedback ### Feature requests Have an idea for a new feature? We want to hear it! **Submit:** [Chainstack Feedback](https://ideas.chainstack.com/en?b=6968ecf76c93cb94db0422c8) When submitting feature requests, please include: * Description of the feature * Your use case / why it's valuable * Any specific requirements or constraints ### General feedback We welcome all feedback — positive or constructive: **Submit:** [Chainstack Feedback](https://ideas.chainstack.com/en?b=6968ecf76c93cb94db0422c8) ## Contact summary | Purpose | Contact | | - | - | | Technical support | [support.chainstack.com](https://support.chainstack.com/hc/en-us) | | Feature requests | [Chainstack Feedback](https://ideas.chainstack.com/en?b=6968ecf76c93cb94db0422c8) | # Supported deployments Source: https://docs.chainstack.com/docs/self-hosted/supported-clients-and-protocols All blockchain protocols, networks, clients, and resource requirements available for deployment in Chainstack Self-Hosted, grouped by architecture family. This page is the canonical catalog of every deployment available in Chainstack Self-Hosted — the protocols, networks, clients, client versions, resource requirements, and exposed ports. The [System requirements](/docs/self-hosted/requirements) and [Deploying nodes](/docs/self-hosted/deploying-nodes) pages link here for the per-deployment details. ## Available deployments | Protocol | Network | Clients | vCPU | RAM | Storage | | - | - | - | - | - | - | | Ethereum | Mainnet | Reth v2.6.0 + Prysm v7.1.8 | 8 | 32 GiB | \~2 TB | | Ethereum | Sepolia | Reth v2.6.0 + Prysm v7.1.8 | 8 | 32 GiB | \~1.3 TB | | Ethereum | Hoodi | Reth v2.6.0 + Prysm v7.1.8 | 8 | 32 GiB | 250 GB | | Optimism | Mainnet | OP-Reth v2.4.4 + OP-Node v1.19.7 | 12 | 48 GiB | \~2 TB | | Optimism | Sepolia | OP-Reth v2.4.4 + OP-Node v1.19.7 | 6 | 24 GiB | 400 GB | | Base | Mainnet | Base-Reth v1.2.0 + Base-Node v1.2.0 | 12 | 48 GiB | \~3.5 TB | | Base | Sepolia | Base-Reth v1.2.0 + Base-Node v1.2.0 | 6 | 24 GiB | \~1 TB | | Unichain | Mainnet | OP-Reth v2.4.4 + OP-Node v1.19.7 | 12 | 48 GiB | \~2 TB | | Unichain | Sepolia | OP-Reth v2.4.4 + OP-Node v1.19.7 | 6 | 24 GiB | 300 GB | | Zora | Mainnet | OP-Reth v2.4.4 + OP-Node v1.19.7 | 12 | 48 GiB | \~2 TB | | Zora | Sepolia | OP-Reth v2.4.4 + OP-Node v1.19.7 | 6 | 24 GiB | 500 GB | | World Chain | Mainnet | World-Chain v2.4.3 + OP-Node v1.19.7 | 12 | 48 GiB | \~1.3 TB | | World Chain | Sepolia | World-Chain v2.4.3 + OP-Node v1.19.7 | 6 | 24 GiB | 100 GB | | Blast | Mainnet | Blast-Geth v1.8.0 + Blast-Node v1.8.0 | 6 | 24 GiB | 500 GB | | Blast | Sepolia | Blast-Geth v1.7.0 + Blast-Node v1.7.0 | 6 | 24 GiB | 200 GB | | Mantle | Mainnet | Mantle-Geth v1.6.3 + Mantle-Node v1.6.3 | 6 | 24 GiB | 600 GB | | Mantle | Sepolia | Mantle-Geth v1.6.3 + Mantle-Node v1.6.3 | 6 | 24 GiB | 300 GB | | opBNB | Mainnet | OP-Geth v0.5.10 + OpBNB-Node v0.5.5 | 12 | 48 GiB | \~2 TB | | opBNB | Testnet | OP-Geth v0.5.10 + OpBNB-Node v0.5.5 | 6 | 24 GiB | 500 GB | | Arbitrum | Mainnet | Nitro v3.11.4 | 8 | 64 GiB | \~2.5 TB | | Arbitrum | Sepolia | Nitro v3.11.4 | 4 | 32 GiB | \~1.5 TB | | Robinhood Chain | Mainnet | Nitro v3.11.4 | 8 | 64 GiB | 300 GB | | Robinhood Chain | Testnet | Nitro v3.11.4 | 4 | 32 GiB | 300 GB | | Polygon PoS | Mainnet | Bor 2.10.1 + Heimdall 0.11.0 | 12 | 96 GiB | \~6.65 TB | | Polygon PoS | Amoy | Bor 2.10.1 + Heimdall 0.11.0 | 6 | 24 GiB | 170 GB | | Starknet | Mainnet | Pathfinder v0.24.0 | 8 | 32 GiB | \~1 TB | | Starknet | Sepolia | Pathfinder v0.24.0 | 4 | 16 GiB | 400 GB | | TRON | Mainnet | Java-Tron GreatVoyage-v4.8.2.2 | 8 | 32 GiB | \~3 TB | | TRON | Nile | Java-Tron GreatVoyage-Nile-v4.8.2.2-PQ1-build1 | 4 | 16 GiB | 200 GB | | Bitcoin | Mainnet | Bitcoin Core 31.1 | 4 | 8 GiB | \~1 TB | | Bitcoin | Signet | Bitcoin Core 31.1 | 4 | 8 GiB | 100 GB | | Bitcoin | Testnet4 | Bitcoin Core 31.1 | 4 | 8 GiB | 100 GB | | Sui | Mainnet | Sui-Node v1.79.1 | 16 | 64 GiB | 400 GB | | Sui | Testnet | Sui-Node v1.80.0 | 16 | 64 GiB | \~2.2 TB | | Tempo | Mainnet | Tempo v1.14.0 | 4 | 16 GiB | 70 GB | | Tempo | Testnet | Tempo v1.14.0 | 4 | 16 GiB | \~1.1 TB | | Plasma | Mainnet | Reth v1.11.3 + PlasmaBFT v1.1.0 | 5 | 20 GiB | 350 GB | | Plasma | Testnet | Reth v1.11.3 + PlasmaBFT v1.1.0 | 5 | 20 GiB | 250 GB | | Avalanche | Mainnet | AvalancheGo v1.15.0 | 4 | 16 GiB | 400 GB | | Avalanche | Fuji | AvalancheGo v1.15.0 | 4 | 16 GiB | 300 GB | | Arc | Testnet | Arc-Execution 0.8.0 + Arc-Consensus 0.8.0 | 8 | 32 GiB | \~315 GB | | XRP Ledger | Mainnet | xrpld 3.3.0 | 8 | 32 GiB | 50 GB | | XRP Ledger | Testnet | xrpld 3.3.0 | 4 | 16 GiB | 50 GB | | Sonic | Mainnet | sonicd v2.2.2 | 8 | 64 GiB | \~2.5 TB | | Sonic | Testnet | sonicd v2.2.2 | 4 | 32 GiB | 800 GB | | Stable | Mainnet | stabled 1.3.3 | 4 | 16 GiB | 200 GB | | Stable | Testnet | stabled 1.8.0-rc1 | 4 | 16 GiB | 50 GB | | Berachain | Mainnet | Bera-Reth 1.4.4 + Beacond 1.4.1 | 6 | 24 GiB | 250 GB | | Berachain | Bepolia | Bera-Reth 1.4.4 + Beacond 1.4.2-rc.0 | 8 | 36 GiB | \~165 GB | | Cronos | Mainnet | cronosd 1.7.8 | 8 | 32 GiB | 100 GB | | Cronos | Testnet | cronosd 1.7.8 | 4 | 16 GiB | 50 GB | | BNB Smart Chain | Mainnet | Reth-BSC v0.1.2 | 4 | 16 GiB | \~5.5 TB | | BNB Smart Chain | Testnet | Reth-BSC v0.1.2 | 4 | 16 GiB | 900 GB | | Kaia | Mainnet | kend v2.2.2 | 8 | 32 GiB | \~2.75 TB | | Kaia | Kairos | kend v2.2.2 | 4 | 16 GiB | \~1.2 TB | | Ronin | Saigon | Conduit-Reth v2.2.3 + OP-Node v1.19.7 + EigenDA-Proxy 2.7.1 | 7 | 28 GiB | 220 GB | | Hyperliquid | Mainnet | hyperliquid mainnet-full | 8 | 32 GiB | 250 GB | | Hyperliquid | Testnet | hyperliquid testnet-full | 4 | 16 GiB | 300 GB | | Solana | Mainnet | Agave v4.2.2 | 16 | 256 GiB | \~5 TB | | Solana | Devnet | Agave v4.2.2 | 8 | 64 GiB | \~2.5 TB | | Stellar | Mainnet | Stellar-RPC v28.0.1 | 4 | 16 GiB | 400 GB | | Stellar | Testnet | Stellar-RPC v28.0.1 | 4 | 16 GiB | 64 GB | > [!NOTE] > > vCPU and RAM figures are the standard preset totals. Ethereum networks also ship a **Light** preset that uses half the CPU and RAM with the same storage — see [EVM Layer 1](#evm-layer-1-execution-+-consensus). Deployments are grouped by architecture family. Each family shares the same client topology, networking guidance, and operational caveats. ## EVM Layer 1 (execution + consensus) Runs a paired execution client and consensus client. vCPU and RAM are split evenly across the two clients. These deployments bootstrap from a pre-built chain snapshot rather than syncing from genesis. Snapshot bootstrap temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Block explorer | | - | - | - | | Ethereum Mainnet | 1 | [etherscan.io](https://etherscan.io) | | Ethereum Sepolia | 11155111 | [sepolia.etherscan.io](https://sepolia.etherscan.io) | | Ethereum Hoodi | 560048 | [hoodi.etherscan.io](https://hoodi.etherscan.io) | Each Ethereum network offers two preset tiers: * Standard — 8 vCPU / 32 GiB total, for production. * Light — 4 vCPU / 16 GiB total with the same storage, for evaluation. You can move between tiers without resizing disks. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Reth | 8545 | TCP | HTTP JSON-RPC | | Reth | 8546 | TCP | WS JSON-RPC | | Prysm | 5052 | TCP | Beacon API (HTTP) | The execution client's auth RPC port (8551) and the consensus client's P2P port (13000) are used internally between clients and are not exposed. ## EVM Layer 2 — OP Stack Runs a paired execution client and rollup (consensus) client. vCPU and RAM are split across the two clients. > [!WARNING] > > OP Stack deployments require an Ethereum Layer 1 **RPC endpoint** and a **Beacon API endpoint** that you supply. The node uses them to derive Layer 2 state from Layer 1. | Network | Chain ID | Clients | Block explorer | | - | - | - | - | | Optimism Mainnet | 10 | OP-Reth + OP-Node | [optimistic.etherscan.io](https://optimistic.etherscan.io) | | Optimism Sepolia | 11155420 | OP-Reth + OP-Node | [sepolia-optimism.etherscan.io](https://sepolia-optimism.etherscan.io) | | Base Mainnet | 8453 | Base-Reth + Base-Node | [basescan.org](https://basescan.org) | | Base Sepolia | 84532 | Base-Reth + Base-Node | [sepolia.basescan.org](https://sepolia.basescan.org) | | Unichain Mainnet | 130 | OP-Reth + OP-Node | [uniscan.xyz](https://uniscan.xyz) | | Unichain Sepolia | 1301 | OP-Reth + OP-Node | [sepolia.uniscan.xyz](https://sepolia.uniscan.xyz) | | Zora Mainnet | 7777777 | OP-Reth + OP-Node | [explorer.zora.energy](https://explorer.zora.energy) | | Zora Sepolia | 999999999 | OP-Reth + OP-Node | [sepolia.explorer.zora.energy](https://sepolia.explorer.zora.energy) | | World Chain Mainnet | 480 | World-Chain + OP-Node | [worldscan.org](https://worldscan.org) | | World Chain Sepolia | 4801 | World-Chain + OP-Node | [worldchain-sepolia.explorer.alchemy.com](https://worldchain-sepolia.explorer.alchemy.com) | | Blast Mainnet | 81457 | Blast-Geth + Blast-Node | [blastscan.io](https://blastscan.io) | | Blast Sepolia | 168587773 | Blast-Geth + Blast-Node | [testnet.blastscan.io](https://testnet.blastscan.io) | | Mantle Mainnet | 5000 | Mantle-Geth + Mantle-Node | [mantlescan.xyz](https://mantlescan.xyz) | | Mantle Sepolia | 5003 | Mantle-Geth + Mantle-Node | [sepolia.mantlescan.xyz](https://sepolia.mantlescan.xyz) | Optimism (both networks), Base Sepolia, Unichain Sepolia, both World Chain networks, Blast Mainnet, and both Mantle networks bootstrap from a pre-built snapshot (plan for \~2× storage during deploy). Base Mainnet, Unichain Mainnet, both Zora networks, and Blast Sepolia sync without a snapshot. > [!NOTE] > > This family spans 6–12 vCPU. The smaller deployments — Optimism, Base, Unichain, and Zora Sepolia, World Chain Sepolia, both Blast networks, and both Mantle networks — run 6 vCPU / 24 GiB, against the 12 vCPU / 48 GiB the Optimism, Base, Unichain, Zora, and World Chain Mainnet deployments take. Blast pins its client versions per network, so Mainnet and Sepolia run different versions — see the [Available deployments](#available-deployments) table. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | OP-Reth / Base-Reth / World-Chain / Blast-Geth / Mantle-Geth | 8545 | TCP | HTTP JSON-RPC | | OP-Reth / Base-Reth / World-Chain / Blast-Geth / Mantle-Geth | 8546 | TCP | WS JSON-RPC | | OP-Node / Base-Node / Blast-Node / Mantle-Node | 9545 | TCP | Rollup HTTP RPC | The execution client's auth RPC port (8551) is used internally between clients and is not exposed. ## EVM Layer 2 — opBNB Runs a paired execution client and rollup (consensus) client. vCPU and RAM are split across the two clients. > [!WARNING] > > opBNB deployments require a BNB Smart Chain Layer 1 **RPC endpoint** that you supply. opBNB settles to BNB Smart Chain rather than Ethereum, so no Beacon API endpoint is needed. Both networks bootstrap from a pre-built chain snapshot rather than syncing from genesis. Snapshot bootstrap temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Block explorer | | - | - | - | | opBNB Mainnet | 204 | [opbnbscan.com](https://opbnbscan.com) | | opBNB Testnet | 5611 | [testnet.opbnbscan.com](https://testnet.opbnbscan.com) | > [!NOTE] > > Testnet runs at half the Mainnet footprint — 6 vCPU / 24 GiB against 12 vCPU / 48 GiB — and needs 500 GB against \~2 TB. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | OP-Geth | 8545 | TCP | HTTP JSON-RPC | | OP-Geth | 8546 | TCP | WS JSON-RPC | | OpBNB-Node | 9545 | TCP | Rollup HTTP RPC | The execution client's auth RPC port (8551) is used internally between clients and is not exposed. ## EVM Layer 2 — Arbitrum Nitro Runs a single Arbitrum Nitro full node client (Nitro v3.11.4). > [!WARNING] > > Arbitrum Nitro deployments require an Ethereum Layer 1 **RPC endpoint** and a **Beacon API endpoint** that you supply. The Nitro node uses them to derive Layer 2 state from Layer 1. These deployments bootstrap from a pre-built chain snapshot rather than syncing from genesis. Snapshot bootstrap temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Block explorer | | - | - | - | | Arbitrum One Mainnet | 42161 | [arbiscan.io](https://arbiscan.io) | | Arbitrum Sepolia | 421614 | [sepolia.arbiscan.io](https://sepolia.arbiscan.io) | | Robinhood Chain Mainnet | 4663 | [robinhoodchain.blockscout.com](https://robinhoodchain.blockscout.com) | | Robinhood Chain Testnet | 46630 | [explorer.testnet.chain.robinhood.com](https://explorer.testnet.chain.robinhood.com) | ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Nitro | 8547 | TCP | HTTP JSON-RPC | | Nitro | 8548 | TCP | WS JSON-RPC | ## Polygon PoS Runs a paired execution client (Bor) and consensus client (Heimdall). vCPU and RAM are split across the two clients. > [!WARNING] > > Polygon PoS requires an Ethereum Layer 1 **RPC endpoint** that you supply. Heimdall uses it to track checkpoints on Ethereum. | Network | Chain ID | Block explorer | | - | - | - | | Polygon Mainnet | 137 | [polygonscan.com](https://polygonscan.com) | | Polygon Amoy | 80002 | [amoy.polygonscan.com](https://amoy.polygonscan.com) | Polygon Amoy bootstraps from a pre-built snapshot (plan for \~2× storage during deploy); Polygon Mainnet syncs without one. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Bor | 8545 | TCP | HTTP JSON-RPC | | Bor | 8546 | TCP | WS JSON-RPC | | Heimdall | 1317 | TCP | REST API (Cosmos LCD) | The Bor P2P port (30303) and the Heimdall CometBFT RPC (26657) and P2P (26656) ports are used internally and are not exposed. ## Starknet Runs a single full node client. The client version is pinned per network, so Mainnet and Sepolia can run different versions — see the [Available deployments](#available-deployments) table. > [!WARNING] > > Starknet requires an Ethereum Layer 1 **RPC endpoint** that you supply. | Network | Block explorer | | - | - | | Starknet Mainnet | [starkscan.co](https://starkscan.co) | | Starknet Sepolia | [sepolia.voyager.online](https://sepolia.voyager.online) | Starknet Sepolia bootstraps from a pre-built snapshot (plan for \~2× storage during deploy); Starknet Mainnet syncs without one. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Pathfinder | 9545 | TCP | HTTP RPC and WS (`/ws`) | ## TRON Runs a single full node client. The client version is pinned per network, so Mainnet and Nile can run different versions — see the [Available deployments](#available-deployments) table. | Network | Block explorer | | - | - | | TRON Mainnet | [tronscan.org](https://tronscan.org) | | TRON Nile | [nile.tronscan.org](https://nile.tronscan.org) | ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Java-Tron | 8090 | TCP | HTTP JSON-RPC | The gRPC API port (50051) is used internally and is not exposed. ## Bitcoin Runs a single full node client (Bitcoin Core 31.1) with the transaction index (`txindex`) enabled. | Network | Block explorer | | - | - | | Bitcoin Mainnet | [blockstream.info](https://blockstream.info) | | Bitcoin Signet | [mempool.space/signet](https://mempool.space/signet/) | | Bitcoin Testnet4 | [mempool.space/testnet4](https://mempool.space/testnet4/) | Bitcoin Signet bootstraps from a pre-built snapshot (plan for \~2× storage during deploy); Bitcoin Mainnet and Testnet4 sync without one. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Bitcoin Core | 8332 | TCP | HTTP JSON-RPC | The P2P port (8333) is used internally and is not exposed. ## Sui Runs a single full node client. The client version is pinned per network, so Mainnet and Testnet can run different versions — see the [Available deployments](#available-deployments) table. These deployments bootstrap from a pre-built chain snapshot rather than syncing from genesis. Snapshot bootstrap temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Block explorer | | - | - | | Sui Mainnet | [suivision.xyz](https://suivision.xyz) | | Sui Testnet | [testnet.suivision.xyz](https://testnet.suivision.xyz) | > [!NOTE] > > Sui Testnet needs more storage than Mainnet — \~2.2 TB against 400 GB. Mainnet bootstraps from a pruned snapshot, while Testnet bootstraps from a full snapshot and additionally ingests state from a checkpoint archive. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Sui-Node | 9000 | TCP | JSON-RPC and gRPC (multiplexed) | The node serves JSON-RPC and gRPC on the same port — there is no separate gRPC listener. The P2P port (8084/UDP) and the validator network address (8080) are used internally and are not exposed. ## Tempo Runs a single full node client (Tempo v1.14.0) that serves both the execution and the consensus layers, so there is no separate consensus client to configure. Each node runs as a non-validator follower of an upstream endpoint, in archive mode. These deployments bootstrap from a chain snapshot that the client downloads itself rather than syncing from genesis. Unlike the other snapshot-bootstrapped families, Tempo needs no extra storage headroom for the bootstrap — the listed storage figures already cover the temporary peak while the archive is downloaded and extracted. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Block explorer | | - | - | - | | Tempo Mainnet | 4217 | [explore.tempo.xyz](https://explore.tempo.xyz) | | Tempo Testnet | 42431 | [explore.testnet.tempo.xyz](https://explore.testnet.tempo.xyz) | > [!NOTE] > > Tempo Testnet needs considerably more storage than Mainnet — \~1.1 TB against 70 GB — because the test network carries far more history. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Tempo | 8545 | TCP | HTTP JSON-RPC | | Tempo | 8546 | TCP | WS JSON-RPC | Both listeners serve the `eth`, `net`, `web3`, `debug`, and `trace` namespaces. The P2P port (30303 on TCP for RLPx and UDP for peer discovery) is used internally and is not exposed. ## Plasma Runs a paired execution client and consensus client — Reth for execution and the PlasmaBFT observer for consensus — as a non-validator node in full mode. Reth takes 4 vCPU / 16 GiB and PlasmaBFT takes 1 vCPU / 4 GiB. These deployments sync from genesis over the network's public peers rather than restoring from a snapshot, so they need no extra storage headroom beyond the listed storage figures. Expect a longer initial sync than a snapshot-bootstrapped deployment — see [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Block explorer | | - | - | - | | Plasma Mainnet | 9745 | [plasmascan.to](https://plasmascan.to) | | Plasma Testnet | 9746 | [testnet.plasmascan.to](https://testnet.plasmascan.to) | Storage splits into 300 GB for Reth and 50 GB for PlasmaBFT on Mainnet, and 200 GB for Reth and 50 GB for PlasmaBFT on Testnet. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Reth | 8545 | TCP | HTTP JSON-RPC | | Reth | 8546 | TCP | WS JSON-RPC | | PlasmaBFT | 35070 | TCP | Consensus API (HTTP) | Both Reth listeners serve the `eth`, `net`, `web3`, `txpool`, and `debug` namespaces. The execution client's auth RPC port (8551) carries the engine API between the two clients, and the consensus client's P2P port (34070) and telemetry port (9745) are internal — none of the three are exposed. ## Avalanche Runs a single AvalancheGo full node as a non-validator in full mode. One node serves the whole Primary Network, so there is no separate client per chain. Mainnet bootstraps from a pre-built chain snapshot, which temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. Fuji syncs from genesis over the network's public peers and needs no extra headroom. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Client | Block explorer | | - | - | - | - | | Avalanche Mainnet | 43114 | AvalancheGo v1.15.0 | [snowscan.xyz](https://snowscan.xyz) | | Avalanche Fuji | 43113 | AvalancheGo v1.15.0 | [testnet.snowscan.xyz](https://testnet.snowscan.xyz) | The client version is pinned per network, so Mainnet and Fuji can run different versions. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | AvalancheGo | 9650 | TCP | HTTP and WS JSON-RPC for all three chains | All chain endpoints share port 9650 and differ only by path — `/ext/bc/C/rpc` for C-Chain HTTP, `/ext/bc/C/ws` for C-Chain WebSocket, `/ext/bc/X` for X-Chain HTTP, and `/ext/P` for P-Chain HTTP. The node overview lists all four. Only the C-Chain offers a WebSocket endpoint; the X-Chain's pubsub endpoint was removed upstream and nothing replaced it. The P2P port (9651) is internal and is not exposed. ## Arc Runs a paired execution client and consensus client as a non-validator node in full mode, following the network's public endpoints. Arc-Execution takes 6 vCPU / 24 GiB and Arc-Consensus takes 2 vCPU / 8 GiB. These deployments bootstrap from a pre-built chain snapshot rather than syncing from genesis. Snapshot bootstrap temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Block explorer | | - | - | - | | Arc Testnet | 5042002 | [testnet.arcscan.app](https://testnet.arcscan.app) | Storage splits into 250 GB for Arc-Execution and 64 GB for Arc-Consensus. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Arc-Execution | 8545 | TCP | HTTP JSON-RPC | | Arc-Execution | 8546 | TCP | WS JSON-RPC | Both listeners serve the `eth`, `net`, `web3`, `txpool`, `trace`, and `debug` namespaces. Arc-Consensus publishes nothing of its own — its RPC port is the internal upstream of the execution client's `arc` namespace. The execution client's auth RPC port (8551), the consensus client's RPC (31000) and P2P (27000) ports, and both metrics ports are internal and are not exposed. ## XRP Ledger Runs a single xrpld full node as a non-validator in full mode. One process serves both the HTTP JSON-RPC and the WebSocket API. Both networks sync from the network's peers rather than from a snapshot, so they need no extra storage headroom. The node keeps only recent ledger history: online deletion retains the most recent 1,000 ledgers. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Block explorer | | - | - | | XRP Ledger Mainnet | [livenet.xrpl.org](https://livenet.xrpl.org) | | XRP Ledger Testnet | [testnet.xrpl.org](https://testnet.xrpl.org) | > [!NOTE] > > Testnet runs at half the Mainnet compute — 4 vCPU / 16 GiB against 8 vCPU / 32 GiB — and both networks need 50 GB of storage. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | xrpld | 5005 | TCP | HTTP JSON-RPC | | xrpld | 6006 | TCP | WebSocket API | The XRP Ledger subscription methods are available over the WebSocket API only. The peer protocol port (2459) is internal and is not exposed. ## Sonic Runs a single sonicd full node as a non-validator in full mode. One process serves both the HTTP and the WebSocket JSON-RPC. Mainnet bootstraps from a pre-built chain snapshot, which temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. Testnet starts from a pruned genesis export and syncs the remainder from the network, so expect a longer first sync and no extra storage headroom. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Client | Block explorer | | - | - | - | - | | Sonic Mainnet | 146 | sonicd v2.2.2 | [sonicscan.org](https://sonicscan.org) | | Sonic Testnet | 14601 | sonicd v2.2.2 | [testnet.sonicscan.org](https://testnet.sonicscan.org) | ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | sonicd | 18545 | TCP | HTTP JSON-RPC | | sonicd | 18546 | TCP | WS JSON-RPC | Both listeners serve the `eth`, `web3`, `net`, `ftm`, `txpool`, `abft`, `dag`, `trace`, and `debug` namespaces. The P2P port (5050, TCP and UDP) and the metrics port (6060) are internal and are not exposed. ## Stable Runs a single stabled full node as a non-validator in full mode. One process carries both the CometBFT consensus engine and an embedded EVM, so a single node serves the chain end to end and there is no separate consensus client to provision. Both networks bootstrap from a pre-built chain snapshot, which temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Client | Block explorer | | - | - | - | - | | Stable Mainnet | 988 | stabled 1.3.3 | [stablescan.xyz](https://stablescan.xyz) | | Stable Testnet | 2201 | stabled 1.8.0-rc1 | [testnet.stablescan.xyz](https://testnet.stablescan.xyz) | The client version and the consensus timing are pinned per network, so Mainnet and Testnet run different versions and different values. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | stabled | 8545 | TCP | EVM HTTP JSON-RPC | | stabled | 8546 | TCP | EVM WS JSON-RPC | Both listeners serve the `eth`, `net`, `web3`, `debug`, and `txpool` namespaces. The CometBFT RPC port (26657) is internal — it carries the node health surface, not a customer endpoint. The CometBFT P2P port (26656) and the metrics port (26660) are internal as well, and the Cosmos gRPC server is bound to loopback, so none of them are exposed. ## Berachain Runs a paired execution client and consensus client as a non-validator node in full mode. Bera-Reth is a Reth-based execution client that serves the JSON-RPC, and Beacond is Berachain's BeaconKit consensus client running on CometBFT with block building disabled. These deployments bootstrap from a pre-built chain snapshot rather than syncing from genesis. Snapshot bootstrap temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Client | Block explorer | | - | - | - | - | | Berachain Mainnet | 80094 | Bera-Reth 1.4.4 + Beacond 1.4.1 | [berascan.com](https://berascan.com) | | Berachain Bepolia | 80069 | Bera-Reth 1.4.4 + Beacond 1.4.2-rc.0 | [testnet.berascan.com](https://testnet.berascan.com) | The Beacond version is pinned per network, so Mainnet and Bepolia run different versions. Compute and storage also differ per network — Mainnet splits 4 vCPU / 16 GiB and 200 GB for Bera-Reth with 2 vCPU / 8 GiB and 50 GB for Beacond, and Bepolia splits 6 vCPU / 24 GiB and 100 GB for Bera-Reth with 2 vCPU / 12 GiB and 64 GB for Beacond. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Bera-Reth | 8545 | TCP | HTTP JSON-RPC | | Bera-Reth | 8546 | TCP | WS JSON-RPC | Both listeners serve the `eth`, `net`, `web3`, `txpool`, and `debug` namespaces. Beacond publishes nothing of its own — its CometBFT RPC port carries the node health surface, and its ABCI proxy app is bound to loopback. The execution client's auth RPC port (8551), its P2P port (30303, TCP and UDP), Beacond's RPC (26657) and P2P (26656) ports, and both metrics ports are internal and are not exposed. ## Cronos Runs a single cronosd full node as a non-validator in full mode. One process carries both the CometBFT consensus engine and an embedded EVM, so a single node serves the chain end to end and there is no separate consensus client to provision. Both networks bootstrap from a pre-built chain snapshot, which temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Client | Block explorer | | - | - | - | - | | Cronos Mainnet | 25 | cronosd 1.7.8 | [explorer.cronos.org](https://explorer.cronos.org) | | Cronos Testnet | 338 | cronosd 1.7.8 | [explorer.cronos.org/testnet](https://explorer.cronos.org/testnet) | The node stores state with the RocksDB backend, under the network's default pruning policy. Cronos Testnet additionally enables versioned state. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | cronosd | 8545 | TCP | EVM HTTP JSON-RPC | | cronosd | 8546 | TCP | EVM WS JSON-RPC | Both listeners serve the `eth`, `net`, `web3`, `debug`, and `txpool` namespaces. The CometBFT RPC port (26657) is internal — it carries the node health surface, not a customer endpoint. The CometBFT P2P port (26656) and the metrics port (26660) are internal as well. ## BNB Smart Chain Runs a single reth-bsc full node in pruned mode. One process serves the chain end to end, so there is no separate consensus client to provision. Both networks bootstrap from a pre-built chain snapshot, which temporarily requires about **2× the steady-state storage** while the archive is downloaded and extracted — provision the node's volume at the peak. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Client | Block explorer | | - | - | - | - | | BNB Smart Chain Mainnet | 56 | Reth-BSC v0.1.2 | [bscscan.com](https://bscscan.com) | | BNB Smart Chain Testnet | 97 | Reth-BSC v0.1.2 | [testnet.bscscan.com](https://testnet.bscscan.com) | The node runs in pruned mode, keeping recent state rather than the full history. Mainnet is the larger of the two by a wide margin — size the volume from the [Available deployments](#available-deployments) figures, not from the testnet. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Reth-BSC | 8545 | TCP | HTTP JSON-RPC | | Reth-BSC | 8546 | TCP | WS JSON-RPC | Both listeners serve the `eth`, `net`, `web3`, `txpool`, and `debug` namespaces. The P2P port (30303, TCP and UDP) and the metrics port (9001) are internal. ## Kaia Runs a single kend full node with full sync and full garbage collection. One process serves the chain end to end, so there is no separate consensus client to provision. Both networks sync from genesis rather than from a snapshot, so expect a long first sync. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Client | Block explorer | | - | - | - | - | | Kaia Mainnet | 8217 | kend v2.2.2 | [kaiascan.io](https://kaiascan.io) | | Kaia Kairos | 1001 | kend v2.2.2 | [kairos.kaiascan.io](https://kairos.kaiascan.io) | Full garbage collection keeps the state needed to serve current-block queries. The node's transaction pool is sized well above the client default, so it holds a deeper queue of pending transactions under load. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | kend | 8551 | TCP | HTTP JSON-RPC | | kend | 8552 | TCP | WS JSON-RPC | Both listeners serve Kaia's native `klay` namespace alongside the standard `eth`, `net`, `web3`, and `debug` namespaces, so Kaia-specific and Ethereum-compatible calls reach the same endpoint. The P2P port (32323, TCP and UDP), its subport (32324), and the metrics port (61001) are internal. ## Ronin Runs an OP Stack execution client and rollup client alongside a data-availability proxy. Ronin posts its rollup batches to EigenDA rather than to Ethereum calldata, so the rollup client reads generic commitments and resolves the underlying blobs through the proxy. CPU and RAM are split across the three clients. > [!WARNING] > > This family needs three endpoints you supply: an Ethereum Layer 1 RPC endpoint, a Beacon API endpoint, and outbound reachability to the EigenDA disperser. The rollup stalls at the last derived block if any of the three is unavailable. | Network | Chain ID | Clients | Block explorer | | - | - | - | - | | Ronin Saigon | 202601 | Conduit-Reth v2.2.3 + OP-Node v1.19.7 + EigenDA-Proxy 2.7.1 | [saigon-explorer.roninchain.com](https://saigon-explorer.roninchain.com) | The execution client bootstraps from a published state export instead of replaying history from genesis, so it reaches the chain head without a full sync. Transactions submitted to the node are forwarded to the network's sequencer. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Conduit-Reth | 8545 | TCP | HTTP JSON-RPC | | Conduit-Reth | 8546 | TCP | WS JSON-RPC | | OP-Node | 9545 | TCP | Rollup HTTP RPC | Both execution listeners serve the `eth`, `net`, `web3`, `trace`, `debug`, and `txpool` namespaces. The engine auth RPC port (8551), the execution P2P port (30303), and the proxy's REST (3100) and metrics (7300) ports are internal — the rollup client reaches the proxy inside the deployment, not from outside it. ## Hyperliquid Runs a single full node that serves the EVM JSON-RPC. One process serves the chain end to end, so there is no separate consensus client to provision. The network syncs from its public peers rather than from a snapshot. See [Initial sync times](/docs/self-hosted/deploying-nodes#initial-sync-times). | Network | Chain ID | Client | Block explorer | | - | - | - | - | | Hyperliquid Mainnet | 999 | hyperliquid mainnet-full | [hyperevmscan.io](https://hyperevmscan.io) | | Hyperliquid Testnet | 998 | hyperliquid testnet-full | [app.hyperliquid-testnet.xyz/explorer](https://app.hyperliquid-testnet.xyz/explorer) | The node prunes its logs on a rolling 48-hour window, so steady-state disk use stays flat over long runs rather than growing with uptime. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | hyperliquid | 3001 | TCP | HTTP JSON-RPC | The single HTTP listener carries the EVM JSON-RPC. There is no separate WebSocket listener on this deployment. The gossip ports (4001 and 4002) are internal. ## Solana Runs a single full node (validator in RPC mode) on the Agave client. The client version is the same across networks — see the [Available deployments](#available-deployments) table. | Network | Block explorer | | - | - | | Solana Mainnet | [explorer.solana.com](https://explorer.solana.com/?cluster=mainnet-beta) | | Solana Devnet | [explorer.solana.com](https://explorer.solana.com/?cluster=devnet) | Both networks bootstrap from a pre-built snapshot (plan for \~2× storage during deploy). ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Agave | 8899 | TCP | HTTP JSON-RPC | | Agave | 8900 | TCP | WS JSON-RPC | ## Stellar Runs a single stellar-rpc node, which manages a Captive Core process internally for ledger sync and validation. The client version is the same across networks — see the [Available deployments](#available-deployments) table. | Network | Block explorer | | - | - | | Stellar Mainnet | [stellar.expert](https://stellar.expert/explorer/public) | | Stellar Testnet | [stellar.expert](https://stellar.expert/explorer/testnet) | > [!NOTE] > > Stellar Mainnet needs considerably more storage than Testnet — 400 GB against 64 GB — because Mainnet carries far more ledger history. ### Exposed ports | Client | Port | Protocol | Purpose | | - | - | - | - | | Stellar-RPC | 8000 | TCP | HTTP JSON-RPC | Captive Core's internal HTTP and peer ports are used only for the node's own subprocess and are not exposed. # Troubleshooting Source: https://docs.chainstack.com/docs/self-hosted/troubleshooting Diagnose and resolve common Chainstack Self-Hosted issues including pod failures, storage problems, network connectivity, and node synchronization errors. This guide helps you diagnose and resolve common issues with Chainstack Self-Hosted. ## Installation troubleshooting ### Prerequisites check fails Installer fails to verify the requirements. #### kubectl not found ```bash theme={"system"} # Verify kubectl is installed which kubectl # For k3s, ensure KUBECONFIG is set export KUBECONFIG=/etc/rancher/k3s/k3s.yaml ``` #### Helm version too old ```bash theme={"system"} # Check version (v3+ required) helm version # Update Helm if needed curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash ``` #### yq not found or wrong version ```bash theme={"system"} # Must be mikefarah/yq v4+ yq --version # Install correct version wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq chmod +x /usr/local/bin/yq ``` ### Installation times out Helm install hangs or times out after 15 minutes. ```bash theme={"system"} # Check pod status kubectl get pods -n control-panel # Look for pods stuck in Pending or CrashLoopBackOff kubectl describe pod -n control-panel ``` #### PersistentVolumeClaim pending ```bash theme={"system"} kubectl get pvc -n control-panel kubectl describe pvc -n control-panel ``` Ensure storage class exists and is default, and verify sufficient disk space. #### Image pull errors ```bash theme={"system"} kubectl describe pod -n control-panel | grep Events ``` Check network connectivity to container registry and verify no firewall is blocking image pulls. #### Insufficient resources ```bash theme={"system"} kubectl describe node | grep -A 10 "Allocated resources" ``` Ensure node has enough CPU and memory. ### Pods crash looping Pods repeatedly restart (CrashLoopBackOff status). ```bash theme={"system"} # Check pod logs kubectl logs -n control-panel # Check previous container logs if crashing kubectl logs -n control-panel --previous # Describe pod for events kubectl describe pod -n control-panel ``` #### Database connection issues Check PostgreSQL pod is running and verify database credentials match. ```bash theme={"system"} kubectl logs cp-pg-postgresql-0 -n control-panel ``` #### Configuration errors Review the values file for typos and ensure all required environment variables are set. ## Access troubleshooting ### Cannot access web interface Browser cannot reach the Control Panel. #### Verify UI service exists ```bash theme={"system"} kubectl get svc cp-cp-ui -n control-panel ``` #### Check UI pod is running ```bash theme={"system"} kubectl get pods -l app.kubernetes.io/name=cp-ui -n control-panel ``` #### Verify external service ```bash theme={"system"} kubectl get svc -n control-panel | grep -E "(LoadBalancer|NodePort)" ``` #### Test with port forward ```bash theme={"system"} kubectl port-forward svc/cp-cp-ui 8080:80 -n control-panel --address 0.0.0.0 ``` Then access `http://:8080` #### Check firewall Ensure the port is open on your server and check cloud provider security groups if applicable. ### Login fails with valid credentials The login page loads and accepts credentials, but shows "Login failed". #### Backend URL points to in-cluster address This is the most common cause. The UI is configured to reach the deployments API at an in-cluster URL that your browser cannot resolve. 1. Check the configured backend URL: ```bash theme={"system"} kubectl get configmap cp-cp-ui-config -n control-panel -o jsonpath='{.data.config\.json}' ``` If the output shows `{"apiBaseUrl":"http://cp-cp-deployments-api"}`, the UI is using the in-cluster URL. 2. Patch the configmap to use your server's external address: ```bash theme={"system"} kubectl patch configmap cp-cp-ui-config -n control-panel \ --type merge \ -p '{"data":{"config.json":"{\"apiBaseUrl\":\"http://:8081\"}"}}' ``` 3. Restart the UI pod to pick up the change: ```bash theme={"system"} kubectl rollout restart deployment cp-cp-ui -n control-panel ``` 4. Expose the deployments API on port 8081: ```bash theme={"system"} kubectl port-forward svc/cp-cp-deployments-api 8081:8080 -n control-panel --address 0.0.0.0 & ``` 5. Hard-refresh the login page and try again. > [!NOTE] > > This happens when the installer runs with `--yes` or when you press Enter at the backend URL prompt, accepting the in-cluster default. The in-cluster URL works for pod-to-pod communication but is not reachable from a browser outside the cluster. #### Check auth service ```bash theme={"system"} kubectl get pods -l app.kubernetes.io/name=cp-auth -n control-panel kubectl logs -l app.kubernetes.io/name=cp-auth -n control-panel ``` #### Check Keycloak ```bash theme={"system"} kubectl get pods -l app.kubernetes.io/name=keycloak -n control-panel kubectl logs cp-keycloak-0 -n control-panel ``` #### Verify credentials from values file ```bash theme={"system"} grep BOOTSTRAP ~/.config/cp-suite/values/cp-*.yaml ``` ## Node deployment troubleshooting ### Node stuck in deploying state Node deployment does not progress. #### Check workflows service ```bash theme={"system"} kubectl logs -l app.kubernetes.io/name=cp-workflows -n control-panel ``` #### Check Temporal ```bash theme={"system"} kubectl get pods -n control-panel | grep temporal ``` #### Verify resource availability ```bash theme={"system"} kubectl describe nodes | grep -A10 "Allocated resources" ``` #### Check deployments API logs ```bash theme={"system"} kubectl logs -l app.kubernetes.io/name=cp-deployments-api -n control-panel ``` ### Node pod errors Blockchain node pods run in the `control-panel-deployments` namespace. Use the node ID from **Chainstack Self-Hosted > Nodes > Node details** to find the relevant pods. #### Check node pod status ```bash theme={"system"} kubectl get pods -n control-panel-deployments | grep ``` #### Check node pod logs ```bash theme={"system"} # Execution client (reth) kubectl logs -f -n control-panel-deployments # Previous container logs if crashing kubectl logs -n control-panel-deployments --previous ``` #### Check node PVC status ```bash theme={"system"} kubectl get pvc -n control-panel-deployments | grep ``` ### Transient `PopulatorDataSourceNotFound` warning During the first \~10 seconds of a node deployment, `kubectl get events` may show a warning on the reth PVC: ```text theme={"system"} Warning PopulatorDataSourceNotFound ... Data source control-panel-deployments/cp--reth-snapshot not found ``` This is a brief race condition between PVC creation and the snapshot data source being created by the Control Panel workflows. It resolves automatically once `cp-workflows` finishes the post-apply hook (typically within 10–15 seconds). No action is required. If the warning persists for more than a minute, check `cp-workflows` and `cp-bolt` logs: ```bash theme={"system"} kubectl logs -n control-panel -l app.kubernetes.io/name=cp-workflows --tail=50 kubectl logs -n control-panel -l app.kubernetes.io/name=cp-bolt --tail=50 ``` ## System health checks ### Check all components Run this comprehensive health check: ```bash theme={"system"} #!/bin/bash echo "=== Checking Control Panel Health ===" echo -e "\n--- Pod Status (Control Panel) ---" kubectl get pods -n control-panel echo -e "\n--- Pod Status (Node deployments) ---" kubectl get pods -n control-panel-deployments echo -e "\n--- Services ---" kubectl get svc -n control-panel echo -e "\n--- PVC Status (Control Panel) ---" kubectl get pvc -n control-panel echo -e "\n--- PVC Status (Node deployments) ---" kubectl get pvc -n control-panel-deployments echo -e "\n--- Recent Events ---" kubectl get events -n control-panel --sort-by='.lastTimestamp' | tail -20 kubectl get events -n control-panel-deployments --sort-by='.lastTimestamp' | tail -20 ``` ### Check resource usage ```bash theme={"system"} # Node resources kubectl top nodes # Pod resources (requires metrics-server) kubectl top pods -n control-panel ``` ### Check logs for errors ```bash theme={"system"} # All pods, last hour timestamp=$(date +%s) touch debug-$timestamp.txt debug=debug-$timestamp.txt for pod in $(kubectl get pods -n control-panel -o name); do echo "=== $pod ===" >> $debug kubectl logs $pod -n control-panel --since=1h 2>/dev/null >> $debug done for pod in $(kubectl get pods -n control-panel-deployments -o name); do echo "=== $pod ===" >> $debug kubectl logs $pod -n control-panel-deployments --since=1h 2>/dev/null >> $debug done ``` ## Recovery procedures ### Restart all services ```bash theme={"system"} # Restart all deployments kubectl rollout restart deployment -n control-panel # Watch pods restart kubectl get pods -n control-panel -w ``` ### Reset to clean state > [!WARNING] > > This destroys all data. Only use as last resort. ```bash theme={"system"} # Uninstall completely cpctl uninstall # Reinstall cpctl install ``` ### Recover from database issues If PostgreSQL has issues: ```bash theme={"system"} # Check PostgreSQL logs kubectl logs cp-pg-postgresql-0 -n control-panel # Check PVC status kubectl get pvc -n control-panel | grep postgresql ``` ## Getting help If you cannot resolve an issue, check the [FAQ](/docs/self-hosted/faq) for known issues. ### Gather diagnostic information ```bash theme={"system"} cpctl status > diagnostic.txt kubectl get events -n control-panel >> diagnostic.txt kubectl get events -n control-panel-deployments >> diagnostic.txt timestamp=$(date +%s) touch debug-$timestamp.txt debug=debug-$timestamp.txt for pod in $(kubectl get pods -n control-panel -o name); do echo "=== $pod ===" >> $debug kubectl logs $pod -n control-panel --since=1h 2>/dev/null >> $debug done for pod in $(kubectl get pods -n control-panel-deployments -o name); do echo "=== $pod ===" >> $debug kubectl logs $pod -n control-panel-deployments --since=1h 2>/dev/null >> $debug done ``` ### Contact support * [Chainstack Support Center](https://support.chainstack.com/hc/en-us) * [Chainstack Feedback](https://ideas.chainstack.com/en?b=6968ecf76c93cb94db0422c8) When contacting support, include: * Version of Chainstack Self-Hosted * Kubernetes distribution and version * `diagnostic.txt` file * `debug-$timestamp.txt` file * Other relevant error messages and logs * Steps to reproduce the issue # Uninstallation Source: https://docs.chainstack.com/docs/self-hosted/uninstallation Completely remove Chainstack Self-Hosted from your Kubernetes cluster using cpctl, including cleanup of persistent volumes and namespace resources. > [!WARNING] > > Uninstallation permanently deletes all data including deployed nodes, configuration, and user data. This cannot be undone. Use `--keep-pvcs` if you want to preserve blockchain node data. ## Basic uninstall ```bash theme={"system"} cpctl uninstall ``` This removes the Helm release and deletes all associated PersistentVolumeClaims. You will be prompted to confirm before anything is deleted. ## Preserve data To remove the Control Panel but keep the blockchain node data on disk: ```bash theme={"system"} cpctl uninstall --keep-pvcs ``` This is useful if you plan to reinstall and reattach the existing volumes. ## Preview before deleting To see what would be deleted without actually deleting anything: ```bash theme={"system"} cpctl uninstall --dry-run ``` ## Uninstall flags | Flag | Description | Default | | - | - | - | | `--keep-pvcs` | Preserve PersistentVolumeClaims (blockchain node data) | `false` | | `--dry-run` | Preview resources to be deleted without deleting | `false` | | `--delete-namespace` | Delete the Control Panel namespace after uninstall | `false` | | `--delete-workload-namespace` | Delete the workload namespace after uninstall | `false` | | `--workload-namespace` | Workload namespace name | `-deployments` | | `--force` | Force removal: strip finalizers, kill stuck pods, delete namespace | `false` | | `--timeout` | Timeout for the uninstall operation | `5m0s` | | `-y, --yes` | Skip confirmation prompts (for automation) | `false` | | `--log-file` | Write operations log to a file | — | # Upgrade Source: https://docs.chainstack.com/docs/self-hosted/upgrade Upgrade your Chainstack Self-Hosted installation to the latest version using cpctl, with pre-upgrade checks and rollback guidance for safe updates. Use `cpctl upgrade` to update the Control Panel to a new version in place. ## Before you upgrade * **Running nodes are unaffected.** The upgrade only restarts Control Panel pods. Blockchain node pods in `control-panel-deployments` continue running throughout. * **No downgrades.** Before any changes are made, `cpctl upgrade` runs the following preflight checks: `ClusterAccess`, `ReleaseExists`, `ReleaseNotPending`, `VersionCompatibility`, `NotDowngrade`, `MaxMajorJump`, `PendingPods`. If any check fails, the upgrade is aborted. * **Values are backed up automatically.** Before applying changes, your current values are saved to `~/.config/cp-suite/values/pre-upgrade---.yaml`. Pass `--no-backup` to skip this. ## Upgrade ```bash theme={"system"} cpctl upgrade -v VERSION ``` > [!NOTE] > > Replace `VERSION` with the target version — see [Release notes](/docs/self-hosted/release-notes) for available versions. The upgrade is atomic: if the deployment fails or times out, Helm automatically rolls back to the previous revision. After passing preflight checks and backing up your values, `cpctl upgrade` computes a server-side diff and presents an upgrade plan before prompting for confirmation: ```text theme={"system"} ╭────────────────────────────────────────────────────────────────────────╮ │ Upgrade Plan │ ├────────────────────────────────────────────────────────────────────────┤ │ Release: cp (namespace: control-panel) │ │ From: cp-distributed v1.7.1 (rev 1) │ │ To: cp-distributed v1.8.0 │ │ Values: preserved (backed up) │ │ Mode: atomic (auto-rollback on fail) │ │ Backup: /root/.config/cp-suite/values/pre-upgrade-cp-1-…│ ├────────────────────────────────────────────────────────────────────────┤ │ Changes: │ │ ~ Deployment/cp-cp-ui (containers, metadata.labels, pod.labels) │ │ ~ Deployment/cp-cp-deployments-api (containers, initContainers, …) │ │ + Job/cp-temporal-schema-2 │ │ - Job/cp-temporal-schema-1 │ │ ... and more │ ╰────────────────────────────────────────────────────────────────────────╯ Proceed with upgrade? [y/N]: ``` On success: ```text theme={"system"} ✓ Upgrade to v1.8.0 completed (revision 1 → 2) ``` After a successful upgrade, a journal is written to your config directory for auditability. ## Upgrade flags | Flag | Description | Default | | - | - | - | | `-v, --version` | Chart version to upgrade to (required) | — | | `-f, --values` | Path to custom values file | — | | `--set key=value` | Set Helm values (repeatable) | — | | `-s, --storage-class` | Storage class for persistent volumes | — | | `--chart-registry` | OCI registry URL | Public CP registry | | `--show-diff` | Show full resource-level diff (not just summary) | `false` | | `--no-diff` | Skip diff preview | `false` | | `--no-backup` | Skip values backup before upgrade | `false` | | `--ignore-preflight-errors` | Comma-separated preflight check names to skip; use `all` to skip all | — | | `--preflight-timeout` | Preflight phase timeout | `1m0s` | | `--skip-version-check` | Bypass version compatibility validation | `false` | | `--reset-values` | Reset values to chart defaults, discarding current values | `false` | | `--force` | Force resource update through delete/recreate. Causes brief service disruption. Use only when a normal upgrade fails due to immutable field changes. | `false` | | `--timeout` | Helm upgrade timeout | `15m0s` | | `-y, --yes` | Skip confirmation prompts | `false` | ## Troubleshooting If an upgrade fails, check the upgrade journal for a detailed error log: ```bash theme={"system"} cat ~/.config/cp-suite/upgrade-journal.jsonl # or inspect the pre-upgrade backup ls ~/.config/cp-suite/values/pre-upgrade-*.yaml ``` To preview what an upgrade would change without applying anything: ```bash theme={"system"} cpctl upgrade -v VERSION --show-diff --dry-run ``` ## Next steps * [Uninstallation](/docs/self-hosted/uninstallation) — Remove Chainstack Self-Hosted from your cluster * [Installation](/docs/self-hosted/installation) — Reinstall from scratch # Send Warp transactions with web3.js, ethers.js, web3.py Source: https://docs.chainstack.com/docs/sending-warp-transaction-with-web3js-ethersjs-web3py-and-ethclientgo Send Warp transactions through Trader Nodes using web3.js, ethers.js, web3.py, and Go ethclient for faster block inclusion via bloXroute relay. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * Chainstack Trader Nodes use a bloXroute-powered BDN behind the scenes to send your transactions to validators at high speed – a Warp transaction. * Improves transaction propagation vs. normal node endpoints, so your transaction is seen sooner and has better chances in tight mempool conditions. * No extra sign-up is needed for bloXroute; you keep using standard Ethereum JSON-RPC calls. * Examples are given for web3.js, ethers.js, web3.py, and Go’s ethclient. > [!NOTE] > > The Warp transactions feature is available starting from the [paid plans](https://chainstack.com/pricing/). > > Warp transactions (not requests, just the transactions) consumed are billed separately. For details, see [Pricing](https://chainstack.com/pricing/) with the [extra usage](/docs/manage-your-billing#manage-the-extra-usage-setting) setting enabled. ## Main article > [!NOTE] > > ### EVM transactions > > This guide focuses on the EVM [Trader Nodes](/docs/warp-transactions): Ethereum, BNB Smart Chain, Polygon. ## Introduction A [Warp](/docs/warp-transactions) transaction is an integration of Chainstack and the high-speed [bloXroute](https://bloxroute.com/) network. Warp transactions are designed to reduce transaction times and ensure efficient delivery to their destination. Simply put, when a transaction is sent through a Trader Node with Warp transactions endpoint, it is placed on an "expressway" for blockchain transaction propagation. This means that compared to sending transactions through a standard EVM node, a Warp transaction can reach validators much more quickly, significantly reducing transaction finalization time. This article describes how to implement Warptransactions with Chainstack and popular Web3 libraries. ## What is a Warp transaction, and what is bloXroute? When a transaction is sent to a normal EVM node, it is propagated to all nodes within the blockchain network. It moves from node to node until a validator validates it or eventually reaches all nodes. Depending on network conditions, this process usually takes a few hundred milliseconds to a few seconds. > [!NOTE] > > Check out how to test transaction propagation by reading [Monitoring transaction propagation from node to mempool in EVM networks with Python](/docs/monitoring-transaction-propagation-from-node-to-mempool-in-evm-networks-with-python). ![](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/e0f1e07-normal-transaction-propagation.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=23da1266269e0475f315d55e307846c2) Figure 1. A normal transaction propagation in a nutshell A bloXroute distribution network (BDN), which can be found in more detail in the [BDN Architecture](https://docs.bloxroute.com/bdn-architecture), is a special network with a direct connection to a set of validators. The core of the BDN is a high-capacity, low-latency global network of servers optimized to propagate transactions and blocks quickly. In general, a transaction sent to the BDN will be delivered to a validator in an expedited manner. ![](https://files.readme.io/4d64c44-transaction-propagation-with-bloxroute.png) Figure 2. Transaction propagation through BDN According to their [website](https://bloxroute.com/why-bloxroute/), bloXroute's BDN provides several benefits, including: * Faster transaction propagation: bloXroute's BDN can propagate transactions up to 400-1,000 ms faster than the traditional peer-to-peer (P2P) network. This can be a significant advantage for applications that require real-time transaction processing, such as trading and DeFi. * Improved network stability: bloXroute's BDN is more stable than the P2P network. This is because the servers are connected using a dedicated network, reducing the risk of congestion and packet loss. * Increased scalability: bloXroute's BDN is designed to scale to meet the needs of future blockchain applications. The network is dynamic and can be easily scaled up or down as needed. ### What about Trader Nodes with Warp transactions? A Trader Node with Warp transactions enabled is a hybrid of a normal EVM node and a BDN, combining the best of both worlds. In most circumstances, it behaves just like a normal EVM node. However, when it receives a transaction from the user, it immediately forwards the transaction to the closest [bloXroute cloud API](https://docs.bloxroute.com/introduction/cloud-api-ips), which serves as the gateway to the BDN. The transaction does not follow the normal propagation process. There are a few benefits of using Trader Nodes with Warp transactions: * Users can use a Trader Node with Warp transactions to avoid the need for separate signup for the [bloXroute service](https://bloxroute.com/pricing/). This is particularly convenient for users who send a low number of transactions each month. * bloXroute uses a special set of APIs for authentication and sending transactions, which may not be compatible with the customer's existing implementation. In contrast, a Chainstack Trader Node with Warp transactions leverages the standard Ethereum JSON-RPC API, making it compatible with most mainstream tools and libraries, such as Metamask, web3.py, and web3.js. * When monitoring transactions within a network, using a Trader Node with Warp transactions may be a better option since it is directly connected to other peers in the network. ## How to access a Trader Node with Warp transactions on Chainstack The Trader Node transactions feature is available on the [paid plans](https://chainstack.com/pricing/) for the following protocols: * Ethereum * Solana * BNB Smart Chain To deploy a [Trader Node](/docs/trader-node), in the wizard select a Trader Node > Warp transactions. This is the gateway to your application. ## How to execute a Warp transaction A Warp transaction is a type of transaction that is dispatched to your node using the [eth\_sendRawTransaction](/reference/ethereum-sendrawtransaction) RPC method. This method is a standard Ethereum procedure for transmitting transactions. In this section, we will dive deeper into the process of sending Warp transactions using some of the most widely used Web3 libraries. We will provide examples for web3.js, ethers.js, web3.py, and ethClient.go. ### Executing a Warp transaction with web3.js web3.js is a library that allows you to interact with a local or remote Ethereum node using HTTP, IPC, or WebSocket. Here's how you can send a Warp transaction using web3.js: ```javascript Javascript theme={"system"} const Web3 = require("web3"); var url = "YOUR_CHAINSTACK_TRADER_NODE_ENDPOINT" var publicKey = "YOUR_ADDRESS" var privateKey = "YOUR_PRIVATE_KEY" const web3 = new Web3(url); const transaction = { from: publicKey, to: publicKey, value: 100, gas: 30000, gasPrice: 1000000000, }; const signedTransaction = await web3.eth.accounts.signTransaction(transaction, privateKey); const transactionHash = await web3.eth.sendSignedTransaction(signedTransaction.rawTransaction); const transactionReceipt = await web3.eth.getTransactionReceipt(transactionHash); console.log(transactionReceipt); ``` ## Executing a Waro\p transaction with ethers.js ethers.js is a complete Ethereum library and wallet implementation prioritizing compactness and simplicity. Here's how you can send a Warp transaction using ethers.js: ```javascript Javascript theme={"system"} const { ethers } = require("ethers"); var url = "YOUR_CHAINSTACK_TRADER_NODE_ENDPOINT" var publicKey = "YOUR_ADDRESS" var privateKey = "YOUR_PRIVATE_KEY" async function main() { const provider = new ethers.JsonRpcProvider(url); balance = await provider.getBalance(publicKey) console.log("balance is " + balance) var nonce = await provider.getTransactionCount(publicKey); console.log("nonce is " + nonce) var wallet = new ethers.Wallet(privateKey, provider) var transaction = { 'to': publicKey, 'value': 100, 'maxGasFee': 6000000000000000 }; console.log("sending tx") wallet.sendTransaction(transaction) .then((txObj) => { console.log('txHash', txObj.hash) }) } main(); ``` ## Executing a Warp transaction with web3.py web3.py is a Python library for interacting with Ethereum. It's commonly used for scripting, back-end development, and server-side tasks. Here's how you can send a Warp transaction using web3.py: ```python Python theme={"system"} import web3 url = "YOUR_CHAINSTACK_TRADER_NODE_ENDPOINT" publicKey = "YOUR_ADDRESS" privateKey = "YOUR_PRIVATE_KEY" w3 = web3.Web3(Web3.HTTPProvider(url)) transaction = { "from": publicKey, "to": publicKey, "value": 100, "gas": 300000, "gasPrice": 1000000000, } signed_transaction = w3.eth.account.sign_transaction(transaction, privateKey) transaction_hash = w3.eth.send_raw_transaction(signed_transaction.rawTransaction) transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash) print(transaction_receipt) ``` ## Executing a Warp transaction with ethClient.go ethClient.go is a Go-based Ethereum client that allows you to interact with the Ethereum blockchain. Here's how you can send a Warp transaction using ethClient.go: ```go go theme={"system"} package main import ( "fmt" "math/big" "github.com/ethereum/go-ethereum/ethclient" ) func main() { client, err := ethclient.Dial("YOUR_CHAINSTACK_TRADER_NODE_ENDPOINT") if err != nil { panic(err) } publicKey := "" privateKey := "" // Create the transaction transaction := types.Transaction{ From: publicKey, To: publicKey, Value: big.NewInt(1000000000000), // in wei Gas: 3000, GasPrice: big.NewInt(1000000000), // in wei } signedTransaction, err := transaction.Sign(types.Secp256k1PrivateKey(crypto.FromECDSA(common.HexToECDSA(privateKey)))) if err != nil { panic(err) } txHash, err := client.SendTransaction(signedTransaction) if err != nil { panic(err) } fmt.Println(txHash) } ``` ## Conclusion This article has provided an in-depth look at the integration between Chainstack and bloXroute, the mechanics of Warp transactions, and how to execute Warp transactions using the most popular Web3 libraries. Feel free to ping me on Twitter or Telegram if you have any questions. > [card] Wuzhong Zhu > > ![Wuzhong Zhu](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1548860905064017921/xCKUgveS_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=075e8dc4db13d9384c63bf4904eb520d) > > Developer Advocate @ Chainstack > \ > BUIDLs on Ethereum, zkEVMs, and The Graph protocol > > [https://twitter.com/wuzhongzhu](https://twitter.com/wuzhongzhu) > > [https://www.linkedin.com/in/wuzhong-zhu-44563589/](https://www.linkedin.com/in/wuzhong-zhu-44563589/) > > [https://github.com/wuzhong-zhu](https://github.com/wuzhong-zhu) # Smart contracts Source: https://docs.chainstack.com/docs/smart-contracts-glossary Glossary of smart contract terms covering Solidity, EVM opcodes, gas mechanics, proxy patterns, access control, and common contract design patterns. ## Keccak256 Keccak256 is a cryptographic hash function that generates a unique, fixed-size string of bytes for each unique input it receives. This feature makes it useful for ensuring data integrity, as any change in the input data leads to a different hash output. It's virtually impossible to derive the original input from the hash output, making it a one-way function. In Ethereum: 1. Keccak256 is used to generate Ethereum addresses from public keys. 2. It's used to verify the integrity of transactions. 3. It's used for efficient and secure data storage and retrieval in Merkle Trees. 4. It's used to identify functions in Ethereum's contract ABI. Keccak256 is crucial for the security and functionality of Ethereum and similar blockchain systems. ## Account An account in the context of Ethereum refers to an entity with an ether (ETH) balance that can send transactions on the Ethereum network. There are two types of accounts: externally owned accounts (EOAs), which are controlled by private keys, and contract accounts, which are deployed smart contracts. ## Address In the context of blockchain, an address refers to the hashed form of a public key that belongs to a specific user or contract. It is used to identify participants on the blockchain network. In Ethereum, addresses are typically represented as hexadecimal strings and are used to send and receive transactions. ## Address checksumming A checksummed address is a standard Ethereum address with certain characters capitalized to include a checksum validation. Checksumming is a way of including error-detection codes in an Ethereum address. The goal of checksumming is to prevent errors when an address is typed manually. An Ethereum address is a 40 character hexadecimal string derived from the last 20 bytes of the Keccak256 hash of the public key. To create a checksummed address, the address (excluding the '0x' prefix) is hashed using Keccak256, which outputs a 64 character hexadecimal string. This string is used to determine the capitalization of each alphanumeric character in the original address: if the Nth digit of the Keccak hash is 8 or more, the Nth character in the original address is capitalized, if it is a letter. Here's a simplified explanation of how it works: 1. You have an original Ethereum address: `0xae2fc483527b8ef99eb5d9b44875f005ba1fae13` (all lowercase). 2. You hash the lowercase hexadecimal address using Keccak256, which produces a new hash. 3. You go through each character of the original address: * If the corresponding digit in the hash is 8 or greater, you capitalize that character. * If the corresponding digit in the hash is less than 8, the character remains lowercase. 4. The resulting Ethereum address with this specific capitalization is the checksummed address. The capitalization adds an extra layer of validation: if the address is mistyped, the chances are high that the capitalization will not match the correct one, and the address can be recognized as invalid. It's important to note that not all tools and services recognize checksummed addresses, and they may treat the addresses as case-insensitive. However, using checksummed addresses can significantly reduce the risk of funds being sent to incorrect addresses due to typing errors. ## Chainlink VRF Chainlink Verifiable Random Function (VRF) is a provably-fair and verifiable source of randomness designed for smart contracts. It's a solution to one of the major challenges in blockchain development: generating a secure and truly random number. Here's how it works: **Request**: A smart contract requests randomness from the Chainlink VRF. This request includes a seed value, which can be any number - often, the current block's hash or timestamp is used. **Generate**: Chainlink VRF generates a random number and a cryptographic proof. The random number is generated based on the seed and the private key of the oracle node. **Respond**: The random number and the proof are sent back to the smart contract. **Verify**: The smart contract verifies the proof. It checks that the Oracle node used its known public key and the seed from the original request to generate the random number. If the proof is valid, the random number is accepted. This process ensures that the random number is truly random and that the oracle node cannot tamper with the result. It provides a reliable way for smart contracts to obtain secure and provably fair random numbers. Chainlink VRF is often used in blockchain games, gambling apps, and anywhere else a random input is needed in a smart contract. ## Token A token represents an asset or value on a blockchain. Tokens can be fungible or non-fungible. Fungible tokens, such as ERC-20 tokens, are identical and mutually interchangeable, while non-fungible tokens (NFTs), such as ERC-721 tokens, are unique and have distinct characteristics. ## ERC-20 ERC-20 is a widely adopted standard for creating and issuing tokens on the Ethereum blockchain. It defines a set of rules and functions that allow for the implementation of fungible tokens. Many popular cryptocurrencies, such as USDT, Apecoin, Dogecoin, and Shiba Inu are ERC-20 tokens. ## ERC-721 ERC-721 is a standard for creating and managing non-fungible tokens (NFTs) on the Ethereum blockchain. Unlike ERC-20 tokens, each ERC-721 token is unique and has its own distinct properties and ownership. ERC-721 tokens are commonly used to represent digital collectibles, in-game assets, or unique items. ## Renounced ownership of smart contracts Renouncing ownership of a smart contract means the original developer or owner relinquishes control over the contract and the associated assets. This is often done to enhance trust and decentralization within the Web3 ecosystem. When ownership is renounced, the contract becomes immutable, and nobody can change how it operates. ## Smart contract ABI ABI stands for Application Binary Interface and is a standardized way to interact with smart contracts on the Ethereum network. It defines the functions and data structures of a smart contract in a format that can be understood and used by external entities. The ABI specifies the encoding and decoding rules for data exchange between contracts and external applications. ## Smart contract audit A smart contract audit is a comprehensive review and analysis of a protocol's smart contract code to identify potential security vulnerabilities or flaws. The audit aims to ensure that the smart contract functions as intended, without any loopholes or weaknesses that could compromise the security or user funds. Audits are typically conducted by specialized firms or security experts. ## Solidity Solidity is the most popular programming language used by Web3 developers to write smart contracts for the Ethereum blockchain. It is a statically-typed, high-level language specifically designed for creating smart contracts. Solidity enables developers to define the behavior and logic of contracts and is supported by a range of development tools and frameworks. ## Total value locked Total value locked (TVL) is a metric used to assess the total value of assets locked or invested in a specific decentralized finance (DeFi) platform or smart contract. It represents the sum of all the assets (tokens or cryptocurrencies) users have deposited or staked within the protocol. TVL is often used as an indicator of a protocol's popularity and the amount of trust users place in it. ## Transactions Transactions in the context of blockchain are cryptographically signed instructions initiated by accounts. A transaction represents a state-changing operation on the Ethereum network, such as transferring ETH from one account to another, invoking a smart contract function, or deploying a new smart contract. Transactions contain relevant information, such as sender and recipient addresses, transaction value, gas price, and data payload. # Solana: Address lookup tables Source: https://docs.chainstack.com/docs/solana-address-lookup-tables Use Solana address lookup tables to fit more accounts into transactions and overcome the 1232-byte size limit for complex instructions. **TLDR:** * Solana transactions are capped at 1232 bytes, which limits the number of accounts a single transaction can reference. * Address lookup tables (ALTs) let you store frequently used addresses on-chain and reference them by index instead of including the full 32-byte public key in every transaction. * With ALTs, a versioned transaction (v0) can reference up to 256 accounts per table—enough for complex DeFi interactions, Jupiter swaps, and multi-program calls. * This guide walks you through creating a lookup table, adding addresses, and using it in a versioned transaction. * Lookup tables are a v0 feature. [Transaction v1](/docs/solana-transaction-v1) does not support them and raises the size limit to 4096 bytes instead. ## Why you need address lookup tables > [!NOTE] > > Everything on this page applies to v0 transactions, which are not deprecated. A [v1 transaction](/docs/solana-transaction-v1) cannot reference a lookup table at all—it carries up to 64 addresses inline and gets a 4096-byte envelope to fit them in. Every v0 or legacy Solana transaction has a hard 1232-byte size limit. Each account address takes 32 bytes, so a transaction with \~30 accounts already exhausts most of the budget before adding instructions. You will hit this limit when: * Performing Jupiter or Raydium swaps that route through multiple pools * Building multi-instruction transactions (swap + transfer + memo) * Interacting with programs that require many accounts (Serum/OpenBook order books, Meteora DLMM) The error looks like: ``` Transaction too large: XXXX > 1232 ``` Address lookup tables solve this by storing addresses on-chain. Instead of embedding the full 32-byte key, the transaction references a table address and a 1-byte index—saving 31 bytes per account. ## Prerequisites * A Chainstack Solana node endpoint. [Deploy one](https://console.chainstack.com/). * Node.js 18+ * A funded Solana wallet (creating a lookup table costs \~0.003 SOL in rent) ### Project setup ```bash theme={"system"} mkdir solana-alt && cd solana-alt npm init -y npm install @solana/web3.js dotenv npm install --save-dev ts-node typescript npx tsc --init touch main.ts .env ``` Add your credentials to `.env`: ``` SOLANA_RPC=YOUR_CHAINSTACK_ENDPOINT PRIVATE_KEY=your_base58_private_key ``` ## Create a lookup table A lookup table is an on-chain account that holds a list of public keys. You create one with `createLookupTable`, which returns the table address and a creation instruction. ```typescript TypeScript theme={"system"} import { Connection, Keypair, AddressLookupTableProgram, TransactionMessage, VersionedTransaction, } from "@solana/web3.js"; import bs58 from "bs58"; import "dotenv/config"; const connection = new Connection(process.env.SOLANA_RPC!); const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY!)); async function createLookupTable(): Promise { const slot = await connection.getSlot(); const [createInstruction, lookupTableAddress] = AddressLookupTableProgram.createLookupTable({ authority: payer.publicKey, payer: payer.publicKey, recentSlot: slot, }); const latestBlockhash = await connection.getLatestBlockhash(); const message = new TransactionMessage({ payerKey: payer.publicKey, recentBlockhash: latestBlockhash.blockhash, instructions: [createInstruction], }).compileToV0Message(); const transaction = new VersionedTransaction(message); transaction.sign([payer]); const txid = await connection.sendTransaction(transaction); await connection.confirmTransaction({ signature: txid, blockhash: latestBlockhash.blockhash, lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, }); console.log("Lookup table created:", lookupTableAddress.toBase58()); console.log("Transaction:", txid); return lookupTableAddress.toBase58(); } createLookupTable(); ``` Run with: ```bash theme={"system"} ts-node main.ts ``` ## Extend the lookup table After creation, add the addresses you need to reference in your transactions. You can add up to 30 addresses per `extendLookupTable` instruction (limited by transaction size), and a table can hold up to 256 entries total. ```typescript TypeScript theme={"system"} import { Connection, Keypair, PublicKey, AddressLookupTableProgram, TransactionMessage, VersionedTransaction, } from "@solana/web3.js"; import bs58 from "bs58"; import "dotenv/config"; const connection = new Connection(process.env.SOLANA_RPC!); const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY!)); // Replace with your lookup table address from the previous step const LOOKUP_TABLE_ADDRESS = new PublicKey("YOUR_LOOKUP_TABLE_ADDRESS"); async function extendLookupTable() { // Add the addresses you frequently use in transactions const addressesToAdd = [ new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), // Token Program new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"), // Associated Token Program new PublicKey("11111111111111111111111111111111"), // System Program new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // USDC mint new PublicKey("So11111111111111111111111111111111111111112"), // Wrapped SOL // Add more addresses as needed for your use case ]; const extendInstruction = AddressLookupTableProgram.extendLookupTable({ payer: payer.publicKey, authority: payer.publicKey, lookupTable: LOOKUP_TABLE_ADDRESS, addresses: addressesToAdd, }); const latestBlockhash = await connection.getLatestBlockhash(); const message = new TransactionMessage({ payerKey: payer.publicKey, recentBlockhash: latestBlockhash.blockhash, instructions: [extendInstruction], }).compileToV0Message(); const transaction = new VersionedTransaction(message); transaction.sign([payer]); const txid = await connection.sendTransaction(transaction); await connection.confirmTransaction({ signature: txid, blockhash: latestBlockhash.blockhash, lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, }); console.log("Lookup table extended with", addressesToAdd.length, "addresses"); console.log("Transaction:", txid); } extendLookupTable(); ``` > [!WARNING] > > After extending a lookup table, you must **wait for the next slot** before using it in a transaction. The newly added addresses are not available in the same slot they were added. ## Use the lookup table in a transaction Once the table is populated, pass it as an address lookup table when compiling your versioned transaction message. The runtime resolves the indices at execution time. ```typescript TypeScript theme={"system"} import { Connection, Keypair, PublicKey, SystemProgram, TransactionMessage, VersionedTransaction, } from "@solana/web3.js"; import bs58 from "bs58"; import "dotenv/config"; const connection = new Connection(process.env.SOLANA_RPC!); const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY!)); const LOOKUP_TABLE_ADDRESS = new PublicKey("YOUR_LOOKUP_TABLE_ADDRESS"); async function sendWithLookupTable() { // Fetch the lookup table account const lookupTableAccount = ( await connection.getAddressLookupTable(LOOKUP_TABLE_ADDRESS) ).value; if (!lookupTableAccount) { throw new Error("Lookup table not found"); } console.log( "Lookup table has", lookupTableAccount.state.addresses.length, "addresses" ); // Build your instructions as usual const instructions = [ SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: payer.publicKey, lamports: 1000, }), // Add more instructions that reference accounts in the lookup table ]; const latestBlockhash = await connection.getLatestBlockhash(); // Pass the lookup table when compiling — this is the key step const message = new TransactionMessage({ payerKey: payer.publicKey, recentBlockhash: latestBlockhash.blockhash, instructions, }).compileToV0Message([lookupTableAccount]); const transaction = new VersionedTransaction(message); transaction.sign([payer]); const txid = await connection.sendTransaction(transaction); await connection.confirmTransaction({ signature: txid, blockhash: latestBlockhash.blockhash, lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, }); console.log("Transaction with lookup table:", txid); } sendWithLookupTable(); ``` The critical line is `.compileToV0Message([lookupTableAccount])` — this tells the runtime to resolve account references from the lookup table instead of embedding full public keys. ## When to use address lookup tables | Scenario | Without ALT | With ALT | | - | - | - | | Simple SOL transfer (2 accounts) | Works fine | Not needed | | SPL token transfer (5–7 accounts) | Usually fits | Not needed | | Jupiter swap (15–25 accounts) | Likely exceeds 1232 bytes | Required | | Multi-hop swap + transfer + memo (30+ accounts) | Always exceeds 1232 bytes | Required | On [transaction v1](/docs/solana-transaction-v1) the same scenarios fit inline: the 4096-byte envelope holds the full 64-address maximum without a table. > [!TIP] > > If you use the same set of accounts repeatedly (e.g., the same token pair, the same DEX pool), create the lookup table once and reuse it across all your transactions. The table persists on-chain until you explicitly close it. ## Additional resources * [Solana documentation on address lookup tables](https://solana.com/docs/advanced/lookup-tables) * [Versioned transactions](/docs/solana-how-to-priority-fees-faster-transactions) — priority fees guide uses v0 transactions * [Transaction expiry handling](/docs/solana-how-to-handle-the-transaction-expiry-error) — retry logic for transactions using ALTs * [Solana: transaction v1 explained](/docs/solana-transaction-v1) — the format that replaces the need for lookup tables # Solana Agave 2.0 upgrade reference Source: https://docs.chainstack.com/docs/solana-agave-20-upgrade-reference Quick reference for the Solana node client transition from Solana Labs to Agave, covering deprecated methods, renamed endpoints, and migration steps. **TLDR:** * Agave replaces the original Solana node client and deprecates several outdated calls by October 21, 2024. * Most newly recommended methods (e.g., `getSignatureStatuses`) are already standard, so your existing workflows likely remain unaffected. * The code examples illustrate how to switch from `confirmTransaction` to `getSignatureStatuses` in both JavaScript and Python. ## In brief [Agave](https://github.com/anza-xyz/agave) is the Rust-based Solana node client that is also a fork of the original Solana Labs client. The primary purpose of Agave replacing the original Solana Labs client is make the network multi-client. The multi-client part mostly means the introduction of another node client — the C-based Firedancer. Nodes are expected to upgrade to Agave v2.0 by **October 21, 2024**. For you as a developer, this means there are a few methods that will be deprecated with other methods replacing them. The methods that are getting the deprecation have already been outdated for quite a bit of time and all of the replacement methods (except for [isBlockhashValid](/reference/isblockhashvalid)) aren't new. So the chances are you won't be significantly affected. See: > [card] [Agave v2.0 release schedule](https://github.com/anza-xyz/agave/wiki/v2.0-Release-Schedule) > [card] [Agave v2.0 transition guide](https://github.com/anza-xyz/agave/wiki/Agave-v2.0-Transition-Guide) ## Methods: deprecated and replaced | Deprecated call | Replacement call | | - | - | | confirmTransaction | [getSignatureStatuses](/reference/solana-getsignaturestatuses) | | getSignatureStatus | [getSignatureStatuses](/reference/solana-getsignaturestatuses) | | getSignatureConfirmation | [getSignatureStatuses](/reference/solana-getsignaturestatuses) | | getConfirmedSignaturesForAddress | [getSignaturesForAddress](/reference/solana-getsignaturesforaddress) | | getConfirmedBlock | [getBlock](/reference/solana-getblock) | | getConfirmedBlocks | [getBlocks](/reference/solana-getblocks) | | getConfirmedBlocksWithLimit | [getBlocksWithLimit](/reference/getblockswithlimit) | | getConfirmedTransaction | [getTransaction](/reference/gettransaction) | | getConfirmedSignaturesForAddress2 | [getSignaturesForAddress](/reference/solana-getsignaturesforaddress) | | getRecentBlockhash | [getLatestBlockhash](/reference/solana-getlatestblockhash) | | getFees | [getFeeForMessage](/reference/solana-getfeeformessage) | | getFeeCalculatorForBlockhash | [isBlockhashValid](/reference/isblockhashvalid) or [getFeeForMessage](/reference/solana-getfeeformessage) | | getFeeRateGovernor | [getFeeForMessage](/reference/solana-getfeeformessage) | | getSnapshotSlot | [getHighestSnapshotSlot](/reference/solana-gethighestsnapshotslot) | | getStakeActivation | [getAccountInfo \| Solana](/reference/solana-getaccountinfo) ([alternative approach](https://solana.stackexchange.com/questions/15710/the-alternative-method-to-get-the-stake-account-status-since-getstakeactivation)) | ## Replacing `confirmTransaction` with `getSignatureStatuses` ```javascript @solana/web3.js theme={"system"} import { Connection, Transaction, SendOptions } from '@solana/web3.js'; async function sendAndConfirmTransaction( connection: Connection, transaction: Transaction, timeout: number = 30000, // 30 seconds default timeout options?: SendOptions ): Promise { // Send the transaction const signature = await connection.sendTransaction(transaction, options?.signers || [], { ...options, skipPreflight: options?.skipPreflight || false }); // Create a promise that will reject after the timeout const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject(new Error(`Transaction confirmation timeout after ${timeout}ms`)); }, timeout); }); // Create the confirmation promise const confirmationPromise = (async () => { let done = false; while (!done) { // Get the status of the transaction const response = await connection.getSignatureStatuses([signature]); const status = response.value[0]; if (status) { if (status.err) { throw new Error(`Transaction failed: ${status.err.toString()}`); } // Check if we have enough confirmations if (status.confirmationStatus === 'finalized') { done = true; return signature; } } // Wait a bit before checking again await new Promise(resolve => setTimeout(resolve, 1000)); } })(); // Race between timeout and confirmation try { return await Promise.race([confirmationPromise, timeoutPromise]); } catch (error) { // If it's a timeout error, we should still return the signature if (error.message.includes('timeout')) { return signature; } throw error; } } // Example usage async function example() { const connection = new Connection('CHAINSTACK_NODE'); const transaction = new Transaction(); // ... add your transaction instructions here ... try { const signature = await sendAndConfirmTransaction(connection, transaction, 60000, { skipPreflight: false, // ... other options }); console.log('Transaction confirmed:', signature); } catch (error) { console.error('Transaction failed:', error); } } ``` ```python solana.py theme={"system"} from solana.rpc.api import Client from solders.transaction import Transaction from solana.rpc.commitment import Commitment from typing import Optional, List, Union import time import asyncio from concurrent.futures import TimeoutError class TransactionConfirmationError(Exception): """Custom exception for transaction confirmation errors""" pass async def send_and_confirm_transaction( client: Client, transaction: Transaction, signers: List, timeout: int = 30, # timeout in seconds commitment: Union[str, Commitment] = "finalized" ) -> str: """ Send and confirm a transaction using getSignatureStatuses Args: client: Solana client instance transaction: Transaction to send signers: List of signers for the transaction timeout: Maximum time to wait for confirmation in seconds commitment: Commitment level to use Returns: Transaction signature Raises: TransactionConfirmationError: If transaction fails or times out """ # Send the transaction try: from solana.rpc.types import TxOpts response = client.send_transaction( transaction, opts=TxOpts( skip_preflight=False, preflight_commitment=commitment ) ) signature = str(response.value) except Exception as e: raise TransactionConfirmationError(f"Failed to send transaction: {str(e)}") # Start time for timeout tracking start_time = time.time() async def confirm_transaction() -> str: while True: try: # Check if we've exceeded timeout if time.time() - start_time > timeout: raise TimeoutError(f"Transaction confirmation timeout after {timeout} seconds") # Get transaction status response = client.get_signature_statuses([signature]) if response.value[0] is None: # Transaction not yet processed, wait and retry await asyncio.sleep(1) continue status = response.value[0] # Check if transaction failed if status.err: raise TransactionConfirmationError( f"Transaction failed: {status.err}" ) # Check confirmation status conf_status = status.confirmation_status if conf_status == "finalized": return signature # Wait before checking again await asyncio.sleep(1) except TimeoutError: # Return signature even on timeout return signature except Exception as e: if not isinstance(e, TimeoutError): raise TransactionConfirmationError(f"Confirmation failed: {str(e)}") try: return await confirm_transaction() except Exception as e: raise TransactionConfirmationError(str(e)) # Example usage async def example(): # Initialize client client = Client("CHAINSTACK_NODE") # Create and populate your transaction transaction = Transaction() # ... add your transaction instructions here ... try: signature = await send_and_confirm_transaction( client=client, transaction=transaction, signers=[], # Add your signers here timeout=60, # 60 seconds timeout commitment="finalized" ) print(f"Transaction confirmed: {signature}") except TransactionConfirmationError as e: print(f"Transaction failed: {str(e)}") # Run the example if __name__ == "__main__": asyncio.run(example()) ``` # Solana: Capped onchain budgets for AI agents Source: https://docs.chainstack.com/docs/solana-agent-allowances-x402 Give an AI agent a spending budget it can draw against autonomously, using a Solana fixed-delegation allowance — and see how it complements x402 for agentic commerce. Built in Python against a Chainstack node. **TLDR:** * An AI agent that pays for things shouldn't hold the keys to your whole wallet. The Solana Subscriptions & Allowances program lets you grant it a **fixed delegation** — a capped, optionally time-limited budget it can spend against on its own. * The agent gets its own keypair and draws against the budget without you signing each payment; the program enforces the cap and you can revoke at any time. * This is the standing-budget complement to **x402**, which handles stateless pay-per-call payments. Together they cover both sides of agentic commerce: per-request access and a bounded autonomous spend. * We build it in Python against a Chainstack node — grant an allowance, have the agent spend against it, and watch the program reject an overspend. ## Why agents need a budget primitive Hand an autonomous agent your wallet and you've handed it everything. The moment it can sign for one payment, it can sign for all of them. That's the wrong trust model for software that runs unattended. What you actually want is the model you already use with cards and corporate spend: a **capped allowance**. The agent can spend up to a limit, on its own, for as long as you allow — and not a token more. On Solana, the [Subscriptions & Allowances program](/docs/solana-subscriptions-and-allowances) gives you exactly that primitive with its **fixed delegation**. ## Allowances and x402 are complementary If you're building agent payments on Solana, you'll meet two primitives. They solve different halves of the problem: | | x402 | Fixed-delegation allowance | | - | - | - | | Shape | Per-request payment over HTTP 402 | A standing, pre-authorized budget | | State | Stateless — no account, no standing authorization | An onchain delegation the agent draws against | | Best for | Pay-per-call API access, metered usage, discovery | A bounded autonomous spend across many actions | | Authorization | The agent pays for each call as it goes | You authorize once; the agent spends within the cap | x402 is how an agent pays for a single API call without an account or subscription. A fixed-delegation allowance is how you give that agent a wallet-scoped budget in the first place. A realistic agent uses both: you grant it, say, a 50-token monthly allowance, and it spends that down via x402 calls and other onchain payments — never able to exceed what you authorized. The Solana Foundation's agentic-commerce work (x402, and the MPP spending-limit specs) points at exactly this pairing. > [!NOTE] > > The same idea powers infrastructure billing. An agent that consumes paid services — RPC tiers, data feeds, tool APIs — can be handed a capped allowance and left to pull what it needs, when it needs it, with a hard ceiling you set once. ## How a fixed delegation works The mechanics are the allowance model from the [pillar guide](/docs/solana-subscriptions-and-allowances): one **Subscription Authority** (SA) PDA per `(user, mint)` becomes the single `u64::MAX` delegate on your token account, and individual delegation PDAs hold the real limits. For an agent budget, the delegation's **delegatee is the agent's wallet**: * You (the **delegator**) create a fixed delegation naming the agent as delegatee, with a total `amount` and an optional `expiry_ts`. * The agent (the **delegatee**) calls `transferFixed`, **signing with its own key**, to move tokens from your account to whoever it's paying. Each transfer decrements the remaining budget. * When the budget hits zero or the expiry passes, transfers stop. You can also revoke the delegation PDA at any time to cut it off immediately. Only the agent can spend this particular delegation, only up to the cap, only before expiry — and it never touches the rest of your balance. ## Prerequisites * Python 3.10+ * `pip install solana solders` * A Chainstack Solana **devnet** node endpoint * Some devnet SOL from the [Chainstack faucet](https://faucet.chainstack.com/solana-testnet-faucet) ## Build it in Python As in the pillar guide, there's no Python SDK, so we build the instructions by hand. The setup below shares the same foundation; the new pieces are the two fixed-delegation instructions. **1. Constants, helpers, and instruction builders** ```python theme={"system"} import struct from solana.rpc.api import Client from solana.rpc.commitment import Confirmed from solders.keypair import Keypair from solders.pubkey import Pubkey from solders.instruction import Instruction, AccountMeta from solders.message import MessageV0 from solders.transaction import VersionedTransaction from solders.system_program import transfer, TransferParams from spl.token.client import Token from spl.token.constants import TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID CHAINSTACK_DEVNET = "YOUR_CHAINSTACK_SOLANA_DEVNET_ENDPOINT" PROGRAM_ID = Pubkey.from_string("De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44") SYS_PROGRAM = Pubkey.from_string("11111111111111111111111111111111") EVENT_AUTHORITY = Pubkey.find_program_address([b"event_authority"], PROGRAM_ID)[0] client = Client(CHAINSTACK_DEVNET, commitment=Confirmed) def sa_pda(user, mint): return Pubkey.find_program_address([b"SubscriptionAuthority", bytes(user), bytes(mint)], PROGRAM_ID) def delegation_pda(sa, delegator, delegatee, nonce): return Pubkey.find_program_address( [b"delegation", bytes(sa), bytes(delegator), bytes(delegatee), nonce.to_bytes(8, "little")], PROGRAM_ID) def ata(owner, mint): return Pubkey.find_program_address( [bytes(owner), bytes(TOKEN_PROGRAM_ID), bytes(mint)], ASSOCIATED_TOKEN_PROGRAM_ID)[0] def meta(pubkey, signer, writable): return AccountMeta(pubkey=pubkey, is_signer=signer, is_writable=writable) def send(ixs, payer, signers): bh = client.get_latest_blockhash().value.blockhash tx = VersionedTransaction(MessageV0.try_compile(payer.pubkey(), ixs, [], bh), signers) sig = client.send_raw_transaction(bytes(tx)).value client.confirm_transaction(sig, commitment=Confirmed) return str(sig) def read_sa_init_id(sa): data = client.get_account_info(sa).value.data return int.from_bytes(data[98:106], "little", signed=True) def ix_init_subscription_authority(user, mint): sa, _ = sa_pda(user, mint) return Instruction(PROGRAM_ID, bytes([0]), [ meta(user, True, True), meta(sa, False, True), meta(mint, False, False), meta(ata(user, mint), False, True), meta(SYS_PROGRAM, False, False), meta(TOKEN_PROGRAM_ID, False, False), ]) def ix_create_fixed_delegation(user, mint, agent, nonce, amount, expiry_ts=0): sa, _ = sa_pda(user, mint) deleg, _ = delegation_pda(sa, user, agent, nonce) # CreateFixedDelegationData: nonce u64, amount u64, expiry_ts i64, expected_sa_init_id i64. data = bytes([1]) + struct.pack(" [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Alpenglow consensus explained Source: https://docs.chainstack.com/docs/solana-alpenglow-consensus What Solana's Alpenglow upgrade changes: Votor consensus, sub-second finality, and the end of on-chain vote transactions. Plus what changed for RPC integrations. **TLDR:** * Alpenglow is the biggest change to Solana's consensus since launch: it replaces TowerBFT with Votor and removes Proof of History (PoH) from consensus, targeting \~100–150ms finality instead of TowerBFT's \~12.8s. * Validator governance approved it (SIMD-0326) in September 2025. After running on devnet and testnet, it activated on mainnet on October 21, 2026, in epoch 1064. * This first upgrade ships Votor only. Rotor, the planned Turbine replacement, is deferred to a separate later proposal, so block propagation does not change yet. * On-chain vote transactions are gone. They were roughly 60% of all Solana transactions, so removing them frees block space and reshapes validator economics. * For RPC and node operators, the headline is finality: the `confirmed` and `finalized` commitment levels collapse into one near-instant certificate, so audit any code that hardcodes `confirmed` or polls for finality. ## Main article Alpenglow is the most significant change to Solana's core protocol since the network launched. It rebuilds how validators agree on the chain, the part of Solana that decides which blocks are real and when they are final. The goal is to take finality from TowerBFT's \~12.8 seconds down to around 100–150 milliseconds, which is a category change rather than an incremental speedup. If you build on Solana or run infrastructure for it, Alpenglow changes assumptions you have probably baked into your code, your streaming pipelines, and your caching. This guide explains what Alpenglow is, what it changes under the hood, what happened during the mainnet switch, and, most importantly for a node and RPC audience, what changes for your integration. To test your integration, you'll want a [Chainstack Solana RPC](https://chainstack.com/build-better-with-solana/) endpoint. For the building blocks this guide assumes, see [Solana: blocks vs slots explained](/docs/understanding-the-difference-between-blocks-and-slots-on-solana) and [Solana: understanding block time](/docs/solana-understanding-block-time). ## What Alpenglow is Alpenglow is a new consensus protocol designed by [Anza](https://www.anza.xyz/blog/alpenglow-a-new-consensus-for-solana), first presented in May 2025. The design has two parts: * Votor — the voting and finalization engine that replaces TowerBFT. * Rotor — a planned block-propagation layer that would eventually replace Turbine. The governance proposal that was actually approved, [SIMD-0326](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0326-alpenglow.md), covers Votor and the related economic changes. Rotor is explicitly held back for a separate proposal. > [!NOTE] > > **Status.** Alpenglow runs on mainnet, devnet, and testnet. On mainnet, epoch 1064 started on October 21, 2026, at 13:36 UTC, and the cluster switched to Alpenglow at slot 459,653,000, about 18 minutes later. See the [Solana Alpenglow upgrade page](https://solana.com/upgrades/alpenglow) for the current status. > [!NOTE] > > This first upgrade is **Votor only**. Rotor, and with it any change to how shreds propagate across the network, is deferred to a future, separately numbered proposal that does not exist yet. Until then, Turbine remains the data-dissemination layer and your bandwidth and relay assumptions do not change. ## What changes under the hood At a high level, here is the before-and-after. Note that several rows are deliberately unchanged: Alpenglow is a consensus upgrade, not a wholesale rewrite of the network. | Aspect | Before (TowerBFT + PoH) | With Alpenglow (Votor) | | - | - | - | | Finality | \~12.8s, rooted | \~100–150ms certificate (target) | | Voting | On-chain vote transactions | Off-chain BLS votes aggregated into certificates | | Timing | Proof of History | \~200ms slots plus local timeouts | | Block propagation | Turbine | Turbine, unchanged (Rotor is a later upgrade) | | Validator vote cost | Vote transaction fees | 0.8 SOL admission ticket per epoch, burned | | Byzantine tolerance | \~33% | 20%, plus 20% crash tolerance | ### Votor: voting and finality, rebuilt With TowerBFT, validators cast votes as on-chain transactions, and a block reaches deterministic finality only after it is rooted roughly 32 slots deep, about 12.8 seconds. Votor replaces that with direct, off-chain voting: * Validators broadcast their votes directly to other validators as lightweight messages, not as on-chain transactions. * Any node can aggregate those votes into a compact BLS12-381 certificate once a stake threshold is reached, and only that certificate (about 1,000 bytes) is anchored on-chain. * Two finalization paths run concurrently. The fast path finalizes a block in a single round when at least 80% of stake participates; the slow path finalizes in two rounds when at least 60% participates in each. Whichever crosses its threshold first finalizes the block. The result is deterministic finality in roughly 100–150ms instead of \~12.8s, with one block still produced about every 200ms. > [!WARNING] > > The \~100–150ms numbers are Anza's targets for validators that receive votes directly. RPC nodes that are not staked learn about finalization from a later block, which can add about one block interval. Cite these as targets until they are measured on mainnet. ### Proof of History leaves consensus Proof of History acted as a cryptographic clock that helped validators agree on time and ordering. Alpenglow removes PoH from consensus and replaces it with local timeout timers: each validator sets per-slot deadlines and votes to skip a slot if the block does not arrive in time. The protocol is partially synchronous, so clock drift cannot break safety. The slot time was reduced separately from Alpenglow, from 400ms to 200ms in four stages that completed in October 2026. Two side effects matter if you read ledger data directly. Tick entries leave the shred stream, and the `Clock` sysvar timestamp gets a new source: the leader now sets the timestamp for its block within a bounded range, instead of the stake-weighted median of vote timestamps. Re-check any logic tuned to the old behavior, including [getBlockTime](/reference/solana-getblocktime) consumers. ### The end of vote transactions Because votes moved off-chain, the on-chain vote transactions that dominated Solana are gone. Before Alpenglow, roughly 60% of all Solana transactions were validator votes. Removing them frees a large share of block space for user transactions. Vote fees are replaced by a Validator Admission Ticket (VAT): a 0.8 SOL fee charged once per epoch, deducted before a validator is admitted for the next epoch and then burned. Voting validators need to hold at least 0.8 SOL above the rent-exempt minimum in the vote account to pay it. Two caveats are worth stating plainly: * The VAT is burned rather than redistributed, which removes the per-epoch vote-fee flow that reached block proposers. Some validators have flagged this as a meaningful economic change, not a pure saving. * Lower running costs reduce the barrier to entry. Independent estimates put the minimum profitable stake dropping from roughly 4,850 SOL to roughly 450 SOL, though the exact figure depends on the SOL price. ### The 20+20 security model Alpenglow uses what Anza calls a 20+20 model: safety holds as long as 20% or less of stake is adversarial, and liveness holds with up to an additional 20% offline or crashed, about 40% non-responsive stake tolerated in total. This is a deliberate trade-off. It is lower than the classic 33% Byzantine threshold of two-round protocols, exchanged for single-round speed and stronger crash-fault tolerance. The design includes formal safety proofs, which TowerBFT lacked. The trade-off is real and has been debated in governance, so it is worth understanding rather than glossing over. ### What is not changing yet: Rotor and Turbine It is easy to read about Alpenglow and assume block propagation is changing, since much of the public commentary focuses on Rotor, the proposed single-hop, erasure-coded successor to Turbine. For this upgrade, it is not. Rotor is split into its own future proposal that has no number or activation date yet, and Turbine stays in place. If you are planning around bandwidth, relays, or shred propagation, nothing changes on that front with this upgrade. ## What happened during the mainnet switch The switch follows [SIMD-0384](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0384-alpenglow-migration.md): 1. Epoch 1064 started on October 21, 2026, at 13:36 UTC, and the feature gate activated. The cluster kept running TowerBFT for the first 5,000 slots, about 18 minutes. 2. From slot 459,653,000, blocks carried only vote transactions and nodes stopped sending commitment updates to RPC. User transactions were not included in this period. 3. Once validators with at least 82% of stake signed a genesis certificate, nodes dropped the vote-only blocks after the Alpenglow genesis block and continued under Alpenglow. Confirmed user transactions were not rolled back. During the migration period: * Transactions sent in this period did not land, and clients had to sign them again after the switch. * Subscriptions at `confirmed` or `finalized` paused. * Streams at `processed` showed vote-only blocks that the network then dropped. If you index across the switch, data captured at `processed` from those slots is not part of the chain. To check whether a cluster runs Alpenglow, call `getAgGenesisCert`. It returns `null` on a cluster still on TowerBFT and the Alpenglow genesis certificate after the switch. On mainnet and devnet, it returns the certificate. ## What Alpenglow means for RPC and node operators This is where most teams feel Alpenglow. Even though it is an L1 consensus change, it reshapes assumptions buried in client code, streaming pipelines, and caches. ### Commitment levels collapse into one On TowerBFT, Solana's three commitment levels meant: * `processed` — the most recent block, which may still be on a fork. * `confirmed` — voted on by the cluster, available in roughly 500–600ms (optimistic). * `finalized` — rooted and deterministic, available in roughly 12.8s. Under Alpenglow, `confirmed` and `finalized` are the same: both point to the latest block with a finalization certificate. The practical consequences: * Code that defaults to `confirmed` to "go fast" while accepting rollback risk no longer has a meaningful trade-off to make. * Point new code at `finalized`, which now arrives as fast as `confirmed`. `confirmed` keeps working and is removed in a later release. * In [getSignatureStatuses](/reference/solana-getsignaturestatuses), the `confirmations` count comes from TowerBFT vote lockouts. Under Alpenglow, it stays at `0` until the transaction is final and then becomes `null`. If your code waits for a number of confirmations, check `confirmationStatus` instead. * The [getBlockCommitment](/reference/solana-getblockcommitment) method exposes the TowerBFT lockout structure, which Votor does not use, so its meaning changes fundamentally. ### From polling to push When finality arrives in well under a second and is encoded in a single certificate, polling a signature's status in a loop becomes wasteful and often slower than the event it is waiting for. Move finality detection to push: subscribe with [signatureSubscribe](/reference/signaturesubscribe-solana) rather than polling [getSignatureStatuses](/reference/solana-getsignaturestatuses). Watch your caches too: any cache with a TTL above \~250ms can start serving stale data once finality is near-instant, so add certificate-aware invalidation. ### Streaming and Geyser pipelines If you consume Geyser or [Yellowstone gRPC](/docs/yellowstone-grpc-geyser-plugin) streams, three things change: * Vote transactions vanish from the transaction stream. Any consumer that counts, filters, or parses votes, or that sizes itself around vote volume, must adapt to a stream that is roughly 60% smaller in transaction count. * One slot can carry more than one candidate block, and Geyser tags every event with a `bank_id`. Buffer data per slot and `bank_id`, and stop assuming one block per slot. `bank_id` is local to each node, so if you merge streams from more than one connection, match blocks by blockhash. * Slot-status notifications move to a sub-second finality cadence rather than \~12.8s. Pipelines that special-case the `finalized` status, for example by retroactively marking ancestor slots as finalized, should be reviewed against the new certificate-based model. ### Archival and ledger growth Removing vote transactions cuts roughly 60% of transaction volume, which means slower ledger growth and smaller snapshots and archives over time. Planned compute-unit and block-size increases will offset some of that, so the net effect is not yet settled. Note also that the ledger changed shape at activation: historical data before slot 459,653,000 contains vote transactions, and data after it does not. This is relevant for anyone indexing or running historical queries across the boundary, and for anyone who reports transaction counts or TPS. ### Validator operators: client version, BLS key, and VAT A mainnet validator needs: * v4.3.2 or later of an Agave-based client: Agave, Jito-Solana, or Harmonic Salsa. Firedancer and Frankendancer were not supported during the migration. * A BLS public key registered on the vote account ([SIMD-0387](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0387-bls-pubkey-management-in-vote-account.md)). Since the VAT feature activated on mainnet on July 22, 2026, validators without a registered BLS key are excluded from the admitted validator set. * At least 0.8 SOL above the rent-exempt minimum in the vote account for the VAT. The first fee was charged at the start of epoch 1064 and paid for epoch 1065. ### A migration checklist **1. Audit commitment assumptions** Search your SDKs, RPC handlers, and internal services for hardcoded `confirmed` usage, `confirmations` counts, and signature-status polling loops. **2. Shift finality detection to push** Replace polling with subscriptions, and shorten caches to sub-250ms with certificate-aware invalidation. **3. Review streaming consumers** Update Geyser and Yellowstone gRPC consumers for the disappearance of vote transactions, for `bank_id`, and for the new finality cadence. **4. Test against a cluster that runs Alpenglow** Mainnet and devnet both run Alpenglow. Use `getAgGenesisCert` to confirm that a cluster has switched. ## Timeline * September 2025 — validator governance approved SIMD-0326 with 98.27% of votes in favor (52% of stake participated). * December 2025 — demonstrated publicly at Breakpoint. * May 2026 — went live on the community test cluster, including the live "Alpenswitch" migration from TowerBFT. * July 22, 2026 — the VAT feature activated on mainnet. * September 25, 2026 — Alpenglow activated on devnet. Testnet also runs it. * October 2026 — the slot time reduction from 400ms to 200ms completed. * October 21, 2026 — mainnet activation. Epoch 1064 started at 13:36 UTC, and the cluster switched to Alpenglow at slot 459,653,000, about 18 minutes later. ## Avoid these assumptions * Assuming block propagation changes — Rotor is not in this upgrade. Turbine is unchanged, so bandwidth and relay assumptions hold for now. * Hardcoding `confirmed` as the fast path — once `confirmed` and `finalized` collapse, the optimistic-versus-deterministic trade-off your code relies on goes away. * Expecting `finalized` to take \~12.8s — on mainnet, `finalized` now arrives as fast as `confirmed`. Alpenglow is a large change, but most of the preparation for it is unglamorous and code-level: find your commitment-level assumptions, move from polling to push, and make your streaming and caching layers finality-aware. # Solana: Analyzing adjacent transactions for priority fees Source: https://docs.chainstack.com/docs/solana-analyzing-adjacent-transactions-for-priority-fees Analyze adjacent transactions in Solana blocks to calculate optimal priority fees based on actual compute unit prices paid by neighboring transactions. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). > **Part of the [How to land transactions on Solana](/docs/solana-how-to-land-transactions) guide.** **TLDR:** * This Python script inspects the fee usage in blocks adjacent to your own target transaction on Solana, focusing on the same on-chain program. * It decodes [compute budget](/docs/solana-compute-budget) instructions (priority fees) to show both your transaction’s fee and those of other transactions in nearby blocks. * By comparing priority fees, you can tune your own transaction fees to optimize speed or cost, and troubleshoot performance. * Useful for pump.fun or any scenario where outbidding other participants at the block level is crucial. ## Main article This guide shows you how to analyze transaction priority fees for transactions adjacent to your target transaction on Solana. This is particularly useful when developing trading strategies or troubleshooting transaction execution on programs like pump.fun. What the script in this tutorial does is look for transactions adjacent to the one you specify (usually your own) and checks all other transactions involving the same program and prints the priority fees in the same block and the adjacent blocks to easily see how your particular transaction stacks up and how you can tune it to win more races. ## Prerequisites * Python 3.7+ * Packages: `pip install solana base58` ## Understanding Solana fees The recommended reading list: * [How to use Priority Fees to unlock faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) * [Estimate Priority Fees with getRecentPrioritizationFees](/docs/solana-estimate-priority-fees-getrecentprioritizationfees) * [Solana Trader Nodes](/docs/solana-trader-nodes) * Or the official Solan docs: [Fees on Solana](https://solana.com/docs/core/fees). ## Implementation If you end up in this specific scenario as outlined in the beginning of the article, here's things that you need to have to get the results: * A program address that you are interacting with to get the races simply won or tactically won (like trying to get your transaction execution at a certain place in the race instead of trying to be first), or simply how much on an edge you need to be the first. Which you can also do with [How to use Priority Fees to unlock faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) and [Estimate Priority Fees with getRecentPrioritizationFees](/docs/solana-estimate-priority-fees-getrecentprioritizationfees) as mentioned. * A transaction signature — this can be the signature of your transaction of a transaction of an account that you get through your own [humint](https://en.wikipedia.org/wiki/Human_intelligence_\(intelligence_gathering\)) ways. The script itself: ```python Python theme={"system"} from solana.rpc.api import Client from solana.rpc.commitment import Commitment from solders.signature import Signature import base58 import json from typing import List, Optional, Tuple PROGRAM_ID = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" COMPUTE_BUDGET_ID = "ComputeBudget111111111111111111111111111111" def get_transaction_slot(client: Client, signature_str: str) -> Optional[int]: try: signature_bytes = base58.b58decode(signature_str) signature = Signature(signature_bytes) response = client.get_transaction( signature, max_supported_transaction_version=1 ) if hasattr(response, 'value') and response.value: return response.value.slot return None except Exception as e: print(f"Error getting transaction slot: {e}") return None def calculate_transaction_fees(tx) -> Tuple[float, float]: total_fee_lamports = tx.meta.fee total_fee_sol = total_fee_lamports / 1_000_000_000 cu_price_micro_lamports = None cu_limit = None for ix in tx.transaction.message.instructions: program_id = tx.transaction.message.account_keys[ix.program_id_index] if str(program_id) == COMPUTE_BUDGET_ID: data = base58.b58decode(ix.data) if len(data) >= 9 and data[0] == 3: cu_price_micro_lamports = int.from_bytes(data[1:9], 'little') elif len(data) >= 4 and data[0] == 2: cu_limit = int.from_bytes(data[1:4], 'little') if cu_price_micro_lamports is not None and cu_limit is not None: priority_fee_lamports = (cu_price_micro_lamports * cu_limit) // 1_000_000 priority_fee_sol = priority_fee_lamports / 1_000_000_000 else: priority_fee_sol = 0 return total_fee_sol, priority_fee_sol def get_block_transactions(client: Client, slot: int, target_signature: str) -> List[tuple]: try: response = client.get_block(slot, max_supported_transaction_version=1) if not response.value: return [] transactions = [] for tx in response.value.transactions: if any(str(account) == PROGRAM_ID for account in tx.transaction.message.account_keys): signature = str(tx.transaction.signatures[0]) total_fee, priority_fee = calculate_transaction_fees(tx) writable_accounts = [] for idx, account in enumerate(tx.transaction.message.account_keys): if idx < tx.transaction.message.header.num_required_signatures: writable_accounts.append(str(account)) compute_units = tx.meta.compute_units_consumed if hasattr(tx.meta, 'compute_units_consumed') else None tx_info = { 'signature': signature, 'total_fee': total_fee, 'priority_fee': priority_fee, 'block': slot, 'writable_accounts': writable_accounts, 'compute_units': compute_units } if signature == target_signature: transactions.insert(0, tx_info) else: transactions.append(tx_info) return transactions except Exception as e: print(f"Error getting block transactions for slot {slot}: {e}") return [] def analyze_slots(rpc_url: str, signature: str, slots_back: int, slots_forward: int): client = Client(rpc_url) current_slot = get_transaction_slot(client, signature) if not current_slot: print("Could not find transaction slot") return print(f"\nAnalyzing slots {current_slot-slots_back} to {current_slot+slots_forward}") print("=" * 80) for slot in range(current_slot - slots_back, current_slot + slots_forward + 1): transactions = get_block_transactions(client, slot, signature) print(f"\nSlot {slot}:") print("-" * 40) if transactions: target_tx = None other_txs = [] for tx in transactions: if tx['signature'] == signature: target_tx = tx else: other_txs.append(tx) other_txs.sort(key=lambda x: x['priority_fee'], reverse=True) if target_tx: print("\n" + "⭐️" * 40) print("🎯 TARGET TRANSACTION FOUND 🎯") print("⭐️" * 40) print(f"\nSignature: {target_tx['signature']}") print(f"Block: {target_tx['block']}") print(f"{'Total fee:':<15} {target_tx['total_fee']:>12.8f} SOL") print(f"{'Priority fee:':<15} {target_tx['priority_fee']:>12.8f} SOL") print(f"{'Base fee:':<15} {(target_tx['total_fee'] - target_tx['priority_fee']):>12.8f} SOL") print(f"Compute Units: {target_tx['compute_units']}") print("\nWritable Accounts:") for account in target_tx['writable_accounts']: print(f" {account}") print("⭐️" * 40 + "\n") if other_txs: print(f"Other transactions in slot {slot} (sorted by priority fee):") print("-" * 40) for tx in other_txs: print(f"\nSignature: {tx['signature']}") print(f"Block: {tx['block']}") print(f"Fee: {tx['total_fee']:.8f} SOL (Priority: {tx['priority_fee']:.8f} SOL)") print(f"Compute Units: {tx['compute_units']}") print("Writable Accounts:") for account in tx['writable_accounts']: print(f" {account}") print("-" * 40) else: print("No relevant transactions") def main(): RPC_URL = "CHAINSTACK_NODE_RPC" SIGNATURE = "TRANSACTION_SIGNATURE" SLOTS_BACK = 1 # Number of slots to check back SLOTS_FORWARD = 1 # Number of slots to check forward analyze_slots(RPC_URL, SIGNATURE, SLOTS_BACK, SLOTS_FORWARD) if __name__ == "__main__": main() ``` In the script: * PROGRAM\_ID — set to the [pump.fun executable](https://solscan.io/account/6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P). Set it to any executable on-chain program you interact with. * COMPUTE\_BUDGET\_ID — set to the [Solana system program](https://solscan.io/account/ComputeBudget111111111111111111111111111111) responsible for compute units and fees. We use in the script to find and decode the priority fees. * RPC\_URL — tour Chainstack [Solana node endpoint](https://console.chainstack.com/). * SIGNATURE — the transactions signature (hash) around which you want to analyze similar transactions. * SLOTS\_BACK — number of slots to travel back from the slot where you transaction landed (was included in the block). * SLOTS\_FORWARD — number of slot to travel forward to see who used what fees after you transaction landed. To use the script, just run `python check_adjacent.py` or whatever you name it. Example output: ```shell Shell theme={"system"} Slot 309220412: ---------------------------------------- ⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️ 🎯 TARGET TRANSACTION FOUND 🎯 ⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️ Signature: 3Bd4wJ3pZrPYbhnHpUbZFXfunD2A1dciNx8umhhTEg4KLYmP1nERNbKEbHP9j6K8CcJgdekRi3PhUqVjdsHcsMSA Block: 309220412 Total fee: 0.00200500 SOL Priority fee: 0.00200000 SOL Base fee: 0.00000500 SOL Compute Units: 57900 Writable Accounts: 8SDfmRCMwn4qNvBpnvMTX7hunGHXWEJ5odhazNVcFyBw ⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️ Other transactions in slot 309220412 (sorted by priority fee): ---------------------------------------- ``` ## Use cases This tool is particularly useful for: * Analyzing competitor trading strategies * Optimizing your own priority fees * Troubleshooting failed transactions * Understanding market competition in specific slots * Developing automated trading systems ## Conclusion Understanding transaction priority fees and their impact on execution is crucial for successful trading on Solana. This tool helps you analyze the competitive landscape and optimize your trading strategy by providing insights into how other traders are setting their priority fees. For more Solana development tools and guides, see [Mastering Solana](/docs/solana-development). > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Anchor development Source: https://docs.chainstack.com/docs/solana-anchor-development Build, test, and deploy Solana programs with Anchor — project scaffolding, account constraints, IDL generation, LiteSVM testing, and client interaction. **TLDR:** * [Anchor](https://www.anchor-lang.com/) is the standard framework for building Solana programs. It handles account validation, serialization, and security checks through Rust macros. * `anchor init` scaffolds a full workspace: program source, tests, deployment config, and TypeScript types. * The `#[program]` macro defines instructions, `#[derive(Accounts)]` defines account validation, and `#[account]` defines on-chain state. * Anchor 1.0.0 (April 2026) ships breaking changes: `@anchor-lang/core` replaces `@coral-xyz/anchor`, Surfpool replaces solana-test-validator, and LiteSVM is the default test template. ## Prerequisites Install the following before starting. The guide targets Anchor 1.0.0. | Tool | Version | Install | | - | - | - | | Rust | 1.89.0+ | `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \| sh` | | Solana CLI | 3.1.10+ | `sh -c "\$(curl -sSfL https://release.anza.xyz/v3.1.10/install)"` | | Anchor CLI | 1.0.0 | `cargo install avm --git https://github.com/solana-foundation/anchor --locked && avm install 1.0.0 && avm use 1.0.0` | | Node.js | 22+ | [nodejs.org](https://nodejs.org/) | | Yarn | 1.22+ | `npm install -g yarn` | | Surfpool | 1.1.2+ | [Installation instructions](https://github.com/solana-foundation/surfpool?tab=readme-ov-file#installation) | Verify your setup: ```shell theme={"system"} anchor --version # anchor-cli 1.0.0 solana --version # solana-cli 3.1.x rustc --version # rustc 1.89.0+ surfpool --version # surfpool 1.1.2+ ``` > [!WARNING] > > Anchor 1.0.0 uses Surfpool instead of solana-test-validator for `anchor test`. If Surfpool is not installed, `anchor test` will fail with a connection error. If you cannot install Surfpool, use `anchor test --validator legacy` to fall back to solana-test-validator. > [!NOTE] > > Anchor 1.0.0 no longer requires the Solana CLI to be on your PATH for most commands. The `anchor` CLI ships native implementations of `balance`, `airdrop`, `address`, and `deploy`. You still need the Solana CLI for operations like `solana-keygen` and `solana program close`. ## Creating a project Scaffold a new Anchor workspace: ```shell theme={"system"} anchor init my-counter cd my-counter ``` This creates a modular project structure with separate files for instructions, state, constants, and errors. If you prefer everything in a single `lib.rs`, use `--template single`: ```shell theme={"system"} anchor init my-counter --template single ``` > [!TIP] > > The modular template (default) is recommended for production programs. It scales better as your program grows. ### Test templates Anchor 1.0.0 generates a LiteSVM Rust test by default. You can pick a different template: ```shell theme={"system"} anchor init my-counter --test-template mocha # TypeScript with Mocha anchor init my-counter --test-template mollusk # Mollusk test library ``` ## Project structure After `anchor init`, the workspace looks like this: ``` my-counter/ ├── programs/ │ └── my-counter/ │ ├── src/ │ │ ├── lib.rs # Entry point, module declarations │ │ ├── constants.rs # Program constants │ │ ├── error.rs # Custom error codes │ │ ├── instructions.rs # Re-exports instruction modules │ │ ├── instructions/ │ │ │ └── initialize.rs # Instruction handler │ │ └── state.rs # Account state definitions │ └── Cargo.toml ├── tests/ │ └── my-counter.ts # Test file (or src/ for Rust tests) ├── target/ │ ├── deploy/ # Compiled .so and keypair │ ├── idl/ # Generated IDL JSON │ └── types/ # Generated TypeScript types ├── Anchor.toml # Workspace configuration ├── Cargo.toml # Rust workspace manifest ├── rust-toolchain.toml # Rust MSRV pinning └── package.json ``` Key directories: * `programs/` — your on-chain programs. A workspace can contain multiple programs. * `target/deploy/` — the compiled program binary (`.so` file) and program keypair. * `target/idl/` — the generated IDL JSON file that clients use to interact with your program. * `target/types/` — TypeScript type definitions generated from the IDL. ### Anchor.toml The workspace configuration file controls which cluster you deploy to, which wallet signs transactions, and what scripts run during testing: ```toml theme={"system"} [toolchain] package_manager = "yarn" [features] resolution = true skip-lint = false [programs.localnet] my_counter = "YourProgramId111111111111111111111111111111" [provider] cluster = "localnet" wallet = "~/.config/solana/id.json" [scripts] test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 \"tests/**/*.ts\"" [hooks] ``` You can optionally pin versions in the `[toolchain]` section: ```toml theme={"system"} [toolchain] anchor_version = "1.0.0" solana_version = "3.1.10" package_manager = "yarn" ``` > [!NOTE] > > The `[registry]` section has been removed in Anchor 1.0.0. If you see it in older examples, delete it. ## Program anatomy An Anchor program is built around four macros. Here is a complete counter program that demonstrates all of them: ```rust theme={"system"} use anchor_lang::prelude::*; declare_id!("Cntr1111111111111111111111111111111111111111"); #[program] pub mod my_counter { use super::*; pub fn initialize(ctx: Context) -> Result<()> { let counter = &mut ctx.accounts.counter; counter.count = 0; counter.authority = ctx.accounts.authority.key(); msg!("Counter initialized by {}", counter.authority); Ok(()) } pub fn increment(ctx: Context) -> Result<()> { let counter = &mut ctx.accounts.counter; counter.count = counter.count.checked_add(1) .ok_or(ErrorCode::Overflow)?; msg!("Counter incremented to {}", counter.count); Ok(()) } } #[derive(Accounts)] pub struct Initialize<'info> { #[account( init, payer = authority, space = 8 + Counter::INIT_SPACE, seeds = [b"counter", authority.key().as_ref()], bump, )] pub counter: Account<'info, Counter>, #[account(mut)] pub authority: Signer<'info>, pub system_program: Program<'info, System>, } #[derive(Accounts)] pub struct Increment<'info> { #[account( mut, seeds = [b"counter", authority.key().as_ref()], bump, has_one = authority, )] pub counter: Account<'info, Counter>, pub authority: Signer<'info>, } #[account] #[derive(InitSpace)] pub struct Counter { pub count: u64, pub authority: Pubkey, } #[error_code] pub enum ErrorCode { #[msg("Counter overflow")] Overflow, } ``` The sections below break down each macro. ### declare\_id! Sets the program's on-chain address. By default, this matches the public key from `/target/deploy/my_counter-keypair.json`. ```rust theme={"system"} declare_id!("Cntr1111111111111111111111111111111111111111"); ``` If the keypair and source code fall out of sync (for example, after cloning a repo), run: ```shell theme={"system"} anchor keys sync ``` > [!WARNING] > > Anchor 1.0.0 checks that the `declare_id!` value matches the keypair file during `anchor build`. A mismatch produces a compile error. Use `anchor build --ignore-keys` to skip this check if needed. ### #\[program] Defines the module containing your instruction handlers. Each public function in this module becomes a callable instruction: ```rust theme={"system"} #[program] pub mod my_counter { use super::*; pub fn initialize(ctx: Context) -> Result<()> { // instruction logic Ok(()) } } ``` Every handler receives a `Context` as its first parameter, where `T` is the accounts struct. Additional parameters become the instruction arguments that clients pass in. The `Context` struct provides: * `ctx.accounts` — the validated accounts * `ctx.program_id` — the program's public key * `ctx.remaining_accounts` — extra accounts not in the struct * `ctx.bumps` — PDA bump seeds found during validation ### #\[derive(Accounts)] Defines which accounts an instruction requires and how to validate them. Each field specifies an account with its type and constraints: ```rust theme={"system"} #[derive(Accounts)] pub struct Initialize<'info> { #[account( init, payer = authority, space = 8 + Counter::INIT_SPACE, seeds = [b"counter", authority.key().as_ref()], bump, )] pub counter: Account<'info, Counter>, #[account(mut)] pub authority: Signer<'info>, pub system_program: Program<'info, System>, } ``` Anchor validates all constraints before executing the instruction handler. If any check fails, the instruction aborts with a descriptive error. Common account types: | Type | Purpose | | - | - | | `Account<'info, T>` | Deserialized account owned by this program | | `Signer<'info>` | Must have signed the transaction | | `SystemAccount<'info>` | Any system-owned account | | `Program<'info, T>` | An executable program account | | `UncheckedAccount<'info>` | No validation — use with explicit checks | ### #\[account] Defines the data layout for accounts your program creates. Anchor adds an 8-byte discriminator as a prefix to distinguish account types: ```rust theme={"system"} #[account] #[derive(InitSpace)] pub struct Counter { pub count: u64, // 8 bytes pub authority: Pubkey, // 32 bytes } ``` The discriminator is the first 8 bytes of `SHA256("account:Counter")`. It is automatically set during `init` and verified during deserialization. ## Account constraints Constraints are the core of Anchor's security model. They go inside `#[account(...)]` attributes and tell Anchor what to validate before executing your instruction. ### Initialization constraints Create a new account and set its discriminator: ```rust theme={"system"} #[account( init, payer = authority, space = 8 + Counter::INIT_SPACE, )] pub counter: Account<'info, Counter>, ``` The `init` constraint requires: * `payer` — the account paying the rent * `space` — total bytes (8-byte discriminator + your data) Use `init_if_needed` to create only if the account does not already exist. This requires the `init-if-needed` feature in your `Cargo.toml`: ```toml theme={"system"} [dependencies] anchor-lang = { version = "1.0.0", features = ["init-if-needed"] } ``` ### PDA constraints Validate that an account is a PDA derived from specific seeds: ```rust theme={"system"} #[account( seeds = [b"counter", authority.key().as_ref()], bump, )] pub counter: Account<'info, Counter>, ``` When combined with `init`, this creates the PDA account: ```rust theme={"system"} #[account( init, payer = authority, space = 8 + Counter::INIT_SPACE, seeds = [b"counter", authority.key().as_ref()], bump, )] pub counter: Account<'info, Counter>, ``` Anchor finds and stores the canonical bump automatically. Access it in your handler with `ctx.bumps.counter`. > [!WARNING] > > Use fixed-length seeds or include a separator between variable-length seeds. Without this, `seeds = [b"ab", b"cd"]` and `seeds = [b"abcd"]` produce the same PDA, which can lead to seed collision vulnerabilities. ### Mutability and ownership ```rust theme={"system"} #[account(mut)] // Account must be mutable #[account(mut, has_one = authority)] // Must be mutable AND counter.authority == authority.key() #[account(owner = some_program.key())] // Account owned by a specific program #[account(close = destination)] // Close account, send lamports to destination ``` ### Reallocation Resize an existing account: ```rust theme={"system"} #[account( mut, realloc = 8 + NewLayout::INIT_SPACE, realloc::payer = authority, realloc::zero = false, )] pub data_account: Account<'info, NewLayout>, ``` ### SPL token constraints Create or validate token accounts: ```rust theme={"system"} #[account( init, payer = authority, token::mint = mint, token::authority = authority, )] pub token_account: Account<'info, TokenAccount>, #[account( init, payer = authority, mint::decimals = 6, mint::authority = authority, )] pub mint: Account<'info, Mint>, ``` ### Accessing instruction arguments in constraints Use `#[instruction(...)]` to reference instruction arguments in your constraints: ```rust theme={"system"} #[derive(Accounts)] #[instruction(title: String)] pub struct CreatePost<'info> { #[account( init, payer = author, space = 8 + 32 + 4 + title.len(), seeds = [b"post", author.key().as_ref(), title.as_bytes()], bump, )] pub post: Account<'info, Post>, #[account(mut)] pub author: Signer<'info>, pub system_program: Program<'info, System>, } ``` > [!WARNING] > > In Anchor 1.0.0, the compiler enforces that `#[instruction(...)]` argument types and order match the handler signature. Mismatches that were previously silent now produce errors. ## Space calculation Every account needs enough space for the 8-byte discriminator plus its data fields. ### Type sizes | Type | Bytes | | - | - | | `bool` | 1 | | `u8` / `i8` | 1 | | `u16` / `i16` | 2 | | `u32` / `i32` | 4 | | `u64` / `i64` | 8 | | `u128` / `i128` | 16 | | `Pubkey` | 32 | | `[T; N]` | `size(T) * N` | | `Vec` | `4 + (size(T) * max_elements)` | | `String` | `4 + max_bytes` | | `Option` | `1 + size(T)` | | `Enum` | `1 + largest_variant` | ### Manual calculation ```rust theme={"system"} #[account] pub struct GameState { pub player: Pubkey, // 32 pub score: u64, // 8 pub level: u16, // 2 pub active: bool, // 1 } // Total: 8 (discriminator) + 32 + 8 + 2 + 1 = 51 bytes ``` ### Using InitSpace The `InitSpace` derive macro calculates space automatically. Use `#[max_len(...)]` for dynamic types: ```rust theme={"system"} #[account] #[derive(InitSpace)] pub struct UserProfile { pub authority: Pubkey, #[max_len(50)] pub username: String, // 4 + 50 = 54 bytes #[max_len(5, 50)] pub interests: Vec, // 4 + (5 * (4 + 50)) = 274 bytes pub points: u64, } // In the accounts struct: #[account(init, payer = authority, space = 8 + UserProfile::INIT_SPACE)] pub profile: Account<'info, UserProfile>, ``` The `#[max_len(5, 50)]` on `Vec` means: up to 5 strings, each up to 50 bytes. ## Building and the IDL Build your program: ```shell theme={"system"} anchor build ``` This produces three outputs: 1. **Program binary** — `target/deploy/my_counter.so` — the compiled BPF bytecode. 2. **IDL file** — `target/idl/my_counter.json` — a JSON description of your program's instructions, accounts, and types. 3. **TypeScript types** — `target/types/my_counter.ts` — type definitions generated from the IDL. ### IDL structure The IDL maps your Rust program to a format that clients can consume: ```json theme={"system"} { "address": "Cntr1111111111111111111111111111111111111111", "metadata": { "name": "my_counter", "version": "0.1.0", "spec": "0.1.0" }, "instructions": [ { "name": "initialize", "discriminator": [175, 175, 109, 31, 13, 152, 155, 237], "accounts": [ { "name": "counter", "writable": true, "pda": { ... } }, { "name": "authority", "writable": true, "signer": true }, { "name": "system_program", "address": "11111111111111111111111111111111" } ], "args": [] } ], "accounts": [ { "name": "Counter", "discriminator": [255, 176, 4, 245, ...] } ], "types": [ { "name": "Counter", "type": { "kind": "struct", "fields": [...] } } ] } ``` ### On-chain IDL via Program Metadata Program Anchor 1.0.0 replaces the legacy IDL storage mechanism with the [Program Metadata Program (PMP)](https://github.com/solana-foundation/anchor/pull/3798). When you run `anchor program deploy`, the IDL is uploaded automatically. To skip the upload: ```shell theme={"system"} anchor program deploy --no-idl ``` ## Testing Anchor 1.0.0 defaults to LiteSVM for Rust-based testing. LiteSVM runs an in-process Solana VM—faster startup and teardown than `solana-test-validator`. ### TypeScript tests For TypeScript tests (generated with `--test-template mocha`), the test file uses `@anchor-lang/core`: ```typescript theme={"system"} import * as anchor from "@anchor-lang/core"; import { Program } from "@anchor-lang/core"; import { MyCounter } from "../target/types/my_counter"; import { PublicKey } from "@solana/web3.js"; import assert from "assert"; describe("my-counter", () => { const provider = anchor.AnchorProvider.env(); anchor.setProvider(provider); const program = anchor.workspace.myCounter as Program; const wallet = provider.wallet as anchor.Wallet; it("initializes the counter", async () => { const [counterPda] = PublicKey.findProgramAddressSync( [Buffer.from("counter"), wallet.publicKey.toBuffer()], program.programId ); const tx = await program.methods .initialize() .accounts({ authority: wallet.publicKey, }) .rpc(); const counter = await program.account.counter.fetch(counterPda); assert.equal(counter.count.toNumber(), 0); assert.ok(counter.authority.equals(wallet.publicKey)); console.log("Initialize tx:", tx); }); it("increments the counter", async () => { const [counterPda] = PublicKey.findProgramAddressSync( [Buffer.from("counter"), wallet.publicKey.toBuffer()], program.programId ); const tx = await program.methods .increment() .accounts({ authority: wallet.publicKey, }) .rpc(); const counter = await program.account.counter.fetch(counterPda); assert.equal(counter.count.toNumber(), 1); console.log("Increment tx:", tx); }); }); ``` > [!WARNING] > > Anchor 1.0.0 renamed the TypeScript package from `@coral-xyz/anchor` to `@anchor-lang/core`. All existing tutorials and Stack Overflow answers that show `@coral-xyz/anchor` need the import updated. ### Running tests Anchor 1.0.0 uses Surfpool as the default local validator for `anchor test`: ```shell theme={"system"} anchor test ``` This starts Surfpool, builds and deploys your program, runs the test suite, and shuts down the validator. To use the legacy solana-test-validator instead: ```shell theme={"system"} anchor test --validator legacy ``` To run tests against an already-running local validator: ```shell theme={"system"} anchor test --skip-local-validator ``` ## Deploying ### Local deployment Start a local validator and deploy: ```shell theme={"system"} solana-test-validator # In a separate terminal: anchor program deploy ``` > [!TIP] > > If `anchor program deploy` fails with a 503 error on `get_recommended_micro_lamport_fee`, pass a manual compute unit price: > > ```shell theme={"system"} > anchor program deploy --with-compute-unit-price 1 > ``` > > This is a [known issue](https://github.com/solana-foundation/anchor/issues/4255) that affects some local RPC configurations. ### Deploying to devnet 1. Set your cluster to devnet in `Anchor.toml`: ```toml theme={"system"} [provider] cluster = "Devnet" wallet = "~/.config/solana/id.json" ``` 2. Fund your wallet: ```shell theme={"system"} solana airdrop 5 --url devnet ``` 3. Build and deploy: ```shell theme={"system"} anchor build anchor program deploy ``` > [!NOTE] > > `anchor program deploy` in 1.0.0 automatically uploads the IDL to the on-chain Program Metadata Program. The IDL becomes fetchable by any client using `Program.fetchIdl`. ### Deploying to mainnet with Chainstack 1. [Deploy a Solana node](https://chainstack.com/build-better-with-solana/) on Chainstack or use the Chainstack Solana endpoint. 2. Set your cluster to your Chainstack endpoint in `Anchor.toml`: ```toml theme={"system"} [provider] cluster = "https://solana-mainnet.core.chainstack.com/YOUR_CHAINSTACK_KEY" wallet = "~/.config/solana/id.json" ``` 3. Build and deploy: ```shell theme={"system"} anchor build anchor program deploy ``` > [!WARNING] > > Mainnet deployment requires real SOL. Verify your program thoroughly on devnet before deploying to mainnet. ## Client interaction The IDL enables typed client interactions. Anchor's TypeScript client (in the `@anchor-lang/core` package) deserializes accounts and instruction arguments automatically. ### Connecting to a program ```typescript theme={"system"} import * as anchor from "@anchor-lang/core"; import { Program } from "@anchor-lang/core"; import { Connection, PublicKey, Keypair } from "@solana/web3.js"; import { MyCounter } from "../target/types/my_counter"; import idl from "../target/idl/my_counter.json"; const CHAINSTACK_ENDPOINT = "CHAINSTACK_NODE_URL"; const connection = new Connection(CHAINSTACK_ENDPOINT, "confirmed"); // Load your wallet keypair from a JSON file (must be funded) import fs from "fs"; const secretKey = Uint8Array.from( JSON.parse(fs.readFileSync("/path/to/keypair.json", "utf-8")) ); const wallet = new anchor.Wallet(Keypair.fromSecretKey(secretKey)); const provider = new anchor.AnchorProvider(connection, wallet, { commitment: "confirmed", }); const program = new Program(idl as MyCounter, provider); ``` > [!NOTE] > > The `anchor.workspace` object is only available inside `anchor test`. For standalone scripts and frontend code, load the IDL JSON file directly and instantiate the program with `new Program(idl, provider)`. ### Sending transactions The wallet must be funded before sending transactions. On devnet, request an airdrop first: ```typescript theme={"system"} // Initialize const tx = await program.methods .initialize() .accounts({ authority: wallet.publicKey, }) .rpc(); console.log("Transaction signature:", tx); ``` ### Fetching accounts ```typescript theme={"system"} const [counterPda] = PublicKey.findProgramAddressSync( [Buffer.from("counter"), wallet.publicKey.toBuffer()], program.programId ); // Fetch a single account const counter = await program.account.counter.fetch(counterPda); console.log("Count:", counter.count.toNumber()); console.log("Authority:", counter.authority.toBase58()); // Fetch all counter accounts const allCounters = await program.account.counter.all(); allCounters.forEach((item) => { console.log("Address:", item.publicKey.toBase58()); console.log("Count:", item.account.count.toNumber()); }); // Fetch with filters const myCounters = await program.account.counter.all([ { memcmp: { offset: 16, // 8 discriminator + 8 count bytes: wallet.publicKey.toBase58(), }, }, ]); ``` ### Listening to events If your program emits events with the `emit!` macro: ```rust theme={"system"} #[event] pub struct CounterIncremented { pub authority: Pubkey, pub new_count: u64, } // In your instruction handler: emit!(CounterIncremented { authority: counter.authority, new_count: counter.count, }); ``` Subscribe from the client: ```typescript theme={"system"} const listener = program.addEventListener("counterIncremented", (event) => { console.log("New count:", event.newCount.toNumber()); }); // Later, remove the listener: program.removeEventListener(listener); ``` ## Common gotchas ### CpiContext takes Pubkey, not AccountInfo Anchor 1.0.0 removed the redundant `AccountInfo` copy from `CpiContext`. If you are adapting pre-1.0 code: ```rust theme={"system"} // Pre-1.0 (broken in 1.0.0) CpiContext::new( ctx.accounts.token_program.to_account_info(), Transfer { ... } ) // Anchor 1.0.0 CpiContext::new( ctx.accounts.token_program.key(), Transfer { ... } ) ``` ### Duplicate mutable accounts are now rejected Passing the same mutable account twice in an instruction errors by default. If you intentionally need this, opt in with `dup`: ```rust theme={"system"} #[derive(Accounts)] pub struct SwapAccounts<'info> { #[account(mut)] pub account_a: Account<'info, TokenAccount>, #[account(mut, dup)] pub account_b: Account<'info, TokenAccount>, } ``` ### AccountInfo is deprecated in Accounts structs Using `AccountInfo` directly inside `#[derive(Accounts)]` now produces a compile warning. Use `UncheckedAccount` instead: ```rust theme={"system"} // Deprecated pub some_account: AccountInfo<'info>, // Preferred /// CHECK: Validated in instruction logic pub some_account: UncheckedAccount<'info>, ``` ### Space must include the 8-byte discriminator Every `init` constraint needs 8 extra bytes for the discriminator: ```rust theme={"system"} // Wrong — account will be too small #[account(init, payer = user, space = Counter::INIT_SPACE)] // Correct #[account(init, payer = user, space = 8 + Counter::INIT_SPACE)] ``` ### Error codes are limited to one block Anchor 1.0.0 restricts `#[error_code]` to a single definition per program. If you have multiple error blocks, consolidate them: ```rust theme={"system"} #[error_code] pub enum ErrorCode { #[msg("Counter overflow")] Overflow, #[msg("Unauthorized")] Unauthorized, #[msg("Invalid input")] InvalidInput, } ``` ### Surfpool vs solana-test-validator `anchor test` now uses Surfpool by default. If you encounter compatibility issues: ```shell theme={"system"} # Fall back to the legacy validator anchor test --validator legacy ``` Surfpool is faster but less feature-complete than `solana-test-validator`. Use the legacy validator when you need full RPC method support or when cloning accounts from mainnet. ## Reference repos These are the source repositories we worked against while writing this guide. They stay closer to reality than docs — check them first when something here looks off. * [solana-foundation/anchor](https://github.com/solana-foundation/anchor) — Anchor 1.0 source and the reference for macros and for `anchor build`, `anchor keys sync`, and `anchor deploy` behavior * [solana-foundation/solana-bootcamp-2026](https://github.com/solana-foundation/solana-bootcamp-2026) — bootcamp's Anchor projects used as a correctness reference * [txtx/surfpool](https://github.com/txtx/surfpool) — local validator used in the Anchor mainnet-fork workflow # Solana archive nodes: Data availability & tooling Source: https://docs.chainstack.com/docs/solana-archive-nodes-the-backbone-of-solanas-data-availability-and-developer-tooling Understand Solana archive nodes for full historical data access, covering use cases in analytics, auditing, and developer tooling on Chainstack. The Solana ecosystem is pretty rich & diverse in the projects, dapps, developer tooling, and the need for robust and scalable data management solutions is as crucial as ever. Solana archive nodes, heavy as they are, play a pivotal role in providing the ecosystem with a decentralized, high-performance, and extensible infrastructure for storing and accessing historical blockchain data. ## The importance of Solana archive nodes Solana archive nodes are critical for the overall Web3 ecosystem. From an end-user perspective, these nodes serve as an immutable record of the Solana blockchain's history, allowing users to access and verify past transaction data, providing transparency and accountability. The Solana explorer, a web-based tool that allows users to browse the Solana blockchain, is powered by these archive nodes. From a developer's standpoint, Solana archive nodes are crucial for building robust and reliable applications on top of the Solana blockchain. Developers can leverage the historical data stored in these nodes to implement advanced analytics and reporting features, facilitate data-driven decision making, ensure data integrity and auditability, and enable cross-chain interoperability. This comprehensive data storage serves several critical functions: 1. Data availability: Solana archive nodes ensure that historical blockchain data is readily available and accessible, enabling users and applications to access past transaction records, account balances, and other on-chain information as needed. You can ingest the Solana archive data (including other protocols) with Chainstack nodes. 2. Performance: Solana archive nodes, similar to the regular full nodes, are designed to provide fast and efficient access to historical data. 3. End-user benefits: For end-users, Solana archive nodes enable access to historical data and transaction records, which can be useful for tasks such as auditing, research, and analytical purposes. 4. Developer benefits: Developers building applications on Solana can leverage the archive node system to access historical data and build more sophisticated features and functionality. This can include things like on-chain analytics, data visualization, and historical data-driven applications. A few examples: 1. Explorers & analytics platforms — Companies like Solana FM, Solana Beach, and Solscan leverage Solana archive nodes to provide comprehensive transaction history, account balances, and advanced analytics for the Solana ecosystem. 2. DeFi apps — DeFi platforms that operate on the Solana blockchain, such as Serum, Raydium, and Saber, rely on archive nodes to retrieve historical trading data, calculate yields, and power their financial services. 3. Non-Fungible Token (NFT) Marketplaces: NFT platforms built on Solana, including Magic Eden and Solanart, use archive nodes to track the ownership history, trading volumes, and other metadata for NFTs on the Solana network. 4. Solana-based gaming and metaverse projects, like Aurory and Star Atlas, leverage archive nodes to store and retrieve player activity and use it for analytics, ## Key architectural components The foundation of a Solana archive node is its custom-built ledger database designed for append-only writes of massive data volumes. Solana uses RocksDB, a high-performance key-value store based on LevelDB, to achieve the throughput and scalability needed for blockchain data storage. Archive nodes store the full ledger history, which can span hundreds of terabytes and is constantly growing. To keep storage costs manageable, older data is progressively moved to cheaper "cold storage" tiers like Amazon S3 Glacier. Operators use sophisticated data tiering and recovery mechanisms to strike the right balance between cost and access latency. On top of the ledger storage, archive nodes maintain several index structures mapping block heights and signatures to the physical storage location. This allows quickly looking up and retrieving any historical transaction or block by its signature or height. With large amounts of data to store, archive nodes need robust redundancy and replication to protect against data loss. Ledger data is typically stored with 3x replication, spread across multiple disks, machines, and even geographic regions. Solana's [Turbine block propagation protocol](https://docs.solanalabs.com/consensus/turbine-block-propagation) allows streaming large amounts of data to replicas with minimal redundancy. To allow scaling storage beyond a single machine, the archive is horizontally sharded across multiple nodes. Solana's architecture provides a framework for seamlessly sharding the state. New nodes joining the cluster can quickly sync by downloading a snapshot of the full state at a given point. ## Retrieving Historical Data Developers can access historical data through Solana's JSON RPC APIs exposed by archive nodes. Here's an example of fetching an old transaction using the JavaScript `@solana/web3.js` library: ```javascript Javascript theme={"system"} const solanaWeb3 = require("@solana/web3.js"); const connection = new solanaWeb3.Connection('CHAINSTACK_ARCHIVE_NODE'); (async () => { const oldTx = await connection.getTransaction('txSignature', { commitment: 'confirmed', maxSupportedTransactionVersion: 1 }); console.log(oldTx); })(); ``` The [`getTransaction`](/reference/gettransaction) method allows specifying a transaction signature to look up. Archive nodes use their indexes to quickly locate and return the requested transaction, even if it occurred months ago. ## Conclusion Solana archive nodes are a critical component of the Solana blockchain infrastructure, ensuring the long-term scalability, integrity, and utility of the network. By providing secure, decentralized, and performant access to historical blockchain data, these nodes empower end-users and developers alike to unlock new insights, build innovative applications, and drive the continued growth of the Solana ecosystem. As Solana continues to gain traction and adoption, the importance of its archive node system will only increase. Developers and users can rest assured that the Solana blockchain is backed by a robust and scalable data management infrastructure, ready to support the demands of the Web3 future. # Solana blockSubscribe 1009 error on WebSocket Source: https://docs.chainstack.com/docs/solana-blocksubscribe-1009-error-on-websocket Fix WebSocket close code 1009 (message too big) when subscribing to large Solana blocks. Raise max_size in Python websockets and maxPayload in Node ws. **TLDR:** * WebSocket close code `1009` (`MESSAGE_TOO_BIG`) fires when the inbound frame exceeds the client's payload cap — common on Solana `blockSubscribe` because mainnet blocks can be tens of MiB. * Python `websockets` defaults to a 1 MiB cap (`max_size=2**20`). Raise or disable it on the `connect()` call. * Node `ws` defaults to a 100 MiB cap (`maxPayload`). Usually fine, but disable it (`Infinity`) for full blocks. * Raising the cap stops `1009`, but `blockSubscribe` itself is [unstable upstream](https://solana.com/docs/rpc/websocket/blocksubscribe). For production, use [Yellowstone gRPC Geyser](/docs/yellowstone-grpc-geyser-plugin). > [!WARNING] > > **`blockSubscribe` is marked [unstable by the Solana team](https://solana.com/docs/rpc/websocket/blocksubscribe).** The fixes below address client-side `1009` only — for production, use [Yellowstone gRPC Geyser](/docs/yellowstone-grpc-geyser-plugin) instead. ## What close code 1009 means Per [RFC 6455 §7.4.1](https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1), close code `1009` (`MESSAGE_TOO_BIG`) signals that an endpoint received a frame or message larger than it is willing or able to process. It is a defensive cap meant to protect the client from resource exhaustion or a hostile server flooding it with oversized frames. On Solana, [`blockSubscribe`](https://solana.com/docs/rpc/websocket/blocksubscribe) pushes the full block payload — transactions, inner instructions, log messages, balance deltas. On Mainnet this is routinely well above 1 MiB and can climb past 10 MiB during heavy activity. A library default of 1 MiB will drop the connection on the first large block. The symptom differs by library: * **Python `websockets`** raises `PayloadTooBig`, then closes with code `1009`. * **Node `ws`** emits a `close` event with `code === 1009`. Code `1006` ("abnormal closure") often shows up on the same workload after the `1009` fix — that one is the upstream instability, not a payload-size issue. Raising the client cap will not fix `1006`; switching to Geyser will. ## Python: `websockets` default limit The [`websockets`](https://websockets.readthedocs.io/) library sets `max_size = 2**20` bytes (1 MiB) per message and `max_queue = 32` buffered messages. Any inbound frame larger than `max_size` triggers: ``` websockets.exceptions.PayloadTooBig: over size limit (length > max_size) ConnectionClosedError: sent 1009 (message too big) ``` ### Fix Pass `max_size` to `connect()` — raise it, or set `None` to disable the cap: ```python Python theme={"system"} import asyncio import json import websockets WS_URL = "wss://solana-mainnet.core.chainstack.com/AUTH_KEY" async def main(): async with websockets.connect(WS_URL, max_size=None) as ws: await ws.send(json.dumps({ "jsonrpc": "2.0", "id": 1, "method": "blockSubscribe", "params": ["all", {"encoding": "json", "transactionDetails": "full", "showRewards": False, "maxSupportedTransactionVersion": 1}], })) async for msg in ws: print(msg) asyncio.run(main()) ``` Guidance: * `max_size=None` — disable the cap entirely. * `max_size=10 * 1024 * 1024` — 10 MiB ceiling, a reasonable headroom for `blockSubscribe`. Keep `max_queue` at its default unless you have a slow consumer; raising it just hides backpressure. ## Node.js: `ws` default limit The [`ws`](https://github.com/websockets/ws) library defaults to `maxPayload = 100 * 1024 * 1024` (100 MiB), which usually covers Solana blocks. If you ever see code `1009`, raise the cap or disable it: ```javascript Node.js theme={"system"} const WebSocket = require("ws"); const WS_URL = "wss://solana-mainnet.core.chainstack.com/AUTH_KEY"; const ws = new WebSocket(WS_URL, { maxPayload: Infinity, // disable cap // or: maxPayload: 10 * 1024 * 1024 // 10 MiB }); ws.on("open", () => { ws.send(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "blockSubscribe", params: ["all", { encoding: "json", transactionDetails: "full", showRewards: false, maxSupportedTransactionVersion: 1 }], })); }); ws.on("message", (data) => console.log(data.toString())); ws.on("close", (code, reason) => console.error("closed", code, reason.toString())); ``` Guidance: * `maxPayload: 0` or `Infinity` — no cap. * `maxPayload: ` — custom cap. Set it to your worst-case block size with headroom. ## Trimming the payload instead If you can live with less detail, narrow what `blockSubscribe` returns. This also makes the stream noticeably more stable — our internal load testing showed `transactionDetails: "signatures"` survives 1,000+ concurrent clients while `"full"` started disconnecting after \~40 seconds at a single client. * `transactionDetails: "signatures"` — drop full transaction bodies, keep signatures only. **Most reliable option if you don't need the full body.** * `transactionDetails: "none"` — block metadata only, no transactions. * `showRewards: false` — skip the rewards array. * Filter to a single program via `{"mentionsAccountOrProgram": ""}` in `params[0]` — only blocks containing that program's transactions are pushed. See the [`blockSubscribe` reference](https://solana.com/docs/rpc/websocket/blocksubscribe) for the full parameter list. ## Production path: Yellowstone gRPC Geyser Even after raising the client cap, `blockSubscribe` itself is upstream-flagged as [unstable on busy programs](https://solana.com/docs/rpc/websocket/blocksubscribe). Our load tests show connections drop within \~40 seconds even at 1 concurrent client when `transactionDetails: "full"` is used. Symptoms include `1006` (abnormal closure) closes and silently dropped notifications — not just the `1009` case the fixes above address. The recommended production path is [Yellowstone gRPC Geyser](/docs/yellowstone-grpc-geyser-plugin) — a Chainstack add-on that delivers the same data (blocks, transactions, account updates, slots) over a gRPC stream, with no RFC-6455 payload caps and without the upstream `blockSubscribe` instability. Existing `blockSubscribe` consumers migrate to a gRPC `subscribe` call against the Geyser endpoint. See: * [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) * [Solana: listening to programs using Geyser and Yellowstone gRPC (Node.js)](/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js) * [Solana: listening to pump.fun token mint using Geyser](/docs/solana-listening-to-pumpfun-token-mint-using-geyser) ## Billing note Each `blockSubscribe` push counts as 1 [request unit](/docs/request-units#http-requests-vs-websocket-subscriptions). On Mainnet that is one RU per slot — roughly two per second when the network is healthy. Factor the subscription cost into your plan before pointing a long-running consumer at `blockSubscribe`. ## See also * [Solana tooling](/docs/solana-tooling) * [Handle real-time data using WebSockets with JavaScript and Python](/docs/handle-real-time-data-using-websockets-with-javascript-and-python) * [Request units](/docs/request-units#http-requests-vs-websocket-subscriptions) * [EVM node connection: HTTP vs WebSocket](/docs/evm-node-connection-http-vs-websocket) # Solana: Understanding the compute budget Source: https://docs.chainstack.com/docs/solana-compute-budget How Solana compute units work — default allocations, SetComputeUnitLimit and SetComputeUnitPrice instructions, and optimizing compute for lower fees. **TLDR:** * Every Solana instruction gets a default compute unit (CU) allocation. Non-builtin programs get 200,000 CUs; builtin programs (System, Stake, Vote) get 3,000 CUs. * The transaction-level cap is 1,400,000 CUs. * Priority fees are calculated from the **requested** CU limit, not actual usage — overspending on CUs wastes money. * Use `simulateTransaction` to measure actual CU usage, then set `SetComputeUnitLimit` to the measured value + 10% margin. * On [transaction v1](/docs/solana-transaction-v1), these settings move out of Compute Budget Program instructions and into a `transactionConfig` field on the message. ## How compute units work Every transaction on Solana consumes compute units (CUs). CUs are the Solana equivalent of gas — they measure the computational cost of executing instructions. The runtime enforces a CU budget per transaction; if the transaction exceeds it, execution aborts with `ComputationalBudgetExceeded`. ### Default allocation When you don't include a `SetComputeUnitLimit` instruction, the default budget is calculated based on instruction types: ``` default_cu_limit = (num_builtin_instructions × 3,000) + (num_non_builtin_instructions × 200,000) ``` * **Builtin instructions** (System Program, Stake, Vote, Compute Budget) — 3,000 CUs each per [SIMD-0170](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0170-builtin-instruction-cost-and-budget.md) * **Non-builtin instructions** (your program, Token Program, any SBF-deployed program) — 200,000 CUs each * The result is clamped to the transaction cap of **1,400,000 CUs** A typical transaction with 1 SystemProgram transfer + 1 ComputeBudget instruction + 1 SPL Token instruction gets: `(2 × 3,000) + (1 × 200,000) = 206,000 CUs` as the default budget. ### Why defaults are wasteful Consider a simple SOL transfer with a priority fee: * 2 builtin instructions (transfer + SetComputeUnitPrice) = 6,000 CUs allocated * But the actual transfer uses \~450 CUs If you set a priority fee of 10,000 micro-lamports per CU, the fee on 6,000 CUs is 60,000,000 micro-lamports (0.06 lamports). If you set `SetComputeUnitLimit` to 500 CUs (enough for the transfer + margin), the fee drops to 5,000,000 micro-lamports — a 12x reduction. > [!WARNING] > > The priority fee is determined by the **requested** compute unit limit, not the actual number of compute units used. If you set a CU limit that is too high or use the default, you pay for unused compute. ## Compute Budget Program instructions The Compute Budget Program (`ComputeBudget111111111111111111111111111111`) has 4 instructions: | Instruction | Parameter | Type | Description | | - | - | - | - | | `SetComputeUnitLimit` | `units` | u32 | Max CUs the transaction may consume | | `SetComputeUnitPrice` | `micro_lamports` | u64 | CU price in micro-lamports (priority fee) | | `RequestHeapFrame` | `bytes` | u32 | Heap size for each program (32K–256K, multiple of 1024) | | `SetLoadedAccountsDataSizeLimit` | `bytes` | u32 | Max total bytes of account data the transaction may load | **Rules:** * Only **one** of each instruction variant is allowed per transaction. Duplicates cause a `DuplicateInstruction` error and the transaction fails. * `SetComputeUnitLimit` accepts any u32 but is clamped to 1,400,000. * `SetComputeUnitPrice` accepts any u64 (0 to u64::MAX). ## Optimizing your compute budget The optimal workflow is: 1. **Simulate** your transaction to measure actual CU consumption 2. **Add a margin** (10–20%) to the simulated value 3. **Set the CU limit** with `SetComputeUnitLimit` 4. **Set the CU price** with `SetComputeUnitPrice` based on recent priority fees ### Step 1: Simulate and measure ```typescript TypeScript theme={"system"} import { Connection, Keypair, SystemProgram, TransactionMessage, VersionedTransaction, ComputeBudgetProgram, } from "@solana/web3.js"; import bs58 from "bs58"; import "dotenv/config"; const connection = new Connection(process.env.SOLANA_RPC!); const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY!)); async function optimizeComputeBudget() { const latestBlockhash = await connection.getLatestBlockhash(); // Your transaction instructions (without CU limit set yet) const instructions = [ ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 10_000 }), SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: payer.publicKey, lamports: 1000, }), ]; // Build and simulate WITHOUT SetComputeUnitLimit const message = new TransactionMessage({ payerKey: payer.publicKey, recentBlockhash: latestBlockhash.blockhash, instructions, }).compileToV0Message(); const transaction = new VersionedTransaction(message); transaction.sign([payer]); const simulation = await connection.simulateTransaction(transaction); const unitsConsumed = simulation.value.unitsConsumed || 0; console.log("Simulated CUs:", unitsConsumed); // Add 10% margin const cuLimit = Math.ceil(unitsConsumed * 1.1); console.log("Setting CU limit to:", cuLimit); // Rebuild with the optimized CU limit const optimizedInstructions = [ ComputeBudgetProgram.setComputeUnitLimit({ units: cuLimit }), ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 10_000 }), SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: payer.publicKey, lamports: 1000, }), ]; const optimizedMessage = new TransactionMessage({ payerKey: payer.publicKey, recentBlockhash: latestBlockhash.blockhash, instructions: optimizedInstructions, }).compileToV0Message(); const optimizedTx = new VersionedTransaction(optimizedMessage); optimizedTx.sign([payer]); const sig = await connection.sendTransaction(optimizedTx); console.log("Transaction:", sig); } optimizeComputeBudget(); ``` ## Block-level limits The Solana scheduler imposes block-level CU caps that affect how many transactions fit per block: * **48,000,000 CUs per block** — total compute budget across all transactions * **12,000,000 CUs per account per block** — write-lock limit per account These block limits are why tight CU budgets matter: a transaction requesting 200,000 CUs that only uses 500 blocks other transactions from fitting in the same block, reducing overall throughput. ## Priority fee calculation The total priority fee for a transaction: ``` priority_fee = ceil(SetComputeUnitPrice × SetComputeUnitLimit / 1,000,000) ``` Where `SetComputeUnitPrice` is in micro-lamports and the result is in lamports. The division uses ceiling — non-integer results round up. The fee is **always based on the requested limit**, not actual usage. ## Compute budget on transaction v1 Legacy and v0 transactions set the compute budget with [Compute Budget Program instructions](#compute-budget-program-instructions). A [v1 transaction](/docs/solana-transaction-v1) carries the same settings in a `transactionConfig` field on the message instead: ```json theme={"system"} "transactionConfig": { "computeUnitLimit": 117372, "heapSize": null, "loadedAccountsDataSizeLimit": 14097421, "priorityFee": 839349 } ``` Two differences matter when you read v1 traffic: * The priority fee is a total in lamports, not a rate in micro-lamports per compute unit. A v1 transaction pays `5000 × numRequiredSignatures + priorityFee`, with no further arithmetic. * Code that derives limits or fees by scanning for Compute Budget instructions finds nothing on a v1 transaction. Read `transactionConfig` when it is present, and fall back to the instruction scan for legacy and v0. To compare a v0 rate against a v1 total, convert the rate with the [priority fee calculation](#priority-fee-calculation) formula: `ceil(SetComputeUnitPrice × SetComputeUnitLimit / 1,000,000)`. ## Additional resources * [Priority fees for faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) — practical guide to setting priority fees * [Estimate priority fees with getRecentPrioritizationFees](/docs/solana-estimate-priority-fees-getrecentprioritizationfees) — dynamic fee estimation * [Analyzing adjacent transactions for priority fees](/docs/solana-analyzing-adjacent-transactions-for-priority-fees) — advanced fee analysis * [simulateTransaction](/reference/solana-simulatetransaction) — simulate to measure CU usage * [Solana: transaction v1 explained](/docs/solana-transaction-v1) — the `transactionConfig` field and the fee unit change # Solana: Creating a trading and sniping pump.fun bot Source: https://docs.chainstack.com/docs/solana-creating-a-pumpfun-bot Build a fully coded Python bot that interacts directly with pump.fun Solana programs and accounts for token trading without relying on third-party APIs. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). [How to research & build a bot (pump.fun Solana example)](https://www.youtube.com/embed/7valHrMLvZw) **TLDR** * This Python-based Solana bot monitors the pump.fun on-chain program to detect new tokens, automatically buys them, and optionally sells them soon after. The same bot also supports [letsbonk.fun](https://letsbonk.fun) via a config switch. * It decodes transaction data using the pump.fun Anchor IDL, fetching token details like mint addresses and bonding curves. * You configure one or more bot instances as `.yaml` files in the `bots/` directory (strategy, slippage, priority fees, exit rules) and run them with `pump_bot`. * The cookbook provides standalone reference scripts on Anchor discriminators, price fetching, and subscribing to Solana websockets via `logsSubscribe`, `blockSubscribe`, and Geyser. The pump.fun project is generating millions of USD in fees and in the volume as evidenced by [pump.fun DefiLlama project page](https://defillama.com/protocol/pump#information). On top of that, there are project clones popping up. There are no API endpoints provided by pump.fun itself. At the same time, there are a few third-party projects out there that have figured out how to interact with the pump.fun on-chain programs & accounts and what these are, and they now provide the APIs. In this project, you'll get to be able to *directly* interact with the pump.fun program & accounts. See the full project code, including the cookbook of example scripts, in the respective [GitHub repository](https://github.com/chainstacklabs/pumpfun-bonkfun-bot). ## Overview The project uses the pump.fun IDL to interact with the program and the accounts. It's very useful to know that the pump.fun program and all the associated accounts is built with the [Anchor framework](https://www.anchor-lang.com/), as a lot of the decoding comes back to Anchor, like the [8 byte instruction signatures/discriminators](https://www.anchor-lang.com/docs/basics/idl). The other thing important to know is that the tokens created with pump.fun are 6 decimal tokens, which is different from the default Solana 9 decimal SPL tokens. The bot subscribes over WebSocket to a Solana mainnet RPC node and extracts all the newly minted coins created by the pump.fun main program ID [6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P](https://solscan.io/account/6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P). It can listen with `logsSubscribe` (supported by every provider), `blockSubscribe`, or a Geyser stream for the lowest latency. Once a new coin is minted, the bot buys it and then exits according to your configured strategy. When a token's bonding curve completes, its liquidity graduates to PumpSwap—pump.fun's own AMM. You set the behavior per bot instance in a `.yaml` config file; see [Configure and run the bot](#configure-and-run-the-bot). ## Implementation The bot is installed and run with [uv](https://github.com/astral-sh/uv). Your secrets—RPC HTTP and WebSocket endpoints, the wallet private key, and optional Geyser credentials—go in a `.env` file, and each bot instance is a `.yaml` file in the `bots/` directory. The core logic has two paths: a listener that detects new tokens, and the buy/sell execution. ### Listener To construct a buy (and later a sell) transaction, the bot needs the global program and account addresses plus the new token's variables: * `mint` — the created token address * `bondingCurve` — the bonding curve account for the new token; this account defines the token price * `associatedBondingCurve` — the associated token account that holds the tokens bought and sold against the bonding curve The bot can obtain these addresses three ways, fastest last: * **`logsSubscribe`** — subscribes to the pump.fun program logs and derives the `associatedBondingCurve` on the fly (it is a PDA of the `bondingCurve`, the token program, and the `mint`). This works on every provider and is the default path. * **`blockSubscribe`** — subscribes to full blocks filtered to the pump.fun program's `create` instruction and extracts the addresses. Reliable on Chainstack, but not every provider supports it. * **Geyser** — a gRPC stream for the lowest latency. See the [Geyser implementation guide](/docs/solana-listening-to-pumpfun-token-mint-using-geyser) and [Yellowstone Geyser](/docs/yellowstone-grpc-geyser-plugin). > [!NOTE] > > Earlier versions of the bot relied on `blockSubscribe`, which has stability issues with busy programs and isn't available on every plan. The bot now defaults to `logsSubscribe` (deriving the associated bonding curve locally) and supports Geyser for production. See [Solana: Listening to pump.fun token mint using only logsSubscribe](/docs/solana-listening-to-pumpfun-token-mint-using-only-logssubscribe). ### Buy and sell Using the `mint` and your wallet address (from the private key in `.env`), the bot creates an associated token account to hold the token, fetches the price from the `bondingCurve` account, and submits the buy. The sell path is the reverse and simpler—it fetches the price and submits a sell. A couple of notes: * The [Anchor instruction discriminators](https://www.anchor-lang.com/docs/basics/idl) are precalculated. See [`cookbook/solana/anchor_calculate_discriminator.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/solana/anchor_calculate_discriminator.py). * `extreme_fast_mode` (in the bot config) skips the bonding-curve stabilization wait and the RPC price check and buys a fixed token amount directly—faster, but less precise. With it off, the bot waits and price-checks first so the new token's data has propagated across the cluster. > [!WARNING] > > pump.fun ships breaking on-chain program upgrades—for example, the [2026-04-28 fee-recipient change](https://github.com/pump-fun/pump-public-docs/blob/main/docs/BREAKING_FEE_RECIPIENT.md) altered the buy/sell account lists. When the program changes, account lists and IDLs must be updated, so always cross-check against a recent successful transaction and pull the latest from the repository before running. ## Configure and run the bot ### Install ```shell theme={"system"} git clone https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git cd pumpfun-bonkfun-bot uv sync source .venv/bin/activate uv pip install -e . ``` ### Configure Copy `.env.example` to `.env` and add your Solana RPC endpoints and wallet private key. Then edit or copy a `.yaml` template in `bots/`—each file is a separate bot instance. Key settings: * `platform` — `"pump_fun"` (default) or `"lets_bonk"` to trade on [letsbonk.fun](https://letsbonk.fun) instead * `trade.buy_amount`, `trade.buy_slippage`, `trade.sell_slippage` — position size and slippage tolerance * `trade.exit_strategy` — `"time_based"`, `"tp_sl"` (take-profit/stop-loss), or `"manual"` * `trade.extreme_fast_mode` — skip the price check and buy a fixed token amount for speed * `priority_fees` — `enable_fixed` with a `fixed_amount`, or `enable_dynamic` (estimates via `getRecentPrioritizationFees`); see [Solana: priority fees for faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) * `filters` — restrict trading to tokens matching a name/symbol or created by a specific address * `geyser` — endpoint and auth token for the Geyser listener ### Run ```shell theme={"system"} # Run as the installed package pump_bot # Or run directly uv run src/bot_runner.py ``` Each enabled bot in `bots/` runs as its own instance, so you can run several strategies at once. > [!NOTE] > > This bot is for learning, not production. Also by Chainstack: [pumpfun-cli](https://github.com/chainstacklabs/pumpfun-cli) (a terminal client for trading, launching, and managing tokens, with smart routing between the bonding curve and PumpSwap) and [pumpclaw](https://github.com/chainstacklabs/pumpclaw) (an agent skill that lets AI assistants like Claude Code and Codex operate pumpfun-cli). ## Cookbook examples The [`cookbook/`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/tree/main/cookbook) directory has standalone scripts for understanding how the bot works and how to interact with Solana, grouped by platform and then by action. Each script takes its input as a command-line argument—run it with `uv run `, and pass `--help` to see the options. Highlights: * [`cookbook/pumpfun/listen/pumpfun_listen_tokens_logsubscribe.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/listen/pumpfun_listen_tokens_logsubscribe.py) — detects new tokens using only `logsSubscribe`, deriving the associated bonding curve on the fly (no extra `getTransaction` call) * [`tools/compare_listeners.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/tools/compare_listeners.py) — runs the listener methods side by side to see which detects new tokens first * [`cookbook/pumpfun/read/pumpfun_read_curve.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/read/pumpfun_read_curve.py) — checks whether a token's bonding curve is still active or has completed * [`cookbook/pumpfun/graduation/pumpfun_listen_migrations_logsubscribe.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/graduation/pumpfun_listen_migrations_logsubscribe.py) — detects graduations to PumpSwap; see [Listening to pump.fun migrations to PumpSwap](/docs/solana-listening-to-pumpfun-migrations-to-raydium) * [`cookbook/pumpfun/read/pumpfun_derive_curve_addresses.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/read/pumpfun_derive_curve_addresses.py) — derives the bonding curve and associated bonding curve addresses for a token * [`cookbook/solana/anchor_calculate_discriminator.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/solana/anchor_calculate_discriminator.py) — calculates the 8-byte Anchor discriminator for any instruction or account name * [`cookbook/pumpswap/`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/tree/main/cookbook/pumpswap) — buy and sell examples against the PumpSwap AMM * [`cookbook/pumpfun/trade/pumpfun_buy_token_v2.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/trade/pumpfun_buy_token_v2.py) / [`pumpfun_sell_token_v2.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/trade/pumpfun_sell_token_v2.py) — buy or sell a token you name by its mint address; the buy script supports `--dry-run` to simulate instead of spending * [`cookbook/pumpfun/trade/pumpfun_snipe_token_blocksubscribe.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/trade/pumpfun_snipe_token_blocksubscribe.py) — waits for the next token created on pump.fun over `blockSubscribe`, then buys it; this is a sniper, not a manual buy of a specific token > [!WARNING] > > The trade scripts submit real transactions and spend real funds. Read the module docstring before running one. The IDLs in `idl/` are vendored from [pump-fun/pump-public-docs](https://github.com/pump-fun/pump-public-docs); refresh them from there when the program changes. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Create a stake account and delegate SOL with web3.js Source: https://docs.chainstack.com/docs/solana-delegate-sol-staking Create a stake account and delegate (stake) SOL to a validator on Solana using @solana/web3.js and the StakeProgram, through a Chainstack node. **TLDR:** * Staking SOL natively is a two-step flow: create and initialize a **stake account** you own, then **delegate** it to a validator's vote account. * You build both transactions with `@solana/web3.js` (`StakeProgram.createAccount` and `StakeProgram.delegate`) and send them through a Chainstack Solana node. * Fund the stake account with the **rent-exempt reserve plus the amount you want to stake**, and stake at least the network minimum — currently **1 SOL** (`getStakeMinimumDelegation`). Delegating less fails with `InsufficientDelegation`. * Delegation is not instant: your stake warms up and becomes active at the start of the next epoch, and rewards accrue from then on. ## How delegation works on Solana Solana uses delegated proof of stake. You do not hand SOL to a validator — instead you create a **stake account** that you fully control, then delegate the SOL held in it to a validator's **vote account**. The validator produces and votes on blocks; you earn a share of the staking rewards, minus the validator's commission. A stake account is a regular account owned by the Stake program, with three things that matter here: * Authorities — a **staker** (can delegate, deactivate, and split the stake) and a **withdrawer** (can withdraw the SOL and change either authority). You hold both, so you keep custody the whole time. * Rent-exempt reserve — like every Solana account, a stake account must hold a minimum balance to stay rent-exempt. This reserve sits on top of the delegated amount and is not staked. * Delegation — once you delegate, the account records the validator's vote account, the delegated amount, and the epoch the stake activates in. Two limits decide how much SOL the account needs: * Minimum delegation — the network rejects delegations below `getStakeMinimumDelegation()`, currently **1 SOL** (1,000,000,000 lamports). Delegating less returns the Stake program error `InsufficientDelegation` (custom error `0xc`). * Rent exemption — a 200-byte stake account needs `getMinimumBalanceForRentExemption(StakeProgram.space)` lamports to be rent-exempt: **1,666,240 lamports** (about 0.00166624 SOL) at the current rate. The rate falls as [Solana's rent reduction](/docs/solana-rent-reduction-reclaim-excess-rent) proceeds, so read it from the RPC instead of hardcoding it. So a stake account that delegates the 1 SOL minimum must be funded with `1,666,240 + 1,000,000,000 = 1,001,666,240` lamports (about 1.00166624 SOL) at the current rate, plus a little SOL in your wallet for transaction fees. > [!NOTE] > > **Prerequisites** > > * A [Chainstack account](https://console.chainstack.com) and a deployed Solana node > * [Node.js](https://nodejs.org/en/download) 18 or above > * A funded Solana wallet — a bit more than the amount you plan to stake, so around 1.05 SOL to stake the 1 SOL minimum and cover fees > [!WARNING] > > This tutorial spends real SOL when run against mainnet. Test on devnet first — deploy a Solana devnet node, fund your wallet from a devnet faucet, and run the exact same code. The minimum delegation and rent-exempt reserve are identical on devnet and mainnet. ## Deploy a Chainstack Solana node > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) Copy the node's HTTPS and WSS endpoints from the **Access and credentials** tab. You pass both to the `Connection` constructor when you connect to your node. ## Set up the project Create a project directory and install `@solana/web3.js`. Add `bs58` only if you import a base58 secret key (for example, one exported from a wallet like Phantom). ```bash Shell theme={"system"} mkdir solana-staking && cd solana-staking npm init -y npm install @solana/web3.js bs58 ``` This tutorial uses CommonJS (`require`). Each step is a fragment that runs inside an `async` function so it can use `await` — the [complete script](#full-script) assembles them into one runnable file. The same calls work unchanged with ES module `import` syntax. ## Connect to your Solana node Initialize a `Connection` with your Chainstack HTTPS and WSS endpoints and a commitment level. `confirmed` is a good default — it waits for the transaction to be confirmed by the cluster without waiting for full finalization. Pass the WSS endpoint explicitly. `@solana/web3.js` opens a WebSocket to confirm transactions; with only the HTTPS endpoint it derives a WebSocket URL from that host, which does not match a Chainstack node's separate WSS endpoint, and `sendAndConfirmTransaction` then times out with `TransactionExpiredBlockheightExceededError`. ```javascript connection.js theme={"system"} const { Connection } = require("@solana/web3.js"); const connection = new Connection("", { commitment: "confirmed", wsEndpoint: "", }); ``` ## Load your wallet Your wallet pays the transaction fees, funds the stake account, and becomes the stake account's staker and withdrawer authority. Load it with `Keypair.fromSecretKey`. Pick the form that matches how your key is stored: ```javascript wallet.js theme={"system"} const { Keypair } = require("@solana/web3.js"); const bs58 = require("bs58"); const fs = require("fs"); // Option A — a byte-array key file, e.g. the one the Solana CLI writes to id.json const wallet = Keypair.fromSecretKey( new Uint8Array(JSON.parse(fs.readFileSync("/path/to/id.json", "utf8"))) ); // Option B — a base58 secret key string (for example exported from a wallet), // supplied through an environment variable const walletFromBase58 = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY)); ``` > [!WARNING] > > Never hardcode or commit a secret key. Load it from a file outside your repository or from an environment variable, and use a dedicated wallet for testing. ## Choose a validator You delegate to a validator's **vote account**, not its identity address. List active validators with [`getVoteAccounts`](/reference/solana-getvoteaccounts) and pick one: ```javascript validators.js theme={"system"} const { current } = await connection.getVoteAccounts(); // Each entry describes one validator. The fields you care about: // votePubkey - the vote account you delegate to // nodePubkey - the validator's identity address // activatedStake - total stake delegated to it (lamports) // commission - percentage the validator keeps from rewards // lastVote - the most recent slot it voted on (liveness) console.log(current.length, "active validators"); console.log(current[0]); ``` Choose deliberately — weigh commission, current stake, and reliability, and consider spreading stake across smaller validators to support decentralization. Explorers such as Solana Explorer, Solscan, and validators.app list vote accounts and performance history. ## Create the stake account Creating a stake account is one transaction that both allocates the account and initializes its authorities. `StakeProgram.createAccount` returns a ready-to-send `Transaction` containing two instructions — a System program `createAccount` and a Stake program `Initialize`. Fund it with the rent-exempt reserve plus the amount you want to stake, and set both authorities to your wallet with `Authorized`. Pass a `Lockup` of `new Lockup(0, 0, PublicKey.default)` for no lockup — a zero timestamp and epoch mean the stake is never locked, and the default custodian address is `11111111111111111111111111111111`. ```javascript create-stake-account.js theme={"system"} const { Keypair, PublicKey, StakeProgram, Authorized, Lockup, sendAndConfirmTransaction, } = require("@solana/web3.js"); // A fresh keypair for the stake account itself. const stakeAccount = Keypair.generate(); // Rent-exempt reserve + the amount to delegate (at least the network minimum). const rentExempt = await connection.getMinimumBalanceForRentExemption(StakeProgram.space); const { value: minDelegation } = await connection.getStakeMinimumDelegation(); const stakeAmount = minDelegation; // 1 SOL minimum; raise this to stake more const lamports = rentExempt + stakeAmount; const createStakeAccountTx = StakeProgram.createAccount({ fromPubkey: wallet.publicKey, stakePubkey: stakeAccount.publicKey, authorized: new Authorized(wallet.publicKey, wallet.publicKey), // staker, withdrawer lockup: new Lockup(0, 0, PublicKey.default), // no lockup lamports, }); // A recent blockhash and fee payer make the transaction sendable and give // sendAndConfirmTransaction a valid window to confirm within. const created = await connection.getLatestBlockhash(); createStakeAccountTx.recentBlockhash = created.blockhash; createStakeAccountTx.lastValidBlockHeight = created.lastValidBlockHeight; createStakeAccountTx.feePayer = wallet.publicKey; // The new stake account must also sign, because it is being created. const createSignature = await sendAndConfirmTransaction( connection, createStakeAccountTx, [wallet, stakeAccount] ); console.log("Stake account:", stakeAccount.publicKey.toBase58()); console.log("Create signature:", createSignature); ``` ## Delegate the stake With the stake account created and initialized, delegate it to your chosen validator's vote account. `StakeProgram.delegate` returns a `Transaction` with a single Stake program `DelegateStake` instruction, signed by the staker authority — your wallet. ```javascript delegate.js theme={"system"} const { PublicKey, StakeProgram, sendAndConfirmTransaction } = require("@solana/web3.js"); // The vote account of the validator you chose. Replace with your own selection. const votePubkey = new PublicKey("CcaHc2L43ZWjwCHART3oZoJvHLAe9hzT2DJNUpBzoTN1"); const delegateTx = StakeProgram.delegate({ stakePubkey: stakeAccount.publicKey, authorizedPubkey: wallet.publicKey, votePubkey, }); const delegated = await connection.getLatestBlockhash(); delegateTx.recentBlockhash = delegated.blockhash; delegateTx.lastValidBlockHeight = delegated.lastValidBlockHeight; delegateTx.feePayer = wallet.publicKey; const delegateSignature = await sendAndConfirmTransaction(connection, delegateTx, [wallet]); console.log("Delegate signature:", delegateSignature); ``` > [!TIP] > > You can combine both instructions into a single transaction — add the `createStakeAccountTx` and `delegateTx` instructions to one `Transaction` — because the runtime processes them in order, so the delegate step runs against the just-initialized account. This tutorial keeps them separate for clarity. ## Verify the delegation Read the stake account back with `getParsedAccountInfo`. Right after delegating, the account type is `delegated` and `deactivationEpoch` is `18446744073709551615` (the maximum `u64`, meaning the stake is not scheduled to deactivate). ```javascript verify.js theme={"system"} const info = await connection.getParsedAccountInfo(stakeAccount.publicKey); console.log(JSON.stringify(info.value.data.parsed, null, 2)); ``` The parsed output shows the authorities, the rent-exempt reserve, and the delegation. The example values here come from an existing on-chain stake account — yours will differ: ```json Example output theme={"system"} { "info": { "meta": { "authorized": { "staker": "9KAQBvu7C8pBTDzt2swuTFsiGmshjdV7BXj71fVxYZ1Z", "withdrawer": "9KAQBvu7C8pBTDzt2swuTFsiGmshjdV7BXj71fVxYZ1Z" }, "lockup": { "custodian": "11111111111111111111111111111111", "epoch": 0, "unixTimestamp": 0 }, "rentExemptReserve": "2282880" }, "stake": { "creditsObserved": 2294237959, "delegation": { "activationEpoch": "775", "deactivationEpoch": "18446744073709551615", "stake": "17175076774", "voter": "CcaHc2L43ZWjwCHART3oZoJvHLAe9hzT2DJNUpBzoTN1" } } }, "type": "delegated" } ``` The `rentExemptReserve` field always reads `2282880`, the reserve before the rent reduction. The Stake program writes that fixed value and applies the current minimum instead. Your stake does not earn rewards immediately. It **warms up** and becomes fully active at the start of the epoch after `activationEpoch`. Track the current epoch with [`getEpochInfo`](/reference/solana-getepochinfo). To unstake later, the staker authority calls `StakeProgram.deactivate` (the stake cools down over the next epoch), then the withdrawer calls `StakeProgram.withdraw` to move the SOL back out. ## Full script The complete flow, ready to run with `node stake.js` after setting your endpoint, key path, and validator vote account: ```javascript stake.js theme={"system"} const { Connection, Keypair, PublicKey, StakeProgram, Authorized, Lockup, sendAndConfirmTransaction, LAMPORTS_PER_SOL, } = require("@solana/web3.js"); const fs = require("fs"); const connection = new Connection("", { commitment: "confirmed", wsEndpoint: "", }); // Your funded wallet — pays fees, funds the stake account, and is its authority. const wallet = Keypair.fromSecretKey( new Uint8Array(JSON.parse(fs.readFileSync("/path/to/id.json", "utf8"))) ); // The validator vote account to delegate to. Replace with your own selection. const votePubkey = new PublicKey("CcaHc2L43ZWjwCHART3oZoJvHLAe9hzT2DJNUpBzoTN1"); async function main() { // 1. Work out how much SOL the stake account needs. const rentExempt = await connection.getMinimumBalanceForRentExemption(StakeProgram.space); const { value: minDelegation } = await connection.getStakeMinimumDelegation(); const stakeAmount = minDelegation; // 1 SOL minimum; raise this to stake more const lamports = rentExempt + stakeAmount; console.log(`Funding stake account with ${lamports / LAMPORTS_PER_SOL} SOL`); // 2. Create and initialize the stake account. const stakeAccount = Keypair.generate(); const createStakeAccountTx = StakeProgram.createAccount({ fromPubkey: wallet.publicKey, stakePubkey: stakeAccount.publicKey, authorized: new Authorized(wallet.publicKey, wallet.publicKey), lockup: new Lockup(0, 0, PublicKey.default), lamports, }); let bh = await connection.getLatestBlockhash(); createStakeAccountTx.recentBlockhash = bh.blockhash; createStakeAccountTx.lastValidBlockHeight = bh.lastValidBlockHeight; createStakeAccountTx.feePayer = wallet.publicKey; const createSignature = await sendAndConfirmTransaction( connection, createStakeAccountTx, [wallet, stakeAccount] ); console.log("Stake account:", stakeAccount.publicKey.toBase58()); console.log("Create signature:", createSignature); // 3. Delegate the stake to the validator. const delegateTx = StakeProgram.delegate({ stakePubkey: stakeAccount.publicKey, authorizedPubkey: wallet.publicKey, votePubkey, }); bh = await connection.getLatestBlockhash(); delegateTx.recentBlockhash = bh.blockhash; delegateTx.lastValidBlockHeight = bh.lastValidBlockHeight; delegateTx.feePayer = wallet.publicKey; const delegateSignature = await sendAndConfirmTransaction(connection, delegateTx, [wallet]); console.log("Delegate signature:", delegateSignature); // 4. Read the delegation back. const info = await connection.getParsedAccountInfo(stakeAccount.publicKey); console.log(JSON.stringify(info.value.data.parsed, null, 2)); } main().catch((err) => { console.error(err); process.exit(1); }); ``` > [!TIP] > > The script prints two signatures and a `delegated` stake account whose `delegation.voter` matches your chosen validator's vote account. Look either signature up on a Solana explorer to confirm it on-chain. ## What's next > [card] [Solana tooling](/docs/solana-tooling) > > Libraries, clients, and tools for building on Solana with Chainstack. > [card] [Solana API methods](/docs/solana-methods) > > The full JSON-RPC method reference, including `getVoteAccounts` and `getEpochInfo`. # Mastering Solana Source: https://docs.chainstack.com/docs/solana-development Get started with Solana development on Chainstack, covering node deployment, RPC methods, program interaction, token operations, and DeFi integration. Check the guides on the left pane. # Solana: Durable nonces for offline signing Source: https://docs.chainstack.com/docs/solana-durable-nonces Use Solana durable nonces for offline signing, delayed submission, and multi-sig workflows that keep transactions valid beyond the blockhash window. **TLDR:** * Normal Solana transactions expire after \~80 seconds (150 slots) because they reference a recent blockhash. * Durable nonces replace the recent blockhash with a stored nonce value, removing the expiry window entirely. * The nonce must be advanced as the first instruction in the transaction. * This enables offline signing, scheduled transactions, and multi-party approval flows. ## The problem: blockhash expiry Every Solana transaction includes a `recentBlockhash` — a reference to a recent block that acts as a timestamp and replay-prevention mechanism. If the transaction is not confirmed within \~150 slots (\~80 seconds), it expires and is dropped. This is a problem when: * **Offline signing** — the signer is air-gapped and can't access the network in real time * **Multi-sig approval** — multiple parties need to sign, which takes longer than 80 seconds * **Scheduled execution** — you want to build a transaction now and submit it later * **Custodial workflows** — approval chains in enterprise systems ## How durable nonces work A nonce account is a special System Program account that stores: * **authority** — the pubkey authorized to advance the nonce * **durable nonce** — a hash value derived from a recent blockhash * **lamports per signature** — the fee rate when the nonce was last advanced Instead of a recent blockhash, the transaction uses the nonce value. The nonce does not expire — it stays valid until someone advances it. The lifecycle: 1. Create and initialize a nonce account 2. Build the transaction with `AdvanceNonceAccount` as the **first instruction** and the nonce value as the `recentBlockhash` 3. Sign the transaction (can be done offline, days or weeks later) 4. Submit when ready — the nonce is advanced atomically, preventing replay > [!WARNING] > > The `AdvanceNonceAccount` instruction **must** be the first instruction in the transaction. If it's not at index 0, the transaction is processed as a regular (non-nonce) transaction and will fail if the blockhash is stale. ## Prerequisites The examples are shown in both Solana JavaScript libraries — both are actively maintained, so use whichever fits your project. `@solana/kit` is the newer, tree-shakable, functional SDK; `@solana/web3.js` is the classic `Connection`/`PublicKey` API. > [tab] @solana/kit > > ```bash theme={"system"} > npm install @solana/kit @solana-program/system dotenv > ``` > [tab] @solana/web3.js (classic) > > ```bash theme={"system"} > npm install @solana/web3.js bs58 dotenv > ``` ## Create a nonce account > [tab] @solana/kit > > ```typescript TypeScript theme={"system"} > import { > createSolanaRpc, > createSolanaRpcSubscriptions, > createKeyPairSignerFromBytes, > getBase58Encoder, > generateKeyPairSigner, > pipe, > createTransactionMessage, > setTransactionMessageFeePayerSigner, > setTransactionMessageLifetimeUsingBlockhash, > appendTransactionMessageInstructions, > signTransactionMessageWithSigners, > sendAndConfirmTransactionFactory, > getSignatureFromTransaction, > } from "@solana/kit"; > import { > getCreateAccountInstruction, > getInitializeNonceAccountInstruction, > getNonceSize, > fetchNonce, > SYSTEM_PROGRAM_ADDRESS, > } from "@solana-program/system"; > import "dotenv/config"; > > const rpc = createSolanaRpc(process.env.SOLANA_RPC!); > const rpcSubscriptions = createSolanaRpcSubscriptions(process.env.SOLANA_WSS!); > // @solana/kit decodes base58, so no separate bs58 package is needed > const payer = await createKeyPairSignerFromBytes( > getBase58Encoder().encode(process.env.PRIVATE_KEY!) > ); > > async function createNonceAccount() { > const nonceAccount = await generateKeyPairSigner(); > const space = BigInt(getNonceSize()); // nonce accounts are 80 bytes > const lamports = await rpc.getMinimumBalanceForRentExemption(space).send(); > > const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); > > const message = pipe( > createTransactionMessage({ version: 0 }), > (m) => setTransactionMessageFeePayerSigner(payer, m), > (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), > (m) => appendTransactionMessageInstructions([ > getCreateAccountInstruction({ > payer, > newAccount: nonceAccount, > lamports, > space, > programAddress: SYSTEM_PROGRAM_ADDRESS, > }), > getInitializeNonceAccountInstruction({ > nonceAccount: nonceAccount.address, > nonceAuthority: payer.address, // nonce authority > }), > ], m), > ); > > const signedTx = await signTransactionMessageWithSigners(message); > await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTx, { > commitment: "confirmed", > }); > console.log("Nonce account:", nonceAccount.address); > console.log("Transaction:", getSignatureFromTransaction(signedTx)); > > // Fetch the nonce value > const nonce = await fetchNonce(rpc, nonceAccount.address); > console.log("Nonce value:", nonce.data.blockhash); > } > > createNonceAccount(); > ``` > [tab] @solana/web3.js (classic) > > ```typescript TypeScript theme={"system"} > import { > Connection, > Keypair, > NONCE_ACCOUNT_LENGTH, > SystemProgram, > Transaction, > sendAndConfirmTransaction, > } from "@solana/web3.js"; > import bs58 from "bs58"; > import "dotenv/config"; > > const connection = new Connection(process.env.SOLANA_RPC!); > const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY!)); > const nonceKeypair = Keypair.generate(); > > async function createNonceAccount() { > const lamports = await connection.getMinimumBalanceForRentExemption( > NONCE_ACCOUNT_LENGTH > ); > > const transaction = new Transaction().add( > SystemProgram.createAccount({ > fromPubkey: payer.publicKey, > newAccountPubkey: nonceKeypair.publicKey, > lamports, > space: NONCE_ACCOUNT_LENGTH, > programId: SystemProgram.programId, > }), > SystemProgram.nonceInitialize({ > noncePubkey: nonceKeypair.publicKey, > authorizedPubkey: payer.publicKey, // nonce authority > }) > ); > > const sig = await sendAndConfirmTransaction(connection, transaction, [ > payer, > nonceKeypair, > ]); > console.log("Nonce account:", nonceKeypair.publicKey.toBase58()); > console.log("Transaction:", sig); > > // Fetch the nonce value > const nonceAccount = await connection.getNonce(nonceKeypair.publicKey); > console.log("Nonce value:", nonceAccount?.nonce); > } > > createNonceAccount(); > ``` ## Build and sign a transaction with a durable nonce > [tab] @solana/kit > > ```typescript TypeScript theme={"system"} > import { > createSolanaRpc, > createKeyPairSignerFromBytes, > getBase58Encoder, > address, > pipe, > createTransactionMessage, > setTransactionMessageFeePayerSigner, > setTransactionMessageLifetimeUsingDurableNonce, > appendTransactionMessageInstructions, > signTransactionMessageWithSigners, > getBase64EncodedWireTransaction, > lamports, > } from "@solana/kit"; > import { getTransferSolInstruction, fetchNonce } from "@solana-program/system"; > import "dotenv/config"; > > const rpc = createSolanaRpc(process.env.SOLANA_RPC!); > const payer = await createKeyPairSignerFromBytes( > getBase58Encoder().encode(process.env.PRIVATE_KEY!) > ); > > const NONCE_ACCOUNT = address("YOUR_NONCE_ACCOUNT_ADDRESS"); > > async function buildNonceTransaction() { > // Fetch the current nonce value > const nonce = await fetchNonce(rpc, NONCE_ACCOUNT); > > // setTransactionMessageLifetimeUsingDurableNonce prepends the required > // AdvanceNonceAccount instruction as the first instruction automatically. > const message = pipe( > createTransactionMessage({ version: 0 }), > (m) => setTransactionMessageFeePayerSigner(payer, m), > (m) => setTransactionMessageLifetimeUsingDurableNonce({ > nonce: nonce.data.blockhash, > nonceAccountAddress: NONCE_ACCOUNT, > nonceAuthorityAddress: payer.address, > }, m), > (m) => appendTransactionMessageInstructions([ > // Your actual instruction(s) > getTransferSolInstruction({ > source: payer, > destination: payer.address, > amount: lamports(1000n), > }), > ], m), > ); > > // Sign — this can be done offline > const signedTx = await signTransactionMessageWithSigners(message); > > // Serialize for later submission > const base64Tx = getBase64EncodedWireTransaction(signedTx); > console.log("Signed transaction (base64):", base64Tx); > console.log("This transaction will not expire. Submit it whenever ready."); > > return base64Tx; > } > > buildNonceTransaction(); > ``` > [tab] @solana/web3.js (classic) > > ```typescript TypeScript theme={"system"} > import { > Connection, > Keypair, > PublicKey, > SystemProgram, > Transaction, > NonceAccount, > } from "@solana/web3.js"; > import bs58 from "bs58"; > import "dotenv/config"; > > const connection = new Connection(process.env.SOLANA_RPC!); > const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY!)); > > const NONCE_PUBKEY = new PublicKey("YOUR_NONCE_ACCOUNT_ADDRESS"); > > async function buildNonceTransaction() { > // Fetch the current nonce value > const nonceAccountInfo = await connection.getAccountInfo(NONCE_PUBKEY); > const nonceAccount = NonceAccount.fromAccountData(nonceAccountInfo!.data); > const nonceValue = nonceAccount.nonce; > > // Build the transaction — AdvanceNonceAccount MUST be the first instruction > const transaction = new Transaction(); > transaction.recentBlockhash = nonceValue; // use nonce as blockhash > transaction.feePayer = payer.publicKey; > > transaction.add( > // First instruction: advance the nonce > SystemProgram.nonceAdvance({ > noncePubkey: NONCE_PUBKEY, > authorizedPubkey: payer.publicKey, > }), > // Your actual instruction(s) > SystemProgram.transfer({ > fromPubkey: payer.publicKey, > toPubkey: payer.publicKey, > lamports: 1000, > }) > ); > > // Sign — this can be done offline > transaction.sign(payer); > > // Serialize for later submission > const serialized = transaction.serialize(); > const base64Tx = serialized.toString("base64"); > console.log("Signed transaction (base64):", base64Tx); > console.log("This transaction will not expire. Submit it whenever ready."); > > return base64Tx; > } > > buildNonceTransaction(); > ``` ## Submit the transaction later > [tab] @solana/kit > > ```typescript TypeScript theme={"system"} > import { createSolanaRpc } from "@solana/kit"; > import "dotenv/config"; > > const rpc = createSolanaRpc(process.env.SOLANA_RPC!); > > async function submitNonceTransaction(base64Tx: string) { > // Broadcast the stored, serialized transaction as-is > const signature = await rpc > .sendTransaction(base64Tx, { encoding: "base64", preflightCommitment: "confirmed" }) > .send(); > console.log("Submitted:", signature); > > // Poll until the transaction is confirmed. A durable-nonce transaction is > // valid until the nonce is advanced, so there is no blockhash to expire. > while (true) { > const { value } = await rpc.getSignatureStatuses([signature]).send(); > const status = value[0]; > if (status?.confirmationStatus === "confirmed" || status?.confirmationStatus === "finalized") { > console.log("Confirmed:", status.err ? "FAILED" : "SUCCESS"); > break; > } > await new Promise((resolve) => setTimeout(resolve, 1000)); > } > } > > // Paste the base64 from the previous step > submitNonceTransaction("YOUR_BASE64_TX_HERE"); > ``` > [tab] @solana/web3.js (classic) > > ```typescript TypeScript theme={"system"} > import { Connection, PublicKey } from "@solana/web3.js"; > import "dotenv/config"; > > const connection = new Connection(process.env.SOLANA_RPC!); > > const NONCE_PUBKEY = new PublicKey("YOUR_NONCE_ACCOUNT_ADDRESS"); > > async function submitNonceTransaction(base64Tx: string) { > const buffer = Buffer.from(base64Tx, "base64"); > > const sig = await connection.sendRawTransaction(buffer); > console.log("Submitted:", sig); > > // For nonce transactions, use nonce-aware confirmation (not blockhash-based) > const confirmation = await connection.confirmTransaction({ > signature: sig, > nonceAccountPubkey: NONCE_PUBKEY, > nonceValue: "THE_NONCE_VALUE_USED_IN_THE_TRANSACTION", > minContextSlot: 0, > }); > console.log("Confirmed:", confirmation.value.err ? "FAILED" : "SUCCESS"); > } > > // Paste the base64 from the previous step > submitNonceTransaction("YOUR_BASE64_TX_HERE"); > ``` ## Failure behavior Understanding how nonce transactions fail is important: * **Validation failure** (nonce already used, authority not signed, account not found) — the entire transaction is dropped. No fees collected, no state changes. * **Execution failure** (an instruction returns an error after validation passes) — the nonce is still advanced and fees are still collected. This prevents replay while ensuring the validator is compensated. ## Additional resources * [How to handle the transaction expiry error](/docs/solana-how-to-handle-the-transaction-expiry-error) — retry logic for standard (non-nonce) transactions * [Solana durable nonces documentation](https://solana.com/docs/core/transactions/durable-nonces) * [sendTransaction](/reference/solana-sendtransaction) — submit the serialized nonce transaction # Solana: The escrow pattern Source: https://docs.chainstack.com/docs/solana-escrow-pattern Build a trustless token-swap escrow on Solana with Anchor — PDA-owned vaults, CPI from a PDA signer, and atomic maker/taker exchange with refund. **TLDR:** * Escrow is the canonical Solana program pattern — every DEX, order book, and marketplace reuses the same primitives. * A maker deposits token A into a PDA-owned vault and records how much token B they want. A taker completes the swap atomically, or the maker refunds at any time. * The program demonstrates PDA-signed CPIs, the Token interface (works with both legacy SPL Token and Token-2022), atomic two-legged transfers, and returning rent when accounts close. * Uses Anchor 1.0.0 (April 2026). All three canonical reference implementations — `solana-foundation/anchor/tests/escrow`, `solana-foundation/solana-bootcamp-2026/04-escrow`, and `solana-developers/program-examples/tokens/escrow/anchor` — converge on the patterns in this guide. ## Why learn the escrow pattern An escrow is a program that holds assets during a trade and releases them only when both sides have performed. Two parties swap tokens without trusting each other or a third party: * The **maker** opens an offer: "I will give X of token A for Y of token B". * The **taker** fulfills the offer: they send Y of token B to the maker, and the program releases X of token A to them — all in one atomic transaction. * If no taker appears, the maker **refunds** and reclaims their tokens. The same shape shows up everywhere: * AMMs (Raydium, Orca) — a pool is an escrow with two token vaults and a pricing curve. * Order books (Phoenix, OpenBook) — each resting order is a small escrow. * NFT marketplaces (Magic Eden, Tensor) — a listing is an escrow of one NFT for N of a fungible token. * Limit orders (Jupiter, Kamino) — a trigger-based escrow that fills when price crosses a threshold. Learn the escrow and you have learned the mechanical core of Solana DeFi. Every pattern in this guide — PDA vaults, PDA-signed CPIs, atomic multi-leg transfers, account closure — reappears in every production program. > [!NOTE] > > This guide assumes you have read [Solana: Anchor development](/docs/solana-anchor-development) and [Solana: Program derived addresses and cross-program invocations](/docs/solana-program-derived-addresses-and-cross-program-invocations). Concepts like `#[derive(Accounts)]`, PDA derivation, and basic CPIs are not re-explained here. ## Prerequisites Install the tooling if you have not done so already. This guide targets Anchor 1.0.0. | Tool | Version | Install | | - | - | - | | Rust | 1.89.0+ | `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \| sh` | | Solana CLI | 3.1.10+ | `sh -c "\$(curl -sSfL https://release.anza.xyz/v3.1.10/install)"` | | Anchor CLI | 1.0.0 | `cargo install avm --git https://github.com/solana-foundation/anchor --locked && avm install 1.0.0 && avm use 1.0.0` | | Node.js | 22+ | [nodejs.org](https://nodejs.org/) | | Yarn | 1.22+ | `npm install -g yarn` | Verify your setup: ```shell theme={"system"} anchor --version # anchor-cli 1.0.0 solana --version # solana-cli 3.1.x rustc --version # rustc 1.89.0+ ``` ## The design Four accounts carry state across the escrow lifecycle: * **Escrow (PDA)** — holds swap metadata: who made it, which mints, how much token B they want. Derived from `["escrow", maker, seed]` so a single maker can run many concurrent escrows with different `seed` values. * **Vault (ATA owned by the Escrow PDA)** — holds the maker's deposited token A. The vault is an associated token account whose authority is the Escrow PDA, so only the program (signing for the PDA) can move tokens out. * **Maker and taker ATAs** — standard associated token accounts for mint A and mint B held by the two parties. The PDA that owns the vault is the same PDA that stores the metadata. This is deliberate: one PDA, one vault, one record — and the PDA signer seeds are identical for every CPI the program makes. Three instructions move the system through its states: ``` ┌───────────┐ │ (empty) │ └─────┬─────┘ │ maker calls make(seed, receive, amount) ▼ ┌─────────────────────────────────────────────┐ │ Escrow open. Vault holds `amount` of mint_a │ └────┬────────────────────────────────────┬───┘ │ │ │ taker calls take() │ maker calls refund() ▼ ▼ ┌─────────────────┐ ┌──────────────────┐ │ Maker got mint_b│ │ Maker got mint_a │ │ Taker got mint_a│ │ back from vault │ │ Escrow closed │ │ Escrow closed │ └─────────────────┘ └──────────────────┘ ``` Both `take` and `refund` close the vault and the Escrow account, returning the rent lamports to the maker. ## Project setup Scaffold a new Anchor workspace: ```shell theme={"system"} anchor init anchor-escrow cd anchor-escrow ``` The default `--template modular` layout gives you separate files for instructions, state, and errors — the structure this guide uses. Replace the placeholder content with the files below. ### Cargo.toml Open `programs/anchor-escrow/Cargo.toml` and set the dependencies: ```toml theme={"system"} [dependencies] anchor-lang = { version = "1.0.0", features = ["init-if-needed"] } anchor-spl = "1.0.0" ``` > [!WARNING] > > `init-if-needed` is gated behind a feature flag because it opens a **re-initialization attack vector**: an attacker who can close the account between two of the program's instructions can then call the instruction again and the `init_if_needed` branch will re-create the account from scratch — wiping state, zeroing balances, or handing ownership to a new authority depending on the constraints. Anchor forces an explicit opt-in so you have to acknowledge this and mitigate it. Our `take` and `refund` use the flag deliberately (so the taker does not have to pre-create recipient ATAs), and the `associated_token::authority = …` constraint plus the fact that the accounts carry no state between calls make the re-init path safe. Always pair `init_if_needed` with full mint/authority/token-program constraints, and think twice about using it on any account that stores program state. ### Program ID Generate a program keypair and write its public key into `declare_id!` and `Anchor.toml`: ```shell theme={"system"} anchor keys sync ``` ## State Create `programs/anchor-escrow/src/state.rs`: ```rust theme={"system"} use anchor_lang::prelude::*; #[account] #[derive(InitSpace)] pub struct Escrow { pub seed: u64, pub maker: Pubkey, pub mint_a: Pubkey, pub mint_b: Pubkey, pub receive: u64, pub bump: u8, } ``` The six fields record everything the program needs at `take` or `refund` time: * `seed` — a maker-chosen `u64` used as the PDA seed. Lets a single maker run many concurrent escrows with different seeds. * `maker` — the wallet that created the offer. Used for `has_one` checks and to return rent. * `mint_a`, `mint_b` — the two mints involved. Stored so `take` and `refund` can validate the mint accounts the caller passes in. * `receive` — how much of mint B the taker must send to complete the swap. * `bump` — the PDA bump seed, cached at `make` time so we do not re-derive on every CPI. `#[derive(InitSpace)]` generates `Escrow::INIT_SPACE` — the byte size of all fields. The final `space` for the account is `8 + Escrow::INIT_SPACE` (the 8 leading bytes are the Anchor discriminator). ## Errors Create `programs/anchor-escrow/src/error.rs`: ```rust theme={"system"} use anchor_lang::prelude::*; #[error_code] pub enum EscrowError { #[msg("Amount must be greater than zero")] InvalidAmount, #[msg("Escrow maker does not match")] InvalidMaker, #[msg("Mint A does not match the escrow")] InvalidMintA, #[msg("Mint B does not match the escrow")] InvalidMintB, } ``` Anchor 1.0.0 restricts a program to a single `#[error_code]` block. Keep all variants here. ## The `make` instruction The maker deposits `amount` of mint A into a new vault and records how much of mint B they want in return. Create `programs/anchor-escrow/src/instructions/make.rs`: ```rust theme={"system"} use anchor_lang::prelude::*; use anchor_spl::{ associated_token::AssociatedToken, token_interface::{transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked}, }; use crate::error::EscrowError; use crate::state::Escrow; #[derive(Accounts)] #[instruction(seed: u64)] pub struct Make<'info> { #[account(mut)] pub maker: Signer<'info>, #[account( init, payer = maker, space = 8 + Escrow::INIT_SPACE, seeds = [b"escrow", maker.key().as_ref(), seed.to_le_bytes().as_ref()], bump, )] pub escrow: Account<'info, Escrow>, #[account(mint::token_program = token_program)] pub mint_a: InterfaceAccount<'info, Mint>, #[account(mint::token_program = token_program)] pub mint_b: InterfaceAccount<'info, Mint>, #[account( mut, associated_token::mint = mint_a, associated_token::authority = maker, associated_token::token_program = token_program, )] pub maker_ata_a: InterfaceAccount<'info, TokenAccount>, #[account( init, payer = maker, associated_token::mint = mint_a, associated_token::authority = escrow, associated_token::token_program = token_program, )] pub vault: InterfaceAccount<'info, TokenAccount>, pub associated_token_program: Program<'info, AssociatedToken>, pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } impl<'info> Make<'info> { fn populate_escrow(&mut self, seed: u64, receive: u64, bump: u8) -> Result<()> { self.escrow.set_inner(Escrow { seed, maker: self.maker.key(), mint_a: self.mint_a.key(), mint_b: self.mint_b.key(), receive, bump, }); Ok(()) } fn deposit_tokens(&mut self, amount: u64) -> Result<()> { transfer_checked( CpiContext::new( self.token_program.key(), TransferChecked { from: self.maker_ata_a.to_account_info(), mint: self.mint_a.to_account_info(), to: self.vault.to_account_info(), authority: self.maker.to_account_info(), }, ), amount, self.mint_a.decimals, ) } } pub fn handler(ctx: Context, seed: u64, receive: u64, amount: u64) -> Result<()> { require_gt!(receive, 0, EscrowError::InvalidAmount); require_gt!(amount, 0, EscrowError::InvalidAmount); ctx.accounts.populate_escrow(seed, receive, ctx.bumps.escrow)?; ctx.accounts.deposit_tokens(amount) } ``` What each constraint buys you: * `#[instruction(seed: u64)]` — surfaces the `seed` argument so we can reference it in PDA seeds. Anchor validates at deserialization time that `[b"escrow", maker, seed]` derives to the `escrow` account the caller passed in. * `init` on `escrow` — allocates and zero-fills the account, paid for by the maker, sized to `8 + Escrow::INIT_SPACE`. * `init` on `vault` — creates the associated token account, paid for by the maker, with the Escrow PDA as authority. Anchor calls the Associated Token Program internally; no manual CPI needed. * `mint::token_program = token_program` — asserts the mint is owned by the Token program that the caller passed. Prevents mixing SPL Token and Token-2022 mints under a single program account. * `associated_token::token_program = token_program` — same invariant, applied to ATAs. The handler runs two side effects: * `populate_escrow` — writes the escrow state in one `set_inner` call. Using `set_inner` is cleaner than five individual field assignments and generates fewer compute units. * `deposit_tokens` — CPIs into the Token program with `transfer_checked`, which validates the mint decimals against the transfer amount. The authority is the maker (a real signer on the transaction), so no PDA signing is needed yet. > [!TIP] > > `transfer_checked` is strictly safer than the older `transfer` — it fails if the decimals on the mint account disagree with the value the caller claims. Always prefer `transfer_checked` for user-facing transfers. ### The token interface Notice the types: `Interface<'info, TokenInterface>`, `InterfaceAccount<'info, Mint>`, `InterfaceAccount<'info, TokenAccount>`. These come from `anchor_spl::token_interface` and work with **either** SPL Token or Token-2022 mints. The concrete `Program<'info, Token>` and `Account<'info, TokenAccount>` from `anchor_spl::token` only work with legacy SPL Token. For new programs, default to the interface types. The cost is zero — runtime dispatch is resolved by the `token_program` account the caller passes. The benefit is that your program correctly types Token-2022 accounts and accepts both legacy and Token-2022 mints. Type compatibility is not runtime compatibility, though: extensions that require extra CPI logic — transfer hooks (need `remaining_accounts` containing the hook program plus its validation accounts), confidential transfers (encrypted amounts), and several others — still need explicit handling. See the [Token-2022 extensions](#token-2022-extensions) section below for the full list and the hazards that apply to an escrow. ## The `take` instruction The taker atomically: 1. Sends `escrow.receive` of mint B from their ATA to the maker's ATA. 2. Receives the entire vault balance of mint A. 3. Triggers the program to close the vault and the Escrow account, with rent going to the maker. Create `programs/anchor-escrow/src/instructions/take.rs`: ```rust theme={"system"} use anchor_lang::prelude::*; use anchor_spl::{ associated_token::AssociatedToken, token_interface::{ close_account, transfer_checked, CloseAccount, Mint, TokenAccount, TokenInterface, TransferChecked, }, }; use crate::error::EscrowError; use crate::state::Escrow; #[derive(Accounts)] pub struct Take<'info> { #[account(mut)] pub taker: Signer<'info>, /// The original maker. Receives mint B from the taker and the rent /// reclaimed from the closed escrow + vault accounts. #[account(mut)] pub maker: SystemAccount<'info>, #[account( mut, close = maker, seeds = [b"escrow", maker.key().as_ref(), escrow.seed.to_le_bytes().as_ref()], bump = escrow.bump, has_one = maker @ EscrowError::InvalidMaker, has_one = mint_a @ EscrowError::InvalidMintA, has_one = mint_b @ EscrowError::InvalidMintB, )] pub escrow: Box>, pub mint_a: Box>, pub mint_b: Box>, #[account( mut, associated_token::mint = mint_a, associated_token::authority = escrow, associated_token::token_program = token_program, )] pub vault: Box>, #[account( init_if_needed, payer = taker, associated_token::mint = mint_a, associated_token::authority = taker, associated_token::token_program = token_program, )] pub taker_ata_a: Box>, #[account( mut, associated_token::mint = mint_b, associated_token::authority = taker, associated_token::token_program = token_program, )] pub taker_ata_b: Box>, #[account( init_if_needed, payer = taker, associated_token::mint = mint_b, associated_token::authority = maker, associated_token::token_program = token_program, )] pub maker_ata_b: Box>, pub associated_token_program: Program<'info, AssociatedToken>, pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } impl<'info> Take<'info> { fn transfer_to_maker(&mut self) -> Result<()> { transfer_checked( CpiContext::new( self.token_program.key(), TransferChecked { from: self.taker_ata_b.to_account_info(), to: self.maker_ata_b.to_account_info(), mint: self.mint_b.to_account_info(), authority: self.taker.to_account_info(), }, ), self.escrow.receive, self.mint_b.decimals, ) } fn withdraw_and_close_vault(&mut self) -> Result<()> { let signer_seeds: [&[&[u8]]; 1] = [&[ b"escrow", self.maker.to_account_info().key.as_ref(), &self.escrow.seed.to_le_bytes()[..], &[self.escrow.bump], ]]; transfer_checked( CpiContext::new_with_signer( self.token_program.key(), TransferChecked { from: self.vault.to_account_info(), to: self.taker_ata_a.to_account_info(), mint: self.mint_a.to_account_info(), authority: self.escrow.to_account_info(), }, &signer_seeds, ), self.vault.amount, self.mint_a.decimals, )?; close_account(CpiContext::new_with_signer( self.token_program.key(), CloseAccount { account: self.vault.to_account_info(), authority: self.escrow.to_account_info(), destination: self.maker.to_account_info(), }, &signer_seeds, )) } } pub fn handler(ctx: Context) -> Result<()> { ctx.accounts.transfer_to_maker()?; ctx.accounts.withdraw_and_close_vault() } ``` Three mechanics in this instruction are worth walking through. ### `has_one` prevents wrong-account substitution The `has_one = maker`, `has_one = mint_a`, `has_one = mint_b` constraints tell Anchor: "the account named `maker` passed in the instruction must equal `escrow.maker`". Same for the two mints. Without these checks, a malicious taker could pass in any `mint_a` account and the program would happily process the swap — meaning the taker could drain the vault using a different (valueless) mint account and still pretend the constraints match. The `has_one` binding makes the escrow state the authoritative record and the passed-in accounts merely views onto it. The PDA derivation already binds `maker` to the escrow (the seeds include `maker.key()`), so the `has_one = maker` is technically redundant for that account — but it costs almost nothing and makes the security property explicit. > [!WARNING] > > Always bind every `Pubkey` field stored in a PDA via `has_one`. The PDA derivation only covers the seeds, not the fields inside the account data. Skipping `has_one` on a stored mint or authority field is one of the most common sources of Solana program exploits. ### `close = maker` reclaims rent When `close = maker` is set on an `#[account(mut, ...)]`, Anchor: 1. Zero-fills the account data after the instruction returns. 2. Sets the account's lamport balance to zero and transfers the lamports to `maker`. 3. Assigns the account to the System program so it cannot be reopened with the same address. This is how escrows return rent to the maker at the end of the swap — without `close`, the rent lamports would stay locked forever. ### Who pays for `maker_ata_b` The `init_if_needed` constraint on `maker_ata_b` lists `payer = taker`. The taker funds the maker's receive ATA if it does not already exist — which makes sense economically, because the taker is the one who wants the swap to complete. If the ATA already exists (maker had previously received mint B), `init_if_needed` does nothing and the taker pays no extra rent. The `associated_token::authority = maker` constraint binds the new ATA to the maker as its sole token-account authority, so the taker cannot substitute an account they control. ### Signing CPIs as a PDA The most distinctive pattern in this program is the PDA-signed transfer from the vault back to the taker: ```rust theme={"system"} let signer_seeds: [&[&[u8]]; 1] = [&[ b"escrow", self.maker.to_account_info().key.as_ref(), &self.escrow.seed.to_le_bytes()[..], &[self.escrow.bump], ]]; transfer_checked( CpiContext::new_with_signer( self.token_program.key(), TransferChecked { authority: self.escrow.to_account_info(), ... }, &signer_seeds, ), ... )?; ``` `CpiContext::new_with_signer` tells the runtime: "the authority on this CPI is a PDA, and here are the seeds used to derive it". The Solana runtime regenerates the PDA from the seeds, verifies it matches the `authority` account, and grants the CPI the signer privilege as if the PDA had signed the outer transaction. Two details to notice: * The seed list **must end with the bump byte**. The bump is the last seed Anchor pushes through when deriving the canonical PDA. * The program can sign as a PDA **only if the PDA was derived from its own program ID**. The runtime rejects PDA signing attempts from the wrong program. The same seed list is reused for the subsequent `close_account` CPI — the vault's authority is the Escrow PDA, so the program signs as the PDA to close the vault and sweep its rent lamports back to the maker. ### Order of operations The handler runs `transfer_to_maker` **before** `withdraw_and_close_vault`. Vault safety does not depend on this ordering — Solana transactions are atomic end-to-end, so any failed sub-call anywhere in the instruction rolls back every prior state change, including the vault release. The ordering is a **code-clarity and cost-efficiency** convention: fail fast on the cheap external check (does the taker actually hold enough mint B?) before doing the more expensive PDA-signed vault sweep. If you reversed the order the program would still be safe, just less readable and slightly more wasteful when the taker cannot pay. ## The `refund` instruction If no taker shows up, the maker can cancel the escrow and reclaim their token A. Create `programs/anchor-escrow/src/instructions/refund.rs`: ```rust theme={"system"} use anchor_lang::prelude::*; use anchor_spl::{ associated_token::AssociatedToken, token_interface::{ close_account, transfer_checked, CloseAccount, Mint, TokenAccount, TokenInterface, TransferChecked, }, }; use crate::error::EscrowError; use crate::state::Escrow; #[derive(Accounts)] pub struct Refund<'info> { #[account(mut)] pub maker: Signer<'info>, #[account( mut, close = maker, seeds = [b"escrow", maker.key().as_ref(), escrow.seed.to_le_bytes().as_ref()], bump = escrow.bump, has_one = maker @ EscrowError::InvalidMaker, has_one = mint_a @ EscrowError::InvalidMintA, )] pub escrow: Box>, #[account(mint::token_program = token_program)] pub mint_a: Box>, #[account( mut, associated_token::mint = mint_a, associated_token::authority = escrow, associated_token::token_program = token_program, )] pub vault: Box>, #[account( init_if_needed, payer = maker, associated_token::mint = mint_a, associated_token::authority = maker, associated_token::token_program = token_program, )] pub maker_ata_a: Box>, pub associated_token_program: Program<'info, AssociatedToken>, pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } impl<'info> Refund<'info> { fn withdraw_and_close_vault(&mut self) -> Result<()> { let signer_seeds: [&[&[u8]]; 1] = [&[ b"escrow", self.maker.to_account_info().key.as_ref(), &self.escrow.seed.to_le_bytes()[..], &[self.escrow.bump], ]]; transfer_checked( CpiContext::new_with_signer( self.token_program.key(), TransferChecked { from: self.vault.to_account_info(), to: self.maker_ata_a.to_account_info(), mint: self.mint_a.to_account_info(), authority: self.escrow.to_account_info(), }, &signer_seeds, ), self.vault.amount, self.mint_a.decimals, )?; close_account(CpiContext::new_with_signer( self.token_program.key(), CloseAccount { account: self.vault.to_account_info(), authority: self.escrow.to_account_info(), destination: self.maker.to_account_info(), }, &signer_seeds, )) } } pub fn handler(ctx: Context) -> Result<()> { ctx.accounts.withdraw_and_close_vault() } ``` `Refund` is `Take` with half the work: * Only the maker signs — no taker, so no second token transfer. * No mint B involved. The vault sweeps back to the maker's `mint_a` ATA (re-created with `init_if_needed` if the maker closed it after `make`). * The escrow `has_one = maker` constraint ensures only the original maker can refund. Combined with the `maker: Signer` requirement, nobody else can trigger this instruction. Note the absence of `has_one = mint_b`. `refund` does not touch mint B, so the constraint is unnecessary. ## Instruction module and `lib.rs` Create `programs/anchor-escrow/src/instructions/mod.rs`: ```rust theme={"system"} pub mod make; pub mod refund; pub mod take; pub use make::*; pub use refund::*; pub use take::*; ``` The glob re-exports produce a harmless ambiguous-glob warning about three `handler` symbols — keep the globs anyway, because Anchor's `#[program]` macro requires them to generate its client-side account helper modules. Replace `programs/anchor-escrow/src/lib.rs` with the entry point: ```rust theme={"system"} #![allow(unexpected_cfgs)] use anchor_lang::prelude::*; pub mod error; pub mod instructions; pub mod state; pub use instructions::*; declare_id!("ReplaceWithYourProgramId1111111111111111111"); #[program] pub mod anchor_escrow { use super::*; pub fn make(ctx: Context, seed: u64, receive: u64, amount: u64) -> Result<()> { instructions::make::handler(ctx, seed, receive, amount) } pub fn take(ctx: Context) -> Result<()> { instructions::take::handler(ctx) } pub fn refund(ctx: Context) -> Result<()> { instructions::refund::handler(ctx) } } ``` ## Building and IDL ```shell theme={"system"} anchor build ``` This compiles the program to `target/deploy/anchor_escrow.so` and emits the IDL at `target/idl/anchor_escrow.json`. If the build fails with a `declare_id!` mismatch, run `anchor keys sync` and rebuild. ## A TypeScript test for the full lifecycle Create `tests/anchor-escrow.ts`. This test spins up make → take → refund against the Anchor provider (which points at Surfpool or `solana-test-validator` depending on your `Anchor.toml`): ```typescript theme={"system"} import * as anchor from "@anchor-lang/core"; import { BN, Program } from "@anchor-lang/core"; import { AnchorEscrow } from "../target/types/anchor_escrow"; import { createMint, getOrCreateAssociatedTokenAccount, mintTo, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, getAccount, } from "@solana/spl-token"; import { Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram } from "@solana/web3.js"; import { assert } from "chai"; describe("anchor-escrow", () => { const provider = anchor.AnchorProvider.env(); anchor.setProvider(provider); const program = anchor.workspace.anchorEscrow as Program; const maker = Keypair.generate(); const taker = Keypair.generate(); const seed = new BN(Math.floor(Math.random() * 1_000_000)); let mintA: PublicKey; let mintB: PublicKey; let makerAtaA: PublicKey; let makerAtaB: PublicKey; let takerAtaA: PublicKey; let takerAtaB: PublicKey; let escrowPda: PublicKey; let vault: PublicKey; const amount = new BN(1_000_000_000); // 1.0 of mint A const receive = new BN(500_000_000); // 0.5 of mint B before(async () => { // Fund both wallets. for (const kp of [maker, taker]) { const sig = await provider.connection.requestAirdrop(kp.publicKey, 5 * LAMPORTS_PER_SOL); await provider.connection.confirmTransaction(sig, "confirmed"); } // Mints: maker mints both for convenience. mintA = await createMint(provider.connection, maker, maker.publicKey, null, 9); mintB = await createMint(provider.connection, maker, maker.publicKey, null, 9); // Maker starts with 1.0 of mint A. Taker starts with 1.0 of mint B. makerAtaA = (await getOrCreateAssociatedTokenAccount(provider.connection, maker, mintA, maker.publicKey)).address; takerAtaB = (await getOrCreateAssociatedTokenAccount(provider.connection, taker, mintB, taker.publicKey)).address; await mintTo(provider.connection, maker, mintA, makerAtaA, maker, 1_000_000_000); await mintTo(provider.connection, maker, mintB, takerAtaB, maker, 1_000_000_000); // Derive the escrow PDA and the vault ATA. [escrowPda] = PublicKey.findProgramAddressSync( [Buffer.from("escrow"), maker.publicKey.toBuffer(), seed.toArrayLike(Buffer, "le", 8)], program.programId, ); vault = anchor.utils.token.associatedAddress({ mint: mintA, owner: escrowPda }); }); it("make: maker opens the escrow", async () => { await program.methods .make(seed, receive, amount) .accounts({ maker: maker.publicKey, escrow: escrowPda, mintA, mintB, makerAtaA, vault, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, }) .signers([maker]) .rpc(); const vaultAccount = await getAccount(provider.connection, vault); assert.equal(vaultAccount.amount.toString(), amount.toString()); const makerAtaABal = await getAccount(provider.connection, makerAtaA); assert.equal(makerAtaABal.amount.toString(), "0"); const escrowAccount = await program.account.escrow.fetch(escrowPda); assert.equal(escrowAccount.maker.toBase58(), maker.publicKey.toBase58()); assert.equal(escrowAccount.mintA.toBase58(), mintA.toBase58()); assert.equal(escrowAccount.mintB.toBase58(), mintB.toBase58()); assert.equal(escrowAccount.receive.toString(), receive.toString()); }); it("take: taker completes the swap", async () => { takerAtaA = anchor.utils.token.associatedAddress({ mint: mintA, owner: taker.publicKey }); makerAtaB = anchor.utils.token.associatedAddress({ mint: mintB, owner: maker.publicKey }); await program.methods .take() .accounts({ taker: taker.publicKey, maker: maker.publicKey, escrow: escrowPda, mintA, mintB, vault, takerAtaA, takerAtaB, makerAtaB, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, }) .signers([taker]) .rpc(); const takerAtaABal = await getAccount(provider.connection, takerAtaA); assert.equal(takerAtaABal.amount.toString(), amount.toString()); const makerAtaBBal = await getAccount(provider.connection, makerAtaB); assert.equal(makerAtaBBal.amount.toString(), receive.toString()); const escrowInfo = await provider.connection.getAccountInfo(escrowPda); assert.isNull(escrowInfo, "escrow should be closed"); const vaultInfo = await provider.connection.getAccountInfo(vault); assert.isNull(vaultInfo, "vault should be closed"); }); it("refund: maker reclaims after a separate open", async () => { // Open a new escrow with a different seed. const refundSeed = new BN(Math.floor(Math.random() * 1_000_000) + 1_000_000); const [refundEscrow] = PublicKey.findProgramAddressSync( [Buffer.from("escrow"), maker.publicKey.toBuffer(), refundSeed.toArrayLike(Buffer, "le", 8)], program.programId, ); const refundVault = anchor.utils.token.associatedAddress({ mint: mintA, owner: refundEscrow }); // Top the maker up again so they have something to deposit. await mintTo(provider.connection, maker, mintA, makerAtaA, maker, 200_000_000); await program.methods .make(refundSeed, new BN(1), new BN(200_000_000)) .accounts({ maker: maker.publicKey, escrow: refundEscrow, mintA, mintB, makerAtaA, vault: refundVault, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, }) .signers([maker]) .rpc(); await program.methods .refund() .accounts({ maker: maker.publicKey, escrow: refundEscrow, mintA, vault: refundVault, makerAtaA, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, }) .signers([maker]) .rpc(); const makerAtaABal = await getAccount(provider.connection, makerAtaA); assert.equal(makerAtaABal.amount.toString(), "200000000"); const refundEscrowInfo = await provider.connection.getAccountInfo(refundEscrow); assert.isNull(refundEscrowInfo); const refundVaultInfo = await provider.connection.getAccountInfo(refundVault); assert.isNull(refundVaultInfo, "refund vault should be closed"); }); }); ``` Run the full lifecycle: ```shell theme={"system"} anchor test ``` A successful run produces three green checkmarks — one per instruction. > [!TIP] > > If `anchor test` fails with a connection error, Surfpool is not installed and Anchor 1.0.0 defaults to it. Either install Surfpool or fall back to the legacy validator with `anchor test --validator legacy`. ## Deploying to devnet and mainnet Switch to devnet: ```shell theme={"system"} solana config set --url https://api.devnet.solana.com solana airdrop 2 anchor program deploy --provider.cluster devnet ``` > [!NOTE] > > If `anchor program deploy` returns a 503 from the default devnet RPC, add `--with-compute-unit-price 1` — the built-in fee-bump routes the upload through a higher-priority lane. This is a known workaround for deployment rate-limits. See [Anchor issue #4255](https://github.com/solana-foundation/anchor/issues/4255). Deploying to mainnet with a [Chainstack Solana node](https://console.chainstack.com/): ```shell theme={"system"} anchor program deploy \ --provider.cluster https://solana-mainnet.core.chainstack.com/YOUR_AUTH_KEY \ --provider.wallet ~/.config/solana/mainnet.json ``` Chainstack mainnet endpoints do not rate-limit program deploys the way the public RPC does, so large programs upload in one shot without retry logic. ## Production considerations ### Partial fills The program as written requires the taker to fulfill the escrow in full — `take` always transfers the entire vault. Extending to partial fills requires tracking a remaining balance in the Escrow struct and computing a proportional amount of mint B per call. Most production order-book programs (OpenBook, Phoenix) instead prefer many small escrows over a single partially-fillable one, because the accounting is simpler and the program cannot end up holding a dust amount that no rational taker will fill. ### Price staleness An escrow is a fixed offer: the maker locks in a price at `make` time and lives with whatever the market does until a taker arrives or they refund. For long-lived offers this is a real risk — a token whose market price doubles will get filled immediately, probably at a loss to the maker. Production protocols mitigate with: * **Expiry timestamps** — add an `expires_at: i64` field; `take` fails after `Clock::get()?.unix_timestamp > expires_at`. * **Oracle-pegged takers** — require the taker to provide a Pyth or Switchboard price update and enforce a bound. This turns the escrow into a slippage-protected swap. * **Short refund latency** — keep the maker able to cancel cheaply and frequently, so they can rebalance. ### Front-running and the taker set The program as written is permissionless: anyone holding mint B and enough SOL for fees may call `take`. For simple bilateral OTC swaps this is benign — the maker gets their desired mint B regardless of who the taker is. But if you extend the Escrow state to gate `take` on a specific `taker: Pubkey`, the `make` transaction becomes a front-running target: a searcher watching the mempool can construct their own `take` against the just-created escrow. Gating by the taker's pubkey is not sufficient on its own; you need either a commit-reveal flow, a signed authorization blob from the maker, or an off-chain private relay. ### Economic attacks on the vault The Escrow PDA owns the vault, and the vault holds real tokens. Always check: * **No `close` without `mut`** — you cannot close an account that is not marked mutable. * **`has_one` on every stored `Pubkey`** — missing `has_one` on a stored mint or authority is the most common escrow exploit. The PDA derivation only covers seeds, not stored fields. * **`associated_token::authority = escrow`** — if the vault authority is set to anything other than the Escrow PDA, the program loses control. * **`mint::token_program` and `associated_token::token_program`** — always thread the `token_program` account through every constraint. Mixing SPL Token and Token-2022 accounts under a single program instance is a common source of subtle state bugs. ### Token-2022 extensions The program uses `TokenInterface`, so it accepts Token-2022 mints. Several extensions are quietly hostile to the escrow pattern and require explicit handling before you accept arbitrary user-supplied mints: * **Transfer fees** — the mint deducts a fee from each transfer, so the vault ends up holding less than the `amount` the maker thought they deposited. `take` reads `self.vault.amount` and withdraws whatever the vault actually holds — which is fine from a correctness standpoint but changes what the taker receives. Either document the behavior or reject transfer-fee mints at `make` time. * **Default account state = Frozen** — the mint ships with all new accounts frozen until the freeze authority thaws them. Your `make` instruction creates the vault ATA successfully, but the very next `transfer_checked` fails because the vault is frozen. Because `make` is a single instruction, the failed deposit rolls back the vault initialization atomically — the maker's `mint_a` ATA is untouched and nothing is locked. The failure is still a bad user experience, so reject frozen-by-default mints at `make` time to surface the error cleanly. * **Withheld transfer fees on the vault** — if the vault accrues withheld fees (from Token-2022 mints that redirect a fraction of every transfer to the mint), `close_account` rejects the close. The program must call `harvest_withheld_tokens_to_mint` on the vault before the close, or the vault cannot be cleaned up and the escrow cannot be finalized. * **CPI guard** — a token account with CPI guard enabled rejects outgoing transfers made via a cross-program invocation. The vault is set up and manipulated entirely through CPIs, so enabling CPI guard on the vault (directly or through a propagated extension) freezes the escrow mid-flight. * **Close account authority** — this Token-2022 extension lets the mint designate a separate "closer" per token account, overriding the account's own authority. A vault whose mint propagates a non-PDA close authority to new ATAs cannot be closed by the program's PDA-signed `close_account` CPI; the close returns `MissingRequiredSignature` and the vault becomes unclosable. * **Memo transfer on the maker's ATA** — if the maker enables the `MemoTransfer` extension on their `maker_ata_b`, every incoming transfer must carry a memo instruction. The taker's `transfer_checked` in `take` has no memo attached, so the maker's extension choice bricks the swap. Common on stablecoins and regulated tokens. Note that this is a **token-account extension**, not a mint extension, so it is invisible to mint TLV inspection — and the maker can enable it any time before `take` is called. Mitigations: have the client check `RequiredMemoTransfers` on `maker_ata_b` before submitting `take`, or document that escrows into memo-required ATAs are out of scope. * **Transfer hooks** — fire a CPI into a third-party program on every transfer. Your `transfer_checked` call now passes through arbitrary code *and* requires extra `remaining_accounts` (the hook program plus its validation accounts) that our fixed `TransferChecked` context does not provide. This also opens an indirect-reentrancy surface: the hook program can CPI back into your escrow during the middle of `take`, before the vault close has finalized. Whitelist trusted hook programs or reject mints with transfer hooks entirely; an arbitrary-hook-accepting escrow is not safe without dedicated reentrancy guards. * **Confidential transfers** — amounts are encrypted. The vault balance readout in `take` and `refund` would need an auditor key. * **Non-transferable** — a non-transferable Token-2022 mint cannot be swapped; your program refuses any transfer and the maker cannot refund. Reject these at `make` time. * **Permanent delegate** — the mint authority can unilaterally move tokens out of any account, including your vault. Reject mints with this extension if you are holding user funds. > [!WARNING] > > For an escrow that accepts arbitrary user-supplied mints, inspect the Token-2022 extension list on both mints at `make` time and reject the set of mint-level extensions you have not handled. The `anchor_spl::token_2022_extensions` module exposes parsers for every extension; read the mint's `TLV` data and check for each `ExtensionType` you disallow. **Account-level extensions** (`MemoTransfer`, `CpiGuard`, `CloseAccountAuthority`) are a separate class — they live on the token account, not the mint, and can be toggled after the escrow is opened. Handle these at the client layer or reject any account that carries one at `take` / `refund` time. ## Common gotchas ### `init_if_needed` without constraints masks bugs `init_if_needed` in `take` for `taker_ata_a` and `maker_ata_b` is convenient — the caller does not need to pre-create destination ATAs. But if you forget the `associated_token::mint` and `associated_token::authority` constraints, Anchor will happily initialize a fresh, empty ATA with whatever seeds the caller provides. Always pair `init_if_needed` with full mint/authority/token-program constraints so Anchor validates the account whether it exists or not. ### Seed-ordering mistakes The PDA seeds are positional. `["escrow", maker, seed]` is not the same PDA as `["escrow", seed, maker]`. Every reference to the seed list — in the `Make.seeds` constraint, in `signer_seeds` for the `take` and `refund` CPIs, and on the client side — must list the seeds in the same order. A mismatch manifests as `ConstraintSeeds` errors at runtime; the compiler cannot catch it. ### Re-using the same seed If a maker calls `make` with the same `seed` twice (without a refund in between), the second call fails with `AccountAlreadyInitialized` — the PDA is already taken. For UX, either: * Increment the seed client-side each time (e.g., take the current unix timestamp as the seed). * Let the client derive the PDA up front and fail fast if the account exists. ### `close` only clears lamports at instruction end Anchor's `close = destination` runs after the handler returns. You cannot read from or transfer from an account in the same instruction after it has been closed — because at instruction-runtime it is still "alive" as far as the handler's account data is concerned. The vault close and the final token transfer must happen **before** the handler returns, and the close semantic takes effect during the account-writeback phase of the runtime. ### BN serialization of u64 seeds On the client, `seed.toArrayLike(Buffer, "le", 8)` produces the 8-byte little-endian encoding that matches `seed.to_le_bytes()` in Rust. Getting either the endianness or the length wrong (e.g., `"be"` or length 4) yields a different PDA. Use the same helper in every derivation path. ### Mint decimals must be consistent `transfer_checked` fails if the `decimals` you pass do not match the mint account. If you read the mint once and cache `decimals` on the escrow state to save a mint fetch on `take`, make sure nothing mutates the mint's decimals between `make` and `take` — Token-2022 extensions that modify mint metadata can surprise you here. ## Further reading * [Solana: Anchor development](/docs/solana-anchor-development) — project setup, constraints, deployment, testing * [Solana: Program derived addresses and cross-program invocations](/docs/solana-program-derived-addresses-and-cross-program-invocations) — PDA derivation and CPI mechanics * [Solana: Token Extensions (Token-2022)](/docs/solana-token-extensions) — extension semantics your escrow may need to handle * [Anchor 1.0.0 release notes](https://github.com/solana-foundation/anchor/releases/tag/v1.0.0) — breaking changes from pre-1.0 Anchor * [`solana-foundation/anchor` `tests/escrow`](https://github.com/solana-foundation/anchor/tree/master/tests/escrow) — canonical integration test inside the Anchor monorepo * [`solana-foundation/solana-bootcamp-2026` `04-escrow`](https://github.com/solana-foundation/solana-bootcamp-2026/tree/main/04-escrow) — maker/take/refund with LiteSVM tests * [`solana-developers/program-examples` `tokens/escrow`](https://github.com/solana-developers/program-examples/tree/main/tokens/escrow) — parallel native Rust and Anchor implementations * [`solana-program/escrow`](https://github.com/solana-program/escrow) — an alternative production-grade escrow program maintained by the Solana Program Library. It uses a different architecture (TLV-encoded state with pluggable Timelock, Hook, Arbiter, and Block-Token-Extensions modules) and is admin-controlled rather than peer-to-peer; consider it when you need those lifecycle hooks or an arbitrator role. The Anchor maker/taker pattern in this guide is simpler and more suitable when two parties swap directly without governance. ## Reference repos These are the source repositories we worked against while writing this guide. They stay closer to reality than docs — check them first when something here looks off. * [solana-developers/program-examples](https://github.com/solana-developers/program-examples) — canonical escrow examples (`tokens/escrow`); instruction flow and account layout verified here * [solana-program/escrow](https://github.com/solana-program/escrow) — SPL escrow reference; canonical for the vault-PDA ownership pattern * [solana-foundation/anchor](https://github.com/solana-foundation/anchor) — Anchor 1.0 test suite includes `tests/escrow`; check for Anchor-idiomatic patterns * [solana-foundation/solana-bootcamp-2026](https://github.com/solana-foundation/solana-bootcamp-2026) — bootcamp's `04-escrow` project cross-checked the maker, take, and refund flow # Solana: Estimate priority fees with RPC methods Source: https://docs.chainstack.com/docs/solana-estimate-priority-fees-getrecentprioritizationfees Estimate Solana transaction priority fees using getRecentPrioritizationFees to calculate compute unit prices for faster confirmation on Chainstack. > **Part of the [How to land transactions on Solana](/docs/solana-how-to-land-transactions) guide.** **TLDR:** * The `getRecentPrioritizationFees` method provides real-time insights into recent priority fee data on Solana. * It helps you dynamically gauge how much extra fee (in micro-lamports/Compute Unit) others are paying. * You can then set your transaction priority fee accordingly, balancing costs with faster confirmation needs. * This guide shows how to implement and analyze recent prioritization fees using TypeScript, with side-by-side examples for both `@solana/kit` and the classic `@solana/web3.js`. ## Main article Within blockchain technology, transaction processing efficiency is a cornerstone of network performance and user satisfaction. At the heart of this efficiency lies the nuanced concept of prioritization fees—a critical element that ensures transactions are processed promptly. Recognizing the importance of this aspect, Solana has introduced the `getRecentPrioritizationFees` method provides users with up-to-date information about these fees, allowing them to have an idea of the current fees attached to successful transactions and to estimate the priority fee to attach to transactions dynamically. In this guide, we’ll explore the `getRecentPrioritizationFees` method, its operational mechanics, and its role in enhancing transaction efficiency. We will also discuss practical applications, demonstrating how to integrate this method into your decentralized applications (DApps) with TypeScript. Examples are shown for both Solana JavaScript libraries — `@solana/kit` (the newer, tree-shakable, functional SDK, formerly `@solana/web3.js` v2) and the classic `@solana/web3.js` (the `Connection`/`PublicKey` API) — both of which are actively maintained. ## What are Priority Fees on Solana? Priority fees on Solana allow users to expedite their transactions by paying an additional fee, measured in [micro-lamports](/docs/solana-glossary#micro-lamports) per [Compute Unit](/docs/solana-glossary#compute-units). These optional fees are added to the base transaction fee, typically 5000 lamports per signature. The rationale behind priority fees is to make transactions more appealing to validator nodes for block inclusion, thereby ensuring quicker processing. This system effectively allows users to bid for transaction processing priority, which is especially useful during high network congestion. Validators are motivated to prioritize transactions offering higher fees per compute unit, ensuring efficient use of network resources. While transactions can proceed without priority fees, adding them increases the likelihood of faster execution. ## Deploy a Chainstack Solana node Before getting started, deploy a Solana node on Chainstack so you are ready to play with the code and examples. This will provide direct blockchain access, which is essential for effectively exploring features like the getRecentPrioritizationFees method. 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Understand the \`getRecentPrioritizationFees method The `getRecentPrioritizationFees` method offers real-time insights into the network's current trends of prioritization fees. These fees are calculated per compute unit and are paid by transactions seeking to secure a higher priority in the block processing queue. By leveraging these prioritization fees, transactions can gain precedence over others in the same block, thus ensuring quicker inclusion and execution on the blockchain. Opting to pay a prioritization fee is voluntary, yet it significantly enhances a transaction's chance of being included in the forthcoming block. This strategic payment allows transactions to outpace others, offering lower or no prioritization fees. The method returns a response composed of objects, each representing a prioritization fee observed in a recent slot or block on the Solana blockchain. ### Parameters * **Array**: This parameter accepts a variety of base-58 encoded public key strings representing Solana account addresses, with a cap of 128 addresses. ### Response The method's response is structured as an array of objects, with each object shedding light on a prioritization fee observed in a recent slot (block). The detailed attributes of each object include: * **Slot**: Identifies the specific slot (or block) on the Solana blockchain where the transactions tied to the observed prioritization fees were processed. * **prioritizationFee**: Indicates the fee amount, in micro-lamports, that was paid by at least one transaction in the specified slot to secure a higher processing priority. ## cURL example Below is a practical demonstration of a simple `getRecentPrioritizationFees` request using cURL. This example queries the Orca Whirlpool SOL-USDC pool state account — a writable state account that competing swap transactions lock. Pass writable state accounts (pool state, reserves, user token accounts) for meaningful results. > [!WARNING] > > Do not pass program IDs (Jupiter, Raydium, pump.fun, etc.) to this method. Programs are executable and read-only in user transactions, so they are not present in the per-account writable fee map. Passing a program ID returns near-empty data and gives a misleading picture of recent fees. ```shell Shell theme={"system"} curl --request POST \ --url YOUR_SOLANA_ENDPOINT \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "getRecentPrioritizationFees", "params": [ [ "HJPjoWUrhoZzkNfRpHuieeFk9WcZWjwy6PBjZ81ngndJ" ] ] } ' ``` > [!NOTE] > > You can find more explanations and examples in the [Chainstack API reference](/reference/solana-getrecentprioritizationfees). ## Explore the project This simple project is designed as a comprehensive guide for developers looking to optimize their transactions on the Solana blockchain through the strategic use of priority fees. By harnessing the `getRecentPrioritizationFees` method, the provided code dynamically fetches recent prioritization fee data, offering a deep dive into the current fee landscape. This project will provide various priority fee measurements based on the `getRecentPrioritizationFees` response: * Average Prioritization Fee, including slots with zero fees. * Average Prioritization Fee excluding slots with zero fees. * Median Prioritization Fee excluding slots with zero fees. > [!WARNING] > > Each value returned by `getRecentPrioritizationFees` is the floor for that slot — the cheapest fee paid by any transaction writing to your supplied accounts. Computing the average or median therefore gives you the average or median *floor*, not the average or median fee actually paid. To produce a competitive bid, take the maximum of non-zero values (or a high percentile like p95) over the 150-slot window and multiply by an urgency factor — typically 1.5x for normal load, 3x or higher when racing against other transactions for the same accounts. ### Environment setup * **Node.js**: Ensure you have [Node.js](https://nodejs.org/en/download/current) (version 18 or above) installed on your machine. Node.js is essential for running the scripts and managing the dependencies of the Raydium SDK. * **npm**: Node Package Manager is the default package manager for Node.js projects. It is used to install the necessary dependencies for this project. npm comes pre-installed with Node.js, so you don't need to install it separately. * **Solana wallet**: A Solana wallet with some SOL and SLP tokens. > [!NOTE] > > If you prefer, you can also use Yarn as an alternative package manager. Yarn is a fast, reliable, and secure dependency management tool. After installing Node.js, you can install Yarn by running `npm install --global yarn` in your terminal. ## Set up a TypeScript project Before working on the code, we need to set up a TypeScript project. This will provide us with a structured environment for our code and allow us to take advantage of TypeScript's type-checking and other features. Follow these steps to create a new TypeScript project: **Create a new directory for your project**: Open your terminal and navigate to the location where you want to create your project directory. Run the following command to create a new directory: ```bash Bash theme={"system"} mkdir solana-getRecentPrioritizationFees ``` **Initialize a new Node.js project**: Navigate into the newly created directory and initialize a new Node.js project by running: ```bash Bash theme={"system"} cd solana-getRecentPrioritizationFees npm init -y ``` This will create a `package.json` file, which is used to manage project dependencies. **Install TypeScript and type definitions**: Next, we must install TypeScript and the Node.js type definitions. Both `@solana/kit` and `@solana/web3.js` ship their own TypeScript types, so no separate SDK types package is needed. Run the following command: ```bash Bash theme={"system"} npm install --save-dev typescript @types/node ``` **Create a TypeScript configuration file**: To configure TypeScript for our project, we must create a `tsconfig.json` file. Run the following command to generate a basic configuration file: ```bash Bash theme={"system"} tsc --init ``` This will create a `tsconfig.json` file with default settings. You can customize these settings as needed for your project. **Create a source file**: Let's create our first TypeScript file where we'll write our code. ```bash Bash theme={"system"} touch main.ts ``` ### Install required packages We must install a package to interact with the Solana blockchain, plus `dotenv` to load environment variables. This guide shows two Solana JavaScript libraries side by side — **both are actively maintained**, so pick whichever fits your project: * **`@solana/kit`** — the newer, tree-shakable, functional SDK (formerly `@solana/web3.js` v2). Its maintainers recommend it for new code. * **`@solana/web3.js`** — the classic object-oriented API (`Connection`, `PublicKey`) used across most existing Solana codebases. The `dotenv` package (used by both) loads environment variables from a `.env` file, which helps store sensitive information like private keys. > [tab] @solana/kit > > ```bash Bash theme={"system"} > npm install @solana/kit dotenv > ``` > > Your `package.json` should then include: > > ```json JSON theme={"system"} > "dependencies": { > "@solana/kit": "^7.0.0", > "dotenv": "^16.4.5" > } > ``` > [tab] @solana/web3.js (classic) > > ```bash Bash theme={"system"} > npm install @solana/web3.js dotenv > ``` > > Your `package.json` should then include: > > ```json JSON theme={"system"} > "dependencies": { > "@solana/web3.js": "^1.99.0", > "dotenv": "^16.4.5" > } > ``` With these packages installed, you'll have the tools to connect to a Solana node and read recent prioritization fees using TypeScript. In the next section, we'll start coding. ## Set up your environment variables This tutorial will use sensitive information such as private keys and RPC node URLs. It's crucial to keep this information secure and avoid committing it to version control systems like Git. We'll use the `dotenv` package to load environment variables from a `.env` file to achieve this. Follow these steps to set up your environment variables: 1. Create a `.env` file: In the root directory of your project, create a new file called `.env`. This file will store your environment variables. 2. Add your environment variables: Open the `.env` file and add the following variables, replacing the placeholders with your actual values: ``` SOLANA_RPC="YOUR_HTTPS_CHAINSTACK_URL" ``` * `SOLANA_RPC`: This variable should contain your Solana node's HTTP RPC URL. If you're using a Chainstack node, the RPC URL is in the node's credentials. ### How to use environment variables Once the `.env` file is set up, you can load environment variables in your TypeScript code (e.g., `main.ts`), import the `dotenv` package, and load the environment variables at the beginning of your script: ```typescript TypeScript theme={"system"} import "dotenv/config"; ``` This will load the environment variables from the `.env` file into the `process.env` object, allowing you to access them using `process.env.VARIABLE_NAME`. **Access environment variables**: You can now access the environment variables in your code like this: ```typescript TypeScript theme={"system"} const rpcUrl = process.env.SOLANA_RPC; ``` By following this approach, you can keep sensitive information like private keys and node URLs out of your codebase, making it more secure and easier to manage different environments (e.g., development, staging, production). > [!NOTE] > > Remember to add the `.env` file to your `.gitignore` file to prevent it from being committed to version control systems, as it contains sensitive information. ## Code walkthrough Now that we have set up the project, installed the required packages, and configured the environment variables, it's time to put everything together and implement the code: ### Add the code 1. Open the `main.ts` file you created earlier in your preferred code editor. 2. Paste the following code into the `main.ts` file: > [tab] @solana/kit > > ```typescript TypeScript theme={"system"} > import { createSolanaRpc, address } from "@solana/kit"; > import "dotenv/config"; > > // Strongly type the environment variable getter > function getEnvVariable(key: string): string { > const value = process.env[key]; > if (!value) { > throw new Error(`Environment variable ${key} is not set.`); > } > return value; > } > > const getPrioritizationFees = async () => { > try { > const rpc = createSolanaRpc(getEnvVariable("SOLANA_RPC")); > > // Orca Whirlpool SOL-USDC pool state — a writable account swap transactions lock. > const writableAccount = address("HJPjoWUrhoZzkNfRpHuieeFk9WcZWjwy6PBjZ81ngndJ"); > > const prioritizationFeeObjects = await rpc > .getRecentPrioritizationFees([writableAccount]) > .send(); > > if (prioritizationFeeObjects.length === 0) { > console.log("No prioritization fee data available."); > return; > } > > // @solana/kit returns slot and prioritizationFee as bigint, so coerce to > // number before doing arithmetic — mixing bigint and number throws. > const fees = prioritizationFeeObjects.map((f) => Number(f.prioritizationFee)); > const slots = prioritizationFeeObjects.map((f) => Number(f.slot)).sort((a, b) => a - b); > > // Extract slots range > const slotsRangeStart = slots[0]; > const slotsRangeEnd = slots[slots.length - 1]; > > // Calculate the average including zero fees > const averageFeeIncludingZeros = fees.length > 0 > ? Math.floor(fees.reduce((acc, fee) => acc + fee, 0) / fees.length) > : 0; > > // Filter out prioritization fees that are equal to 0 for other calculations > const nonZeroFees = fees.filter((fee) => fee !== 0); > > // Calculate the average of the non-zero fees > const averageFeeExcludingZeros = nonZeroFees.length > 0 > ? Math.floor(nonZeroFees.reduce((acc, fee) => acc + fee, 0) / nonZeroFees.length) > : 0; > > // Calculate the median of the non-zero fees > const sortedFees = nonZeroFees.sort((a, b) => a - b); > let medianFee = 0; > if (sortedFees.length > 0) { > const midIndex = Math.floor(sortedFees.length / 2); > medianFee = sortedFees.length % 2 !== 0 > ? sortedFees[midIndex] > : Math.floor((sortedFees[midIndex - 1] + sortedFees[midIndex]) / 2); > } > > console.log(`Slots examined for priority fees: ${prioritizationFeeObjects.length}`) > console.log(`Slots range examined from ${slotsRangeStart} to ${slotsRangeEnd}`); > console.log('====================================================================================') > > // You can use averageFeeIncludingZeros, averageFeeExcludingZeros, and medianFee in your transactions script > console.log(` 💰 Average Prioritization Fee (including slots with zero fees): ${averageFeeIncludingZeros} micro-lamports.`); > console.log(` 💰 Average Prioritization Fee (excluding slots with zero fees): ${averageFeeExcludingZeros} micro-lamports.`); > console.log(` 💰 Median Prioritization Fee (excluding slots with zero fees): ${medianFee} micro-lamports.`); > } catch (error) { > console.error('Error fetching prioritization fees:', error); > } > }; > > getPrioritizationFees(); > ``` > [tab] @solana/web3.js (classic) > > ```typescript TypeScript theme={"system"} > import { Connection, PublicKey } from '@solana/web3.js'; > import 'dotenv/config'; > > // Strongly type the environment variable getter > function getEnvVariable(key: string): string { > const value = process.env[key]; > if (!value) { > throw new Error(`Environment variable ${key} is not set.`); > } > return value; > } > > // Define interfaces for more explicit typing > interface PrioritizationFeeObject { > slot: number; > prioritizationFee: number; > } > > interface Config { > lockedWritableAccounts: PublicKey[]; > } > > const getPrioritizationFees = async () => { > try { > const SOLANA_RPC = getEnvVariable('SOLANA_RPC'); > const connection = new Connection(SOLANA_RPC); > > const publicKey = new PublicKey('HJPjoWUrhoZzkNfRpHuieeFk9WcZWjwy6PBjZ81ngndJ'); > > const config: Config = { > lockedWritableAccounts: [publicKey] > }; > > const prioritizationFeeObjects = await connection.getRecentPrioritizationFees(config) as PrioritizationFeeObject[]; > > if (prioritizationFeeObjects.length === 0) { > console.log('No prioritization fee data available.'); > return; > } > > // Extract slots and sort them > const slots = prioritizationFeeObjects.map(feeObject => feeObject.slot).sort((a, b) => a - b); > > // Extract slots range > const slotsRangeStart = slots[0]; > const slotsRangeEnd = slots[slots.length - 1]; > > // Calculate the average including zero fees > const averageFeeIncludingZeros = prioritizationFeeObjects.length > 0 > ? Math.floor(prioritizationFeeObjects.reduce((acc, feeObject) => acc + feeObject.prioritizationFee, 0) / prioritizationFeeObjects.length) > : 0; > > // Filter out prioritization fees that are equal to 0 for other calculations > const nonZeroFees = prioritizationFeeObjects > .map(feeObject => feeObject.prioritizationFee) > .filter(fee => fee !== 0); > > // Calculate the average of the non-zero fees > const averageFeeExcludingZeros = nonZeroFees.length > 0 > ? Math.floor(nonZeroFees.reduce((acc, fee) => acc + fee, 0) / nonZeroFees.length ) > : 0; > > // Calculate the median of the non-zero fees > const sortedFees = nonZeroFees.sort((a, b) => a - b); > let medianFee = 0; > if (sortedFees.length > 0) { > const midIndex = Math.floor(sortedFees.length / 2); > medianFee = sortedFees.length % 2 !== 0 > ? sortedFees[midIndex] > : Math.floor((sortedFees[midIndex - 1] + sortedFees[midIndex]) / 2); > } > > console.log(`Slots examined for priority fees: ${prioritizationFeeObjects.length}`) > console.log(`Slots range examined from ${slotsRangeStart} to ${slotsRangeEnd}`); > console.log('====================================================================================') > > // You can use averageFeeIncludingZeros, averageFeeExcludingZeros, and medianFee in your transactions script > console.log(` 💰 Average Prioritization Fee (including slots with zero fees): ${averageFeeIncludingZeros} micro-lamports.`); > console.log(` 💰 Average Prioritization Fee (excluding slots with zero fees): ${averageFeeExcludingZeros} micro-lamports.`); > console.log(` 💰 Median Prioritization Fee (excluding slots with zero fees): ${medianFee} micro-lamports.`); > } catch (error) { > console.error('Error fetching prioritization fees:', error); > } > }; > > getPrioritizationFees(); > ``` This script is a practical application designed for Solana blockchain developers to fetch and analyze recent prioritization fees, aiding in informed decision-making for transaction fee settings. Here's a breakdown of its components and how each part contributes to the overall functionality: Both versions do the same work; they differ only in how you open the connection and call the method. ### Import dependencies and setup * **Solana SDK**: The `@solana/kit` version imports `createSolanaRpc` and `address`; the `@solana/web3.js` version imports `Connection` and `PublicKey`. Both interact with the Solana blockchain and handle public key addresses. * **Dotenv**: Imports configuration for environment variables, allowing the script to securely access the Solana RPC endpoint specified in your environment. ### Utility function * **`getEnvVariable`**: A utility function that retrieves environment variables securely. It throws an error if the specified environment variable is not found, ensuring the script has all the necessary configurations. ### Main async `getPrioritizationFees` function * **Initialize the connection**: `@solana/kit` creates an RPC client with `createSolanaRpc`; `@solana/web3.js` creates a `new Connection`. Both use the RPC endpoint from your environment variables. * **Define the writable account**: Specifies the writable account(s) for which the prioritization fees will be fetched — `address(...)` in `@solana/kit`, `new PublicKey(...)` in `@solana/web3.js`. * **Fetch prioritization fees**: Retrieves an array of fee objects with recent fee data. In `@solana/kit` you call `getRecentPrioritizationFees([writableAccount]).send()` (RPC methods return a request object dispatched with `.send()`) and the `slot`/`prioritizationFee` fields come back as `bigint`, so coerce them with `Number()` before arithmetic. In `@solana/web3.js` you call `connection.getRecentPrioritizationFees({ lockedWritableAccounts: [...] })` and the fields are already `number`. * **Handle no data case**: Checks if no prioritization fee data was returned and logs a message indicating the absence of data. * **Data analysis**: * Sorts and analyzes the slots of the returned fee objects to determine the range of slots examined. * Calculates the average prioritization fee, including and excluding zeros, to provide insights into the overall fee trends. * Determines the median prioritization fee from the non-zero fees to give a middle-point fee estimate less influenced by outliers. ### Calculations and output * **Sort and calculate averages/median**: Performs numerical analysis on the fetched fee data to calculate average and median fees, offering a comprehensive view of fee trends. These metrics are essential for developers looking to optimize transaction costs based on current network conditions. * **Logging results**: This function outputs the analysis results, including the range of slots examined and the calculated average and median fees, providing actionable insights for setting transaction priority fees. ### Error handling * **Catch block**: This block captures and logs any errors encountered during the execution of the async function, ensuring that issues are reported for debugging. ### Run the script Now that we have the code and understand how it works, it’s time to run it. You can edit the writable account(s) you pass to the method: > [tab] @solana/kit > > ```typescript TypeScript theme={"system"} > const writableAccount = address("HJPjoWUrhoZzkNfRpHuieeFk9WcZWjwy6PBjZ81ngndJ"); > > const prioritizationFeeObjects = await rpc > .getRecentPrioritizationFees([writableAccount]) > .send(); > ``` > [tab] @solana/web3.js (classic) > > ```typescript TypeScript theme={"system"} > const publicKey = new PublicKey('HJPjoWUrhoZzkNfRpHuieeFk9WcZWjwy6PBjZ81ngndJ'); > > const config: Config = { > lockedWritableAccounts: [publicKey] > }; > ``` To run the code after you configured the parameters, run the following command in the terminal: ```shell Shell theme={"system"} ts-node main.ts ``` This will fetch the data and log the results in the console; it will look similar to this: ```shell Shell theme={"system"} $ ts-node main.ts Slots examined for priority fees: 150 Slots range examined from 255369379 to 255369528 ==================================================================================== 💰 Average Prioritization Fee (including slots with zero fees): 1053 micro-lamports. 💰 Average Prioritization Fee (excluding slots with zero fees): 4938 micro-lamports. 💰 Median Prioritization Fee (excluding slots with zero fees): 4261 micro-lamports. ``` Now you can use `averageFeeIncludingZeros`, `averageFeeExcludingZeros`, and `medianFee` in your transactions script to add fees dynamically. > [!NOTE] > > Learn how to add Priority Fees to your transactions by reading **[Solana: How to use Priority Fees to unlock faster transactions](/docs/solana-how-to-priority-fees-faster-transactions).** ## Conclusion This guide gets into the `getRecentPrioritizationFees` method on Solana, offering a comprehensive overview and practical examples of leveraging priority fees to enhance transaction processing efficiency. We've explored the concept of priority fees, set up a Solana node on Chainstack, and provided a detailed walkthrough of fetching and analyzing prioritization fee data using TypeScript. Through this process, we've gained insights into the importance of dynamically estimating fees to balance cost efficiency with transaction speed. This knowledge empowers developers to optimize their DApps on the Solana blockchain, ensuring transactions are processed timely without incurring unnecessary costs. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: getAccountInfo vs getMultipleAccounts Source: https://docs.chainstack.com/docs/solana-getaccountinfo-getmultipleaccounts Compare getAccountInfo and getMultipleAccounts Solana RPC methods for efficient account data retrieval with batching strategies on Chainstack nodes. **TLDR:** * `getAccountInfo` is straightforward and optimal when querying a single Solana account. * `getMultipleAccounts` retrieves multiple accounts in one request, reducing network overhead and improving scalability. * Both methods support various encoding options (`base58`, `base64`, `base64+zstd`, `jsonParsed`) to handle different data sizes and formats. * In bulk data scenarios like decentralized finance and game leaderboards, `getMultipleAccounts` is a clear winner for efficiency. ## Main article In the dynamic world of Solana blockchain development, efficient data retrieval is key. **`getAccountInfo`** and **`getMultipleAccounts`** are two methods that cater to this need, each with unique strengths and use cases. **`getAccountInfo`**, a fundamental method in the Solana ecosystem, is tailored for simplicity and precision. It's the go-to choice for obtaining information about a specific account using just the account's public key. This method shines in its straightforward approach, making it ideal for scenarios where details about individual accounts are the focus. Contrastingly, **`getMultipleAccounts`** steps in as a method for bulk data retrieval. Designed to handle the demands of batch processing, this method efficiently retrieves data for multiple accounts in a single request while it can still handle requests about a single account. This dual capability, combined with its time-efficient processing, positions `getMultipleAccounts` as a highly efficient tool, especially when dealing with complex, data-intensive tasks. Understanding these two methods' nuanced differences and optimal applications is crucial for Solana developers looking to optimize their performance and data-handling capabilities. ## Get a Solana RPC endpoint Before getting started, it's essential to have access to a [reliable Solana RPC endpoint](https://chainstack.com/build-better-with-solana/). Chainstack offers a convenient and efficient way to deploy and manage Solana nodes. Follow these detailed steps to sign up on Chainstack, deploy your node, and access your endpoint credentials: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Using `getAccountInfo` The **`getAccountInfo`** method is a fundamental feature of the Solana blockchain, designed for retrieving detailed information about a single account. This method is particularly valuable for scenarios where focused, individual account data is required, such as in account management tools or when tracking specific account activities. **Method Structure:** * **Endpoint**: It targets the `getAccountInfo` endpoint in the Solana JSON RPC API. * **Parameters**: * **Account Public Key**: The account's public key for which information is requested. * **Encoding Options**: Similar to `getMultipleAccounts`, it offers encoding options like `base58`, `base64`, `base64+zstd`, and `jsonParsed`. The choice of encoding affects how the account data is presented: * `base58`: Suitable for smaller account data, this encoding is slower but widely used for simplicity. * `base64`: Ideal for larger account data, providing base64 encoded data of any size. * `base64+zstd`: Efficient for large account data, compressing with Zstandard before encoding. * `jsonParsed`: Offers a more human-readable JSON format, especially useful when dealing with accounts associated with well-known programs. **cURL Example**: To retrieve account information using`curl`, the command would be structured as follows: ```bash Bash theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT -X POST -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getAccountInfo", "params": [ "Hr5GK3f5WqqLsr4TJ7cgVCnDaM5gY8QrD2GTPZ7K3Kpz", { "encoding": "base58" } ] } ' ``` **Response Structure**: The method responds with a JSON object, which includes: * **`jsonrpc`**: Denoting the version of the JSON RPC used. * **`result`**: The main body of the response. * **`context`**: Provides contextual information like the `slot` number. * **`value`**: An object containing detailed information about the account, such as: * **`lamports`**: The number of Lamports contained in the account. * **`owner`**: The public key of the program to which this account is assigned, in base-58 encoding. * **`data`**: The account's data is provided in the encoding format specified in the request. * **`executable`**: Indicates if the account contains a program. * **`rentEpoch`**: Specifies the rent epoch for the account. * **`space`**: The size of the account's data in bytes. The **`getAccountInfo`** method is a crucial tool for developers needing detailed, singular account insights on the Solana blockchain, offering a straightforward approach to accessing specific account data. ## Using `getMultipleAccounts` The **`getMultipleAccounts`** method is a robust feature of the Solana blockchain, allowing for efficient data retrieval from multiple accounts in a single network request. This method is particularly useful for applications that simultaneously process or display various accounts' information. **Method Structure:** * **Endpoint**: The method targets the `getMultipleAccounts` endpoint in the Solana JSON RPC API. * **Parameters**: * **Account Public Keys**: An array of public keys representing the accounts whose information you want to retrieve. * **Encoding Options**: The format in which account data should be returned. Choices include `base58`, `base64`, `base64+zstd`, and `jsonParsed`. Each encoding serves different purposes: * `base58`: A traditional encoding, but slower and limited to account data sizes of less than 129 bytes. * `base64`: Returns base64 encoded data for account data of any size. * `base64+zstd`: Compresses the account data using Zstandard compression before encoding it in base64. This is efficient for large data sizes. * `jsonParsed`: Tries to return account data in a more human-readable JSON format, leveraging program-specific parsers. **cURL Example**: Here's how you can use `curl` to call this method: ```bash Bash theme={"system"} curl --location 'CHAINSTACK_SOLANA_RPC' \ --header 'Content-Type: application/json' \ --data ' { "jsonrpc": "2.0", "id": 1, "method": "getMultipleAccounts", "params": [ [ "Hr5GK3f5WqqLsr4TJ7cgVCnDaM5gY8QrD2GTPZ7K3Kpz", "EAaijviraKWCWsVZtiZ5thhXoyoB5RP3HH1ZiLeLDcuv" ], { "encoding": "base58" } ] } ' ``` **Response Structure**: The response from this method is a JSON object, which includes: * **`jsonrpc`**: The JSON RPC version (usually "2.0"). * **`result`**: Contains the actual response data. * **`context`**: Provides context like the `slot` number. * **`value`**: An array of account information objects or `null` if an account doesn't exist. Each object includes: * **`lamports`**: The number of lamports in the account. * **`owner`**: The public key of the program this account is associated with, in base-58 encoding. * **`data`**: The account data, either as a base64 encoded string or a JSON object, depending on the requested encoding. * **`executable`**: A boolean indicating if the account contains a program. * **`rentEpoch`**: The epoch when the account will next owe rent. * **`space`**: The data size of the account in bytes. With **`getMultipleAccounts`**, developers can significantly streamline their data retrieval processes, particularly in applications where multiple account datasets are frequently accessed or monitored. ## Running `getMultipleAccounts` Method in JavaScript and Python To use the `getMultipleAccounts `in JavaScript or Python, you must first set up your environment with the necessary libraries. Below are the instructions and code snippets for both languages. ### Prerequisites Ensure you have Node.js installed for JavaScript and Python 3 for Python; you will also need a Solana Chainstack RPC endpoint. > [!NOTE] > > Learn how to setup Node.js projects by reading **[Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project)** ### JavaScript Setup and Usage 1. **Install the Solana Web3.js Library**: The Solana Web3.js library provides the necessary tools to interact with the Solana blockchain. To install it, use `npm`: ``` npm install @solana/web3.js ``` 2. **JavaScript Code**: ```javascript Javascript theme={"system"} const { Connection, PublicKey } = require('@solana/web3.js'); async function fetchMultipleAccountsInfo() { // Initialize connection to the Solana endpoint const connection = new Connection('YOUR_CHAINSTACK_ENDPOINT'); // Array of public keys for multiple accounts const accountPublicKeys = [ new PublicKey('Hr5GK3f5WqqLsr4TJ7cgVCnDaM5gY8QrD2GTPZ7K3Kpz'), new PublicKey('EAaijviraKWCWsVZtiZ5thhXoyoB5RP3HH1ZiLeLDcuv'), // Add more account public keys as needed ]; // Fetch information for multiple accounts const multipleAccountsInfo = await connection.getMultipleAccountsInfo(accountPublicKeys); // Process and display multiple accounts information console.log(multipleAccountsInfo); } fetchMultipleAccountsInfo().catch(error => { console.error('Error fetching multiple accounts info:', error); }); ``` This code initializes a connection to the Solana blockchain, fetches information for multiple accounts, and logs the results. Replace `'YOUR_CHAINSTACK_ENDPOINT'` with your actual endpoint. ### Python Setup and Usage 1. **Install the Solana Py Library**: The Solana Py library is a Python interface for Solana's JSON RPC API. Install it using pip: ``` pip install solana cachetools ``` 2. **Python Code**: ```python Python theme={"system"} from solana.rpc.api import Client from solders.pubkey import Pubkey # Initialize connection to the Solana cluster solana_client = Client("YOUR_CHAINSTACK_ENDPOINT") # Array of public keys for multiple accounts public_keys = [ Pubkey.from_string("Hr5GK3f5WqqLsr4TJ7cgVCnDaM5gY8QrD2GTPZ7K3Kpz"), Pubkey.from_string("EAaijviraKWCWsVZtiZ5thhXoyoB5RP3HH1ZiLeLDcuv"), # Convert additional public keys to PublicKey objects as needed ] # Fetch information for multiple accounts multiple_accounts_info = solana_client.get_multiple_accounts(public_keys) # Process and display multiple accounts' information print(multiple_accounts_info) ``` This Python snippet performs actions similar to the JavaScript code. Update `'YOUR_CHAINSTACK_ENDPOINT'` and the public keys with the correct values. > [!NOTE] > > `solders` is a high-performance Python toolkit for Solana, written in Rust. This library provides a robust set of solutions for working with core SDK functionalities such as keypairs, public keys (pubkeys), signing, and serializing transactions. ## Advantages of `getMultipleAccounts` over `getAccountInfo` in Solana applications In the dynamic environment of blockchain technology, particularly on the Solana network, efficiently managing data retrieval is crucial. The `getMultipleAccounts` method offers significant advantages over the traditional `getAccountInfo` method, especially in scenarios requiring bulk data processing. 1. Reduced Network Overhead: By allowing the retrieval of information from multiple accounts in a single network request, `getMultipleAccounts` substantially reduces the network overhead. This is particularly advantageous in decentralized applications (DApps) where frequent data synchronization is essential, such as real-time asset tracking systems or decentralized finance (DeFi) platforms. 2. Optimized Batch Processing: Developers managing applications that require concurrent monitoring or updating of numerous accounts find `getMultipleAccounts` invaluable. For instance, fetching the states of multiple player accounts at once for leaderboard updates in a blockchain-based gaming platform becomes far more efficient. 3. Enhanced Scalability: As applications grow to handle hundreds or thousands of accounts, `getMultipleAccounts` facilitates scalability. This is evident in large-scale wallet services or exchange platforms, where bulk account data retrieval is routine. 4. Improved Developer Experience: This method simplifies implementing code that interacts with multiple accounts, streamlining the development process. This benefit shines in complex applications like analytics tools or portfolio trackers, where developers must regularly aggregate and process data from many accounts. ## Conclusion In Solana blockchain development, the choice between **`getAccountInfo`** and **`getMultipleAccounts`** transcends mere technical preference; it's a strategic decision that aligns with the specific demands of your application. While **`getAccountInfo`** excels in scenarios requiring detailed insights into individual accounts, **`getMultipleAccounts`** emerges as a powerful ally for applications dealing with bulk data operations, offering a blend of efficiency and scalability. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: getTokenLargestAccounts for SPL holders Source: https://docs.chainstack.com/docs/solana-gettokenlargestaccounts-rpc-method Use getTokenLargestAccounts to view SPL token holder distribution, whale wallets, and concentration metrics through a Chainstack Solana RPC node. > [!WARNING] > > `getTokenLargestAccounts` is available on [Dedicated Nodes](/docs/dedicated-node) only. **TLDR:** * `getTokenLargestAccounts` retrieves the top 20 largest SPL token accounts in one request. * Useful for distribution analysis, showing holders’ addresses and token balances. * Covered examples in cURL, JavaScript, and Python, including a real-life data visualization scenario. * Ideal for developers, analysts, or anyone needing quick insights into token distribution on Solana. ## Main article Understanding the distribution and dynamics of token holdings is crucial for developers, analysts, and investors alike. Within the Solana ecosystem, a high-performance blockchain designed for fast and scalable decentralized apps and marketplaces, the `getTokenLargestAccounts` method emerges as a pivotal tool for gleaning insights into the distribution of SPL (Solana Program Library) Tokens. The `getTokenLargestAccounts` RPC method offers a window into the Solana blockchain, allowing users to retrieve information about the 20 largest accounts for a specific SPL Token in one go, making it one of the most efficient methods for token distribution analysis. This guide is designed to walk you through the essentials of the `getTokenLargestAccounts` method, from understanding its parameters and responses to implementing basic examples in JavaScript. Python, and exploring real-life use cases. Let’s start with the basics first. ## Get a Solana RPC endpoint Before diving into the practical applications of the `getTokenLargestAccounts` method, you must ensure access to a [reliable Solana RPC endpoint](https://chainstack.com/build-better-with-solana/). Chainstack provides a seamless and effective platform to deploy and manage Solana nodes. Here's how you get started: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## `getTokenLargestAccounts` RPC method The `getTokenLargestAccounts` method stands as an integral part of the Solana blockchain's suite of RPC calls, designed specifically for identifying the major token holders of any given SPL Token. This method is instrumental for those seeking to analyze token distribution, providing a clear view of top token accounts at a glance. **Method structure:** * **Endpoint**: It directly targets the `getTokenLargestAccounts` endpoint within the Solana JSON RPC API, facilitating an efficient query process for retrieving SPL Token holder information. * **Parameters**: * **Token Mint public key**: This parameter requires the public key of the token's mint to identify the specific SPL Token. The public key must be provided in a base-58 encoded string format. * **Config (optional)**: An optional configuration object that can include: * **`finalized`**: Targets the latest block recognized as finalized by a supermajority, offering the highest level of data reliability. * **`confirmed`**: Queries the most recent block voted on by a supermajority, balancing speed with a reasonable level of certainty. * **`processed`**: Provides data from the most recent block in the node's ledger, prioritizing real-time information over completeness. **cURL example**: A simple way to test `getTokenLargestAccounts` is by using `cURL`. Run the following command to retrieve the top 20 holders of the [Ray token](https://solscan.io/token/4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R) at the `finalized` commitment: ```shell Shell theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT -X POST -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getTokenLargestAccounts", "params": [ "4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R", { "commitment": "finalized" } ] } ' ``` **Response structure**: The response from this method is a structured JSON object containing: * **`jsonrpc`**: Indicates the JSON RPC version used for the query. * **`result`**: The core component of the response, which includes: * **`value`**: An array of objects, each representing one of the top 20 largest token accounts, with fields such as: * **`address`**: The base58 encoded string of the token account's address. * **`amount`**: The token balance of the account, represented as a string of u64, not adjusted for decimals. * **`decimals`**: The number of decimal places for the token, aiding in accurately representing the amount. * **`uiAmountString`**: The token balance adjusted for decimals, providing a more user-friendly representation of the amount. The response will look similar to the following example: ```shell Shell theme={"system"} "jsonrpc": "2.0", "result": { "context": { "apiVersion": "1.17.17", "slot": 245539306 }, "value": [ { "address": "DmKR61BQk5zJTNCK9rrt8fM8HrDH6DSdE3Rt7sXKoAKb", "amount": "138590933138157", "decimals": 6, "uiAmount": 138590933.138157, "uiAmountString": "138590933.138157" }, { "address": "fArUAncZwVbMimiWv5cPUfFsapdLd8QMXZE4VXqFagR", "amount": "126223990000046", "decimals": 6, "uiAmount": 126223990.000046, "uiAmountString": "126223990.000046" }, . . . ``` ## Integrate `getTokenLargestAccounts` into JavaScript and Python projects `cURL` is an excellent tool for initial tests, but DApps demand more sophisticated frameworks. JavaScript and Python, two of the most widely used languages in blockchain development, offer the robustness and flexibility required for such tasks. This section showcases the development workflow by guiding you through the setup process and providing practical examples in both languages. ### Prerequisites Before proceeding, ensure you have Node.js installed for JavaScript development and Python 3 for Python. A Solana RPC endpoint from Chainstack is also necessary for the tasks ahead. > [!NOTE] > > For a comprehensive tutorial on setting up Node.js projects, visit **[Web3 Node.js: From Zero to a Full-fledged Project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project)**. ### JavaScript setup and usage 1. Install the Solana Web3.js library: Interacting with the Solana blockchain requires the Solana Web3.js library. Install it using `npm`: ```shell Shell theme={"system"} npm install @solana/web3.js ``` 2. JavaScript example: ```javascript Javascript theme={"system"} const { Connection, PublicKey } = require('@solana/web3.js'); async function fetchTokenLargestAccounts() { const connection = new Connection('YOUR_CHAINSTACK_ENDPOINT'); const tokenMintPublicKey = new PublicKey('4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R'); const largestAccounts = await connection.getTokenLargestAccounts(tokenMintPublicKey, 'finalized'); console.log(largestAccounts); } fetchTokenLargestAccounts().catch(error => console.error('Error fetching token largest accounts:', error)); ``` Remember to replace `'YOUR_CHAINSTACK_ENDPOINT'` with your specific Solana endpoint. Executing this script will yield results similar to those obtained through `cURL` but in a format that's easier to process and analyze. ### Python setup and usage 1. Install the Solana.py library: To interact with Solana's JSON RPC API in Python, install the necessary library using `pip`: > [!NOTE] > > It is highly recommended to create a new Python virtual environment for this project; create one with `python3 -m venv solana-project`, then activate it with `source solana-project/bin/activate`. ```shell Shell theme={"system"} pip install solana cachetools ``` 2. Python example: ```python Python theme={"system"} from solana.rpc.api import Client from solders.pubkey import Pubkey solana_client = Client("YOUR_CHAINSTACK_ENDPOINT") token_mint_public_key = Pubkey.from_string("4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R") largest_accounts = solana_client.get_token_largest_accounts(token_mint_public_key) # Display raw response #print(largest_accounts) # Display the information in a nicer format print("Largest SPL Token Accounts:\n") # Accessing slot and api version slot = largest_accounts.context.slot api_version = getattr(largest_accounts.context, 'api_version', 'Unknown') # Print the data in a nice format print(f"Slot: {slot}\nAPI Version: {api_version}\n") for account in largest_accounts.value: address = account.address ui_amount = account.amount.ui_amount decimals = account.amount.decimals amount = account.amount.amount print(f"Address: {address}\nAmount: {ui_amount} (Decimals: {decimals}, Raw: {amount})\n") ``` After updating `'YOUR_CHAINSTACK_ENDPOINT'`, this script includes some processing to display the result in a more friendly way. By following these examples, you can effectively employ the `getTokenLargestAccounts` method in both JavaScript and Python to simplify the integration of this functionality into blockchain applications or data analyses significantly. ## `getTokenLargestAccounts` use cases Having explored the fundamentals, setup, and theoretical application of the `getTokenLargestAccounts` method within the Solana ecosystem, we now pivot to a practical, real-life use case that demonstrates the method's utility in action. This section will guide you through a Python project designed to fetch and visualize the distribution of SPL Token holdings among the largest accounts. This real-world application bridges the gap between theoretical knowledge and practical implementation, showcasing how developers can utilize Solana's blockchain data to generate insightful visualizations. ### Project overview This project aims to visually represent the token balances held by the top 20 largest accounts of a specific SPL Token. By doing so, analysts, developers, and investors can better understand token distribution. Visualization plays a key role in data analysis, allowing for quick identification of patterns, outliers, and concentrations of token holdings. ### Script structure The provided Python script is structured into several key functions, each designed to fulfill a specific role in the process of data fetching and visualization: 1. Configuration: * Sets up the Solana RPC endpoint and the public key of the SPL Token, whose largest accounts we wish to analyze. This project uses the Raydium token as an example. 2. `fetch_largest_accounts` function: * This function communicates with the Solana blockchain via the `get_token_largest_accounts` method of the Solana Python client. It retrieves data on the 20 largest accounts holding the specified SPL Token, focusing on the finalized state for reliability. 3. `plot_token_balances` function: * Utilizes the `matplotlib` library to plot the token balances of the largest accounts. It creates a bar chart where each bar represents the token balance of an account, providing a visual comparison across the top holders. Account addresses are used as labels on the x-axis, with their corresponding token balances displayed on the y-axis. The y-axis labels are formatted for readability, showing large numbers in a concise format. 4. `main` function: * Orchestrates the process by initializing the Solana client with the specified RPC endpoint, converting the token mint public key string into a usable object, and then calling the `fetch_largest_accounts` function. It extracts the necessary information from the response (addresses and UI amounts) and passes it to the `plot_token_balances` function for visualization. ### Script code Create a new Python project; use the same virtual environment created for the previous example. If you don’t have it installed yet, install `matplotlib` with: ```shell Shell theme={"system"} pip install matplotlib ``` Then create a new file named `[main.py](http://main.py)` and paste the following code: ```python Python theme={"system"} from solana.rpc.api import Client from solders.pubkey import Pubkey import matplotlib.pyplot as plt import matplotlib.ticker as ticker # Configuration SOLANA_RPC_ENDPOINT = "YOUR_CHAINSTACK_ENDPOINT" TOKEN_MINT_PUBLIC_KEY = "4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R" # Ray Token in this example def fetch_largest_accounts(solana_client, token_mint_public_key): """ Fetches the 20 largest accounts for a specific SPL Token. """ print("Fetching the 20 largest SPL Token accounts...") largest_accounts = solana_client.get_token_largest_accounts(token_mint_public_key, 'finalized') print("Data fetched successfully.") return largest_accounts def plot_token_balances(addresses, ui_amounts): """ Plots the token balances for the largest accounts. """ print("Plotting token balances...") x_pos = range(len(addresses)) plt.figure(figsize=(10, 8)) plt.bar(x_pos, ui_amounts, color='skyblue') plt.xticks(x_pos, addresses, rotation=90) formatter = ticker.FuncFormatter(lambda x, pos: '{:,.0f}'.format(x)) plt.gca().yaxis.set_major_formatter(formatter) plt.xlabel('Account Addresses') plt.ylabel('Token Balance') plt.title('Top 20 SPL Token Account Balances') plt.tight_layout() plt.show() print("Plotting completed.") def main(solana_rpc_endpoint, token_mint_public_key_str): solana_client = Client(solana_rpc_endpoint) token_mint_public_key = Pubkey.from_string(token_mint_public_key_str) largest_accounts = fetch_largest_accounts(solana_client, token_mint_public_key) addresses = [str(account.address) for account in largest_accounts.value] ui_amounts = [account.amount.ui_amount for account in largest_accounts.value] plot_token_balances(addresses, ui_amounts) if __name__ == "__main__": print("Starting the SPL Token analysis script...") main(SOLANA_RPC_ENDPOINT, TOKEN_MINT_PUBLIC_KEY) print("Script execution completed.") ``` Make sure you’ve set your Solana endpoint in `SOLANA_RPC_ENDPOINT` before you run the script with: ```bash Bash theme={"system"} python3 main.py ``` ## Conclusion This comprehensive guide has taken you through the journey of understanding and using the `getTokenLargestAccounts` method within the Solana ecosystem, a tool of paramount importance for developers, investors, and analysts alike. From setting up a Solana RPC endpoint with Chainstack to diving deep into the method's structure and integrating it into development projects to a hands-on, real-life application, we've explored the multifaceted utility of this method in unlocking insights into SPL Token distribution. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana glossary Source: https://docs.chainstack.com/docs/solana-glossary Key Solana terms and concepts including associated token accounts, program derived addresses, compute units, priority fees, and transaction structure. ## Associated Token Account In the Solana ecosystem, tokens are not stored directly in wallets or accounts. Instead, each token mint (the token type or contract) has its own [Associated Token Accounts](https://spl.solana.com/associated-token-account) holding actual token balances. Using a well-defined process, these associated token accounts are derived from the wallet address and the token mint address. ## Compute Units Compute units on Solana are a fundamental metric used to quantify the consumption of computational resources by transactions or programs on the Solana blockchain. Every operation a program performs on the blockchain—from simple arithmetic operations to more complex instructions like cryptographic verifications—consumes a certain amount of computational power. Solana quantifies this consumption in terms of compute units. ## Durable Transaction Nonces A [feature within the Solana blockchain](https://docs.solanalabs.com/cli/examples/durable-nonce) that allows transactions to use a persistent, rather than short-lived, reference to a blockhash. This mechanism is designed to facilitate transactions in scenarios where the typical lifespan of a blockhash might not suffice, enhancing transaction reliability and predictability. ## lamport A lamport on Solana represents the smallest denomination of the native cryptocurrency, SOL. Named in honor of Leslie Lamport, a pioneer in distributed systems, one lamport is equivalent to one-billionth of a SOL (0.000000001 SOL). This division allows for fine-grained transactions and operations on the blockchain, ensuring that users can transact with very small amounts of SOL, which is particularly useful for fees, micropayments, and smart contract operations. The use of lamports facilitates precise handling of cryptocurrency on the network, allowing Solana to maintain its high throughput and efficiency while catering to a wide range of transaction sizes. ## Micro-Lamports Within the context of Solana's [compute budget](/docs/solana-compute-budget), micro-lamports are used to calculate transaction prioritization fees. These fees are essential for managing the network's resources by prioritizing transactions based on the computational resources they consume. A micro-lamport is an even smaller unit than a lamport, used specifically in the internal calculations and mechanisms that determine how transactions are prioritized in the processing queue. ## Nonce Account A special type of Solana account that stores a nonce, a unique number used once to signify the current or next blockhash for transactions. Nonce accounts are critical for executing durable transaction nonces, ensuring that transactions can be validated properly over longer periods. ## Nonce Authority An account that has the permission to manage a nonce account, including updating its nonce value and withdrawing funds. Authority can be transferred, allowing for flexible control structures over nonce accounts. ## Rent-Exempt In Solana, every account must hold at least the rent-exempt minimum for its size, `(128 + data size in bytes) × lamports per byte`. The balance pays for the account's storage on every validator and is refunded in full when the account is closed. See [Solana: Rent reduction and reclaiming excess rent](/docs/solana-rent-reduction-reclaim-excess-rent) for the current rate. ## Solana Program A deployed and executed application or smart contract on the Solana blockchain. Programs are the building blocks of Solana's [decentralized applications](/docs/web3-language-and-acronyms#decentralized-application-dapp) (DApps), enabling various functionalities from simple token swaps to complex decentralized finance (DeFi) protocols. # Solana: How to build actions and blinks Source: https://docs.chainstack.com/docs/solana-how-to-build-actions-and-blinks Build Solana Actions and Blinks with Next.js and Chainstack. Create transaction APIs, Meteora Dynamic Vault integration, and shareable blockchain interactions. [Building with Solana Actions & Blinks](https://www.youtube.com/embed/PYdO2AJ7wvI) **TLDR** * Solana Actions and Blinks enable user-friendly transaction flows by exposing an API that wallets and social apps can preview, sign, and submit. * This example uses Next.js to build an Actions endpoint that deposits/withdraws SOL from Meteora’s Dynamic Vaults (Mercurial SDK). * A GET handler returns JSON metadata for Blinks (buttons, inputs, and icons), while a POST handler composes transactions based on user inputs. * Linking your endpoint with dial.to or x.com lets wallets automatically detect, render, and process these transactions in a single click. ## Main article > [!NOTE] > > ### Actions and Blinks under continuous development > > As of this writing, for Blinks to be unfurled (properly displayed), they must be registered with [dial.to](http://dial.to/) and posted on [x.com](http://x.com/) . Additionally, please be aware that Actions and Blinks have been recently introduced and are under continuous development. ## Introduction Solana Actions is an API that returns transactions on the Solana blockchain for previewing, signing, and sending across various contexts, including websites. By linking to an Actions endpoint, users can perform blockchain transactions without leaving the site. The second key component is Blinks. Blinks convert any Solana Action (a link to an endpoint) into a shareable, metadata-rich link. Action-aware clients (e.g., Solana wallets, the Dialect browser extension) detect these links and enhance them with features like icons, buttons, and input text boxes. To put it simple, the Actions act as an API server, while Blinks on a client side render (or unfurl) links to an Actions endpoint into a user-friendly UI. ## Overview The tutorial is based on the Solana [documentation](https://solana.com/docs/advanced/actions) and [guides](https://www.youtube.com/watch?v=kCht01Ycif0) devoted to the Actions and Blinks. The Solana documentation provides an in-depth description of the Actions specification. We will build our own Actions server that handles GET and POST requests, returns metadata for displaying Blinks, and provides a transaction for signing. In our example, we will implement adding and removing funds from one of the [Mecurical Dynamic Vaults](https://app.meteora.ag/vaults) (SOL). Users will have an option to enter how many SOL they want to either deposit or withdraw ([Vault SDK](https://github.com/mercurial-finance/vault-sdk)). > [!NOTE] > > ### Meteora Dynamic Vaults > > Meteora Dynamic Vaults are dynamic yield vaults that rebalance every minute across lending platforms to find the best possible yield while prioritizing keeping user funds as accessible as possible. ## Setup We will use Next.js as a framework for API development. Run the command below with the default parameters. ``` npx create-next-app solana-actions-tutorial cd solana-actions-tutorial ``` Navigate to the app folder and install the required packages. ``` npm install @solana/web3.js @solana/actions @mercurial-finance/vault-sdk@0.5.3 @types/bn.js encoding ``` Create `.env.local` file and add your Chainstack endpoint there. ``` CHAINSTACK_ENDPOINT=YOUR_RPC_NODE ``` All development and testing will be conducted on the devnet, where Mercurial Vaults are also deployed. Please switch your wallet to the devnet and request SOL [here](https://solfate.com/faucet). [The GitHub repository](https://github.com/chainstacklabs/solana-actions-meteora-vault/tree/master) for the tutorial. ## Code walkthrough Let’s create `route.ts` inside `src/app/api/actions/vault` of the app. This file will contain all the handlers compliant with [the Actions specification](https://solana.com/docs/advanced/actions#specification). ### Import dependencies Import the tutorial dependencies. We use the `actions` package for proper reading and composing requests and responses. Other imports are required for interacting with Mercurial vaults. ```typescript TypeScript theme={"system"} import { ActionGetResponse, ActionPostRequest, ActionPostResponse, ACTIONS_CORS_HEADERS, createPostResponse, } from "@solana/actions"; import { clusterApiUrl, Connection, PublicKey, Transaction } from "@solana/web3.js"; import { StaticTokenListResolutionStrategy, TokenInfo, } from "@solana/spl-token-registry"; import VaultImpl from "@mercurial-finance/vault-sdk"; import { BN } from "bn.js"; ``` ### GET request and response The endpoint must respond with JSON that contains [metadata](https://solana.com/docs/advanced/actions#get-request) to render a Blink. Adding `ActionGetResponse` helps us to see which fields should be presented in a body. The fields `icon`, `description`, and `title` are self-explanatory. Note that `icon` must be an absolute URL. Other fields are used for defining buttons and inputs from the user. The `label` field is used for a single button without inputs, while the `actions` in `links` are used to describe buttons with inputs. Please see the example below, where we define two buttons with their respective inputs and query parameters ```typescript TypeScript theme={"system"} export const GET = async (req: Request) => { const baseURL = new URL(req.url).origin; const pathActions = new URL(req.url).pathname; const payload: ActionGetResponse = { icon: new URL("/chainstack_square.png", baseURL).toString(), label: "Meteora", description: "Manage your SOL Vault liquidity", title: "Meteora Dynamic Vault Actions", links: { actions: [ { type: "transaction", label: "Deposit SOL", href: `${pathActions}?action=deposit&amount={amount}`, parameters: [ { name: "amount", label: "Amount", }, ], }, { type: "transaction", label: "Withdraw SOL", href: `${pathActions}?action=withdraw&amount={amount}`, parameters: [ { name: "amount", label: "Amount", }, ], }, ], }, }; return Response.json(payload, { headers: ACTIONS_CORS_HEADERS, }); }; export const OPTIONS = GET; ``` ### POST request and response The purpose of the POST handler is to parse parameters sent by a client and compose a transaction for signing. In our case, we need to understand whether the user wants to deposit or withdraw funds, and the desired amount. First, we need to verify that the user's wallet is valid, which is provided in the request body. If there is an error, include an error description in the `message` key of the response so that a client can properly parse this message (see the `ActionError` interface). For now, let's simplify the parsing of parameters and skip any additional checks. ```typescript TypeScript theme={"system"} export const POST = async (req: Request) => { try { const body: ActionPostRequest = await req.json(); // Ensure the account parameter is valid let userAccount: PublicKey; try { userAccount = new PublicKey(body.account); } catch (err) { return Response.json( { message: "Invalid account provided" }, { status: 400, headers: ACTIONS_CORS_HEADERS, }, ); } const action = new URL(req.url).searchParams.get("action"); const amount = parseFloat(new URL(req.url).searchParams.get("amount") ?? ""); ``` Next, we need to obtain an instance of the SOL vault using the Mercurial SDK. This instance is required to generate the proper instructions. Use your Chainstack endpoint from `.env.local` to have a stable and fast connection to Solana. ```typescript TypeScript theme={"system"} // Retrieve SOL token information const tokenMap = new StaticTokenListResolutionStrategy().resolve(); const SOL_TOKEN_INFO = tokenMap.find(token => token.symbol === "SOL") as TokenInfo; // Get a Vault Implementation instance const connection = new Connection(process.env.CHAINSTACK_ENDPOINT! || clusterApiUrl("devnet")); const vault: VaultImpl = await VaultImpl.create(connection, SOL_TOKEN_INFO); ``` Depending on the user’s choice, get a transaction for either depositing or withdrawing funds. Addiotionally, we need to calculate a so called virtual price. It will help us define how many SOL we will get for a specific amount of LP tokens. ```typescript TypeScript theme={"system"} // Calculate virtual price using the vault's unlocked amount and lp supply const vaultUnlockedAmount = (await vault.getWithdrawableAmount()).toNumber(); const virtualPrice = (vaultUnlockedAmount / vault.lpSupply.toNumber()) || 0; // Create a transaction based on the action let instruction!: Transaction; if (action === "deposit") { instruction = await vault.deposit( userAccount, new BN(amount * 10 ** SOL_TOKEN_INFO.decimals), ); } else if (action === "withdraw") { instruction = await vault.withdraw( userAccount, new BN((amount / virtualPrice) * 10 ** SOL_TOKEN_INFO.decimals), ); } ``` It’s time to create a transaction. We already have instructions from the vault. Let’s add them to the transaction. A user account will pay fee for it. Additionally, we need to specify the latest blockhash - this part may not be obligatory, however there may be unexpected behaviour from the Phantom wallet. To compose a response, we are using a utility provided by the `actions` package. It does some work for us. All we need to do is to pass the prepared transaction and specify a message that a user will see after a transaction is confirmed successfully. > [!NOTE] > > ### Only one transaction > > At the time of writing, only one transaction can be returned. It can be either a Legacy transaction or a Versioned transaction. ```typescript TypeScript theme={"system"} const transaction = new Transaction(); transaction.add(instruction); transaction.feePayer = userAccount; // It's not required, a client will replace it with the latest blockhash // However, Phantom wallet doesn't send a transaction without a blockhash transaction.recentBlockhash = ( await connection.getLatestBlockhash({ commitment: "finalized" }) ).blockhash; // Create a POST response const payload: ActionPostResponse = await createPostResponse({ fields: { transaction: transaction, message: action == "deposit" ? `Deposit ${amount} SOL` : `Withdraw ${amount} SOL`, }, }); return Response.json(payload, { headers: ACTIONS_CORS_HEADERS, }); } catch (err) { return Response.json( { message: "Unknown error occurred" }, { status: 500, headers: ACTIONS_CORS_HEADERS, }, ); } }; ``` ### Parameters validation Let's return to the validation process. To keep the code streamlined, we have placed the validation logic into a separate function. In this function, we validate parameters and return relevant error messages for any invalid values. We will invoke this function in the POST handler and throw an exception if any error messages are returned. ```jsx jsx theme={"system"} function validateQueryParams(requestURL: URL): { action: string; amount: number; errorMessage?: string; } { // Validate the action and amount parameters const action = requestURL.searchParams.get("action"); const amountParam = requestURL.searchParams.get("amount"); // Ensure the action and amount parameters are present if (!action || !amountParam) { return { action: "", amount: 0, errorMessage: "Missing action or amount parameter", }; } // Ensure the action parameter is valid if (action !== "deposit" && action !== "withdraw") { return { action: "", amount: 0, errorMessage: "Invalid action parameter", }; } // Ensure the amount parameter is valid const amount = parseFloat(amountParam); if (isNaN(amount) || amount <= 0) { return { action: "", amount: 0, errorMessage: `Invalid amount for ${action}`, }; } return { action, amount, }; } ``` ### Putting it all together The complete `route.ts` includes both GET and POST handlers, along with the validation of the POST parameters. ```typescript TypeScript theme={"system"} import { ActionGetResponse, ActionPostRequest, ActionPostResponse, ACTIONS_CORS_HEADERS, createPostResponse, } from "@solana/actions"; import { clusterApiUrl, Connection, PublicKey, Transaction } from "@solana/web3.js"; import { StaticTokenListResolutionStrategy, TokenInfo, } from "@solana/spl-token-registry"; import VaultImpl from "@mercurial-finance/vault-sdk"; import { BN } from "bn.js"; export const GET = async (req: Request) => { const baseURL = new URL(req.url).origin; const pathActions = new URL(req.url).pathname; const payload: ActionGetResponse = { icon: new URL("/chainstack_square.png", baseURL).toString(), label: "Meteora", description: "Manage your SOL Vault liquidity", title: "Meteora Dynamic Vault Actions", links: { actions: [ { type: "transaction", label: "Deposit SOL", href: `${pathActions}?action=deposit&amount={amount}`, parameters: [ { name: "amount", label: "Amount", }, ], }, { type: "transaction", label: "Withdraw SOL", href: `${pathActions}?action=withdraw&amount={amount}`, parameters: [ { name: "amount", label: "Amount", }, ], }, ], }, }; return Response.json(payload, { headers: ACTIONS_CORS_HEADERS, }); }; export const OPTIONS = GET; export const POST = async (req: Request) => { try { const body: ActionPostRequest = await req.json(); // Ensure the account parameter is valid let userAccount: PublicKey; try { userAccount = new PublicKey(body.account); } catch (err) { return Response.json( { message: "Invalid account provided" }, { status: 400, headers: ACTIONS_CORS_HEADERS, }, ); } // Validate the action and amount parameters const { action, amount, errorMessage } = validateQueryParams( new URL(req.url), ); if (errorMessage) { return Response.json( { message: errorMessage }, { status: 400, headers: ACTIONS_CORS_HEADERS, }, ); } // Retrieve SOL token information const tokenMap = new StaticTokenListResolutionStrategy().resolve(); const SOL_TOKEN_INFO = tokenMap.find(token => token.symbol === "SOL") as TokenInfo; // Get a Vault Implementation instance const connection = new Connection(process.env.CHAINSTACK_ENDPOINT! || clusterApiUrl("devnet")); const vault: VaultImpl = await VaultImpl.create(connection, SOL_TOKEN_INFO); // Calculate virtual price using the vault's unlocked amount and lp supply const vaultUnlockedAmount = (await vault.getWithdrawableAmount()).toNumber(); const virtualPrice = (vaultUnlockedAmount / vault.lpSupply.toNumber()) || 0; // Create a transaction based on the action let instruction!: Transaction; if (action === "deposit") { instruction = await vault.deposit( userAccount, new BN(amount * 10 ** SOL_TOKEN_INFO.decimals), ); } else if (action === "withdraw") { instruction = await vault.withdraw( userAccount, new BN((amount / virtualPrice) * 10 ** SOL_TOKEN_INFO.decimals), ); } const transaction = new Transaction(); transaction.add(instruction); transaction.feePayer = userAccount; // It's not required, a client will replace it with the latest blockhash // However, Phantom wallet doesn't send a transaction without a blockhash transaction.recentBlockhash = ( await connection.getLatestBlockhash({ commitment: "finalized" }) ).blockhash; // Create a POST response const payload: ActionPostResponse = await createPostResponse({ fields: { transaction: transaction, message: action == "deposit" ? `Deposit ${amount} SOL` : `Withdraw ${amount} SOL`, }, }); return Response.json(payload, { headers: ACTIONS_CORS_HEADERS, }); } catch (err) { return Response.json( { message: "Unknown error occurred" }, { status: 500, headers: ACTIONS_CORS_HEADERS, }, ); } }; function validateQueryParams(requestURL: URL): { action: string; amount: number; errorMessage?: string; } { // Validate the action and amount parameters const action = requestURL.searchParams.get("action"); const amountParam = requestURL.searchParams.get("amount"); // Ensure the action and amount parameters are present if (!action || !amountParam) { return { action: "", amount: 0, errorMessage: "Missing action or amount parameter", }; } // Ensure the action parameter is valid if (action !== "deposit" && action !== "withdraw") { return { action: "", amount: 0, errorMessage: "Invalid action parameter", }; } // Ensure the amount parameter is valid const amount = parseFloat(amountParam); if (isNaN(amount) || amount <= 0) { return { action: "", amount: 0, errorMessage: `Invalid amount for ${action}`, }; } return { action, amount, }; } ``` ## Testing To test the Actions, let’s first run the Next.js development server: ```jsx jsx theme={"system"} npm run dev ``` The Actions are now live on `localhost:3000`. To test the GET request, send it to `localhost:3000/api/actions/vault`. You can simply open this URL in your browser to see the response. To test how unfurling works without registering the app, append the endpoint URL to `dial.to/?action=solana-action:`. For example, `dial.to/?action=solana-action:http://localhost:3000/api/actions/vault`. Open that link in your browser to see a nicely rendered Blink. Congratulations! You can also test your Actions server with the help of [Inspector](https://www.blinks.xyz/inspector). > [!NOTE] > > ### Browser settings > > In case you have an empty page, please check your browser's security settings. For instance, Brave browser has Shields enabled by default. To test the local Blink, please disable Shields. > [!NOTE] > > ### Devnet issues > > Please note that there may be issues with the transaction status displayed on the [dial.to](http://dial.to/) page when using the Devnet. The page might indicate that a transaction is not confirmed, even though it has been confirmed on the blockchain. ## Conclusion In this tutorial, we explored the implementation of Solana Actions and Blinks to interact with Meteora’s Dynamic Vaults. With this feature, users can seamlessly perform blockchain transactions directly from social media or other web applications. ### About author > [card] Anton Sauchyk > > ![Anton Sauchyk](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1817926677730664448/3nNn0T2p_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1d13d3584862f1be3a84fa3f8e38fea7) > > Developer Advocate @ Chainstack > \ > Multiple years of software development and Web3 expertise > > [https://github.com/smypmsa](https://github.com/smypmsa) > > [https://x.com/sensuniama](https://x.com/sensuniama) > > [https://www.linkedin.com/in/anton-sauchyk/](https://www.linkedin.com/in/anton-sauchyk/) # Solana: How to handle the transaction expiry error Source: https://docs.chainstack.com/docs/solana-how-to-handle-the-transaction-expiry-error Diagnose and fix Solana transaction expiration errors with blockhash refresh strategies, retry logic, and confirmation polling on Chainstack nodes. > **Part of the [How to land transactions on Solana](/docs/solana-how-to-land-transactions) guide.** **TLDR:** * The `TransactionExpiredBlockheightExceededError` occurs when Solana transactions aren’t confirmed before a specified block window (the `lastValidBlockHeight`). * Network congestion, high throughput, and delayed transaction propagation can all trigger this error. * Priority fees help prioritize urgent transactions, while robust retry logic ensures expired transactions get resubmitted with updated parameters. * Using multiple geographically distributed RPC endpoints reduces latency and improves transaction success rates. ## Main article Solana stands out in the blockchain ecosystem as a platform designed for speed and efficiency. Known for its impressive capabilities, Solana supports high throughput and low latency, characteristics that are critical for applications requiring rapid transaction processing and scalability. This performance is achieved through innovative technologies like Proof of History (PoH) and a highly optimized network protocol, empowering developers to build fast and scalable decentralized applications. However, the sophisticated architecture of Solana also introduces unique challenges, especially when it comes to managing transactions. Like any high-performance blockchain, Solana requires that transactions be handled precisely to prevent errors that could disrupt user experiences or application functionality. One common issue developers encounter is the `TransactionExpiredBlockheightExceededError`, which occurs when transactions are not confirmed within a specific timeframe. This has been happening more often since the chain is busy and congested. Properly addressing such errors is crucial for maintaining smooth operations on the Solana network, ensuring that applications remain reliable and responsive under various network conditions. In the following sections, we will learn more about this error, exploring its causes and detailing strategies to mitigate its occurrence through priority fees and effective retry logic. ## What is a TransactionExpiredBlockheightExceededError? The `TransactionExpiredBlockheightExceededError` in Solana indicates a specific issue where a transaction fails to be confirmed within a designated window. Every transaction on the Solana blockchain can specify a `lastValidBlockHeight`, which sets the latest block by which the transaction must be confirmed. If the network processes this block without confirming the transaction, it will be automatically dropped, triggering this error. This mechanism ensures that transactions do not remain pending indefinitely and helps maintain the integrity and fluidity of the blockchain operations. In Solana, the concept of `lastValidBlockHeight` is integral to how transactions are managed and processed on the blockchain. Here’s why this mechanism works as it does and the benefits it brings: ### What is the role of recent blockhashes In Solana, each transaction must reference a recent blockhash. This block hash acts as a form of "timestamp" and nonce, indicating when a transaction was created relative to the ledger's state. A block hash is a 32-byte SHA-256 hash and serves several critical functions: * **Transaction uniqueness**: A block hash ensures each transaction is unique because it includes a snapshot of the ledger's state at a specific time. This prevents duplicate transactions within a short time frame. * **Security measure**: By requiring a recent block hash, the network ensures that transactions are recent and relevant, helping to prevent replay attacks where an old transaction is submitted again with malicious intent. Transactions must use a block hash from within the last 150 blocks, representing a window of about 80-90 seconds, given Solana’s block times. This narrow window ensures that: * **Transactions are current**: Transactions referencing a blockhash older than 150 blocks are automatically rejected. This policy enforces that only recent and timely transactions are processed, keeping the network up-to-date. * **Network efficiency**: By discarding old transactions, the network avoids spending resources on processing or storing irrelevant or outdated requests. This helps maintain high throughput and low latency, key features of Solana’s architecture. ### Challenges and side-effects However, this system also introduces certain challenges: * **Forced transaction mortality**: Since transactions are only valid for about 80-90 seconds, they have a very short life. A transaction must be resubmitted with a new blockhash if it is not processed within this window. This can be challenging during network congestion or for applications that batch or schedule transactions. * **Issue of non-uniqueness in high throughput**: In scenarios where many transactions are issued rapidly, there's a high chance that multiple transactions might end up with the same block hash. This is particularly true during peak times. When this happens, transactions can be seen as duplicates and may be rejected or ignored, even if they are legitimate and unique in their intent. * **Operational complexity**: For developers and users, managing a transaction's lifecycle—ensuring it's submitted with a valid, recent blockhash and handling rejections due to timing—adds an extra layer of complexity to application logic. ## What causes a TransactionExpiredBlockheightExceededError? This error typically arises from a few main scenarios that are crucial for developers to understand and mitigate: 1. **Network congestion:** Despite Solana's capability to process tens of thousands of transactions per second, it is not immune to congestion. During peak times, when the number of transactions surges, the network may struggle to process all transactions. If the network fails to confirm a transaction before it reaches the `lastValidBlockHeight`, the transaction will expire. This situation can be exacerbated during intense trading periods or large-scale DApp interactions. 2. **Improper estimation of `lastValidBlockHeight`:** Accurately setting the `lastValidBlockHeight` is crucial and requires a good understanding of the network’s current load and transaction throughput. Developers might underestimate how many blocks are needed for a transaction to be confirmed, leading to a premature expiry. It's important to assess this parameter accurately, considering typical and peak network conditions to avoid such expirations. 3. **Latency from chain leader:** The chain leader in Solana is responsible for producing blocks. Any latency in this process, whether due to hardware issues, network delays, or software problems, can delay the time it takes for transactions to be included in a block. If such latency causes the `lastValidBlockHeight` to be reached before the transaction is processed, the transaction will expire without confirmation. 4. **Delay in propagation:** When transactions are submitted through remote procedure call (RPC) services, there can be a delay in the propagation of the transaction to the network. This delay is particularly problematic if the RPC service does not immediately broadcast the transaction to the network or experiences high traffic. Such delays increase the risk of the transaction not being processed in time, especially if the `lastValidBlockHeight` is set too optimistically. ## How to handle a TransactionExpiredBlockheightExceededError? Successfully handling the TransactionExpiredBlockheightExceededError requires proactive strategies to ensure that transactions are processed within their valid window, even under challenging network conditions. Below, we explore three main strategies: * Priority fees * Retry and re-broadcast logic * Use multiple endpoints with geographically located nodes. ### How to handle transaction expiry with Priority Fees In Solana, priority fees are critical in determining the processing order of transactions submitted to the network. The system prioritizes transactions based on the fees associated with them, which means that transactions with higher fees are more likely to be processed quickly. This is particularly important during high network activity, where the competition for block space can lead to delays in transaction confirmations. To leverage this mechanism, developers can opt to increase the transaction fee—referred to as a priority fee—to boost the priority of their transaction in the processing queue. By doing so, they increase the likelihood that their transaction will be processed before reaching the `lastValidBlockHeight`. This strategy is especially effective in mitigating the `TransactionExpiredBlockheightExceededError`, as it helps ensure that transactions are confirmed within their valid window, even under congested network conditions. > [!NOTE] > > ### How to use Priority Fees > > Learn how to implement priority fees in your transactions using `solana/web3.js`: [Solana: How to use Priority Fees to unlock faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) and [estimate Priority Fees dynamically](/docs/solana-estimate-priority-fees-getrecentprioritizationfees). ### How to implement a retry logic Implementing effective retry logic is crucial for managing transactions in Solana, especially during high network congestion. This approach ensures that transactions stand a higher chance of being processed before reaching the `lastValidBlockHeight`. According to Solana documentation, even though RPC nodes have a generic rebroadcasting algorithm, developers can significantly benefit from customizing their rebroadcasting logic to better suit their application needs. * **Retry mechanism**: The retry mechanism involves resubmitting a transaction with adjusted parameters if the initial submission fails. This could be due to the transaction nearing or exceeding the `lastValidBlockHeight` due to network delays or congestion. Custom rebroadcasting logic allows applications to control the retry process more finely, for example, by adjusting the `lastValidBlockHeight` and transaction fees dynamically based on the current network status. * **Adjusting `lastValidBlockHeight`**: The strategy here involves setting the `lastValidBlockHeight` based on the current block height at each retry attempt. Solana's documentation highlights the use of the `maxRetries` parameter in the [`sendTransaction`](/reference/solana-sendtransaction) method can be set to manage how many times an RPC node should retry sending the transaction to the leader. Developers might choose to set`maxRetries` to a lower number and manually manage the retries within their application logic, using current network conditions to guide the timing of these retries. * **Enhanced control with custom logic**: Developers are encouraged to enable preflight checks that raise errors before the actual submission of transactions. This ensures that all signatures are valid and the referenced block hash is still within the last 150 blocks, helping prevent unnecessary retries. Ensuring that the initial transaction's block hash has expired is critical to avoid duplicating transactions on the network if a transaction must be resubmitted. > [!NOTE] > > ### How to implement a retry logic > > Learn how to implement a retry logic in your transactions using `solana/web3.js`: [Solana: Enhancing SPL Token transfers with retry logic](/docs/enhancing-solana-spl-token-transfers-with-retry-logic). Also, check the [Solana docs](https://solana.com/docs/core/transactions/retry) on how to take care of re-broadcasting transactions. ### How to use multiple endpoints with geographically distributed nodes To enhance the reliability and speed of transaction confirmations on Solana, developers can strategically use multiple RPC endpoints that are geographically closer to the Solana chain leader. This method effectively reduces the inherent network latency that can delay transaction processing. By positioning RPC nodes closer to the chain leader, transactions are quicker relayed to the leader for inclusion in the upcoming blocks. Geographically distributed nodes help minimize the distance data must travel, speeding up the overall transaction time from submission to confirmation. This is crucial during periods of high network activity where every millisecond can determine whether a transaction is confirmed before reaching the `lastValidBlockHeight`. Additionally, using multiple endpoints can provide redundancy, enhancing the resilience of the transaction submission process. If one node experiences issues or excessive traffic, others may submit the transaction successfully. > [!NOTE] > > ### How to use multiple RPC endpoints to optimize DApp performance > > This tutorial explains how to use multiple endpoitns. This is focused on fethcing real time data rather than sending transactions, but can be adapted. > > * [Solana: How to use multiple RPC endpoints to optimize DApp performance](/docs/solana-how-to-use-multiple-rpc-endpoints-optimize-dapp-performance) ## Conclusion Solana stands out for its speed and efficiency in the fast-paced and ever-evolving world of blockchain technology. However, navigating its complexities requires a robust understanding of how transactions are processed and the common pitfalls, such as the `TransactionExpiredBlockheightExceededError`. Developers can enhance transaction success rates by employing strategic mitigation techniques such as using priority fees, implementing retry logic, and leveraging multiple geographically located RPC endpoints to ensure their applications remain robust and responsive. These strategies address the inherent challenges of a high-throughput blockchain like Solana and pave the way for building more resilient and efficient applications. Embracing these practices will equip developers to take full advantage of Solana's capabilities, driving innovation and performance in the decentralized application space. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # How to land transactions on Solana Source: https://docs.chainstack.com/docs/solana-how-to-land-transactions Land Solana transactions reliably: size the compute budget, use a fresh blockhash, submit direct-to-leader through a staked SwQoS connection (Chainstack Warp), and retry correctly. Priority fees only order transactions within a path — the submission path is the real lever. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Landing a Solana transaction reliably comes down to four things working together: a well-formed transaction, a fresh blockhash, the right submission path, and correct retry logic. The single biggest lever is the **submission path** — getting `sendTransaction` to the current leader through a staked stake-weighted quality of service (SwQoS) connection lands far more competitively than broadcasting through a shared public RPC and hoping. Priority fees help, but they only order your transaction *within* a path; they don't get it to the leader. This page is the hub for landing transactions on Solana. Each lever below links to a focused deep-dive with runnable code. ## The four levers that land a transaction | Lever | What it does | Deep-dive | | - | - | - | | Well-formed transaction | Sets a compute unit price and an accurate compute unit limit so the network can schedule and price the transaction correctly | [Priority fees for faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) | | Fresh blockhash | Keeps the transaction inside its \~80–90 second validity window so it isn't dropped as expired | [Handle the transaction expiry error](/docs/solana-how-to-handle-the-transaction-expiry-error) | | Submission path | Routes `sendTransaction` to the current leader through a staked SwQoS connection instead of a shared public RPC — the biggest lever | [Solana Trader Nodes with Warp transactions](/docs/solana-trader-nodes) | | Retry and confirm | Re-broadcasts until confirmation or expiry, then confirms against `lastValidBlockHeight` | [Enhance transfers with retry logic](/docs/enhancing-solana-spl-token-transfers-with-retry-logic) | ## Why transactions fail to land A Solana transaction that never confirms almost always failed at one of four points: * **It never reached the leader.** Submitted through a congested shared RPC, the transaction competes with everyone else for forwarding to the current leader and can be dropped before it ever arrives. * **Its blockhash expired.** Every transaction references a blockhash valid for only 150 blocks (about 60–90 seconds). Miss that window and the network drops it with a `TransactionExpiredBlockheightExceededError`. * **Its compute unit limit was wrong.** An undersized limit makes the transaction fail at execution; an oversized one overpays and can deprioritize it. The limit must match what the transaction actually consumes. * **It was submitted once and abandoned.** Without re-broadcasting, a single dropped attempt is the end. RPC nodes rebroadcast generically, but application-controlled retries land more reliably under congestion. The levers below address each failure point in order. ## Lever 1: build a well-formed transaction A landable transaction sets two [Compute Budget](/docs/solana-compute-budget) Program instructions: a **compute unit price** (the priority fee, in micro-lamports per compute unit) and a **compute unit limit** sized to what the transaction actually consumes. The priority fee you pay is `compute_unit_price × compute_unit_limit`, so an accurate limit both improves landing and avoids overpaying. Size the limit from a live simulation rather than guessing. This example uses [`@solana/kit`](https://github.com/anza-xyz/kit), the newer tree-shakable SDK; the classic [`@solana/web3.js`](https://github.com/solana-foundation/solana-web3.js) (`Connection`/`PublicKey`) is also actively maintained, and the deep-dives linked below show both side by side: ```typescript theme={"system"} import { createSolanaRpc, createSolanaRpcSubscriptions, pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, estimateComputeUnitLimitFactory, signTransactionMessageWithSigners, sendAndConfirmTransactionFactory, getSignatureFromTransaction, } from "@solana/kit"; import { getSetComputeUnitPriceInstruction, getSetComputeUnitLimitInstruction, } from "@solana-program/compute-budget"; const rpc = createSolanaRpc("YOUR_CHAINSTACK_SOLANA_HTTPS_ENDPOINT"); const rpcSubscriptions = createSolanaRpcSubscriptions("YOUR_CHAINSTACK_SOLANA_WSS_ENDPOINT"); // signer: a TransactionSigner (a wallet, or from generateKeyPairSigner) // instructions: your program instructions // microLamportsPerCU: from the local fee market (see below) async function landTransaction(signer, instructions, microLamportsPerCU) { // 1. Fresh blockhash at the confirmed commitment. const { value: blockhash } = await rpc.getLatestBlockhash({ commitment: "confirmed" }).send(); // 2. Draft the message with the priority fee (compute unit price). const draft = pipe( createTransactionMessage({ version: 0 }), (m) => setTransactionMessageFeePayerSigner(signer, m), (m) => setTransactionMessageLifetimeUsingBlockhash(blockhash, m), (m) => appendTransactionMessageInstructions( [getSetComputeUnitPriceInstruction({ microLamports: microLamportsPerCU }), ...instructions], m), ); // 3. Size the compute unit limit from a live simulation, then set it (~10% margin). const units = await estimateComputeUnitLimitFactory({ rpc })(draft); const message = appendTransactionMessageInstructions( [getSetComputeUnitLimitInstruction({ units: Math.ceil(units * 1.1) })], draft); // 4. Sign and send. sendAndConfirmTransactionFactory confirms over websockets against // the blockhash + lastValidBlockHeight — no deprecated confirmTransaction. const signedTx = await signTransactionMessageWithSigners(message); await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTx, { commitment: "confirmed" }); return getSignatureFromTransaction(signedTx); } ``` Choose the compute unit price from the live **local fee market**, not a fixed constant. Solana's fee market is keyed to the *writable accounts* your transaction locks, so pass those accounts — not program IDs — to [`getRecentPrioritizationFees`](/reference/solana-getrecentprioritizationfees). Since Agave 2.0, priority fees go entirely to the block-producing validator. Two ways to estimate the price: * [Estimate priority fees with RPC methods](/docs/solana-estimate-priority-fees-getrecentprioritizationfees) — `getRecentPrioritizationFees` for a percentile-based estimate over your writable accounts. * [Analyze adjacent transactions for priority fees](/docs/solana-analyzing-adjacent-transactions-for-priority-fees) — read what actually landed in recent blocks. For complete runnable scripts, see [Priority fees for faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) (this uses `@solana/kit`, matching the example above) and [Priority fees for a Jupiter swap in Python](/docs/solana-priority-fees-for-a-jupiter-in-python). ## Lever 2: use a fresh blockhash and respect expiry Every Solana transaction references a recent blockhash that is valid for **150 blocks — about 60–90 seconds**. The transaction carries a `lastValidBlockHeight`; if the network reaches that height without confirming it, the transaction is dropped and you get a `TransactionExpiredBlockheightExceededError`. Fetch the blockhash at the `confirmed` commitment right before you sign, track its `lastValidBlockHeight`, and stop retrying once the current block height passes it — then rebuild with a new blockhash. The full diagnosis and handling strategy is in [Handle the transaction expiry error](/docs/solana-how-to-handle-the-transaction-expiry-error). For transactions that must stay valid beyond that window — offline or multi-party signing — use a [durable nonce](/docs/solana-durable-nonces) instead of a recent blockhash. ## Lever 3: choose the right submission path (the biggest lever) Where you submit `sendTransaction` matters more than any fee you attach. There are two paths: * **Shared public RPC.** Your transaction reaches an RPC node that forwards it to the current leader over the regular path. Under congestion it competes with every other transaction for that forwarding, and a share of transactions are dropped before the leader ever sees them. * **Staked SwQoS connection, direct to the leader.** Transactions submitted through a *staked* validator connection get stake-weighted priority forwarding to the leader. This is what [SwQoS](https://solana.com/developers/guides/advanced/stake-weighted-qos) is for, and it's the difference between "the leader saw my transaction" and "my transaction never arrived." [Chainstack Warp transactions](/docs/warp-transactions) route only the `sendTransaction` call to the current leader through bloXroute's staked validator connections, while every other request goes through your standard Chainstack Solana node. Warp is available on both [Solana Trader Nodes](/docs/solana-trader-nodes) and [Global Nodes](/docs/global-elastic-node), works over HTTP (not WebSocket), and needs no custom transaction crafting — you switch the endpoint and your existing setup lands through the staked relay. > [!NOTE] > > Priority fees and the submission path solve different problems. The submission path decides whether your transaction reaches the leader; the priority fee decides its ordering once it's there. A high priority fee on a transaction that never reaches the leader still doesn't land. For competitive or atomic flows — arbitrage, liquidations, high-contention mints — **Jito bundles** are a distinct channel: an all-or-nothing group of up to five transactions that a Jito validator executes in order, prioritized by a tip you attach rather than by the priority fee. With most Solana stake running the Jito client, tips are a primary path for latency-sensitive transactions. Bundles are submitted to Jito's Block Engine (`mainnet.block-engine.jito.wtf`), not through a standard RPC node — so this path runs alongside your Chainstack endpoint rather than through it, and complements SwQoS rather than replacing it. See [Solana MEV protection](/docs/solana-mev-protection) for the anti-sandwich `dontfront` pattern. For live, continuously-measured landing rates comparing Warp against other providers, see the [Chainstack Compare dashboard](https://compare.chainstack.com/). Under heavy, unpredictable load, the most reliable setups **fan out** — submit the same signed transaction through more than one path at once (for example a staked connection plus a public RPC, or add a Jito bundle). De-duplication by signature makes this safe: the network runs a given transaction at most once, no matter how many paths deliver it. ## Lever 4: retry and confirm correctly A single submission is not enough under congestion. Re-broadcast the *same signed transaction* until it either confirms or its blockhash expires — never resubmit with a new blockhash while the old one is still valid, or you risk landing the transaction twice. * For the standard path, `sendAndConfirmTransactionFactory` confirms over websockets against the blockhash and `lastValidBlockHeight` — see the [Lever 1 example](#lever-1-build-a-well-formed-transaction). Avoid the legacy `confirmTransaction` RPC method; it's deprecated. * For an aggressive path under congestion, send with `maxRetries: 0` and `skipPreflight: true`, then run your own loop: re-send the same signed transaction every few seconds and poll [`getSignatureStatuses`](/reference/solana-getsignaturestatuses), stopping when it confirms or the current block height passes `lastValidBlockHeight`. Skip preflight only after a successful simulation — `skipPreflight: true` also skips the check that catches a malformed transaction, so it hides errors if you haven't already validated. * Submit through geographically close endpoints to cut propagation latency. Full patterns are in [Enhance SPL token transfers with retry logic](/docs/enhancing-solana-spl-token-transfers-with-retry-logic) and [Use multiple RPC endpoints to optimize performance](/docs/solana-how-to-use-multiple-rpc-endpoints-optimize-dapp-performance). To confirm and index landed transactions by block and block time, see [Associate transactions with block time using Yellowstone gRPC](/docs/solana-transactions-block-time-yellowstone-grpc). ## Priority fees vs submission path: which matters more? Both, in order. First get the transaction to the leader (submission path), then compete for ordering once it's there (priority fee). A well-tuned priority fee on a shared RPC still loses to a direct-to-leader submission during congestion, because ordering only matters among the transactions the leader actually received. Tune the fee *after* you've fixed the path, not instead of it. ## FAQ **Why does my transaction get `TransactionExpiredBlockheightExceededError`?** Its blockhash aged past the 150-block (\~60–90 second) validity window before the network confirmed it — usually because it was submitted through a congested path, never reached the leader, and wasn't rebroadcast in time. Fix the submission path first, then add retry logic. See [Handle the transaction expiry error](/docs/solana-how-to-handle-the-transaction-expiry-error). **Do higher priority fees guarantee my transaction lands?** No. Priority fees order transactions the leader has already received; they don't get a transaction to the leader. If the transaction is dropped before the leader on a congested shared RPC, no fee saves it. See [Lever 3: choose the right submission path](#lever-3-choose-the-right-submission-path-the-biggest-lever). **What is SwQoS and how do I use it?** Stake-weighted quality of service (SwQoS) gives transactions submitted through a staked validator connection priority forwarding to the leader. On Chainstack you get it by enabling [Warp transactions](/docs/warp-transactions) on a Trader or Global Node — no custom code, just a different endpoint. **How should I size the compute unit limit?** Simulate the transaction, read `unitsConsumed`, and set the limit to that value plus a small margin (around 10%). A limit that matches actual consumption improves scheduling and avoids overpaying, since the fee is `compute_unit_price × compute_unit_limit`. See [Lever 1: build a well-formed transaction](#lever-1-build-a-well-formed-transaction). **Is `sendTransaction` retrying enough on its own?** The RPC node rebroadcasts generically, but application-controlled retries — re-sending the same signed transaction until it confirms or expires — land more reliably under congestion. See [Enhance transfers with retry logic](/docs/enhancing-solana-spl-token-transfers-with-retry-logic). **Which Solana JavaScript library should I use?** Both are actively maintained, so either works. `@solana/kit` is the newer, tree-shakable, functional SDK (its maintainers recommend it for new code) and is shown in [Lever 1](#lever-1-build-a-well-formed-transaction); `@solana/web3.js` is the classic `Connection`/`PublicKey` API used across most existing Solana codebases. The priority-fee deep-dives linked here show both side by side, and the concepts are identical either way. ## Deep-dives > [card] [Priority fees for faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) > [card] [Estimate priority fees with RPC methods](/docs/solana-estimate-priority-fees-getrecentprioritizationfees) > [card] [Analyze adjacent transactions for priority fees](/docs/solana-analyzing-adjacent-transactions-for-priority-fees) > [card] [Priority fees for a Jupiter swap in Python](/docs/solana-priority-fees-for-a-jupiter-in-python) > [card] [Handle the transaction expiry error](/docs/solana-how-to-handle-the-transaction-expiry-error) > [card] [Enhance transfers with retry logic](/docs/enhancing-solana-spl-token-transfers-with-retry-logic) > [card] [Solana Trader Nodes with Warp transactions](/docs/solana-trader-nodes) > [card] [Solana MEV protection](/docs/solana-mev-protection) > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: How to perform token swaps using Raydium Source: https://docs.chainstack.com/docs/solana-how-to-perform-token-swaps-using-the-raydium-sdk Swap tokens on Solana with Raydium's Trade API and Chainstack in TypeScript — get a quote, set priority fees, then build, sign, and send a SOL to USDC swap with versioned transactions. [Token swaps with Raydium SDK on Solana](https://www.youtube.com/embed/NT-oYdV67u4) **TLDR:** * Raydium is a leading Solana AMM. Its hosted **Trade API** quotes and builds a swap for you, routing across Raydium's pool types (AMM v4, CPMM, CLMM) so you don't manage pool data yourself. * The flow: fetch a priority fee, get a quote, build the transaction, then sign it with `@solana/web3.js` and send it through your own Solana node. * This guide swaps SOL → USDC in TypeScript using a Chainstack Solana node for the broadcast. > [!WARNING] > > The old Raydium SDK v1 (`@raydium-io/raydium-sdk`) is end-of-life — its repository was archived in June 2025 and it never supported CPMM or CLMM pools. For new code, use the current [Trade API](https://docs.raydium.io/raydium/build/developer-guides/overview) shown here (simplest for a basic swap), or the [`@raydium-io/raydium-sdk-v2`](https://github.com/raydium-io/raydium-sdk-V2) library for on-chain control. The canonical examples are in [raydium-sdk-V2-demo](https://github.com/raydium-io/raydium-sdk-V2-demo). ## Main article The Solana blockchain is at the forefront of DeFi, known for its speed and cost-efficiency. Central to this ecosystem is Raydium, an automated market maker (AMM) that provides deep liquidity across several pool types — legacy AMM v4, constant-product CPMM, and concentrated-liquidity CLMM. You don't need to know which pool a pair trades in. Raydium's **Trade API** computes the best route across all of them, returns a quote, and builds a ready-to-sign transaction. This guide walks through a SOL → USDC swap in TypeScript, signing and broadcasting through a Chainstack Solana node. ## Prerequisites ### Deploy a Chainstack Solana node Deploy a [Solana RPC endpoint on Chainstack](https://chainstack.com/build-better-with-solana/): > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) ### Local setup * [Node.js](https://nodejs.org/en/download/current) 18 or above (for the global `fetch` used below) * A Solana wallet with some SOL to cover the swap and fees Install the dependencies: ```shell Shell theme={"system"} npm install @solana/web3.js bs58 ``` ## Implementation The Trade API exposes two hosts: `https://api-v3.raydium.io` for general data (including the priority-fee endpoint) and `https://transaction-v1.raydium.io` for the swap compute and build endpoints. The flow is fetch fee → quote → build → sign → send. ```typescript raydium_swap.ts theme={"system"} import { Connection, Keypair, VersionedTransaction } from '@solana/web3.js' import bs58 from 'bs58' // Your Chainstack Solana node and wallet const RPC_ENDPOINT = 'YOUR_CHAINSTACK_SOLANA_NODE' const PRIVATE_KEY = 'YOUR_PRIVATE_KEY' // base58-encoded const SWAP_HOST = 'https://transaction-v1.raydium.io' const BASE_HOST = 'https://api-v3.raydium.io' const INPUT_MINT = 'So11111111111111111111111111111111111111112' // SOL const OUTPUT_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' // USDC const AMOUNT = 1_000_000 // 0.001 SOL, in lamports const SLIPPAGE_BPS = 50 // 0.5% const TX_VERSION = 'V0' async function swap() { const connection = new Connection(RPC_ENDPOINT, 'confirmed') const wallet = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY)) // 1) Priority fee tiers from Raydium (vh / h / m). const { data: fee } = await (await fetch(`${BASE_HOST}/main/auto-fee`)).json() const computeUnitPriceMicroLamports = String(fee.default.h) // 'h' = high tier // 2) Quote — Raydium routes across its pool types for you. const swapResponse = await ( await fetch( `${SWAP_HOST}/compute/swap-base-in?inputMint=${INPUT_MINT}&outputMint=${OUTPUT_MINT}` + `&amount=${AMOUNT}&slippageBps=${SLIPPAGE_BPS}&txVersion=${TX_VERSION}`, ) ).json() console.log(`Quote: ${AMOUNT} -> ${swapResponse.data.outputAmount} (out)`) // 3) Build the swap transaction. wrapSol because the input is native SOL. const { data: txs } = await ( await fetch(`${SWAP_HOST}/transaction/swap-base-in`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ computeUnitPriceMicroLamports, swapResponse, txVersion: TX_VERSION, wallet: wallet.publicKey.toBase58(), wrapSol: true, unwrapSol: false, }), }) ).json() // 4) Deserialize, sign, and send each returned transaction through your node. for (const { transaction } of txs) { const tx = VersionedTransaction.deserialize(Buffer.from(transaction, 'base64')) tx.sign([wallet]) const sig = await connection.sendRawTransaction(tx.serialize(), { skipPreflight: true }) console.log(`Sent: https://solscan.io/tx/${sig}`) } } swap() ``` Run it with a TypeScript runner such as [`tsx`](https://www.npmjs.com/package/tsx): ```shell Shell theme={"system"} npx tsx raydium_swap.ts ``` ### How it works * **Priority fee.** `GET /main/auto-fee` returns suggested compute-unit prices in three tiers — `vh` (very high), `h` (high), and `m` (medium). Pass the chosen tier as `computeUnitPriceMicroLamports` in the build request; Raydium adds the compute-budget instructions for you. * **Quote.** `GET /compute/swap-base-in` takes the input/output mints, the input `amount` (in the input token's smallest unit), and `slippageBps`. `swap-base-in` means you fix the input amount; use `swap-base-out` to fix the output amount instead. Raydium returns the best route and the expected `outputAmount`. * **Build.** `POST /transaction/swap-base-in` returns one or more base64 transactions. Set `wrapSol: true` when the input is native SOL (it wraps to WSOL) and `unwrapSol: true` when the output is SOL. For SPL-to-SPL swaps, pass your input/output token account addresses. * **Versioned transactions.** With `txVersion: 'V0'`, Raydium returns `VersionedTransaction`s; deserialize, sign, and broadcast each through your node. The build can return more than one transaction (for example, when an account needs to be created first), so loop over all of them. > [!NOTE] > > For on-chain control instead of the hosted API — building the swap locally, managing pool state, or working with a specific pool — use [`@raydium-io/raydium-sdk-v2`](https://github.com/raydium-io/raydium-sdk-V2) and its `raydium.cpmm.swap` / `raydium.liquidity.swap` modules. See the [raydium-sdk-V2-demo](https://github.com/raydium-io/raydium-sdk-V2-demo) `src/api/swap.ts` (API path) and `src/cpmm/swap.ts` (on-chain path). ## Conclusion Raydium's Trade API gets you a working Solana swap with minimal code: it handles routing and pool selection, and hands you a transaction to sign and send through your own node. From here you can fix the output amount with `swap-base-out`, swap arbitrary SPL pairs by supplying token accounts, or drop down to `@raydium-io/raydium-sdk-v2` when you need on-chain control. Pair it with a Chainstack Solana node for reliable, low-latency broadcasting. # Solana: Priority fees for faster transactions Source: https://docs.chainstack.com/docs/solana-how-to-priority-fees-faster-transactions Set optimal Solana priority fees using compute budget instructions to accelerate transaction processing and avoid dropped transactions on Chainstack. > **Part of the [How to land transactions on Solana](/docs/solana-how-to-land-transactions) guide.** **TLDR:** * Priority fees on Solana let you attach a higher compute unit price to your transactions, boosting confirmation speed. * They’re especially useful during network congestion or in time-critical situations like NFT drops or rapid trading. * This guide shows how to implement priority fees in a TypeScript/Node.js setup, leveraging the Compute Budget Program. * By tweaking the `microLamports` per compute unit, you can ensure your most critical transactions are processed first. ## Main article Transaction speed and efficiency are crucial factors that can make or break user experiences in blockchain. While the Solana network is known for its high throughput and low latency, there may be times when you need to prioritize your transactions over others, especially during periods of high network congestion. This is where priority fees come into play. Priority fees on Solana allow users to attach a higher fee to their transactions, incentivizing validators to process them more quickly. By setting a higher compute unit price, your transaction gains priority over those with lower fees, ensuring faster confirmation times. This feature is particularly valuable for time-sensitive operations, such as trading on decentralized exchanges or participating in high-demand events like NFT mints. In this tutorial, we'll dive into the concept of priority fees on Solana, exploring how they work and why they are a valuable tool for developers and users alike. We'll walk through a practical example with TypeScript, showing it in both Solana JavaScript libraries side by side — `@solana/kit` (the newer, tree-shakable, functional SDK, formerly `@solana/web3.js` v2) and the classic `@solana/web3.js` (the `Connection`/`PublicKey` API) — both of which are actively maintained. By the end of this guide, you'll have a solid understanding of priority fees and the ability to leverage them to enhance the performance of your Solana-based projects. ## Understand Solana Priority Fees Priority fees on Solana are optional fees, priced in [micro-lamports](/docs/solana-glossary#micro-lamports) per [Compute Unit](/docs/solana-glossary#compute-units) (very small amounts of SOL). These fees can be appended to transactions to incentivize validator nodes to prioritize and include them in blocks more quickly. They come in addition to the base transaction fee of 5000 lamports per signature. When the network is congested with transactions carrying priority fees, validators are economically incentivized to schedule and process transactions with higher fees per compute unit first, ensuring optimal resource utilization. Users can implement priority fees using the Compute Budget Program to modify the compute unit limit and set a compute unit price for their transactions. The higher the compute units required for a transaction, the more fees will be paid when priority fees are added. ## Implement Priority Fees with a practical example This project aims to provide a practical demonstration of how to implement priority fees in Solana transactions. The code showcases a simple Solana transaction that transfers SOL from one account to another with the addition of a priority fee. The example uses the Compute Budget Program to set a compute unit price, which attaches the priority fee. The priority fee is `compute_unit_price × compute_unit_limit`, so in production you should also cap the compute units with a compute-unit-limit instruction sized to what the transaction actually consumes — ideally from a live simulation. The `@solana/kit` version does this with `estimateComputeUnitLimit`; sizing the limit accurately both improves landing and avoids overpaying. ## Prerequisites Before diving into the code and practical implementation, ensuring you have several vital prerequisites is essential. This foundation will set you up for success, enabling a smooth development experience. Let's go through what you need: ### Deploy a Chainstack Solana node Deploy a [reliable Solana RPC endpoint](https://chainstack.com/build-better-with-solana/): > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) ### Set up your environment * **Node.js**: Make sure your machine has Node.js (version 18 or higher) installed. Node.js is a prerequisite for running the scripts and managing the project's dependencies. * **npm**: The Node Package Manager (npm) is the default package manager for Node.js projects. It will be used to install the necessary dependencies for this project. npm comes pre-installed with Node.js, so you don't need to install it separately. * **Solana wallet**: You'll need a Solana wallet with SOL. > [!NOTE] > > If you prefer, you can use Yarn as an alternative package manager. Yarn is a fast, reliable, and secure dependency management tool. After installing Node.js, you can install Yarn globally by running the following command in your terminal: > > `npm install --global yarn` ### Initialize a TypeScript project To get started, we need to set up a TypeScript project. This will give us a structured environment for our code and enable us to leverage TypeScript's features, such as type-checking and other enhancements. Follow these steps to create a new TypeScript project: * **Create a project directory**: Open your terminal and navigate to the location where you want to create your project directory. Run the following command to create a new directory: ``` mkdir solana-priority-fees ``` * **Initialize a new Node.js project**: Navigate into the newly created directory and run the following command to initialize a new Node.js project: ``` cd solana-priority-fees npm init -y ``` This command will create a `package.json` file to store information about your project and its dependencies. * **Install TypeScript**: With your Node.js project initialized, you can now install TypeScript as a development dependency. Run the following command: ``` npm install --save-dev typescript ``` This will add TypeScript to your project's dependencies and create a `node_modules` folder with the required packages. * **Create a TypeScript configuration file**: To configure TypeScript for your project, you must create a `tsconfig.json` file. Run the following command to generate a basic configuration file: ``` npx tsc --init ``` This command will create a `tsconfig.json` file in your project directory with default settings. You can customize these settings as needed for your project. After completing these steps, a TypeScript project will be set up and ready for development. You can start writing TypeScript code in your project directory, and the TypeScript compiler will automatically check for type errors and compile your code. * **Create a source file**: Let's create our first TypeScript file where we'll write our code. ```bash Bash theme={"system"} touch main.ts ``` ### Install required packages To interact with the Solana blockchain, we must install a few packages. This guide shows two Solana JavaScript libraries side by side — **both are actively maintained** — so install the set for whichever you choose. `dotenv` (loads environment variables) and `ts-node` (runs TypeScript directly) are used by both. > [tab] @solana/kit > > ```bash Bash theme={"system"} > npm install @solana/kit @solana-program/compute-budget @solana-program/system dotenv > npm install --save-dev ts-node > ``` > > * `@solana/kit` — the newer, tree-shakable, functional SDK. It also handles base58 encoding/decoding, so no separate `bs58` package is needed. > * `@solana-program/compute-budget` — Compute Budget Program instructions to set the compute unit price (the priority fee) and limit. > * `@solana-program/system` — System Program instructions, including the SOL transfer used in this example. > > Your `package.json` should then include: > > ```json JSON theme={"system"} > "dependencies": { > "@solana/kit": "^7.0.0", > "@solana-program/compute-budget": "^0.15.0", > "@solana-program/system": "^0.12.0", > "dotenv": "^16.4.5" > } > ``` > [tab] @solana/web3.js (classic) > > ```bash Bash theme={"system"} > npm install @solana/web3.js bs58 dotenv > npm install --save-dev ts-node > ``` > > * `@solana/web3.js` — the classic object-oriented Solana SDK (`Connection`, `PublicKey`, `SystemProgram`, `ComputeBudgetProgram`). > * `bs58` — base58 encoding/decoding for the private key. > > Your `package.json` should then include: > > ```json JSON theme={"system"} > "dependencies": { > "@solana/web3.js": "^1.99.0", > "bs58": "^5.0.0", > "dotenv": "^16.4.5" > } > ``` With these packages installed, you'll have the tools to connect to a Solana node, manage a wallet, and send a transaction with a priority fee using TypeScript. In the next section, we'll start coding. ## Set up your environment variables This tutorial will use sensitive information such as private keys and RPC node URLs. It's crucial to keep this information secure and avoid committing it to version control systems like Git. We'll use the `dotenv` package to load environment variables from a `.env` file to achieve this. Follow these steps to set up your environment variables: * **Create a `.env` file**: In the root directory of your project, create a new file called `.env`. This file will store your environment variables. * **Add your environment variables**: Open the `.env` file and add the following variables, replacing the placeholders with your actual values: ``` SOLANA_RPC="YOUR_HTTPS_CHAINSTACK_URL" SOLANA_WSS="YOUR_WEBSOCKET_CHAINSTACK_URL" PRIVATE_KEY="YOUR_PRIVATE_KEY" ``` * `SOLANA_RPC`: This variable should contain your Solana node's HTTP RPC URL. If you're using a Chainstack node, the RPC URL is in the node's credentials. * `SOLANA_WSS`: This variable should contain the WebSocket URL of your Solana node. If you're using a Chainstack node, you can find the WebSocket URL in the node's credentials. * `PRIVATE_KEY`: This variable should contain the private key of the Solana wallet you want to use for token transfers. ### How to use environment variables Once the `.env` file is set up, you can load environment variables in your TypeScript code (e.g., `main.ts`), import the `dotenv` package, and load the environment variables at the beginning of your script: ```typescript TypeScript theme={"system"} import "dotenv/config"; ``` This will load the environment variables from the `.env` file into the `process.env` object, allowing you to access them using `process.env.VARIABLE_NAME`. **Access environment variables**: You can now access the environment variables in your code like this: ```typescript TypeScript theme={"system"} const rpcUrl = process.env.SOLANA_RPC; const wsUrl = process.env.SOLANA_WSS; const privateKey = process.env.PRIVATE_KEY; ``` By following this approach, you can keep sensitive information like private keys and node URLs out of your codebase, making it more secure and easier to manage different environments (e.g., development, staging, production). > [!NOTE] > > Remember to add the `.env` file to your `.gitignore` file to prevent it from being committed to version control systems, as it contains sensitive information. ## Send Solana transactions with priority fees in this code walkthrough Now that we have set up the project, installed the required packages, and configured the environment variables, it's time to put everything together and implement the code. ### Add the code 1. Open the `main.ts` file you created earlier in your preferred code editor. 2. Paste the following code into the `main.ts` file: > [tab] @solana/kit > > ```typescript TypeScript theme={"system"} > import { > createSolanaRpc, createSolanaRpcSubscriptions, lamports, pipe, > createKeyPairSignerFromBytes, getBase58Encoder, > createTransactionMessage, setTransactionMessageFeePayerSigner, > setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, > estimateComputeUnitLimitFactory, signTransactionMessageWithSigners, > sendAndConfirmTransactionFactory, getSignatureFromTransaction, > } from "@solana/kit"; > import { > getSetComputeUnitPriceInstruction, getSetComputeUnitLimitInstruction, > } from "@solana-program/compute-budget"; > import { getTransferSolInstruction } from "@solana-program/system"; > import "dotenv/config"; > > const rpc = createSolanaRpc(process.env.SOLANA_RPC!); > const rpcSubscriptions = createSolanaRpcSubscriptions(process.env.SOLANA_WSS!); > > // Config priority fee and amount to transfer > const PRIORITY_RATE = 25000; // micro-lamports per compute unit > const AMOUNT_TO_TRANSFER = lamports(1_000_000n); // 0.001 SOL (1 SOL = 1_000_000_000 lamports) > > async function sendTransactionWithPriorityFee() { > // Load the fee payer from a base58-encoded secret key. @solana/kit decodes base58 > // with getBase58Encoder, so no separate bs58 package is needed. > const signer = await createKeyPairSignerFromBytes( > getBase58Encoder().encode(process.env.PRIVATE_KEY!) > ); > console.log(`Initial Setup: Public Key - ${signer.address}`); > > // Get the latest blockhash at the confirmed commitment > const { value: latestBlockhash } = await rpc > .getLatestBlockhash({ commitment: "confirmed" }) > .send(); > console.log(" ✅ - Fetched latest blockhash. Last valid height:", latestBlockhash.lastValidBlockHeight); > > // Build the transaction message: attach the priority fee (compute unit price) > // and the self-transfer instruction. > const draftMessage = pipe( > createTransactionMessage({ version: 0 }), > (m) => setTransactionMessageFeePayerSigner(signer, m), > (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), > (m) => appendTransactionMessageInstructions([ > getSetComputeUnitPriceInstruction({ microLamports: PRIORITY_RATE }), > getTransferSolInstruction({ > source: signer, > destination: signer.address, > amount: AMOUNT_TO_TRANSFER, > }), > ], m), > ); > console.log(" ✅ - Built transaction message"); > > // Size the compute unit limit from a live simulation (~10% margin), then set it. > // The priority fee you pay is compute_unit_price × compute_unit_limit, so an > // accurate limit improves landing and avoids overpaying. > const estimatedUnits = await estimateComputeUnitLimitFactory({ rpc })(draftMessage); > const message = appendTransactionMessageInstructions( > [getSetComputeUnitLimitInstruction({ units: Math.ceil(estimatedUnits * 1.1) })], > draftMessage, > ); > console.log(` ✅ - Estimated compute units: ${estimatedUnits}`); > > // Sign the transaction > const signedTransaction = await signTransactionMessageWithSigners(message); > const signature = getSignatureFromTransaction(signedTransaction); > console.log(" ✅ - Transaction signed"); > > console.log(`Sending ${Number(AMOUNT_TO_TRANSFER) / 1e9} SOL from ${signer.address} to ${signer.address} with priority fee rate ${PRIORITY_RATE} micro-lamports`); > > try { > // Send and confirm over websockets against the blockhash + lastValidBlockHeight. > // sendAndConfirmTransactionFactory replaces the deprecated confirmTransaction. > await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })( > signedTransaction, { commitment: "confirmed" } > ); > console.log("🚀 Transaction Successfully Confirmed!", "\n", `https://solscan.io/tx/${signature}`); > } catch (error) { > console.error(error); > } > } > > // Call the function to send the transaction with a priority fee > sendTransactionWithPriorityFee(); > ``` > [tab] @solana/web3.js (classic) > > ```typescript TypeScript theme={"system"} > import { ComputeBudgetProgram, Connection, Keypair, LAMPORTS_PER_SOL, SystemProgram, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; > import bs58 from "bs58"; > import 'dotenv/config'; > > const CHAINSTACK_RPC = process.env.SOLANA_RPC || ""; > const SOLANA_CONNECTION = new Connection(CHAINSTACK_RPC, {wsEndpoint:process.env.SOLANA_WSS, commitment: "confirmed"}); > console.log(`Connected to Solana RPC at ${CHAINSTACK_RPC.slice(0, -36)}`); > > // Decodes the provided environment variable private key and generates a Keypair. > const privateKey = new Uint8Array(bs58.decode(process.env.PRIVATE_KEY!)); > const FROM_KEYPAIR = Keypair.fromSecretKey(privateKey); > console.log(`Initial Setup: Public Key - ${FROM_KEYPAIR.publicKey.toString()}`); > > // Config priority fee and amount to transfer > const PRIORITY_RATE = 25000; // MICRO_LAMPORTS > const AMOUNT_TO_TRANSFER = 0.001 * LAMPORTS_PER_SOL; > > // Instruction to set the compute unit price for priority fee > const PRIORITY_FEE_INSTRUCTIONS = ComputeBudgetProgram.setComputeUnitPrice({microLamports: PRIORITY_RATE}); > > async function sendTransactionWithPriorityFee() { > // Create instructions for the transaction > const instructions: TransactionInstruction[] = [ > SystemProgram.transfer({ > fromPubkey: FROM_KEYPAIR.publicKey, > toPubkey: FROM_KEYPAIR.publicKey, > lamports: AMOUNT_TO_TRANSFER > }), > PRIORITY_FEE_INSTRUCTIONS > ]; > > // Get the latest blockhash > let latestBlockhash = await SOLANA_CONNECTION.getLatestBlockhash('confirmed'); > console.log(" ✅ - Fetched latest blockhash. Last Valid Height:", latestBlockhash.lastValidBlockHeight); > > // Generate the transaction message > const messageV0 = new TransactionMessage({ > payerKey: FROM_KEYPAIR.publicKey, > recentBlockhash: latestBlockhash.blockhash, > instructions: instructions > }).compileToV0Message(); > console.log(" ✅ - Compiled Transaction Message"); > > // Create a VersionedTransaction and sign it > const transaction = new VersionedTransaction(messageV0); > transaction.sign([FROM_KEYPAIR]); > console.log(" ✅ - Transaction Signed"); > > console.log(`Sending ${AMOUNT_TO_TRANSFER / LAMPORTS_PER_SOL} SOL from ${FROM_KEYPAIR.publicKey} to ${FROM_KEYPAIR.publicKey} with priority fee rate ${PRIORITY_RATE} microLamports`); > > try { > // Send the transaction to the network > const txid = await SOLANA_CONNECTION.sendTransaction(transaction, { maxRetries: 15 }); > console.log(" ✅ - Transaction sent to network"); > > // Confirm the transaction > const confirmation = await SOLANA_CONNECTION.confirmTransaction({ > signature: txid, > blockhash: latestBlockhash.blockhash, > lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, > }); > if (confirmation.value.err) { > throw new Error("🚨 Transaction not confirmed."); > } > > // Get the transaction result > const txResult = await SOLANA_CONNECTION.getTransaction(txid, {maxSupportedTransactionVersion: 1}) > console.log('🚀 Transaction Successfully Confirmed!', '\n', `https://solscan.io/tx/${txid}`); > console.log(`Transaction Fee: ${txResult?.meta?.fee} Lamports`); > } catch (error) { > console.log(error); > } > } > > // Call the function to send the transaction with a priority fee > sendTransactionWithPriorityFee(); > ``` Here is a breakdown of the code. Both versions do the same work — connect, set the priority fee, build a transaction, sign it, and send it — and differ only in API shape (`@solana/kit` is functional and asynchronous; `@solana/web3.js` is object-oriented). ### Initialize the connection and account * **Connection**: `@solana/kit` creates an RPC client with `createSolanaRpc` (plus `createSolanaRpcSubscriptions` for the websocket confirmation); `@solana/web3.js` creates a single `new Connection` with the WSS endpoint and commitment. Both read the RPC/WSS endpoints from your environment. * **Account**: Both decode the base58 private key from the environment variable. `@solana/kit` uses `getBase58Encoder` + `createKeyPairSignerFromBytes` to build a `TransactionSigner` (no `bs58` package); `@solana/web3.js` uses `bs58.decode` + `Keypair.fromSecretKey`. ### Configure transaction details * **Priority fee and transfer amount**: Both set a priority fee rate in micro-lamports and a transfer amount. `@solana/kit` expresses the amount as a `lamports(...)` value; `@solana/web3.js` multiplies by `LAMPORTS_PER_SOL`. The priority fee adjusts the compute unit price to influence processing speed. * **Transaction instructions**: Both attach a compute-unit-price instruction (the priority fee) and a SOL transfer. `@solana/kit` uses `getSetComputeUnitPriceInstruction` and `getTransferSolInstruction`; `@solana/web3.js` uses `ComputeBudgetProgram.setComputeUnitPrice` and `SystemProgram.transfer`. The `@solana/kit` version additionally sizes and appends a compute-unit-*limit* instruction after simulating (see below). > [!NOTE] > > In this example, the micro-lamport amount is arbitrary, but you can calculate it more dynamically using the **[getRecentPrioritizationFees](/reference/solana-getrecentprioritizationfees)** method ### Construct and send the transaction * **Fetch the latest blockhash**: Both retrieve the most recent blockhash at the `confirmed` commitment. It ties the transaction to a recent point in the chain's history, and its `lastValidBlockHeight` bounds how long the transaction stays valid. * **Build the message**: `@solana/kit` composes a version 0 message with the `pipe` helper (fee payer signer, blockhash lifetime, instructions); `@solana/web3.js` builds a `TransactionMessage` and calls `.compileToV0Message()`, then wraps it in a `VersionedTransaction`. * **Size the compute unit limit (`@solana/kit`)**: `estimateComputeUnitLimitFactory` simulates the transaction against the node to measure the compute units it consumes, then sets the limit to that value plus a \~10% margin. Sizing the limit accurately is recommended in both SDKs; the classic example omits it for brevity. * **Sign the transaction**: `@solana/kit` uses `signTransactionMessageWithSigners`; `@solana/web3.js` calls `transaction.sign([FROM_KEYPAIR])`. * **Submit and confirm**: `@solana/kit` uses `sendAndConfirmTransactionFactory`, which confirms over websockets against the blockhash and `lastValidBlockHeight`. `@solana/web3.js` calls `sendTransaction` then `confirmTransaction` with the blockhash and `lastValidBlockHeight` (the current form — the older single-signature `confirmTransaction(signature)` overload is the deprecated one). * **Logging and error handling**: Both log key milestones and wrap submission in a `try/catch` so failures are reported. ### Run the script with Priority Fees After familiarizing ourselves with the code, it's time to execute a Solana transaction incorporating a priority fee for faster processing. By default, this script transfers `0.001 SOL` from the sender's account to the same account, demonstrating the application of a priority fee. You can adjust the recipient, the priority fee rate, and the transfer amount by modifying these lines: > [tab] @solana/kit > > ```typescript TypeScript theme={"system"} > // Config priority fee and amount to transfer > const PRIORITY_RATE = 25000; // Adjust the priority fee rate in micro-lamports here > > const AMOUNT_TO_TRANSFER = lamports(1_000_000n); // Change the transfer amount here (in lamports) > > // To send to another wallet, import `address` from @solana/kit and set the > // transfer destination to address("RECIPIENT_ADDRESS") instead of signer.address. > ``` > [tab] @solana/web3.js (classic) > > ```typescript TypeScript theme={"system"} > // Config priority fee and amount to transfer > const PRIORITY_RATE = 25000; // Adjust the priority fee rate in micro-lamports here > > const AMOUNT_TO_TRANSFER = 0.001 * LAMPORTS_PER_SOL; // Change the transfer amount here > > // Adjust the recipient public key for the transfer > const toPubkey = FROM_KEYPAIR.publicKey; // Change to desired recipient's public key > ``` To execute the script after setting up your desired parameters, run the following command in your terminal: ```bash Bash theme={"system"} ts-node main.ts ``` This will initiate the transaction with the applied priority fee, logging the process to the console. The output will resemble the following: > [tab] @solana/kit > > ```shell Shell theme={"system"} > $ ts-node main.ts > > Initial Setup: Public Key - CzNGm14nMopjGYyycMbWqEF2e1aEHcJLKk2CHw9BiZwC > ✅ - Fetched latest blockhash. Last valid height: 235678920 > ✅ - Built transaction message > ✅ - Estimated compute units: 300 > ✅ - Transaction signed > Sending 0.001 SOL from CzNGm14nMopjGYyycMbWqEF2e1aEHcJLKk2CHw9BiZwC to CzNGm14nMopjGYyycMbWqEF2e1aEHcJLKk2CHw9BiZwC with priority fee rate 25000 micro-lamports > 🚀 Transaction Successfully Confirmed! > https://solscan.io/tx/4iJ6MZQ8kBBp4KeZrcrm9fkfL92mccbgD6uMFu84iNkxSi589E1yw4fbpHUcpEk3LhtDYQ76vjbyNQaG52TSaWfT > ``` > [tab] @solana/web3.js (classic) > > ```shell Shell theme={"system"} > $ ts-node main.ts > > Initial Setup: Public Key - CzNGm14nMopjGYyycMbWqEF2e1aEHcJLKk2CHw9BiZwC > ✅ - Fetched latest blockhash. Last Valid Height: 235678920 > ✅ - Compiled Transaction Message > ✅ - Transaction Signed > Sending 0.001 SOL from CzNGm14nMopjGYyycMbWqEF2e1aEHcJLKk2CHw9BiZwC to CzNGm14nMopjGYyycMbWqEF2e1aEHcJLKk2CHw9BiZwC with priority fee rate 25000 microLamports > ✅ - Transaction sent to network > 🚀 Transaction Successfully Confirmed! > https://solscan.io/tx/4iJ6MZQ8kBBp4KeZrcrm9fkfL92mccbgD6uMFu84iNkxSi589E1yw4fbpHUcpEk3LhtDYQ76vjbyNQaG52TSaWfT > Transaction Fee: 10000 Lamports > ``` > [!NOTE] > > Here, you can find a transaction example on [Solscan](https://solscan.io/tx/4iJ6MZQ8kBBp4KeZrcrm9fkfL92mccbgD6uMFu84iNkxSi589E1yw4fbpHUcpEk3LhtDYQ76vjbyNQaG52TSaWfT). You can see the applied priority fee under **Instruction Details > #2 - Compute Budget: Set Compute Unit Price**. ## Conclusion In this guide, we've explored the concept of priority fees on the Solana blockchain, demonstrating their significance and how they can be utilized to ensure faster transaction processing times, particularly during periods of high network congestion. You've learned about the mechanics of priority fees, including how they incentivize validators with higher fees for quicker transaction confirmations. Through a practical example, we've walked you through implementing priority fees in your Solana transactions in TypeScript, using both `@solana/kit` and the classic `@solana/web3.js`. By adjusting the compute unit price, we've shown how you can prioritize your transactions over others, which is especially useful for time-sensitive operations. This guide has equipped you with the knowledge to incorporate priority fees into your Solana-based applications, enhancing the user experience by reducing wait times for transaction confirmations. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Multiple RPC endpoints for dApp performance Source: https://docs.chainstack.com/docs/solana-how-to-use-multiple-rpc-endpoints-optimize-dapp-performance Configure multiple Solana RPC endpoints with load balancing and failover for improved dApp reliability and performance using Chainstack node access. **TLDR:** * Use multiple Solana RPC endpoints to reduce latency and avoid single points of failure. * A distributed setup ensures higher availability and faster data updates, especially for real-time balance monitoring. * This sample Node.js code with a connection pool approach demonstrates how to subscribe to account balance changes across different geographic locations simultaneously. * Environment variables keep your endpoints secure while allowing easy setup and configuration. ## Main article This guide will explore how a distributed approach to querying the Solana blockchain can reduce latency and provide a more resilient infrastructure for your DApps. Whether you're a blockchain enthusiast or a seasoned developer looking to optimize your Solana-based projects, this tutorial is crafted to offer valuable insights and practical steps to achieve a seamless and efficient blockchain interaction experience. ### A brief overview of Solana's architecture Solana stands out in the blockchain world for its unprecedented throughput and minimal transaction costs stemming from its innovative architecture. The Proof of History (PoH) consensus mechanism is at the heart of Solana's efficiency. This unique solution enables greater scalability by creating a historical record proving that an event has occurred at a specific time. This, coupled with the blockchain's underlying Proof of Stake (PoS) consensus, allows Solana to process thousands of transactions per second without compromising decentralization or security. ## A simple project At the heart of this tutorial lies a practical, hands-on example that will guide us through the intricacies of interacting with Solana's network using multiple RPC endpoints. We will develop a robust piece of JavaScript code to manage a pool of Solana connections. This code establishes connections to several RPC endpoints across different geographic locations and intelligently distributes operations amongst these endpoints to achieve optimal performance and reliability. Through this example, we aim to listen to the balance changes of a specific Solana account from multiple nodes in parallel. Using multiple nodes in different regions simultaneously can improve reliability and performance, and the first node to get the updated data will win the race. ### The importance of multiple endpoints The traditional approach of interacting with the blockchain through a single RPC endpoint can be a bottleneck for your application. This is where the strategy of using multiple endpoints across various geographic locations comes into play, offering several key advantages: * **Redundancy**: By having your application connected to multiple nodes, you mitigate the risk of a single point of failure. If one node goes down or becomes unreachable, your application can seamlessly switch to another, ensuring uninterrupted service. This is because each node is physically running on a different machine. * **Reduced Latency**: Latency can vary significantly based on the geographic distance between your user and the RPC endpoint. Generally, the endpoint closer to the user will respond faster, but sometimes, another node may pick up new data sooner. * **Higher Availability**: Different nodes may have varying loads or maintenance schedules. Running multiple endpoints ensures that your application always has access to at least one up-to-date and responsive node, thereby improving the reliability of your service. Throughout this tutorial, we'll guide you through setting up a Solana connection pool to interact with multiple RPC endpoints efficiently. We aim to empower your applications to leverage Solana's high-performance blockchain most effectively, ensuring users enjoy a fast, reliable, and seamless experience. ## Prerequisites Before we get into the code, ensuring you have the right tools and setup is crucial. Let’s walk through everything you’ll need: ### Environment setup To interact with the Solana blockchain and execute the scripts you'll write, certain environmental setups are necessary: * **Node.js**: Install Node.js (version 18 or above) on your machine. Node.js is fundamental for running our scripts and managing dependencies. You can download it from [the official Node.js website](https://nodejs.org/). * **npm (Node Package Manager)**: npm is the default package manager for Node.js and is indispensable for installing the libraries our project requires. It comes bundled with Node.js, so there's no separate installation process. ### Deploy a Chainstack Solana node Deploy multiple [reliable Solana RPC endpoints](https://chainstack.com/build-better-with-solana/). Note that you need to be on a [paid plan](https://chainstack.com/pricing/) to deploy multiple nodes. 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Installing required packages Our project relies on a Solana JavaScript library plus `dotenv` (which loads environment variables from a `.env` file into `process.env`, keeping RPC URLs out of your code). This guide shows both Solana JS libraries side by side — both are actively maintained, so use whichever fits your project: * **`@solana/kit`** — the newer, tree-shakable, functional SDK. Account subscriptions are exposed through a websocket client (`createSolanaRpcSubscriptions`). * **`@solana/web3.js`** — the classic object-oriented API, where a `Connection` object carries both HTTP and websocket calls (`onAccountChange`). To install, navigate to your project directory and run: > [tab] @solana/kit > > ```bash Bash theme={"system"} > npm install @solana/kit dotenv > ``` > [tab] @solana/web3.js (classic) > > ```bash Bash theme={"system"} > npm install @solana/web3.js dotenv > ``` Then add this line to your `package.json` file: ```jsx jsx theme={"system"} "type": "module", ``` ### Setting up environment variables We'll use environment variables to manage our RPC endpoints and other sensitive information. This method keeps our credentials secure and makes our application easily configurable without hardcoding sensitive data: 1. Create a `.env` file in the root of your project directory. 2. Add your Solana RPC URLs and sensitive information to the `.env` file. For instance: ``` # HTTPS endpoints SOLANA_RPC_NODE_1="YOUR_CHAINSTACK_RPC" SOLANA_RPC_NODE_3="YOUR_CHAINSTACK_RPC" SOLANA_RPC_NODE_2="YOUR_CHAINSTACK_RPC" # WSS endpoints SOLANA_WSS_NODE_1="YOUR_CHAINSTACK_WSS" SOLANA_WSS_NODE_3="YOUR_CHAINSTACK_WSS" SOLANA_WSS_NODE_2="YOUR_CHAINSTACK_WSS" ``` ### Additional considerations * **Security**: Always keep your private keys and sensitive information secure. Do not commit your `.env` file or any files containing sensitive data to version control. Make sure to include the `.env` file in your `.gitignore`. With these prerequisites out of the way, you're now set to dive into the code. ## The code Now that we are set, create a new file in your project named `index.js` and paste the following code into it. > [tab] @solana/kit > > ```javascript Javascript theme={"system"} > import { createSolanaRpcSubscriptions, address } from "@solana/kit"; > import "dotenv/config"; > > // Account subscriptions are websocket-only, so each pool entry needs a WSS URL. > class SubscriptionPool { > constructor(endpoints) { > this.subscribers = endpoints.map(endpoint => ({ > wssUrl: endpoint.wss, > rpcSubscriptions: createSolanaRpcSubscriptions(endpoint.wss), > })); > } > } > > let latestLoggedBalance = null; // Shared state to track the latest logged balance > > async function listenForBalanceChanges(publicKey, subscriptionPool, abortSignal) { > const account = address(publicKey); > > // Run one subscription per endpoint concurrently; the first node to see the > // new data logs it first. > await Promise.all(subscriptionPool.subscribers.map(async ({ rpcSubscriptions, wssUrl }) => { > console.log(`Setting up balance change listener for node: ${wssUrl.slice(0,33)}`); > > // subscribe() resolves to an async iterable of notifications > const notifications = await rpcSubscriptions > .accountNotifications(account, { commitment: "confirmed" }) > .subscribe({ abortSignal }); > > for await (const notification of notifications) { > const newBalance = notification.value.lamports; // bigint in @solana/kit > > // Check if this balance change has already been logged > if (latestLoggedBalance !== newBalance) { > const dateTimeString = new Date().toISOString(); // ISO 8601 format > > console.log(`[${dateTimeString}] Balance change detected on node: ${wssUrl.slice(0,33)}`); > console.log(`[${dateTimeString}] New balance: ${newBalance} lamports`); > console.log("======================================================================"); > > // Update the shared state with the new balance > latestLoggedBalance = newBalance; > } > } > })); > } > > (async () => { > const nodeEndpoints = [ > { wss: process.env.SOLANA_WSS_NODE_1 }, > { wss: process.env.SOLANA_WSS_NODE_2 }, > { wss: process.env.SOLANA_WSS_NODE_3 }, > ]; > > const subscriptionPool = new SubscriptionPool(nodeEndpoints); > const publicKey = "G98hD3T33SiJa8WcWgJ9coT5fz1F3ciwJjKnecxxd3Bi"; // Replace with the address you're interested in > > // Listen for balance changes on all nodes; call abortController.abort() to stop > const abortController = new AbortController(); > await listenForBalanceChanges(publicKey, subscriptionPool, abortController.signal); > })(); > ``` > [tab] @solana/web3.js (classic) > > ```javascript Javascript theme={"system"} > import { Connection, PublicKey } from '@solana/web3.js'; > import 'dotenv/config'; > > class ConnectionPool { > constructor(endpoints) { > this.connections = endpoints.map(endpoint => ({ > rpcUrl: endpoint.rpc, > connection: new Connection(endpoint.rpc, { wsEndpoint: endpoint.wss }), > })); > } > } > > let latestLoggedBalance = null; // Shared state to track the latest logged balance > > function listenForBalanceChanges(publicKey, connectionPool) { > connectionPool.connections.forEach(({ connection, rpcUrl }) => { > console.log(`Setting up balance change listener for node: ${rpcUrl.slice(0,33)}`); > connection.onAccountChange(new PublicKey(publicKey), (accountInfo) => { > const newBalance = accountInfo.lamports; > > // Check if this balance change has already been logged > if (latestLoggedBalance !== newBalance) { > const now = new Date(); > const dateTimeString = now.toISOString(); // Converts current time to ISO 8601 format > > console.log(`[${dateTimeString}] Balance change detected on node: ${rpcUrl.slice(0,33)}`); > console.log(`[${dateTimeString}] New balance: ${newBalance} lamports`); > console.log("======================================================================") > > // Update the shared state with the new balance > latestLoggedBalance = newBalance; > } > }, 'confirmed'); > }); > } > > (async () => { > const nodeEndpoints = [ > { rpc: process.env.SOLANA_RPC_NODE_1, wss: process.env.SOLANA_WSS_NODE_1 }, > { rpc: process.env.SOLANA_RPC_NODE_2, wss: process.env.SOLANA_WSS_NODE_2 }, > { rpc: process.env.SOLANA_RPC_NODE_3, wss: process.env.SOLANA_WSS_NODE_3 }, > ]; > > const connectionPool = new ConnectionPool(nodeEndpoints); > const publicKey = "G98hD3T33SiJa8WcWgJ9coT5fz1F3ciwJjKnecxxd3Bi"; // Replace with the address you're interested in > > // Listen for balance changes on all nodes > listenForBalanceChanges(publicKey, connectionPool); > })(); > ``` ## Code breakdown Both versions do the same thing — open one client per endpoint, subscribe to a single account across all of them, and log the first node to report each new balance. They differ only in how the subscription is opened and consumed. Here are the key components: ### Imports and environment variables The `@solana/kit` version imports `createSolanaRpcSubscriptions` and `address`; the `@solana/web3.js` version imports `Connection` and `PublicKey`. Both load `dotenv/config` to read the endpoints from `.env` and keep them out of the code. ### The connection/subscription pool Each pool entry holds one client per endpoint: * `@solana/web3.js` builds a `Connection` per node, passing the HTTP RPC URL plus `{ wsEndpoint }` for its websocket. * `@solana/kit` builds a `createSolanaRpcSubscriptions` client per node. Account subscriptions are websocket-only, which is why the `@solana/kit` pool takes just the `wss` URL for each endpoint. ### Shared state for balance tracking Both keep a single `latestLoggedBalance` so the same balance isn't logged twice when multiple nodes report the same change. In `@solana/kit` this value is a `bigint`; in `@solana/web3.js` it's a `number`. ### Listening for balance changes This is the core API difference between the two libraries: * `@solana/web3.js` registers a **callback**: `connection.onAccountChange(publicKey, (accountInfo) => { ... }, 'confirmed')`, where `accountInfo.lamports` is a `number`. * `@solana/kit` opens an **async iterable**: `await rpcSubscriptions.accountNotifications(account, { commitment: 'confirmed' }).subscribe({ abortSignal })`, then consumes it with `for await (const notification of notifications)`. Each `notification.value.lamports` is a `bigint`. Running one subscription per endpoint under `Promise.all` keeps them all listening concurrently, and the shared `abortSignal` tears them all down cleanly. ### Main execution flow Both define the endpoints from environment variables, build the pool, choose a public key to watch, and start listening. The `@solana/kit` version additionally creates an `AbortController` and awaits the listeners; call `abortController.abort()` to stop every subscription at once. ## Run the script To run the script, make sure to place your HTTPS and WSS endpoints in the `.env` file, then run the start command in the console: ```jsx jsx theme={"system"} node index ``` This will start the program and log any new balance change in real-time, like the following: ```jsx jsx theme={"system"} Setting up balance change listener for node: https://solana-mainnet.core.chain Setting up balance change listener for node: https://nd-094-012-520.p2pify.com Setting up balance change listener for node: https://nd-445-788-065.p2pify.com [2024-04-08T21:00:49.290Z] Balance change detected on node: https://nd-445-788-065.p2pify.com [2024-04-08T21:00:49.290Z] New balance: 1104861848 lamports ====================================================================== [2024-04-08T21:00:49.927Z] Balance change detected on node: https://nd-445-788-065.p2pify.com [2024-04-08T21:00:49.927Z] New balance: 1104856848 lamports ====================================================================== [2024-04-08T21:00:51.788Z] Balance change detected on node: https://nd-445-788-065.p2pify.com [2024-04-08T21:00:51.788Z] New balance: 1104851848 lamports ====================================================================== [2024-04-08T21:00:52.841Z] Balance change detected on node: https://nd-094-012-520.p2pify.com [2024-04-08T21:00:52.841Z] New balance: 1104846848 lamports ====================================================================== [2024-04-08T21:00:54.139Z] Balance change detected on node: https://nd-094-012-520.p2pify.com [2024-04-08T21:00:54.139Z] New balance: 1104841848 lamports ====================================================================== ``` ## Conclusion To wrap up this tutorial, we've explored the world of leveraging multiple RPC endpoints across different geographical locations to enhance the reliability and performance of Solana-based applications. We've demonstrated a pragmatic approach to optimizing blockchain interactions for a seamless user experience by implementing a real-time connection pool to listen for account balance changes. This method minimizes latency, mitigates the risk of single points of failure, and ensures your application remains resilient and responsive under varying network conditions. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Indexing subscription events with Geyser Source: https://docs.chainstack.com/docs/solana-indexing-subscription-events-geyser Decode and stream Solana Subscriptions & Allowances events in Python — created, cancelled, and pull transfers — from RPC or a Chainstack Yellowstone gRPC node, to build billing, revenue, and churn dashboards. **TLDR:** * The Solana Subscriptions & Allowances program emits its events (subscription created, cancelled, resumed, and the three transfer types) as **Anchor-compatible self-CPI inner instructions** — not log lines — tagged with the 8-byte `e445a52e51cb9a1d` prefix plus a 1-byte event type. * Decoding those events gives you off-chain billing state — revenue per plan, active vs. cancelled subscribers, every pull and its amount — without scanning account state. * You can pull events from any transaction over standard RPC, or stream them in real time and low latency through a Chainstack Yellowstone gRPC (Geyser) node. * This guide gives you a complete Python decoder for all six event types, plus a live Yellowstone gRPC listener, validated against live mainnet activity. ## Why index the events If you build anything on the [Subscriptions & Allowances program](/docs/solana-subscriptions-and-allowances) — a SaaS billing backend, a creator-payouts dashboard, a payment gateway — you need an off-chain view of what happened: who subscribed, who cancelled, which pulls succeeded, and how much revenue each plan produced. The program makes this clean because it emits a structured event on every state change. You don't reconstruct billing history by diffing account snapshots; you read the events. The question is only how you get them — by fetching a transaction over RPC, or by streaming them live through Geyser. We'll do both, on top of one shared decoder. ## How the program emits events Most Solana programs that emit events either write base64 blobs into program logs or, like Anchor, emit them through a **self-CPI**: the program calls its own no-op instruction, and the event payload rides along as that inner instruction's data. The Subscriptions program uses the self-CPI approach, which is more reliable than log parsing (logs get truncated; inner instructions don't). Each event's inner-instruction data is laid out as: ```text theme={"system"} [0:8] e445a52e51cb9a1d # Sha256("anchor:event")[:8], little-endian — the same tag Anchor uses [8] event type # 1 byte (see table) [9:] payload # fixed C-packed, little-endian fields ``` The program emits six event types: | Type | Byte | Payload fields | | - | - | - | | `SubscriptionCreated` | 0 | plan, subscriber, mint, created\_ts | | `SubscriptionCancelled` | 1 | plan, subscriber, expires\_at\_ts | | `SubscriptionTransfer` | 2 | subscription, plan, delegator, mint, amount, period\_start\_ts, period\_end\_ts, amount\_pulled\_in\_period, receiver | | `FixedTransfer` | 3 | delegation, delegator, delegatee, mint, amount, remaining\_amount, receiver | | `RecurringTransfer` | 4 | delegation, delegator, delegatee, mint, amount, period\_start\_ts, period\_end\_ts, amount\_pulled\_in\_period, receiver | | `SubscriptionResumed` | 5 | plan, subscriber, resumed\_ts | All pubkeys are 32 raw bytes; all integers are little-endian (`u64` for amounts, `i64` for timestamps). To find these events in a transaction, you look at its **inner instructions**, keep the ones whose program is the subscriptions program, and check whether the data starts with the tag. > [!NOTE] > > `SubscriptionTransfer` (model: subscription plan), `FixedTransfer` (allowance), and `RecurringTransfer` (recurring delegation) are the three "money moved" events — the ones you sum for revenue. `Created`, `Cancelled`, and `Resumed` track the subscription lifecycle for churn. ## Prerequisites * Python 3.10+ * `pip install solders base58 requests` for the decoder and RPC path * `pip install grpcio grpcio-tools` for the Geyser streaming path * A Chainstack Solana node — any node for the RPC path; a Trader or Dedicated Node with a **Yellowstone gRPC** endpoint for streaming ## The decoder This is the shared core — it turns a raw inner-instruction byte string into a typed event dict. Both the RPC and the Geyser paths feed bytes into `decode_event`. ```python theme={"system"} # events.py from solders.pubkey import Pubkey PROGRAM_ID = "De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44" EVENT_TAG = bytes.fromhex("e445a52e51cb9a1d") # Sha256("anchor:event")[:8], little-endian EVENT_NAMES = {0: "SubscriptionCreated", 1: "SubscriptionCancelled", 2: "SubscriptionTransfer", 3: "FixedTransfer", 4: "RecurringTransfer", 5: "SubscriptionResumed"} def _pk(b): # 32 raw bytes -> base58 string return str(Pubkey.from_bytes(b)) def _u64(b): return int.from_bytes(b, "little") def _i64(b): return int.from_bytes(b, "little", signed=True) def decode_event(data: bytes): """data = full inner-instruction data: 8-byte tag + 1-byte type + payload.""" if len(data) < 9 or data[:8] != EVENT_TAG: return None disc = data[8] p = data[9:] name = EVENT_NAMES.get(disc, f"Unknown({disc})") if disc == 0: # SubscriptionCreated return {"event": name, "plan": _pk(p[0:32]), "subscriber": _pk(p[32:64]), "mint": _pk(p[64:96]), "created_ts": _i64(p[96:104])} if disc == 1: # SubscriptionCancelled return {"event": name, "plan": _pk(p[0:32]), "subscriber": _pk(p[32:64]), "expires_at_ts": _i64(p[64:72])} if disc in (2, 4): # SubscriptionTransfer / RecurringTransfer (identical shape) first = "subscription" if disc == 2 else "delegation" second = "plan" if disc == 2 else "delegator" third = "delegator" if disc == 2 else "delegatee" return {"event": name, first: _pk(p[0:32]), second: _pk(p[32:64]), third: _pk(p[64:96]), "mint": _pk(p[96:128]), "amount": _u64(p[128:136]), "period_start_ts": _i64(p[136:144]), "period_end_ts": _i64(p[144:152]), "amount_pulled_in_period": _u64(p[152:160]), "receiver": _pk(p[160:192])} if disc == 3: # FixedTransfer return {"event": name, "delegation": _pk(p[0:32]), "delegator": _pk(p[32:64]), "delegatee": _pk(p[64:96]), "mint": _pk(p[96:128]), "amount": _u64(p[128:136]), "remaining_amount": _u64(p[136:144]), "receiver": _pk(p[144:176])} if disc == 5: # SubscriptionResumed return {"event": name, "plan": _pk(p[0:32]), "subscriber": _pk(p[32:64]), "resumed_ts": _i64(p[64:72])} return {"event": name, "raw_len": len(p)} ``` ## Option A: pull events from a transaction over RPC For backfills, webhooks, or a quick look at one transaction, fetch it with [`getTransaction`](/reference/gettransaction) and walk its inner instructions. In `jsonParsed` encoding, an inner instruction for a program the parser doesn't recognize comes back with a base58 `data` field and a `programId`. ```python theme={"system"} import base58 import requests from events import PROGRAM_ID, decode_event CHAINSTACK_RPC = "YOUR_CHAINSTACK_SOLANA_ENDPOINT" def events_in_tx(signature: str): body = {"jsonrpc": "2.0", "id": 1, "method": "getTransaction", "params": [signature, {"encoding": "jsonParsed", "maxSupportedTransactionVersion": 1}]} result = requests.post(CHAINSTACK_RPC, json=body, timeout=30).json()["result"] events = [] for group in (result["meta"].get("innerInstructions") or []): for ix in group["instructions"]: if ix.get("programId") != PROGRAM_ID: continue ev = decode_event(base58.b58decode(ix["data"])) if ev: events.append(ev) return events if __name__ == "__main__": for ev in events_in_tx("PASTE_A_SUBSCRIPTION_TX_SIGNATURE"): print(ev) ``` Run it against a collect transaction and you get the decoded transfer: ```python theme={"system"} {'event': 'SubscriptionTransfer', 'subscription': 'BCnjQmw2…', 'plan': '6zNfyVQK…', 'delegator': '9VUegdUs…', 'mint': 'EPjFWdd5…', 'amount': 1000000, 'period_start_ts': 1780447663, 'period_end_ts': 1780534063, 'amount_pulled_in_period': 1000000, 'receiver': 'GGFFGrDR…'} ``` `period_end_ts - period_start_ts` is exactly `86400` here — a 24-hour billing period — and `amount` is the tokens pulled this cycle. That's a billing row, straight off the chain. RPC polling is fine for backfills, but for a live dashboard you don't want to poll `getSignaturesForAddress` in a loop. That's what Geyser is for. ## Option B: stream events live with Yellowstone gRPC Chainstack's Yellowstone gRPC (Geyser) endpoint pushes transactions to you as they're confirmed. You subscribe once to transactions that mention the program, and decode events from each one with the same `decode_event`. **1. Generate the Geyser client stubs** Fetch the Yellowstone proto files and generate Python stubs. ```bash theme={"system"} mkdir proto curl -s -o proto/geyser.proto \ https://raw.githubusercontent.com/rpcpool/yellowstone-grpc/master/yellowstone-grpc-proto/proto/geyser.proto curl -s -o proto/solana-storage.proto \ https://raw.githubusercontent.com/rpcpool/yellowstone-grpc/master/yellowstone-grpc-proto/proto/solana-storage.proto python -m grpc_tools.protoc -Iproto \ --python_out=. --grpc_python_out=. \ proto/geyser.proto proto/solana-storage.proto ``` This writes `geyser_pb2.py`, `geyser_pb2_grpc.py`, and the `solana_storage_pb2.py` it depends on into your working directory. **2. Connect, subscribe, and decode** Chainstack Yellowstone endpoints use TLS and an `x-token` passed as gRPC metadata. Get both the gRPC endpoint and the token from your node's details in the [Chainstack console](https://console.chainstack.com/). The one subtlety on the streaming side: an inner instruction references its program by **index** into the transaction's account keys, and for versioned transactions that list is `message.account_keys` plus the addresses loaded from lookup tables. Resolve the full list before checking the program. ```python theme={"system"} # stream.py import asyncio import grpc from solders.pubkey import Pubkey import geyser_pb2 import geyser_pb2_grpc from events import PROGRAM_ID, decode_event GRPC_ENDPOINT = "yellowstone-solana-mainnet.core.chainstack.com:443" # From Access and credentials > Yellowstone gRPC; grpc takes host:port with no scheme X_TOKEN = "YOUR_X_TOKEN" PROGRAM_ID_BYTES = bytes(Pubkey.from_string(PROGRAM_ID)) def make_channel(): auth = grpc.metadata_call_credentials( lambda ctx, cb: cb((("x-token", X_TOKEN),), None) ) creds = grpc.composite_channel_credentials(grpc.ssl_channel_credentials(), auth) return grpc.aio.secure_channel(GRPC_ENDPOINT, creds) def build_request(): req = geyser_pb2.SubscribeRequest() f = req.transactions["subs"] f.account_include.append(PROGRAM_ID) f.vote = False f.failed = False req.commitment = geyser_pb2.CONFIRMED return req def decode_tx(info): # Resolve account keys, including addresses loaded from lookup tables. keys = list(info.transaction.message.account_keys) keys += list(info.meta.loaded_writable_addresses) keys += list(info.meta.loaded_readonly_addresses) for group in info.meta.inner_instructions: for ix in group.instructions: if keys[ix.program_id_index] != PROGRAM_ID_BYTES: continue ev = decode_event(bytes(ix.data)) if ev: yield ev async def main(): async def requests(): yield build_request() while True: await asyncio.sleep(3600) # keep the stream open while True: try: async with make_channel() as channel: stub = geyser_pb2_grpc.GeyserStub(channel) async for update in stub.Subscribe(requests()): if update.WhichOneof("update_oneof") != "transaction": continue for ev in decode_tx(update.transaction.transaction): print(f"[slot {update.transaction.slot}] {ev}") except grpc.aio.AioRpcError as e: print(f"stream dropped ({e.code().name}); reconnecting") await asyncio.sleep(2) if __name__ == "__main__": asyncio.run(main()) ``` Point it at a mainnet Yellowstone node and you see live subscription activity stream in: ```text theme={"system"} [slot 423913010] {'event': 'SubscriptionCancelled', 'plan': '…', 'subscriber': '…', 'expires_at_ts': …} [slot 423912992] {'event': 'SubscriptionTransfer', 'mint': 'EPjFWdd5…', 'amount': 19800, …} [slot 423905157] {'event': 'RecurringTransfer', 'mint': 'So111111…', 'amount': 1000000, …} ``` The reconnect loop matters: long-lived gRPC streams drop occasionally, and you want to resume rather than miss events. For production you'd also persist the last processed slot and replay from it on reconnect using the request's `from_slot` field. ## What you can build Once events are flowing into your own store, the billing views write themselves: * Revenue per plan — sum `amount` on `SubscriptionTransfer` grouped by `plan`. The mint tells you the currency; on mainnet you'll see USDC and wrapped SOL. * Monthly recurring revenue and active subscriber counts — count `SubscriptionCreated` minus `SubscriptionCancelled` (net of `SubscriptionResumed`) per plan. * Churn — `SubscriptionCancelled` events with their `expires_at_ts`, so you know when each subscriber actually lapses. * Failed or missed collections — expected a pull this period and saw no `SubscriptionTransfer`? That subscriber's payment didn't go through; flag it for retry or dunning. * Fee splits — a single collect can emit multiple `SubscriptionTransfer` events to different receivers (for example a 99% / 1% split between a merchant and a platform fee account), and each one decodes cleanly. All of it from a single subscription to one program, decoded with one function, with no billing database to reconcile against the chain — the chain *is* the billing database. ## Conclusion The Subscriptions & Allowances program was built to be indexed: every lifecycle change and every pull emits a typed event as a self-CPI inner instruction, tagged exactly like an Anchor event. Decode the tag and the fixed payload and you have your billing feed. Pull it from RPC for backfills, stream it from a Chainstack Yellowstone gRPC node for live dashboards, and reuse the same decoder for both. Next in this series: handing an AI agent a capped onchain budget with a fixed-delegation allowance, and where that meets x402 for per-call agentic commerce. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: pluggable transaction signing with solana-keychain Source: https://docs.chainstack.com/docs/solana-keychain-signing Sign Solana transactions with one interface across local keypairs, Turnkey, Privy, KMS, and HashiCorp Vault using solana-keychain, then submit through your Chainstack node. **TLDR:** * solana-keychain is an audited Solana Foundation library that gives you one signing interface across 12 backends — local keypairs, Turnkey, Privy, AWS and GCP KMS, Fireblocks, HashiCorp Vault, and more. * The signer it returns is a native Solana Kit signer, so it drops straight into your normal transaction flow. * Moving from a local key to a managed signer is a one-line change — the rest of your build, sign, and send code stays identical. * The signed transaction goes out through your Chainstack endpoint like any other. ## What solana-keychain is [solana-keychain](https://github.com/solana-foundation/solana-keychain) is a Solana Foundation library that provides a single signing interface backed by many implementations. Instead of learning each provider's SDK, you pick a backend and supply its credentials; how the key is stored and how signing happens is abstracted away. It ships for both TypeScript and Rust, is tree-shakeable, and has been audited by Accretion. The key design point: the signer you get back implements Solana Kit's `TransactionPartialSigner` and `MessagePartialSigner` interfaces. It is a Kit signer — you pass it anywhere Kit expects one (fee payer, instruction signer) with no adapter. Your transaction-building and submission code does not change when you switch backends. > [!NOTE] > > The packages are ESM-only. Your `package.json` needs `"type": "module"` (or a bundler that handles ESM), or you will hit a "no exports" error — the same requirement as Solana Kit itself. ## One interface, many backends There are two ways to construct a signer: * `createKeychainSigner({ backend, ...config })` — the unified entry point. The `backend` field selects the implementation and narrows the config to that backend's fields. * `createSigner(config)` — per-backend functions (`createMemorySigner`, `createTurnkeySigner`, and so on), if you prefer to import only what you use. Both return a `Promise`. The examples below use the unified `createKeychainSigner`. ## Sign and send with a local keypair Start with the memory backend — a local keypair, the same thing you would use in a script or for development. First, get a Solana RPC endpoint: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Then install the packages and send a transfer. The signer plugs directly into the Kit flow. ```bash theme={"system"} npm install @solana/keychain @solana/kit @solana-program/system ``` ```typescript TypeScript theme={"system"} import { createKeychainSigner } from "@solana/keychain"; import { address, appendTransactionMessageInstruction, createSolanaRpc, createSolanaRpcSubscriptions, createTransactionMessage, getSignatureFromTransaction, pipe, sendAndConfirmTransactionFactory, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from "@solana/kit"; import { getTransferSolInstruction } from "@solana-program/system"; const rpc = createSolanaRpc("YOUR_CHAINSTACK_HTTPS_ENDPOINT"); const rpcSubscriptions = createSolanaRpcSubscriptions("YOUR_CHAINSTACK_WSS_ENDPOINT"); // Swap only this block to change backend — everything below stays the same. // The memory backend accepts one of: keyPair, privateKey, privateKeyString, or // privateKeyPath (a Solana CLI keypair JSON file; Node only). const signer = await createKeychainSigner({ backend: "memory", privateKeyPath: "/path/to/keypair.json", }); const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const message = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(signer, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstruction( getTransferSolInstruction({ source: signer, destination: address("RecipientAddressHere"), amount: 1_000_000n, // 0.001 SOL }), tx, ), ); const signedTransaction = await signTransactionMessageWithSigners(message); await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction, { commitment: "confirmed", }); console.log("Sent:", getSignatureFromTransaction(signedTransaction)); ``` ## Swap in a managed signer To sign with a managed service instead of a local key, change only the `createKeychainSigner` call. Everything else — building the message, signing, sending through your Chainstack node — is untouched. On-chain, the transaction looks identical regardless of which backend signed it. ```typescript Turnkey theme={"system"} // Keep credentials in environment variables, not in source. const signer = await createKeychainSigner({ backend: "turnkey", organizationId: process.env.TURNKEY_ORG_ID, apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY, apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY, privateKeyId: process.env.TURNKEY_PRIVATE_KEY_ID, // the Turnkey key used to sign publicKey: process.env.TURNKEY_SOLANA_ADDRESS, // base58 Solana address }); ``` ```typescript Privy theme={"system"} const signer = await createKeychainSigner({ backend: "privy", appId: process.env.PRIVY_APP_ID, appSecret: process.env.PRIVY_APP_SECRET, walletId: process.env.PRIVY_WALLET_ID, // the Privy wallet ID, not the Solana address }); ``` > [!WARNING] > > For Privy, the signer needs the **wallet ID** — an opaque identifier from your Privy dashboard — not the wallet's public key. Passing the address will not work. ## Supported backends Pass the `backend` value below to `createKeychainSigner`, or install the matching package and call its `createSigner` function. | `backend` | Package | What it is | | - | - | - | | `memory` | `@solana/keychain-memory` | Local keypair — development and scripts | | `turnkey` | `@solana/keychain-turnkey` | Turnkey wallet API | | `privy` | `@solana/keychain-privy` | Privy wallet API | | `aws-kms` | `@solana/keychain-aws-kms` | AWS KMS | | `gcp-kms` | `@solana/keychain-gcp-kms` | Google Cloud KMS | | `fireblocks` | `@solana/keychain-fireblocks` | Fireblocks | | `vault` | `@solana/keychain-vault` | HashiCorp Vault (transit engine) | | `dfns` | `@solana/keychain-dfns` | Dfns | | `para` | `@solana/keychain-para` | Para | | `cdp` | `@solana/keychain-cdp` | Coinbase Developer Platform | | `crossmint` | `@solana/keychain-crossmint` | Crossmint | | `openfort` | `@solana/keychain-openfort` | Openfort | ## Gotchas * ESM only — set `"type": "module"` in `package.json`, as with Solana Kit. * `privateKeyPath` is Node-only — it reads from the filesystem. The other memory inputs (`keyPair`, `privateKey`, `privateKeyString`) work in any environment. * The memory backend stores the key as a non-extractable `CryptoKey` — you cannot read the raw key back out of the signer. * HashiCorp Vault needs the transit secrets engine enabled and an HTTPS address, and is the most involved backend to set up (tokens, namespaces, and TLS can all trip you up). The hosted services are far quicker to get signing. * For production, pin to a tagged release. The audit covers code through the v1.0.0 tag; backends added later (for example, `memory` and `openfort` in v1.2.0) are newer than the audited commit. ## Rust The Rust crate mirrors the TypeScript API. Backends are feature-gated, and you select the Solana SDK version for your validator target. ```toml Cargo.toml theme={"system"} # Local keypair only: solana-keychain = { version = "1.2", features = ["memory"] } # Managed backends, e.g.: # solana-keychain = { version = "1.2", features = ["turnkey", "fireblocks"] } # For Agave 3.x/4.x, select the v3 SDK (sdk-v2 is the default): # solana-keychain = { version = "1.2", features = ["memory", "sdk-v3"] } ``` Every backend implements the same `SolanaSigner` trait — `pubkey`, `sign_transaction`, `sign_message`, and `is_available` — so swapping backends is the same one-line change as in TypeScript. ## Additional resources * [Transferring SPL tokens on Solana](/docs/transferring-spl-tokens-on-solana-typescript) — build and send token transactions with Solana Kit. * [Enhancing SPL token transfers with retry logic](/docs/enhancing-solana-spl-token-transfers-with-retry-logic) — make submission robust. * [solana-foundation/solana-keychain](https://github.com/solana-foundation/solana-keychain) — source, backend docs, and the audit report. # Solana: Geyser and Yellowstone gRPC in Node.js Source: https://docs.chainstack.com/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js Stream real-time Solana transactions over gRPC using the Yellowstone client in Node.js with a Chainstack Geyser-enabled node for low-latency program data. ## Overview This quick guide shows you how to: * Check the Geyser connection using the @triton-one/yellowstone-grpc client in Node.js * Stream the programs using the @triton-one/yellowstone-grpc client in Node.js ## Prerequisites * Get a [reliable Solana RPC endpoint](https://chainstack.com/build-better-with-solana/) with the [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin). * Install [Yellowstone Node.js gRPC client](https://www.npmjs.com/package/@triton-one/yellowstone-grpc). ## Implementation Once you have the Chainstack Yellowstone gRPC Geyser endpoint and the authentication token, use the following examples to check the connection and to monitor programs. ### Connection checker ```js theme={"system"} const { default: Client } = require("@triton-one/yellowstone-grpc"); const ENDPOINT = "https://yellowstone-solana-mainnet.core.chainstack.com:443"; // From Access and credentials > Yellowstone gRPC; this client needs the https:// scheme const TOKEN = "YOUR_X_TOKEN"; // The x-token from the same tab (async () => { const client = new Client(ENDPOINT, TOKEN); const version = await client.getVersion(); console.log(version); })(); ``` This will print the connection status and the Geyser client version. ### Program watcher Note the use of ping every 10 seconds to keep the connection alive as specified in the docs: [Sending pings to keep the stream alive](https://docs.triton.one/project-yellowstone/dragons-mouth-grpc-subscriptions#sending-pings-to-keep-the-stream-alive). ```js theme={"system"} const { default: Client } = require("@triton-one/yellowstone-grpc"); const Base58 = require('bs58'); const ENDPOINT = "https://yellowstone-solana-mainnet.core.chainstack.com:443"; // From Access and credentials > Yellowstone gRPC; this client needs the https:// scheme const TOKEN = "YOUR_X_TOKEN"; // The x-token from the same tab const PING_INTERVAL_MILLISECONDS = 10000; // Send ping every 10 seconds // DEX Program IDs to watch const DEX_PROGRAM_IDS = [ "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK", "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C", "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo", "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB", "MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG", "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X", "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc" ]; async function main() { const client = new Client(ENDPOINT, TOKEN); try { console.log('Connecting to Solana...'); const stream = await client.subscribe(); // Prepare ping request const pingRequest = { ping: { id: 1 }, accounts: {}, accountsDataSlice: [], transactions: {}, transactionsStatus: {}, blocks: {}, blocksMeta: {}, entry: {}, slots: {}, }; // Set up periodic ping to keep the stream alive const pingInterval = setInterval(async () => { try { await new Promise((resolve, reject) => { stream.write(pingRequest, (err) => { if (err === null || err === undefined) { resolve(); } else { reject(err); } }); }); console.log("Ping sent to keep stream alive"); } catch (error) { console.error("Error sending ping:", error); } }, PING_INTERVAL_MILLISECONDS); const request = { accounts: {}, slots: {}, transactions: { dex: { vote: false, failed: false, accountExclude: [], accountRequired: [], accountInclude: DEX_PROGRAM_IDS } }, transactionsStatus: {}, entry: {}, blocks: {}, blocksMeta: {}, commitment: 'confirmed', accountsDataSlice: [], }; stream.write(request); console.log('Connection established - watching DEX transactions\n'); console.log('Monitoring programs:', DEX_PROGRAM_IDS); stream.on('data', (data) => { // Handle pong responses if (data.pong) { console.log("Received Pong response"); return; } if (data.transaction && data.transaction.transaction) { const tx = data.transaction; try { // Convert signature to string const signature = tx.transaction.signature.toString('hex'); // Find which DEX program was involved in this transaction let involvedPrograms = []; if (tx.transaction.transaction.message.accountKeys) { involvedPrograms = DEX_PROGRAM_IDS.filter(progId => tx.transaction.transaction.message.accountKeys.includes(progId)); } console.log('New DEX Transaction:', { signature: signature, slot: tx.slot, success: tx.transaction.meta?.err === null, accounts: tx.transaction.transaction.message.accountKeys.length, instructions: tx.transaction.transaction.message.instructions.length, lamportFee: tx.transaction.meta?.fee || 0, computeUnits: tx.transaction.meta?.computeUnitsConsumed || 0, involvedDEX: involvedPrograms }); // Log transaction details if (tx.transaction.meta?.logMessages) { console.log('Transaction logs:'); tx.transaction.meta.logMessages.forEach(log => console.log(log)); } console.log('----------------------------------------'); } catch (err) { console.error('Error processing transaction:', err); console.error('Raw signature:', tx.transaction.signature); } } }); stream.on("error", (error) => { console.error('Stream error:', error); clearInterval(pingInterval); // Clean up the ping interval on error }); // Clean up on stream close stream.on("close", () => { console.log("Stream closed"); clearInterval(pingInterval); }); } catch (error) { console.error('Error in subscription process:', error); } } // Run the main function main().catch((err) => { console.error('Unhandled error in main:', err); }); // Handle shutdown process.on('SIGINT', () => { console.log('Shutting down...'); process.exit(); }); ``` This will stream the data from the Solana program IDs as provided in `DEX_PROGRAM_IDS`. For a Python example, see [Solana: Listening to pump.fun token mint using Geyser](/docs/solana-listening-to-pumpfun-token-mint-using-geyser). > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Listening to pump.fun migrations to PumpSwap Source: https://docs.chainstack.com/docs/solana-listening-to-pumpfun-migrations-to-raydium Track pump.fun token graduations to the PumpSwap AMM (formerly Raydium) in real time using Solana WebSocket subscriptions and on-chain event parsing through Chainstack nodes. **TLDR:** * pump.fun tokens start on a bonding curve and, once it completes, **graduate to PumpSwap** — pump.fun's own AMM (program `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA`). * Before March 2025, graduated tokens migrated to Raydium. pump.fun launched PumpSwap on Mar 20, 2025, and graduations have gone there ever since — Raydium is now a rare legacy path. * Use `pumpfun_read_curve.py` to check whether a token's curve is still active or completed. * Use `pumpfun_listen_migrations_logsubscribe.py` (logsSubscribe on the migration program) or `pumpfun_listen_migrations_programsubscribe.py` (programSubscribe on the PumpSwap program) to track live graduations. > [!WARNING] > > This guide previously covered migration to **Raydium**. On **March 20, 2025** pump.fun launched its own AMM, [PumpSwap](https://x.com/pumpdotfun/status/1902762309950292010), and graduated tokens now migrate to PumpSwap instead of Raydium. The bonding-curve mechanics and the migration program are the same; only the destination AMM changed. The old Raydium listener is no longer in the repository. ## Main article For the full pump.fun bot, see [Solana: Creating a trading and sniping pump.fun bot](/docs/solana-creating-a-pumpfun-bot). See also [Solana: Listening to pump.fun token mint using only logsSubscribe](/docs/solana-listening-to-pumpfun-token-mint-using-only-logssubscribe). This article is an add-on for these scripts in the [pumpfun-bonkfun-bot repository](https://github.com/chainstacklabs/pumpfun-bonkfun-bot): > [card] [pumpfun\_read\_curve.py](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/read/pumpfun_read_curve.py) > [card] [pumpfun\_listen\_migrations\_logsubscribe.py](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/graduation/pumpfun_listen_migrations_logsubscribe.py) > [card] [pumpfun\_listen\_migrations\_programsubscribe.py](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/graduation/pumpfun_listen_migrations_programsubscribe.py) > [card] [compare\_migration\_listeners.py](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/tools/compare_migration_listeners.py) This guide shows you how to track the lifecycle of pump.fun tokens from their initial bonding curve state through graduation to PumpSwap using Python scripts. ## Prerequisites * Clone the [pumpfun-bonkfun-bot GitHub repository](https://github.com/chainstacklabs/pumpfun-bonkfun-bot) * Install the dependencies with [uv](https://github.com/astral-sh/uv): `uv sync` * Provide the node HTTP and WebSocket endpoints in your `.env` file as `SOLANA_NODE_RPC_ENDPOINT` and `SOLANA_NODE_WSS_ENDPOINT` ## Understanding pump.fun token graduation Tokens on pump.fun start trading against a bonding curve—a mathematical formula that determines the token's price based on supply and demand. Once the curve's `complete` flag flips to true, the token "graduates" and its liquidity migrates to an AMM. A token graduates when: 1. The bonding curve reaches completion status (tracked by the `complete` flag in the curve's state) 2. The accumulated SOL in the curve reaches the graduation threshold 3. The migration transaction is executed by the protocol **Where the liquidity goes:** since March 20, 2025, graduated tokens migrate to **PumpSwap** (also called Pump AMM), pump.fun's native AMM, program `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA`. Migration is now handled by a permissionless `migrate` instruction that creates a PumpSwap pool and burns the LP tokens (locking the liquidity). Before PumpSwap launched, graduations went to Raydium; that path is effectively retired. After graduation, trading moves from the bonding curve to PumpSwap's constant-product AMM: * Trading mechanics change from bonding curve to AMM * Liquidity is held in a PumpSwap pool * The token becomes tradable through the PumpSwap program and any aggregator that routes to it ## Monitoring tools This guide covers the scripts for tracking graduation: 1. `cookbook/pumpfun/read/pumpfun_read_curve.py` — checks whether a token is still on the bonding curve or has completed and graduated 2. `cookbook/pumpfun/graduation/pumpfun_listen_migrations_logsubscribe.py` — monitors real-time graduations by listening to the migration program's events 3. `cookbook/pumpfun/graduation/pumpfun_listen_migrations_programsubscribe.py` — monitors real-time graduations by watching for new PumpSwap pool accounts 4. `tools/compare_migration_listeners.py` — runs both listeners side by side to compare detection latency ### Checking bonding curve status The `pumpfun_read_curve.py` script lets you check whether a token's bonding curve is still active or has completed and graduated. It derives the bonding curve address from the token mint and makes a [getAccountInfo | Solana](/reference/solana-getaccountinfo) call to read the curve state from the pump.fun program (`6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P`). > [!NOTE] > > The bonding curve account layout has evolved. The current state includes a `creator` field (and an `is_mayhem_mode` flag), so make sure your decoder matches the latest account struct in the repository's `pumpfun_read_curve.py`. The `complete` flag itself is unchanged. #### Usage ```shell Shell theme={"system"} uv run cookbook/pumpfun/read/pumpfun_read_curve.py TOKEN_ADDRESS ``` Replace `TOKEN_ADDRESS` with the Solana address of the token you want to check. #### Example output For a completed (graduated) bonding curve: ``` Token status: -------------------------------------------------- Token mint: TokenAddressHere... Bonding curve: BondingCurveAddressHere... Bump seed: 255 -------------------------------------------------- Bonding curve status: -------------------------------------------------- Creator: CreatorAddressHere... Completed: ✅ Migrated -------------------------------------------------- Note: This bonding curve has completed and liquidity has migrated to PumpSwap. ``` ### Monitoring graduations in real time There are two reliable ways to detect a graduation, and the repository ships a script for each. **Option 1 — listen to the migration program (logsSubscribe).** The migration wrapper program [39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg](https://solscan.io/account/39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg) emits a `Migrate` event when a token graduates. `pumpfun_listen_migrations_logsubscribe.py` subscribes with [logsSubscribe | Solana](/reference/logssubscribe-solana), parses the event, and prints the migration signature, the token mint, and the new PumpSwap pool address. The event carries the `baseMint`, `quoteMint`, decimals, and the base/quote amounts deposited into the pool. > [!WARNING] > > `logsSubscribe` skips transactions with truncated logs (no `Program data` field), so a small number of migrations can be missed. To cover those, follow up with a [`getTransaction`](/reference/gettransaction) call or run the programSubscribe listener below in parallel. **Option 2 — watch for new PumpSwap pools (programSubscribe).** `pumpfun_listen_migrations_programsubscribe.py` subscribes to the PumpSwap program `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` and detects new pool (market) accounts as they are created, filtering by the pool account discriminator and length. This catches every new pool, but consumes a high volume of WebSocket messages. Run `tools/compare_migration_listeners.py` to run both at once and measure which detects a given graduation first across your RPC providers. #### Usage ```shell Shell theme={"system"} # Listen via the migration program's events uv run cookbook/pumpfun/graduation/pumpfun_listen_migrations_logsubscribe.py # Or watch for new PumpSwap pool accounts uv run cookbook/pumpfun/graduation/pumpfun_listen_migrations_programsubscribe.py ``` #### Example output When a graduation occurs: ```shell Shell theme={"system"} Migrate detected! Signature: 5KtPn3... Token (base mint): TokenAddressHere... PumpSwap pool: PoolAddressHere... ================================================== ``` > [!NOTE] > > **Important for production:** `logsSubscribe` and `programSubscribe` can experience stability issues and dropped messages with high-traffic programs, including connection errors and data skipping. For production monitoring, consider using [Geyser as recommended by the Solana team](https://github.com/anza-xyz/agave/issues/3879#issuecomment-2519700883) for more reliable streaming. Learn more about [Yellowstone Geyser](/docs/yellowstone-grpc-geyser-plugin). ## Use cases These monitoring tools are particularly useful for: * Traders who need to adjust their strategies when trading moves to PumpSwap * Arbitrage bots that operate differently on bonding curves vs AMMs * Market makers looking to provide liquidity as soon as tokens graduate * Sniping successful tokens on PumpSwap early * Analytics tools tracking the pump.fun ecosystem ## Conclusion Understanding and monitoring the token graduation process from pump.fun to PumpSwap is crucial for trading strategies. These tools help you stay informed about the state and location of token liquidity, allowing you to adapt your trading approach accordingly. For the complete trading bot implementation, see [Creating a pump.fun trading bot](/docs/solana-creating-a-pumpfun-bot). > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Listening to pump.fun token mint using Geyser Source: https://docs.chainstack.com/docs/solana-listening-to-pumpfun-token-mint-using-geyser Stream pump.fun token mint events in real time using Geyser and Yellowstone gRPC on a Chainstack Solana node for low-latency token launch detection. ## Overview This guide shows you how to listen for pump.fun token creation events in real time using Geyser. In the [pumpfun-bonkfun-bot repository](https://github.com/chainstacklabs/pumpfun-bonkfun-bot), there is the [`cookbook/pumpfun/listen/pumpfun_listen_tokens_geyser.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/listen/pumpfun_listen_tokens_geyser.py) script. It demonstrates how to capture the new token's name, symbol, mint address, user (creator), bonding curve address, and associated bonding curve address—no methods like [blockSubscribe](ref:blocksubscribe-solana) extra RPC calls like [getTransaction](ref:gettransaction) are required. The listener in this repo subscribes to successful transactions that include the pump.fun program and parses only the "Create" instruction (checking the first 8 bytes of instruction data), decoding it to `TokenInfo` in micro‑seconds. ## Implementation details ### How the pump.fun bot uses Geyser 1. Subscription — a single `SubscribeRequest` is sent with a transaction filter that contains the pump.fun program ID, commitment level, transaction status. 2. Parsing — every incoming `SubscribeUpdate.transaction` is checked for the 8‑byte discriminator of the Create instruction. 3. TokenInfo emission — when found, the platform event parser (`PumpFunEventParser` for pump.fun) constructs a `TokenInfo` dataclass (name, symbol, mint, curves, user) and hands it to the existing buyer / seller pipeline. 4. Trading flow — buy, hold, sell, and optional account cleanup happen exactly as with the WebSocket listeners. No other files need modification. The only operational difference is that tokens enter the queue earlier, giving the bot a cheaper entry price. ### Configuration changes To switch the bot to Geyser: 1. Set `listener_type: "geyser"` under `filters` in your bot's `.yaml` config in the `bots/` directory. The [`bots/bot-sniper-1-geyser.yaml`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/bots/bot-sniper-1-geyser.yaml) template has it set already. 2. Add the following to your `.env` file: * `GEYSER_ENDPOINT` — the gRPC endpoint from **Access and credentials** > **Yellowstone gRPC** in host-and-port form, as `yellowstone-solana-mainnet.core.chainstack.com:443`. The bot uses the Python `grpc` package, which takes the host and port with no scheme, so drop any `https://` prefix and add `:443` if the console omits it * `GEYSER_API_TOKEN` — the x-token from the same tab ## Benchmarking listeners Run the benchmark script. It runs for the number of seconds set in `TEST_DURATION` at the top of [`tools/compare_listeners.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/tools/compare_listeners.py): ```bash theme={"system"} uv run tools/compare_listeners.py ``` It launches all available types of listeners (`blockSubscribe`, `logsSubscribe`, Geyser, and PumpPortal) and records all detected mints. The results of the script are tables with statistics on how many and with which latency mints have been detected by each listener. The absolute numbers depend on your provider and network path, but Geyser is consistently the front‑runner. ## Summary Switching to the Geyser listener is a two‑step change—update `.env` and set `listener_type: "geyser"` in your bot's `.yaml` config. All higher‑level trading code remains untouched, yet you gain a tangible latency advantage when sniping newly created pump.fun tokens. ## Related guides * [Solana: Creating a trading and sniping pump.fun bot](/docs/solana-creating-a-pumpfun-bot) * [Solana: Listening to pump.fun migrations to PumpSwap](/docs/solana-listening-to-pumpfun-migrations-to-raydium) ### About author > [card] Anton Sauchyk > > ![Anton Sauchyk](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1817926677730664448/3nNn0T2p_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1d13d3584862f1be3a84fa3f8e38fea7) > > Developer Advocate @ Chainstack > \ > Multiple years of software development and Web3 expertise > > [https://github.com/smypmsa](https://github.com/smypmsa) > > [https://x.com/sensuniama](https://x.com/sensuniama) > > [https://www.linkedin.com/in/anton-sauchyk/](https://www.linkedin.com/in/anton-sauchyk/) # Solana: Listen to pump.fun mints via logsSubscribe Source: https://docs.chainstack.com/docs/solana-listening-to-pumpfun-token-mint-using-only-logssubscribe Capture newly created pump.fun tokens in real time by subscribing to Solana program logs with logsSubscribe and decoding mint event data. **TLDR:** * This script captures newly created pump.fun tokens by subscribing solely to program logs (logsSubscribe). * On seeing the “Create” instruction, it decodes the token name, symbol, URI, mint address, bonding curve address, and user, then computes the associated bonding curve address. * This eliminates the need for extra RPC calls (like blockSubscribe or getTransaction), giving real-time data to enable fast sniping of newly minted tokens. * Compare it with blockSubscribe in separate scripts to see which offers quicker event detection in your setup. ## Main article This guide shows you how to listen for pump.fun token creation events in real time using only the [logsSubscribe](/reference/logssubscribe-solana) method. In the [pumpfun-bonkfun-bot repository](https://github.com/chainstacklabs/pumpfun-bonkfun-bot), the [`cookbook/pumpfun/listen/pumpfun_listen_tokens_logsubscribe.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/cookbook/pumpfun/listen/pumpfun_listen_tokens_logsubscribe.py) script demonstrates how to capture the new token’s name, symbol, mint address, user (creator), bonding curve address, and associated bonding curve address—no methods like [blockSubscribe](/reference/blocksubscribe-solana) or extra RPC calls like [getTransaction](/reference/gettransaction) are required. There's difference in speed in how `blockSubscribe` and `logsSubscribe` work. Also, `blockSubscribe` is available on all Chainstack plans, but with other providers, it may not be available on the free tier plans or even lower tier paid plans. There is also difference in how `blockSubscribe` and `logsSubscribe` work, hence this article and example. * `blockSubscribe` — extracts and decodes transactions as the are streamed in the blocks produced. And the script in the bot decodes them on the fly. This means that we get all the necessary data to do our sniping using the `blockSubscribe` method. * `logsSubscribe` — only prints the pump.fun program logs, in our case—for token mints. The problem is we can get and decode from the logs the new token’s name, symbol, mint address, user (creator), and bonding curve address. But the logs do not contain the associated bonding curve address, which we need to do a sniping/buying transaction. The script discussed in this article (`cookbook/pumpfun/listen/pumpfun_listen_tokens_logsubscribe.py`) solves the issue by computing the associated bonding curve address from the bonding curve address on the fly and prints it. This way it makes it possible to snipe tokens on pump.fun using the `logsSubscribe` method only. which in general has better availability across providers and may work faster for you. > [!WARNING] > > **Note on blockSubscribe limitations:** While `blockSubscribe` provides transaction data, it has known stability issues with high-traffic programs—including connection errors (1006, 1009) and data skipping. For the most reliable real-time data streaming, [Geyser is recommended by the Solana team](https://github.com/anza-xyz/agave/issues/3879#issuecomment-2519700883). See our [Geyser guide](/docs/solana-listening-to-pumpfun-token-mint-using-geyser) for the fastest and most stable implementation. Learn more about [Yellowstone Geyser](/docs/yellowstone-grpc-geyser-plugin). ## How it works * The script subscribes to Solana logs mentioning the pump.fun program (`PUMP_PROGRAM`). * Whenever the program logs an `Instruction: Create` event, the script looks at the `Program data:` line in the logs. * It decodes the data on-the-fly and extracts: * The token name * The token symbol * The token URI * The token mint address * The bonding curve address * The user (creator) address * It then computes the associated bonding curve address (similar to how an associated token account is derived) via `Pubkey.find_program_address()`. By using `logsSubscribe`, you can get new token details almost immediately, which is especially handy if you want to “snipe” tokens the moment they are created. ## Prerequisites 1. A Solana WebSocket endpoint (e.g., from Chainstack). 2. Python 3.11+ with the dependencies installed through [uv](https://github.com/astral-sh/uv): ```shell Shell theme={"system"} uv sync ``` 3. A `.env` file in the repository root that includes your WebSocket endpoint as `SOLANA_NODE_WSS_ENDPOINT`. ## Usage 1. Run: ```bash Bash theme={"system"} uv run cookbook/pumpfun/listen/pumpfun_listen_tokens_logsubscribe.py ``` 2. Watch the console for output like this: ```text text theme={"system"} Listening for new token creations from program: 6EF8rrecthR5... Subscription response: {"jsonrpc":"2.0","result":{"subscription":...}} Signature: 5TYqzsc... name: MyToken symbol: MYTK uri: https://arweave.net/... mint: 8cYMHFE... bondingCurve: 6GUb... user: Hwz3... Associated Bonding Curve: 5YvN... ########################################################################################## ``` ## Script overview 1. **Subscribe to logs** Uses `logsSubscribe` to monitor logs referencing the pump.fun program ID. 2. **Look for “Program log: Instruction: Create”** When that shows in the logs, the script checks subsequent logs for `Program data:` lines. 3. **Decode and parse** The script decodes the base64-encoded `Program data:` and extracts: * **name** (token name) * **symbol** (token symbol) * **uri** (metadata URI) * **mint** (the newly minted token address) * **bondingCurve** (the bonding curve address) * **user** (the token creator) It then calculates the associated bonding curve address via: ```python Python theme={"system"} derived_address, _ = Pubkey.find_program_address( [ bytes(bonding_curve), bytes(TOKEN_PROGRAM_ID), bytes(mint), ], ATA_PROGRAM_ID ) ``` 4. **Print details** Outputs the transaction signature, plus the fields noted above, along with the computed associated bonding curve. Because we rely on logs only, we do not need extra calls like `getTransaction`. This approach is ideal if you want to immediately act on the new token creation events in your trading or monitoring workflows. ## Compare performance The repository's `cookbook/pumpfun/listen/` directory has a listener for each method — `pumpfun_listen_tokens_logsubscribe.py` (logs only), `pumpfun_listen_tokens_blocksubscribe.py`, `pumpfun_listen_tokens_geyser.py`, and `pumpfun_listen_tokens_pumpportal.py` — and [`tools/compare_listeners.py`](https://github.com/chainstacklabs/pumpfun-bonkfun-bot/blob/main/tools/compare_listeners.py) runs them side by side and reports which detects a new token first: ```shell Shell theme={"system"} uv run tools/compare_listeners.py ``` Use it to see which method delivers the event data faster in your environment. ## Next steps * Integrate this script’s output into your buy/sell logic to snipe tokens right as they are created. * Optionally use a separate script or your own logic to monitor the token price (via the bonding curve) to decide when to buy or sell. With `logsSubscribe` alone, you can get the essential details for any new pump.fun token almost instantly. See also: > [card] [Solana: Creating a trading and sniping pump.fun bot](/docs/solana-creating-a-pumpfun-bot) > [card] [Solana: Listening to pump.fun migrations to PumpSwap](/docs/solana-listening-to-pumpfun-migrations-to-raydium) > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Fast unit testing with LiteSVM Source: https://docs.chainstack.com/docs/solana-litesvm-testing Test Solana programs in-process with LiteSVM — up to 100× faster than solana-test-validator, with time-travel, compute budgets, and multi-language support. **TLDR:** * LiteSVM boots a Solana virtual machine inside your test process. No validator, no RPC, no gossip — just an in-memory bank you drive directly from Rust, TypeScript, or Python. * It is 10–100× faster than `solana-test-validator` for unit tests, and the author of `solana-bankrun` has explicitly deprecated bankrun in favor of it. * Anchor v1.0.0 (Apr 2026) ships LiteSVM as the default test backend from `anchor init`. The Solana Foundation's dev-skill document lists LiteSVM alongside Mollusk as the unit-testing default. * This guide walks through the canonical testing stack (LiteSVM → Mollusk → Surfpool), loading and invoking your own program from Rust and TypeScript, Anchor integration via `anchor-litesvm`, SPL token helpers, clock and feature-gate manipulation, and the pitfalls that will trip up anyone coming from `solana-program-test` or bankrun. * All code targets [LiteSVM/litesvm](https://github.com/LiteSVM/litesvm) v0.11.0, the [litesvm](https://www.npmjs.com/package/litesvm) npm package v1.0.0, [brimigs/anchor-litesvm](https://github.com/brimigs/anchor-litesvm) v0.4.0, and [kevinheavey/solders](https://github.com/kevinheavey/solders) v0.27.1 as of April 2026. ## Why LiteSVM Running every test against `solana-test-validator` is slow. Each suite spins up a full validator, waits for genesis, loads every built-in program, and then runs your transactions through the real consensus pipeline. For an iterative red-green-refactor loop, that overhead destroys flow. LiteSVM is the opposite: it constructs a minimal in-process `Bank` containing only the programs and accounts you need, exposes it through an ergonomic `LiteSVM` struct, and lets you send transactions and inspect state with no RPC round-trip. A typical unit test takes tens of milliseconds instead of tens of seconds. The tradeoff is that LiteSVM is not a validator. It does not replicate, it does not gossip, it does not model leader rotation, and its simulation of edge cases like compute-unit pricing, blockhash expiry, or sysvar rollover is opt-in. For end-to-end integration tests you still want [Surfpool](https://github.com/txtx/surfpool) or a real devnet fork. For unit tests — especially the tight loop where you are writing business logic and want fast feedback — LiteSVM is the current best practice. ## Where LiteSVM fits Solana's modern testing stack is a pyramid. Each tier trades fidelity for speed and serves a different purpose. | Tier | Tool | Runs | Best for | | - | - | - | - | | Unit, single-instruction | [Mollusk](https://github.com/anza-xyz/mollusk) | In-process, single program, no bank state | Pure instruction logic, CU benchmarking, fixture-based regression | | Unit, multi-instruction | **LiteSVM** | In-process, full bank, sysvars, CPIs | End-to-end program flows, Anchor tests, token workflows | | Integration | [Surfpool](https://github.com/txtx/surfpool) | Local SVM with optional mainnet state fork | Cross-program state, realistic account layouts, RPC behavior | | End-to-end | Devnet / mainnet | Real network | Final validation before release | Mollusk is Anza's own minimal harness. It focuses on benchmarking a single instruction in isolation and is a better fit than LiteSVM when you want tight CU guarantees on a single entrypoint. The two tools are complementary — Anchor 1.0 ships docs for both. > [!NOTE] > > Under the hood, Surfpool uses LiteSVM as its execution engine. Understanding LiteSVM gives you the mental model for both. ### What LiteSVM replaces | Incumbent | How LiteSVM compares | | - | - | | [`solana-test-validator`](https://docs.anza.xyz/developing/test-validator) | 10–100× faster, no process boundary, no port management. Use the validator when you need a real RPC server or cross-process integration. | | [`solana-program-test`](https://docs.rs/solana-program-test) | Same authors; LiteSVM is the modern successor for new projects. Supports all loaders including upgradeable v3/v4; faster; ergonomic API. `solana-program-test` is still maintained by Anza and works fine for existing test suites. | | [`solana-bankrun`](https://github.com/kevinheavey/solana-bankrun) | **Deprecated by its own author** in favor of the `litesvm` npm package. | ## Install ```bash Rust theme={"system"} cargo add --dev litesvm ``` ```bash TypeScript theme={"system"} yarn add --dev litesvm # or: npm install --save-dev litesvm ``` ```bash Python theme={"system"} pip install solders # LiteSVM ships as `from solders.litesvm import LiteSVM` # There is no standalone `litesvm` package on PyPI. ``` > [!WARNING] > > **Agave 3.1 hard pin.** LiteSVM 0.11.0 pins `agave-feature-set = 3.1.0` and the rest of the `solana-*` crates to 3.x. If your project still depends on `solana-sdk` 1.x or 2.x, you cannot mix crate versions — upgrade the whole project first. Rust MSRV is 1.86. > [!WARNING] > > **No native Windows support.** LiteSVM does not ship Windows binaries ([issue #215](https://github.com/LiteSVM/litesvm/issues/215)). The only supported path today is Docker or WSL2. ### Dependencies and pinning LiteSVM does not re-export the `solana-*` crates its API uses, so your `Cargo.toml` needs each one explicitly. Modern Solana has also dropped the monolithic `solana-sdk` in favor of fine-grained crates — the ones you actually need for a LiteSVM test are: ```toml theme={"system"} [dev-dependencies] litesvm = "0.11" litesvm-token = "0.11" solana-account = "3.2" solana-address = "2.0" solana-keypair = "3.1" solana-message = "3.0" solana-signer = "3.0" solana-system-interface = { version = "2.0", features = ["bincode"] } solana-transaction = "3.0" # If using Anchor (anchor-litesvm requires solana-account 3.4+): anchor-lang = "1.0" anchor-spl = "1.0" anchor-litesvm = "0.4" ``` Prefer caret-style constraints when everything resolves cleanly. If you hit drift — someone else's machine gets a different transitive version — lock the offending crate with `=` or a `Cargo.lock` committed to your repo. > [!WARNING] > > `solana-system-interface` gates the free functions like `transfer`, `create_account` behind the `bincode` feature. Enable it or you will get `no function named 'transfer'` errors. Version drift between these crates is the most common source of compile errors — people keep asking [variations of the same question on Solana Stack Exchange](https://solana.stackexchange.com/questions/tagged/litesvm). If you hit `no method named resize on AccountInfo` or `VersionedTransaction: From not satisfied`, the fix is almost always: ```bash theme={"system"} cargo update solana-account-info --precise 2.3.0 # or pin the offending crate with a `=` requirement above. ``` ## First test: transfer SOL The minimal test that exercises the full loop — airdrop, build a transaction, send it, read accounts back. No program of your own yet. ```rust Rust theme={"system"} use litesvm::LiteSVM; use solana_address::Address; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_system_interface::instruction::transfer; use solana_transaction::Transaction; #[test] fn transfers_sol() { let mut svm = LiteSVM::new(); let alice = Keypair::new(); let bob = Address::new_unique(); svm.airdrop(&alice.pubkey(), 10_000_000_000).unwrap(); let ix = transfer(&alice.pubkey(), &bob, 1_000_000_000); let tx = Transaction::new( &[&alice], Message::new(&[ix], Some(&alice.pubkey())), svm.latest_blockhash(), ); svm.send_transaction(tx).unwrap(); assert_eq!(svm.get_balance(&bob).unwrap(), 1_000_000_000); } ``` ```typescript TypeScript theme={"system"} import { test } from "node:test"; import assert from "node:assert/strict"; import { LiteSVM } from "litesvm"; import { getTransferSolInstruction } from "@solana-program/system"; import { appendTransactionMessageInstruction, createTransactionMessage, generateKeyPairSigner, lamports, pipe, setTransactionMessageFeePayerSigner, signTransactionMessageWithSigners, } from "@solana/kit"; test("transfers SOL", async () => { const svm = new LiteSVM(); const alice = await generateKeyPairSigner(); const bob = await generateKeyPairSigner(); svm.airdrop(alice.address, lamports(10_000_000_000n)); const ix = getTransferSolInstruction({ source: alice, destination: bob.address, amount: lamports(1_000_000_000n), }); const tx = await pipe( createTransactionMessage({ version: 0 }), (m) => setTransactionMessageFeePayerSigner(alice, m), (m) => svm.setTransactionMessageLifetimeUsingLatestBlockhash(m), (m) => appendTransactionMessageInstruction(ix, m), (m) => signTransactionMessageWithSigners(m), ); svm.sendTransaction(tx); assert.strictEqual(svm.getBalance(bob.address), lamports(1_000_000_000n)); }); ``` ```python Python theme={"system"} from solders.keypair import Keypair from solders.litesvm import LiteSVM from solders.message import Message from solders.system_program import transfer, TransferParams from solders.transaction import Transaction from solders.transaction_metadata import TransactionMetadata def test_transfers_sol(): svm = LiteSVM() alice = Keypair() bob = Keypair().pubkey() svm.airdrop(alice.pubkey(), 10_000_000_000) ix = transfer( TransferParams( from_pubkey=alice.pubkey(), to_pubkey=bob, lamports=1_000_000_000, ) ) msg = Message.new_with_blockhash( [ix], alice.pubkey(), svm.latest_blockhash() ) tx = Transaction([alice], msg, msg.recent_blockhash) res = svm.send_transaction(tx) assert isinstance(res, TransactionMetadata) assert svm.get_balance(bob) == 1_000_000_000 ``` The default `LiteSVM::new()` already ships the System Program, SPL Token, and a handful of other core programs loaded, sysvars initialized, and the fee-payer airdrop pubkey funded. Everything else you add explicitly. ## Load and invoke your own program Compile the program once, then point LiteSVM at the `.so`. Every test starts from a fresh VM — no cross-test contamination. ### 1. Build the program ```bash theme={"system"} # In your program crate: cargo build-sbf # Produces target/deploy/.so ``` > [!NOTE] > > Even after Anchor 1.0 removed the Solana CLI requirement for `anchor test`, you still need the Agave toolchain (`cargo-build-sbf` or `cargo-build-bpf`) to compile programs. LiteSVM loads `.so` bytes — it does not compile them. ### 2. Load and invoke ```rust Rust theme={"system"} use litesvm::LiteSVM; use solana_address::Address; use solana_instruction::{AccountMeta, Instruction}; use solana_keypair::Keypair; use solana_message::Message; use solana_signer::Signer; use solana_transaction::Transaction; #[test] fn counter_increments() { let program_id: Address = "CounterProg11111111111111111111111111111111" .parse() .unwrap(); let mut svm = LiteSVM::new(); svm.add_program_from_file(program_id, "target/deploy/counter.so") .unwrap(); let payer = Keypair::new(); svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap(); let counter = Address::new_unique(); // Your program would normally create this PDA; we short-circuit for brevity. svm.set_account( counter, solana_account::Account { lamports: svm.minimum_balance_for_rent_exemption(4), data: vec![0, 0, 0, 0], owner: program_id, executable: false, rent_epoch: 0, }, ) .unwrap(); let ix = Instruction { program_id, accounts: vec![AccountMeta::new(counter, false)], data: vec![0], // Instruction discriminator for `increment`. }; let tx = Transaction::new( &[&payer], Message::new(&[ix], Some(&payer.pubkey())), svm.latest_blockhash(), ); svm.send_transaction(tx).unwrap(); let after = svm.get_account(&counter).unwrap(); assert_eq!(u32::from_le_bytes(after.data[..4].try_into().unwrap()), 1); } ``` ```typescript TypeScript theme={"system"} import { test } from "node:test"; import assert from "node:assert/strict"; import { LiteSVM } from "litesvm"; import { address } from "@solana/kit"; test("counter increments", () => { const svm = new LiteSVM(); const programId = address("CounterProg11111111111111111111111111111111"); svm.addProgramFromFile(programId, "target/deploy/counter.so"); // …build, sign, and send the increment instruction as in the first example. }); ``` `add_program_from_file` loads a BPF loader v2 program by default. If you need the upgradeable loader (loader v3) or loader v4, use `add_program_with_loader`. ### Set accounts directly You can prepopulate any account without running a transaction. This is the single most useful test primitive LiteSVM offers over `solana-test-validator`. ```rust theme={"system"} svm.set_account( some_pda, solana_account::Account { lamports: svm.minimum_balance_for_rent_exemption(data.len()), data, owner: program_id, executable: false, rent_epoch: 0, }, ) .unwrap(); ``` Use it to seed PDAs, reproduce mainnet state by copying accounts from an RPC snapshot, or set up adversarial scenarios that would take dozens of instructions to build up organically. ### Cloning real accounts from mainnet When you need an actual token mint, program state, or any other on-chain account, there are two practical paths. **Option 1 — Dump the raw account with the Solana CLI:** ```bash theme={"system"} solana account \ -u https://solana-mainnet.core.chainstack.com/ \ EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v \ --output-file usdc-mint.bin ``` The `.bin` file is the raw account data. Pack it into `solana_account::Account` and hand it to `svm.set_account`. You still need to supply `lamports` and `owner` — fetch those with `solana account --output json-compact` in the same call. **Option 2 — Fetch at test time via Chainstack RPC:** Use an async `RpcClient::get_account` in your test harness to pull the live state, then `set_account` it into the SVM. This is how [Surfpool's mainnet-fork mode](https://github.com/txtx/surfpool) operates under the hood. If you find yourself doing this often, prefer Surfpool — it is purpose-built for mainnet-backed testing. Either way, do not fake mint data with `data: vec![5]` or similar placeholder bytes. Anchor will reject the account with `AccountNotInitialized (3012)`. Copy real bytes, or construct a proper `spl_token::state::Mint` and serialize it with `Pack::pack`. ## Time travel: clock and slots Testing time-dependent logic is the reason most teams move off `solana-test-validator`. LiteSVM exposes the clock sysvar directly. ```typescript theme={"system"} import { LiteSVM } from "litesvm"; const svm = new LiteSVM(); // Jump ahead 1000 slots. svm.warpToSlot(1000n); // Set an arbitrary unix timestamp (useful for vesting, auctions, staking). const clock = svm.getClock(); clock.unixTimestamp = 1_735_689_600n; // 2025-01-01 UTC svm.setClock(clock); ``` Rust has the same surface via `warp_to_slot` and `set_sysvar::`. Python uses `svm.warp_to_slot(1000)` and `svm.set_sysvar(clock)`. > [!WARNING] > > **Address Lookup Tables need recent slots.** If you create an ALT in a LiteSVM test, the ALT program rejects it with `"N is not a recent slot"` unless the slot is inside the recency window. Call `svm.warp_to_slot(n)` to advance past zero before creating the table, and `svm.expire_blockhash()` between groups of transactions to avoid `BlockhashNotFound` on reused hashes. See [Solana SE #22783](https://solana.stackexchange.com/questions/22783). ## SPL tokens The `@solana/spl-token` JavaScript helpers expect a real RPC and do not work against LiteSVM. The LiteSVM team publishes `litesvm-token` for Rust, which wraps the mint/account setup boilerplate. ```rust theme={"system"} use litesvm::LiteSVM; use litesvm_token::{CreateAssociatedTokenAccount, CreateMint, MintTo, get_spl_account}; use litesvm_token::spl_token::state::{Account as TokenAccount, Mint}; use solana_keypair::Keypair; use solana_native_token::LAMPORTS_PER_SOL; use solana_signer::Signer; #[test] fn mints_and_transfers() { let svm = &mut LiteSVM::new(); let payer = Keypair::new(); svm.airdrop(&payer.pubkey(), 10 * LAMPORTS_PER_SOL).unwrap(); let mint = CreateMint::new(svm, &payer).decimals(6).send().unwrap(); let ata = CreateAssociatedTokenAccount::new(svm, &payer, &mint).send().unwrap(); MintTo::new(svm, &payer, &mint, &ata, 1_000_000).send().unwrap(); let account: TokenAccount = get_spl_account(svm, &ata).unwrap(); assert_eq!(account.amount, 1_000_000); let mint_state: Mint = get_spl_account(svm, &mint).unwrap(); assert_eq!(mint_state.decimals, 6); } ``` For TypeScript there is no equivalent helper crate today. The canonical workaround is to build the `spl-token` instructions manually and send them via `svm.sendTransaction`. See [LiteSVM's SPL token docs](https://www.litesvm.com/docs/testing-with-spl-tokens) for the complete pattern. ## Anchor integration Anchor v1.0.0 (released 2026-04-02) made LiteSVM the default unit-test backend for `anchor init`. For idiomatic Anchor tests you have two options. ### Option 1: raw `litesvm` + `anchor-lang::declare_program!` Hand-assemble instructions against an Anchor IDL. Verbose but dependency-free beyond LiteSVM itself. ### Option 2: `anchor-litesvm` (recommended for most Anchor projects) [`anchor-litesvm`](https://github.com/brimigs/anchor-litesvm) wraps LiteSVM with Anchor-aware sugar: discriminator derivation, account-struct binding, event parsing, token helpers. The maintainer claims \~78% less boilerplate than raw LiteSVM for typical Anchor flows. ```rust theme={"system"} use anchor_litesvm::{AnchorLiteSVM, AssertionHelpers, TestHelpers}; anchor_lang::declare_program!(my_program); #[test] fn initialize_succeeds() { let mut ctx = AnchorLiteSVM::build_with_program( my_program::ID, include_bytes!("../target/deploy/my_program.so"), ); let user = ctx.svm.create_funded_account(10_000_000_000).unwrap(); let ix = ctx.program() .accounts(my_program::client::accounts::Initialize { user: user.pubkey(), system_program: solana_system_interface::program::ID, }) .args(my_program::client::args::Initialize { amount: 100 }) .instruction() .unwrap(); ctx.execute_instruction(ix, &[&user]) .unwrap() .assert_success(); } ``` > [!NOTE] > > `anchor-litesvm` is maintained by a single author and has a short release history (v0.4.0, published 2026-04-09). The API is stable against Anchor 1.0 + LiteSVM 0.11, but for long-running production test suites pin exact versions and watch the upstream for compatibility bumps. ## Compute budget and CU benchmarking Two ways to tighten the [compute budget](/docs/solana-compute-budget) in a LiteSVM test. **Per-transaction** (same API as production — prefer this): ```rust theme={"system"} use solana_compute_budget_interface::ComputeBudgetInstruction; let cb_ix = ComputeBudgetInstruction::set_compute_unit_limit(50_000); // Prepend `cb_ix` to your instructions when building the transaction. ``` **VM-level** for every transaction in the test: ```rust theme={"system"} use litesvm::LiteSVM; use solana_compute_budget::compute_budget::ComputeBudget; // `new_with_defaults(simd_0268_active, simd_0339_active)` — false/false // matches current mainnet-beta. `Default::default()` is gated behind // `dev-context-only-utils` in `solana-compute-budget` and is not a // stable public API. let mut cb = ComputeBudget::new_with_defaults(false, false); cb.compute_unit_limit = 50_000; let svm = LiteSVM::new().with_compute_budget(cb); ``` If a transaction exceeds the budget, `send_transaction` returns a `FailedTransactionMetadata` with the CU overrun in its logs. Pair this with snapshot-style assertions in CI to alert on CU growth the moment a change lands. For pure single-instruction CU benchmarking, reach for [Mollusk](https://github.com/anza-xyz/mollusk) instead — it is purpose-built for that one job. ## Feature gates LiteSVM 0.11 exposes the full Agave `FeatureSet`. Use it to test behavior against future runtime semantics before the feature gate activates on mainnet. ```rust theme={"system"} use litesvm::LiteSVM; use agave_feature_set::FeatureSet; // Start from all active features, then disable one you want to simulate a pre-activation env for. let mut features = FeatureSet::all_enabled(); features.deactivate(&some_feature::id()); let svm = LiteSVM::new().with_feature_set(features); ``` This is particularly useful for validating behavior during the activation window of a SIMD that is live on one cluster but not yet on another. ## Custom syscalls LiteSVM 0.11 lets you register your own syscalls. This is niche but invaluable when testing programs that link against syscalls you want to stub or intercept. ```rust theme={"system"} use litesvm::LiteSVM; let svm = LiteSVM::new().with_custom_syscall(/* name */, /* function pointer */); ``` See [`tests/custom_syscall.rs`](https://github.com/LiteSVM/litesvm/blob/v0.11.0/crates/litesvm/tests/custom_syscall.rs) in the LiteSVM repo for the signature and a worked example. ## Pitfalls Most of these come straight from the LiteSVM issue tracker and community channels — they trip up almost everyone coming from `solana-test-validator`. ### Agave 3.1 lock LiteSVM 0.11 pins `agave-feature-set = 3.1.0` and all `solana-*` crates to 3.x. A project that still depends on `solana-sdk` 1.x or 2.x will hit transitive-version conflicts the moment you add LiteSVM. Upgrade the whole dependency tree to Solana 3.x first, or stay on an older LiteSVM — [0.7.1](https://github.com/LiteSVM/litesvm/releases/tag/v0.7.1) is the last release before the Solana 3.0 bump ([0.8.0 #223](https://github.com/LiteSVM/litesvm/pull/223)). ### `@solana/spl-token` does not work The high-level helpers (`createMint`, `createAssociatedTokenAccount`, …) in `@solana/spl-token` assume a real RPC. They will not work against LiteSVM. Build the SPL-token instructions by hand and send them via `svm.sendTransaction`, or use the Rust `litesvm-token` crate. ### BPF upgradeable loader auto-load Programs deployed via the upgradeable loader (BPF v3) are not always re-registered for subsequent transactions — see [issue #240](https://github.com/LiteSVM/litesvm/issues/240) and [issue #263](https://github.com/LiteSVM/litesvm/issues/263). Until this is resolved, prefer `add_program_from_file` (loader v2) for tests unless you specifically need loader v3/v4 semantics. ### Python bindings lag Rust and JS `solders` 0.27.1 — the PyPI package that ships the Python LiteSVM bindings — was released 2025-11-15. The Rust crate has since shipped feature-gate accounts, custom syscalls, and p-token binary loading in 0.10 and 0.11. Python callers do not have those features yet. For Python-primary projects, pin `solders` explicitly and budget for a version lag. ### No standalone `litesvm` on PyPI There is no separate `litesvm` Python package. You install `solders` and import `from solders.litesvm import LiteSVM`. The Python docs live at [kevinheavey.github.io/solders/tutorials/litesvm.html](https://kevinheavey.github.io/solders/tutorials/litesvm.html). ### `DeclaredProgramIdMismatch (4100)` in Anchor tests Anchor programs bake their ID into `declare_id!`. When you load the `.so` into LiteSVM you must pass the same ID — not the program's on-chain deploy ID, not a freshly generated one. Running `anchor keys sync` updates `declare_id!` in source but does not touch the keypair LiteSVM loads. Easiest fix: read the keypair your build produced. ```rust theme={"system"} use solana_keypair::read_keypair_file; use solana_signer::Signer; let kp = read_keypair_file("target/deploy/my_program-keypair.json").unwrap(); svm.add_program_from_file(kp.pubkey(), "target/deploy/my_program.so").unwrap(); ``` ### Anchor discriminator when decoding accounts `svm.get_account(&pda).data` gives you the raw account buffer including Anchor's 8-byte discriminator. Skip those bytes before borsh-deserializing, or use `Account::try_deserialize` which handles it for you. ```rust theme={"system"} use anchor_lang::AccountDeserialize; let data = svm.get_account(&pda).unwrap().data; let state = MyAccount::try_deserialize(&mut &data[..]).unwrap(); ``` Manual borsh against `&data[..]` (without the 8-byte skip) is the most common cause of `InstructionDidNotDeserialize`. ### Error visibility By default, a failed transaction gives you `FailedTransactionMetadata` but no logs. Pull them explicitly. ```rust Rust theme={"system"} if let Err(meta) = svm.send_transaction(tx) { for log in &meta.meta.logs { eprintln!("{}", log); } } ``` ```typescript TypeScript theme={"system"} const res = svm.sendTransaction(tx); if (res instanceof FailedTransactionMetadata) { console.error(res.err()); console.error(res.meta()?.logs); } ``` `svm.getTransaction(signature)` returns `null` by default in LiteSVM — transaction history storage is opt-in via `.with_transaction_history(capacity)`. ### Precompile syscalls require a feature flag Tests that exercise `secp256k1`, `secp256r1`, or `ed25519` precompiles need both the `precompiles` crate feature and the `agave-precompiles` dev-dep. Without them the precompile verifier silently fails. ```toml theme={"system"} [dev-dependencies] litesvm = { version = "=0.11.0", features = ["precompiles"] } agave-precompiles = "3.1" ``` ### gRPC vs RPC simulation divergence [Issue #317](https://github.com/LiteSVM/litesvm/issues/317), filed 2026-04-03 and still open, reports divergence in simulation output between gRPC and RPC paths. If you rely on simulation results for pre-flight logic, cross-check against a real validator before shipping to production. ### No Windows binaries [Issue #215](https://github.com/LiteSVM/litesvm/issues/215) has been open since September 2025. Docker or WSL2 is the only path on Windows. Teams with mixed-OS developer machines should set this expectation up front. ## When not to use LiteSVM Reach for a different tool if any of these apply: * You need to test network effects, leader rotation, or gossip behavior. Use a real local cluster. * You need mainnet state or cross-program interactions with deployed programs you do not control. Use [Surfpool](https://github.com/txtx/surfpool) with its mainnet-fork mode. * You are running a Windows-only build environment and cannot use Docker. Stick with `solana-test-validator` until LiteSVM ships Windows binaries. * You want CU-accurate benchmarking of a single instruction. Use [Mollusk](https://github.com/anza-xyz/mollusk) — LiteSVM's compute budget is good enough for regression detection but Mollusk is purpose-built for the measurement itself. ## See also * [Solana: Anchor development](/docs/solana-anchor-development) — where Anchor 1.0 makes LiteSVM the default test backend. * [Solana: Program derived addresses and cross-program invocations](/docs/solana-program-derived-addresses-and-cross-program-invocations) — the account-layout knowledge you need to write meaningful LiteSVM tests. * [Solana: Escrow pattern](/docs/solana-escrow-pattern) — a full DeFi primitive you can unit-test in LiteSVM in under 100 ms. * [LiteSVM reference docs (Rust)](https://docs.rs/litesvm/latest/litesvm/). * [LiteSVM TypeScript docs](https://litesvm.github.io/litesvm/). * [solders LiteSVM tutorial (Python)](https://kevinheavey.github.io/solders/tutorials/litesvm.html). * [anchor-litesvm on crates.io](https://crates.io/crates/anchor-litesvm). * [Mollusk](https://github.com/anza-xyz/mollusk) — Anza's single-instruction harness. * [Surfpool](https://github.com/txtx/surfpool) — integration-tier SVM with mainnet-fork support. ## Reference repos These are the source repositories we worked against while writing this guide. They stay closer to reality than docs — check them first when something here looks off. * [LiteSVM/litesvm](https://github.com/LiteSVM/litesvm) — test-harness source and the reference for the API surface, error paths, and feature-gate behavior * [brimigs/anchor-litesvm](https://github.com/brimigs/anchor-litesvm) — Anchor ↔ LiteSVM bridge used in the Rust examples * [anza-xyz/mollusk](https://github.com/anza-xyz/mollusk) — alternative lightweight runtime evaluated and cited for contrast * [kevinheavey/solders](https://github.com/kevinheavey/solders) — Python bindings used by the LiteSVM Python harness * [kevinheavey/solana-bankrun](https://github.com/kevinheavey/solana-bankrun) — predecessor test runtime; useful for understanding migration paths # Solana MCP server Source: https://docs.chainstack.com/docs/solana-mcp-server Connect to the Solana blockchain through the MCP server protocol for AI-assisted development with balance queries, token lookups, and transaction analysis. > [!WARNING] > > **Deprecated.** Use the unified [Chainstack MCP server](/docs/chainstack-mcp-server) at `https://mcp.chainstack.com/mcp` instead. It provides node deployment, documentation search, and platform management in a single remote server — no local setup required. The **Solana MCP server** is part of Chainstack's RPC Nodes MCP server suite, providing AI models with comprehensive access to the Solana blockchain ecosystem. This server enables real-time Solana interactions, account queries, program calls, and transaction analysis. ## Repository The Solana MCP server is available in the [chainstacklabs/rpc-nodes-mcp](https://github.com/chainstacklabs/rpc-nodes-mcp) repository, alongside the EVM MCP server for complete blockchain coverage. ## Supported networks The Solana MCP server supports all Solana networks available on Chainstack: * **Solana Mainnet** - Production Solana network * **Solana Devnet** - Development and testing network ## Available functions The Solana MCP server provides comprehensive Solana blockchain interaction capabilities: ### Account information * **Get account info** - Retrieve detailed account data and metadata * **Get account balance** - Check SOL balance for any account * **Get multiple accounts** - Batch query multiple accounts efficiently * **Get program accounts** - Find all accounts owned by a specific program ### Block and slot operations * **Get current slot** - Retrieve the latest processed slot * **Get block height** - Get the current block height * **Get block information** - Fetch complete block data with transactions * **Get block time** - Retrieve block production timestamp * **Get block commitment** - Check block confirmation status ### Transaction operations * **Get transaction** - Retrieve transaction details by signature * **Get signatures for address** - Find all transactions for an account * **Get signature statuses** - Check transaction confirmation status * **Get recent blockhash** - Get blockhash for transaction construction ### Network and cluster information * **Get cluster nodes** - List all validator nodes in the network * **Get epoch info** - Current epoch details and progress * **Get epoch schedule** - Epoch timing and slot configuration * **Get identity** - Node identity and version information * **Get health** - Check node health and sync status ### Fee and performance data * **Get fee for message** - Estimate transaction fees * **Get recent performance samples** - Network performance metrics * **Get recent prioritization fees** - Current priority fee levels * **Get minimum balance for rent exemption** - Calculate rent-exempt balance ## Use cases The Solana MCP server enables AI applications to: ### DeFi and token analysis * **Monitor SPL token balances** and transfers * **Track liquidity pools** and DeFi protocol states * **Analyze trading patterns** on Solana DEXs * **Monitor yield farming** and staking rewards ### NFT and digital assets * **Query NFT collections** and metadata * **Track NFT ownership** and transfer history * **Monitor marketplace activity** and pricing * **Analyze digital asset trends** and popularity ### Validator and staking operations * **Monitor validator performance** and commissions * **Track staking rewards** and delegations * **Analyze validator rankings** and metrics * **Monitor stake pool operations** and performance ### Program development * **Test program interactions** and account states * **Debug transaction failures** and program errors * **Simulate program calls** with different parameters * **Monitor program usage** and adoption metrics ### Network analytics * **Analyze network performance** and throughput * **Monitor epoch transitions** and validator changes * **Track fee trends** and priority patterns * **Generate network health** reports and metrics ## Integration with AI models See the [RPC nodes MCP README file](https://github.com/chainstacklabs/rpc-nodes-mcp/blob/main/README.md) for more information on how to integrate the Solana MCP server with AI models. The Solana MCP server seamlessly integrates with popular AI development environments: ### Cursor Configure the server in your [Cursor settings](https://docs.cursor.com/context/model-context-protocol) to access Chainstack's knowledge base directly within your conversations. ### Claude Desktop Configure the server in your [Claude Desktop settings](https://modelcontextprotocol.io/quickstart/user) to access Chainstack's knowledge base directly within your conversations. ### Claude Code Configure the server in your [Claude Code settings](https://docs.anthropic.com/en/docs/claude-code/mcp) to access Chainstack's knowledge base directly within your conversations. ### Custom AI applications Use the MCP protocol to integrate Chainstack's documentation into your own AI-powered development tools and assistants. ## Next steps > [card] [EVM MCP server](/docs/evm-mcp-server) > > Add Ethereum and EVM capabilities > [card] [Developer Portal MCP server](/docs/developer-portal-mcp-server) > > Access documentation and guides > [card] [GitHub Repository](https://github.com/chainstacklabs/rpc-nodes-mcp) > > View source code and installation instructions > [card] [Solana API Reference](/reference/solana-getting-started) > > Explore available [Solana methods](/docs/solana-methods) # Solana methods Source: https://docs.chainstack.com/docs/solana-methods Complete reference of supported Solana JSON-RPC and WebSocket methods on Chainstack, including account, block, transaction, and program query calls. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which Solana methods are full vs archive, see [Request units — Solana method scope](/docs/request-units#solana-method-scope). See also [interactive Solana API call examples](/reference/solana-getting-started). For per-method rate limits, plan restrictions, and archive data availability, see [Throughput guidelines](/docs/limits#solana-method-limits). | Method | Availability | Comment | | - | - | - | | [getAccountInfo](/reference/solana-getaccountinfo) | | | | [getBalance](/reference/solana-getbalance) | | | | [getBlock](/reference/solana-getblock) | | [Per-method RPS limit](/docs/limits#solana-method-limits). Supports archive data. Needs `maxSupportedTransactionVersion: 1` for [transaction v1](/docs/solana-transaction-v1) | | [getBlockCommitment](/reference/solana-getblockcommitment) | | | | [getBlockHeight](/reference/solana-getblockheight) | | Supports archive data | | [getBlockProduction](/reference/solana-getblockproduction) | | | | [getBlockTime](/reference/solana-getblocktime) | | [Per-method RPS limit](/docs/limits#solana-method-limits). Supports archive data | | [getBlocks](/reference/solana-getblocks) | | 500K block range. Supports archive data | | [getBlocksWithLimit](/reference/getblockswithlimit) | | 500K block range. Supports archive data | | [getClusterNodes](/reference/solana-getclusternodes) | | | | [getEpochInfo](/reference/solana-getepochinfo) | | | | [getEpochSchedule](/reference/solana-getepochschedule) | | | | [getFeeForMessage](/reference/solana-getfeeformessage) | | | | [getFirstAvailableBlock](/reference/solana-getfirstavailableblock) | | | | [getGenesisHash](/reference/solana-getgenesishash) | | | | [getHealth](/reference/solana-gethealth) | | | | [getHighestSnapshotSlot](/reference/solana-gethighestsnapshotslot) | | | | [getIdentity](/reference/solana-getidentity) | | | | [getInflationGovernor](/reference/solana-getinflationgovernor) | | | | [getInflationRate](/reference/solana-getinflationrate) | | | | [getInflationReward](/reference/solana-getinflationreward) | | | | [getLargestAccounts](/reference/solana-getlargestaccounts) | | [Dedicated Nodes](/docs/dedicated-node) only | | [getLatestBlockhash](/reference/solana-getlatestblockhash) | | | | [getLeaderSchedule](/reference/getleaderschedule) | | | | [getMaxRetransmitSlot](/reference/solana-getmaxretransmitslot) | | | | [getMaxShredInsertSlot](/reference/solana-getmaxshredinsertslot) | | | | [getMinimumBalanceForRentExemption](/reference/solana-getminimumbalanceforrentexemption) | | | | [getMultipleAccounts](/reference/solana-getmultipleaccounts) | | [Per-method RPS limit](/docs/limits#solana-method-limits) | | [getProgramAccounts](/reference/solana-getprogramaccounts) | | Paid plans only. [Rate limit depends on the program size](/docs/limits#getprogramaccounts-limits). Use `dataSize` or `memcmp` filters | | [getRecentPerformanceSamples](/reference/solana-getrecentperformancesamples) | | | | [getRecentPrioritizationFees](/reference/solana-getrecentprioritizationfees) | | | | [getSignatureStatuses](/reference/solana-getsignaturestatuses) | | | | [getSignaturesForAddress](/reference/solana-getsignaturesforaddress) | | Supports archive data | | [getSlot](/reference/solana-getslot) | | | | [getSlotLeader](/reference/solana-getslotleader) | | | | [getSlotLeaders](/reference/solana-getslotleaders) | | | | [getStakeMinimumDelegation](/reference/solana-getstakeminimumdelegation) | | | | [getSupply](/reference/solana-getsupply) | | Paid plans only. [Per-method RPS limit](/docs/limits#solana-method-limits) | | [getTokenAccountBalance](/reference/solana-gettokenaccountbalance) | | | | [getTokenAccountsByDelegate](/reference/solana-gettokenaccountsbydelegate) | | | | [getTokenAccountsByOwner](/reference/solana-gettokenaccountsbyowner) | | Paid plans only. [Per-method RPS limit](/docs/limits#solana-method-limits) | | [getTokenLargestAccounts](/reference/solana-gettokenlargestaccounts) | | | | [getTokenSupply](/reference/solana-gettokensupply) | | [Per-method RPS limit](/docs/limits#solana-method-limits) | | [getTransaction](/reference/gettransaction) | | Supports archive data. Needs `maxSupportedTransactionVersion: 1` for [transaction v1](/docs/solana-transaction-v1) | | [getTransactionCount](/reference/solana-gettransactioncount) | | | | [getVersion](/reference/solana-getversion) | | | | [getVoteAccounts](/reference/solana-getvoteaccounts) | | | | [isBlockhashValid](/reference/isblockhashvalid) | | | | [minimumLedgerSlot](/reference/solana-minimumledgerslot) | | | | requestAirdrop | | Unavailable on Chainstack | | [sendTransaction](/reference/solana-sendtransaction) | | | | [simulateTransaction](/reference/solana-simulatetransaction) | | | | [accountSubscribe](/reference/accountsubscribe-solana) | | | | [accountUnsubscribe](/reference/accountunsubscribe-solana) | | | | [blockSubscribe](/reference/blocksubscribe-solana) | | Needs `maxSupportedTransactionVersion: 1` for [transaction v1](/docs/solana-transaction-v1) | | [blockUnsubscribe](/reference/blockunsubscribe-solana) | | | | [logsSubscribe](/reference/logssubscribe-solana) | | | | [logsUnsubscribe](/reference/logsunsubscribe-solana) | | | | [programSubscribe](/reference/programsubscribe-solana) | | | | [programUnsubscribe](/reference/programunsubscribe-solana) | | | | [rootSubscribe](/reference/rootsubscribe-solana) | | | | [rootUnsubscribe](/reference/rootunsubscribe-solana) | | | | [signatureSubscribe](/reference/signaturesubscribe-solana) | | | | [signatureUnsubscribe](/reference/signatureunsubscribe-solana) | | | | [slotSubscribe](/reference/slotsubscribe-solana) | | | | [slotUnsubscribe](/reference/slotunsubscribe-solana) | | | | [slotsUpdatesSubscribe](/reference/slotsupdatessubscribe-solana) | | | | [slotsUpdatesUnsubscribe](/reference/slotsupdatesunsubscribe-solana) | | | | voteSubscribe | | Unavailable on Chainstack | | voteUnsubscribe | | Unavailable on Chainstack | # Solana: MEV protection with Jito Source: https://docs.chainstack.com/docs/solana-mev-protection Protect Solana transactions from sandwich attacks using Jito dontfront, Chainstack Warp transactions, and Trader Nodes for MEV-resistant execution. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * Sandwich attacks front-run and back-run your transaction within the same bundle, extracting value at your expense. * Jito's `dontfront` feature prevents this by ensuring your transaction must appear at index 0 in any bundle. * You add a pubkey starting with `jitodontfront` as a read-only account to any instruction, then send through Jito's block engine. * Chainstack [Warp transactions](/docs/warp-transactions) optimize for **speed** (bloXroute relay). Jito `dontfront` optimizes for **protection**. Choose based on your use case. ## What is a sandwich attack? A sandwich attack is when a searcher: 1. **Front-runs** your swap by buying the same token before you, pushing the price up 2. Your transaction executes at the inflated price 3. **Back-runs** by selling immediately after, profiting from the price difference This happens at the bundle level — the attacker places their transactions around yours in the same block. The result: you get worse execution, the attacker takes the difference. ## How Jito `dontfront` works Jito's block engine processes transaction bundles. The `dontfront` feature is a simple opt-in: 1. Add a pubkey starting with `jitodontfront` (e.g., `jitodontfront111111111111111111111111111111`) as a **read-only account** to any instruction in your transaction 2. Send the transaction through Jito's block engine endpoint (not a standard RPC) 3. The block engine enforces that your transaction must be at **index 0** in any bundle that includes it Since the attacker's front-running transaction can't appear before yours in the bundle, the sandwich is impossible. > [!WARNING] > > This feature requires sending transactions through Jito's block engine, not standard RPC nodes. The Jito endpoint is `https://mainnet.block-engine.jito.wtf/api/v1/transactions`. ## Implementation ### Prerequisites The example is shown in both Solana JavaScript libraries — both are actively maintained, so use whichever fits your project. `@solana/kit` is the newer, tree-shakable, functional SDK; `@solana/web3.js` is the classic `Connection`/`PublicKey` API. > [tab] @solana/kit > > ```bash theme={"system"} > npm install @solana/kit @solana-program/system dotenv > ``` > [tab] @solana/web3.js (classic) > > ```bash theme={"system"} > npm install @solana/web3.js bs58 dotenv > ``` ### Add `dontfront` protection to a transaction > [tab] @solana/kit > > ```typescript TypeScript theme={"system"} > import { > createSolanaRpc, > address, > lamports, > pipe, > createKeyPairSignerFromBytes, > getBase58Encoder, > createTransactionMessage, > setTransactionMessageFeePayerSigner, > setTransactionMessageLifetimeUsingBlockhash, > appendTransactionMessageInstructions, > signTransactionMessageWithSigners, > getBase64EncodedWireTransaction, > AccountRole, > } from "@solana/kit"; > import { getTransferSolInstruction } from "@solana-program/system"; > import "dotenv/config"; > > // Use your Chainstack endpoint for reads > const rpc = createSolanaRpc(process.env.SOLANA_RPC!); > // @solana/kit decodes base58, so no separate bs58 package is needed > const payer = await createKeyPairSignerFromBytes( > getBase58Encoder().encode(process.env.PRIVATE_KEY!) > ); > > // Jito block engine for sends > const JITO_ENDPOINT = > "https://mainnet.block-engine.jito.wtf/api/v1/transactions"; > > // Any valid pubkey starting with "jitodontfront". Does not need to exist on-chain. > const DONT_FRONT = address("jitodontfront111111111111111111111111111111"); > > // Jito tip accounts — pick one at random to reduce contention > const TIP_ACCOUNTS = [ > "96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5", > "HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe", > "Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY", > "ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49", > "DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh", > "ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt", > "DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL", > "3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT", > ]; > > // Append the dontfront marker as a read-only account to any instruction > function addDontFront(instruction) { > return { > ...instruction, > accounts: [ > ...(instruction.accounts ?? []), > { address: DONT_FRONT, role: AccountRole.READONLY }, > ], > }; > } > > function randomTipAccount() { > return address(TIP_ACCOUNTS[Math.floor(Math.random() * TIP_ACCOUNTS.length)]); > } > > async function sendWithMEVProtection() { > const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); > > // Your actual instruction — add dontfront to it > const transferIx = addDontFront( > getTransferSolInstruction({ > source: payer, > destination: payer.address, > amount: lamports(1_000_000n), // 0.001 SOL > }) > ); > > // Jito tip — minimum 1000 lamports > const tipIx = getTransferSolInstruction({ > source: payer, > destination: randomTipAccount(), > amount: lamports(1000n), > }); > > const message = pipe( > createTransactionMessage({ version: 0 }), > (m) => setTransactionMessageFeePayerSigner(payer, m), > (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), > (m) => appendTransactionMessageInstructions([transferIx, tipIx], m) > ); > > const signedTransaction = await signTransactionMessageWithSigners(message); > > // Send through Jito block engine, NOT standard RPC > const serialized = getBase64EncodedWireTransaction(signedTransaction); > const response = await fetch(JITO_ENDPOINT, { > method: "POST", > headers: { "Content-Type": "application/json" }, > body: JSON.stringify({ > jsonrpc: "2.0", > id: 1, > method: "sendTransaction", > params: [serialized, { encoding: "base64" }], > }), > }); > > const result = await response.json(); > console.log("Sent with dontfront protection:", result.result); > } > > sendWithMEVProtection(); > ``` > [tab] @solana/web3.js (classic) > > ```typescript TypeScript theme={"system"} > import { > Connection, > Keypair, > PublicKey, > SystemProgram, > TransactionMessage, > VersionedTransaction, > TransactionInstruction, > LAMPORTS_PER_SOL, > } from "@solana/web3.js"; > import bs58 from "bs58"; > import "dotenv/config"; > > // Use your Chainstack endpoint for reads > const connection = new Connection(process.env.SOLANA_RPC!); > const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY!)); > > // Jito block engine for sends > const JITO_ENDPOINT = > "https://mainnet.block-engine.jito.wtf/api/v1/transactions"; > > // Any valid pubkey starting with "jitodontfront". Does not need to exist on-chain. > const DONT_FRONT = new PublicKey( > "jitodontfront111111111111111111111111111111" > ); > > // Jito tip accounts — pick one at random to reduce contention > const TIP_ACCOUNTS = [ > "96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5", > "HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe", > "Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY", > "ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49", > "DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh", > "ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt", > "DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL", > "3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT", > ]; > > function addDontFront(instruction: TransactionInstruction): TransactionInstruction { > return new TransactionInstruction({ > ...instruction, > keys: [ > ...instruction.keys, > { pubkey: DONT_FRONT, isSigner: false, isWritable: false }, > ], > }); > } > > function randomTipAccount(): PublicKey { > const idx = Math.floor(Math.random() * TIP_ACCOUNTS.length); > return new PublicKey(TIP_ACCOUNTS[idx]); > } > > async function sendWithMEVProtection() { > const latestBlockhash = await connection.getLatestBlockhash(); > > // Your actual instruction — add dontfront to it > const transferIx = addDontFront( > SystemProgram.transfer({ > fromPubkey: payer.publicKey, > toPubkey: payer.publicKey, > lamports: 0.001 * LAMPORTS_PER_SOL, > }) > ); > > // Jito tip — minimum 1000 lamports > const tipIx = SystemProgram.transfer({ > fromPubkey: payer.publicKey, > toPubkey: randomTipAccount(), > lamports: 1000, > }); > > const message = new TransactionMessage({ > payerKey: payer.publicKey, > recentBlockhash: latestBlockhash.blockhash, > instructions: [transferIx, tipIx], > }).compileToV0Message(); > > const transaction = new VersionedTransaction(message); > transaction.sign([payer]); > > // Send through Jito block engine, NOT standard RPC > const serialized = Buffer.from(transaction.serialize()).toString("base64"); > const response = await fetch(JITO_ENDPOINT, { > method: "POST", > headers: { "Content-Type": "application/json" }, > body: JSON.stringify({ > jsonrpc: "2.0", > id: 1, > method: "sendTransaction", > params: [serialized, { encoding: "base64" }], > }), > }); > > const result = await response.json(); > console.log("Sent with dontfront protection:", result.result); > } > > sendWithMEVProtection(); > ``` ## Chainstack Warp transactions vs Jito `dontfront` Chainstack offers two transaction acceleration paths. They serve different purposes: | | [Warp transactions](/docs/warp-transactions) | Jito `dontfront` | | - | - | - | | **Goal** | Maximum speed — fastest landing | MEV protection — prevent sandwich | | **Mechanism** | bloXroute relay for optimized propagation | Jito block engine enforces bundle index 0 | | **Endpoint** | Your Chainstack RPC (with Warp enabled) | Jito block engine (`mainnet.block-engine.jito.wtf`) | | **Front-running protection** | No — speed-optimized, protection disabled | Yes — core feature | | **Tip required** | No | Yes (minimum 1000 lamports) | | **Best for** | Arbitrage, sniping, latency-sensitive trades | Swaps, DeFi interactions where execution price matters | **Use Warp** when you need to land first (you are the one competing for position). **Use `dontfront`** when you need to protect your execution price (others are competing against you). For reads (getAccountInfo, getBlock, etc.), always use your [Chainstack Solana endpoint](/docs/solana-tooling). ## Tips * **Jito tip amounts** should be minimum 1000 lamports. For `sendTransaction`, use a 70/30 split between priority fee and Jito tip. * **Pick tip accounts at random** to reduce contention across the 8 Jito tip accounts. * When using `sendBundle`, `dontfront` transactions must be contiguous at the front of the bundle with overlapping signers. * `dontfront` is **mainnet and testnet only** — it does not work on devnet. ## Additional resources * [Solana Trader Nodes with Warp transactions](/docs/solana-trader-nodes) — Chainstack's speed-optimized transaction path * [Priority fees for faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) — set optimal priority fees * [How to handle the transaction expiry error](/docs/solana-how-to-handle-the-transaction-expiry-error) — retry logic # Solana: Create and mint an SPL token with web3.js Source: https://docs.chainstack.com/docs/solana-mint-spl-tokens Create an SPL token mint and mint tokens to an account on Solana with @solana/web3.js and @solana/spl-token — createMint, getOrCreateAssociatedTokenAccount, and mintTo. **TLDR:** * Creating an SPL token takes three on-chain steps with `@solana/web3.js` and `@solana/spl-token`: create the mint account that defines the token (`createMint`), create a token account to hold a balance (`getOrCreateAssociatedTokenAccount`), then mint the supply into it (`mintTo`). * Pass both your HTTPS and WSS endpoints to `new Connection()`. With the HTTPS endpoint alone, `@solana/web3.js` derives a WebSocket URL that does not match a Chainstack node, and confirmations fail with `TransactionExpiredBlockheightExceededError`. * Amounts are integers in base units — with 9 decimals, 1,000 tokens is `1000 * 10^9`. * The example runs on Solana devnet with a Chainstack node: it creates the mint, mints 1,000 tokens, and reads the balance back. > [!NOTE] > > This tutorial uses `@solana/web3.js` with `@solana/spl-token` — the maintained classic Solana JavaScript stack. A `@solana/kit` version will follow once the Kit SPL client (`@solana-program/token`) supports kit v7. ## How minting an SPL token works An SPL token lives in two kinds of account: one **mint account** that defines the token — its decimals, current supply, and the authorities allowed to mint or freeze — and one **token account** per holder that stores that holder's balance. Creating a token and giving someone a balance therefore takes three steps: 1. Create and initialize the mint account with `createMint`. This sets the number of decimals and the mint authority. 2. Create a token account owned by the recipient with `getOrCreateAssociatedTokenAccount`. The associated token account (ATA) is a deterministic address derived from the owner and the mint. 3. Mint tokens from the mint into that token account with `mintTo`, signed by the mint authority. Two details decide whether the code runs: * Rent — every Solana account must hold enough lamports to be rent-exempt. A mint account is a fixed 82 bytes (`MINT_SIZE`), which costs 1,066,800 lamports (about 0.00107 SOL) to keep alive at the current rate. The rate falls as [Solana's rent reduction](/docs/solana-rent-reduction-reclaim-excess-rent) proceeds. The `createMint` helper adds this automatically; if you build the transaction yourself, you supply it with `connection.getMinimumBalanceForRentExemption(MINT_SIZE)`. * Base units — token amounts are integers, not decimals. A token with 9 decimals means 1 token equals 1,000,000,000 (`10^9`) base units, so minting 1,000 tokens uses an amount of `1000 * 10^9`. ## Prerequisites * Node.js 18 or later. * A Chainstack Solana node. [Start for free](https://console.chainstack.com/) — no credit card required. This tutorial uses a Solana devnet node so you can mint freely with faucet SOL. * The node's HTTPS and WSS endpoints. On Chainstack, open your node and copy both from the **Access and credentials** tab. * A funded devnet account to pay for the transactions and act as the mint authority. Install the dependencies: ```bash theme={"system"} npm install @solana/web3.js @solana/spl-token bs58 dotenv ``` The examples use ES modules and `dotenv`. Save each file with a `.mjs` extension (or add `"type": "module"` to your `package.json`) so Node treats it as an ES module, and store your endpoints and secret key in a `.env` file: ```bash .env theme={"system"} CHAINSTACK_HTTPS_ENDPOINT="YOUR_CHAINSTACK_HTTPS_ENDPOINT" CHAINSTACK_WSS_ENDPOINT="YOUR_CHAINSTACK_WSS_ENDPOINT" PRIVATE_KEY="YOUR_BASE58_SECRET_KEY" ``` > [!WARNING] > > Add `.env` to your `.gitignore`. It holds a private key, which grants full control of the account — never commit it or share it. ## Set up and fund the payer The payer signs and pays for every transaction and is the mint authority in this tutorial. If you do not already have a devnet keypair, generate one and print its base58 secret key to paste into `.env` as `PRIVATE_KEY`: ```javascript generate-keypair.mjs theme={"system"} import { Keypair } from "@solana/web3.js"; import bs58 from "bs58"; const keypair = Keypair.generate(); console.log("Public key:", keypair.publicKey.toBase58()); console.log("Secret key (base58):", bs58.encode(keypair.secretKey)); ``` Fund the public key with devnet SOL from the [Chainstack faucet](https://faucet.chainstack.com/), or with the Solana CLI: ```bash theme={"system"} solana airdrop 1 YOUR_PUBLIC_KEY --url devnet ``` One SOL is far more than enough — the whole flow costs well under 0.01 SOL in rent and fees. ## Connect to your Chainstack Solana node Create the connection with both your HTTPS and WSS endpoints. `@solana/web3.js` opens a WebSocket to confirm transactions; if you pass only the HTTPS endpoint, it derives a WebSocket URL from that host, which does not match a Chainstack node's separate WSS endpoint. Confirmations then fall back and time out with `TransactionExpiredBlockheightExceededError`. Passing `wsEndpoint` explicitly avoids this. ```javascript theme={"system"} import { Connection, Keypair } from "@solana/web3.js"; import bs58 from "bs58"; import "dotenv/config"; const connection = new Connection(process.env.CHAINSTACK_HTTPS_ENDPOINT, { commitment: "confirmed", wsEndpoint: process.env.CHAINSTACK_WSS_ENDPOINT, }); const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY)); ``` ## Create the mint `createMint` allocates the mint account, makes it rent-exempt, and initializes it in a single call. The arguments are the connection, the fee payer, the mint authority, the freeze authority (`null` for none), and the number of decimals: ```javascript theme={"system"} import { createMint } from "@solana/spl-token"; const decimals = 9; const mint = await createMint( connection, payer, // fee payer payer.publicKey, // mint authority null, // freeze authority (null = none) decimals, ); console.log("Mint:", mint.toBase58()); ``` The returned `mint` is the `PublicKey` of the new token. The mint authority you set here is the only account allowed to mint more of this token later. ## Create the associated token account Balances live in token accounts, not in the mint. `getOrCreateAssociatedTokenAccount` returns the payer's associated token account for this mint, creating it on-chain if it does not exist yet: ```javascript theme={"system"} import { getOrCreateAssociatedTokenAccount } from "@solana/spl-token"; const tokenAccount = await getOrCreateAssociatedTokenAccount( connection, payer, mint, payer.publicKey, // owner ); console.log("Token account:", tokenAccount.address.toBase58()); ``` To mint to someone else, pass their public key as the `owner` argument — the function derives and creates the associated token account they own. ## Mint the tokens `mintTo` mints new tokens from the mint into a token account. It must be signed by the mint authority. The amount is in base units, so multiply the token count by `10^decimals`: ```javascript theme={"system"} import { mintTo } from "@solana/spl-token"; const amount = 1000n * 10n ** BigInt(decimals); // 1,000 tokens const signature = await mintTo( connection, payer, mint, tokenAccount.address, // destination payer.publicKey, // mint authority amount, ); console.log("mintTo signature:", signature); ``` Using a `BigInt` for the amount keeps the math exact for any supply, including tokens with high decimals whose base-unit counts exceed the safe integer range. ## Verify the mint and balance Read the state back from the chain to confirm the mint succeeded. `getMint` returns the mint's on-chain supply and decimals, and `getAccount` returns the token account's balance: ```javascript theme={"system"} import { getMint, getAccount } from "@solana/spl-token"; const mintInfo = await getMint(connection, mint); const accountInfo = await getAccount(connection, tokenAccount.address); console.log("Supply:", mintInfo.supply.toString()); console.log("Balance:", Number(accountInfo.amount) / 10 ** decimals, "tokens"); ``` ## Full script The complete, runnable script combines every step. Save it as `mint-token.mjs` and run it with `node mint-token.mjs`: ```javascript mint-token.mjs theme={"system"} import { Connection, Keypair } from "@solana/web3.js"; import { createMint, getOrCreateAssociatedTokenAccount, mintTo, getMint, getAccount, } from "@solana/spl-token"; import bs58 from "bs58"; import "dotenv/config"; // Connect — pass BOTH the HTTPS and WSS endpoints so confirmations resolve. const connection = new Connection(process.env.CHAINSTACK_HTTPS_ENDPOINT, { commitment: "confirmed", wsEndpoint: process.env.CHAINSTACK_WSS_ENDPOINT, }); // Load the payer, which is also the mint authority. Fund it with devnet SOL first. const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY)); async function main() { const decimals = 9; // 1. Create the mint account. const mint = await createMint( connection, payer, // fee payer payer.publicKey, // mint authority null, // freeze authority (null = none) decimals, ); console.log("Mint:", mint.toBase58()); // 2. Create the payer's associated token account for this mint. const tokenAccount = await getOrCreateAssociatedTokenAccount( connection, payer, mint, payer.publicKey, // owner ); console.log("Token account:", tokenAccount.address.toBase58()); // 3. Mint 1,000 tokens. The amount is in base units: 1000 * 10^decimals. const amount = 1000n * 10n ** BigInt(decimals); const signature = await mintTo( connection, payer, mint, tokenAccount.address, // destination payer.publicKey, // mint authority amount, ); console.log("mintTo signature:", signature); // 4. Verify: read the mint supply and the token account balance back. const mintInfo = await getMint(connection, mint); const accountInfo = await getAccount(connection, tokenAccount.address); console.log("Supply:", mintInfo.supply.toString()); console.log("Balance:", Number(accountInfo.amount) / 10 ** decimals, "tokens"); } main().catch((err) => { console.error(err); process.exit(1); }); ``` Running it prints the new mint address, the token account, the mint signature, and the confirmed balance. Example output from a devnet run: ```text theme={"system"} Mint: GHkhSADGmLAtjKUdriGY5VcvhCWdbGZ7oPrXcK2EK9bq Token account: HRTknUprTDiR5o5GxeCJdYL7CLiWYTmUq86U9zsCJbZk mintTo signature: 3b8dbXKb2jVbzyNqSEyFBBy1soLWQf1qbrrXHGtUpkD4PqgkpmoYhwGrxLxCPBwzgYNrJNcCDi1TzFvEPKm8RQf3 Supply: 1000000000000 Balance: 1000 tokens ``` ## Send everything in one transaction The `createMint`, `getOrCreateAssociatedTokenAccount`, and `mintTo` helpers each send a separate transaction, which is the simplest approach. When you want the mint to be created and funded atomically — all or nothing, in one signature — build the instructions yourself and send them in a single transaction. This is also where you supply the rent-exempt lamports explicitly with `connection.getMinimumBalanceForRentExemption(MINT_SIZE)`: ```javascript mint-token-atomic.mjs theme={"system"} import { Connection, Keypair, SystemProgram, Transaction, sendAndConfirmTransaction, } from "@solana/web3.js"; import { MINT_SIZE, TOKEN_PROGRAM_ID, createInitializeMintInstruction, getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createMintToInstruction, getAccount, } from "@solana/spl-token"; import bs58 from "bs58"; import "dotenv/config"; const connection = new Connection(process.env.CHAINSTACK_HTTPS_ENDPOINT, { commitment: "confirmed", wsEndpoint: process.env.CHAINSTACK_WSS_ENDPOINT, }); const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY)); async function main() { const decimals = 9; const mint = Keypair.generate(); // Rent-exempt lamports for the fixed-size (82-byte) mint account. const rent = await connection.getMinimumBalanceForRentExemption(MINT_SIZE); // The destination associated token account, derived then created in the same transaction. const ata = await getAssociatedTokenAddress(mint.publicKey, payer.publicKey); const amount = 1000n * 10n ** BigInt(decimals); const tx = new Transaction().add( // Allocate and fund the mint account, owned by the Token program. SystemProgram.createAccount({ fromPubkey: payer.publicKey, newAccountPubkey: mint.publicKey, space: MINT_SIZE, lamports: rent, programId: TOKEN_PROGRAM_ID, }), // Initialize the account as a mint. createInitializeMintInstruction(mint.publicKey, decimals, payer.publicKey, null), // Create the destination associated token account. createAssociatedTokenAccountInstruction(payer.publicKey, ata, payer.publicKey, mint.publicKey), // Mint tokens into it. createMintToInstruction(mint.publicKey, ata, payer.publicKey, amount), ); // Both the payer and the new mint account must sign. const signature = await sendAndConfirmTransaction(connection, tx, [payer, mint]); console.log("Signature:", signature); console.log("Mint:", mint.publicKey.toBase58()); const accountInfo = await getAccount(connection, ata); console.log("Balance:", Number(accountInfo.amount) / 10 ** decimals, "tokens"); } main().catch((err) => { console.error(err); process.exit(1); }); ``` ## Move to mainnet The code is identical on mainnet — point the connection at a Chainstack Solana mainnet node's HTTPS and WSS endpoints and fund the payer with real SOL. Two authority decisions matter in production: * Mint authority — whoever holds it can mint more tokens at any time. To cap the supply permanently after your initial mint, revoke it with `setAuthority` using `AuthorityType.MintTokens` and a `null` new authority. * Freeze authority — set to `null` at creation (as in this tutorial) if you never want to freeze holder accounts; the choice cannot be added back later. ## Additional resources * [Transferring SPL tokens on Solana: a step-by-step TypeScript tutorial](/docs/transferring-spl-tokens-on-solana-typescript) — move the tokens you just minted between accounts. * [Solana: how to handle the transaction expiry error](/docs/solana-how-to-handle-the-transaction-expiry-error) — more on the confirmation error the WSS endpoint prevents. * [Solana: token extensions](/docs/solana-token-extensions) — create tokens with Token-2022 features such as transfer fees and on-chain metadata. * [Solana tooling](/docs/solana-tooling) — the libraries, SDKs, and tools for building on Solana with Chainstack. > [!NOTE] > > Verified with `@solana/web3.js` 1.98.4, `@solana/spl-token` 0.4.15, and `bs58` 6.0.0. # Solana: Optimize your getBlock performance Source: https://docs.chainstack.com/docs/solana-optimize-your-getblock-performance Optimize getBlock call performance on Solana by tuning maxSupportedTransactionVersion, encoding, and reward parameters on your Chainstack RPC node. **TLDR:** * `getBlock` is a core Solana RPC method that can dramatically impact performance if misused; don’t pull more data than you need. * Use `json`, `jsonParsed`, `base58`, or `base64` encoding strategically; always enable compression (`gzip`) to reduce huge payloads. * Apply concurrency limits, caching, and backoff retry logic to avoid node overload and handle network hiccups gracefully. * Use robust error handling and consider block ranges for larger-scale data fetches. ## Main article Solana's [getBlock](/reference/solana-getblock) RPC method is a fundamental method that can be tricky and will screw up your application performance in a jiffy if you are not paying attention. This guide provides a comprehensive overview of how to use `getBlock` efficiently, with practical examples in Python and curl. ## Understanding getBlock The `getBlock` method returns detailed information about a confirmed block in Solana's ledger. A block in Solana contains: * A set of transactions * Metadata like block hash, previous block hash * Timing information * Reward data If you think you have a grasp now, check out [Understanding the difference between blocks and slots on Solana](/docs/understanding-the-difference-between-blocks-and-slots-on-solana). Solana is amazing but not for the faint heart. ### What data does getBlock return? A typical response includes: * `blockhash` — the unique hash (ID) of this block (base-58 encoded) * `previousBlockhash` — the hash of the parent block * `parentSlot` — the slot number of the parent block * `blockHeight` — the sequential block height (the number of blocks beneath this block) * `blockTime` — the timestamp when the block was produced, which is yet another trick. See [Solana: Understanding block time](/docs/solana-understanding-block-time). * `transactions` — an array of transactions in the block (if requested) * `signatures` — an array of transaction signatures in the block (if requested) * `rewards` — an array of block rewards (if requested) ### Parameters When calling `getBlock`, you can specify several parameters to control what data you receive: * `commitment` — `finalized` (default) or `confirmed` * `encoding` — `json` (default), `jsonParsed`, `base64`, or `base58`. * `transactionDetails` — `full` (default), `accounts`, `signatures`, or `none` * `rewards` — boolean to include block rewards * `maxSupportedTransactionVersion` — the highest transaction version your client can parse. Set it to `1` for [version 1 transactions](https://solana.com/upgrades/larger-transaction-sizes), which Solana mainnet now produces. A lower value, or an omitted field, makes the call respond with error `-32015` when the block holds a newer transaction. ## Basic example with curl Let's start with the simplest way to fetch a block using curl: ```shell Shell theme={"system"} curl -X POST CHAINSTACK_SOLANA_URL -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [ 329849011, { "encoding": "jsonParsed", "transactionDetails": "full", "commitment": "finalized", "maxSupportedTransactionVersion": 1 } ] }' ``` This request fetches block [329849011](https://solscan.io/block/329849011) with full transaction details in JSON format. Note two things here: * We are using `jsonParsed`, which produces the largest output on the node side and gets transported to you. You should never do this in production with a heavy load. This more of a one-off inspection call than anything else. * If you are using a full and not an archive Solana node, use a block number within the last 20 hours or so. Otherwise this will be an archive call. See [Limits](/docs/limits) at the bottom for the archive node methods availability. ### Getting just the signatures If you're only interested in transaction signatures (hashes), which is much lighter: ```shell Shell theme={"system"} curl -X POST CHAINSTACK_SOLANA_URL -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [ 329849011, { "encoding": "json", "transactionDetails": "signatures", "commitment": "finalized", "maxSupportedTransactionVersion": 1 } ] }' ``` Same note on the block number in `params` as above. Make sure you stay within the last 20 hours or so, unless you want to use an archive call, which is also fine as Chainstack is extremely affordable & transparent with pricing — a full node request is counted as one request, and an archive node request is counted as 2 requests, and that's it. ### Using compression (gzip) for better performance HTTP compression is critical when working with Solana's `getBlock` method due to the large size of block data. Here's why compression is essential and how to implement it: #### Why use compression 1. **Dramatic size reduction** — Solana block data with full transaction details easily take several MBs in JSON format. Gzip compression typically reduces this by 70-90%, bringing it down to a few hundred KB. 2. **Faster response times** — less data transmitted means faster responses, especially important when: * Working with blocks containing many transactions * Operating on networks with limited bandwidth * Fetching multiple blocks in sequence 3. **Reduced bandwidth costs** — If you're paying for bandwidth (e.g., in cloud environments), compression significantly reduces costs. 4. **Server-friendly** — compression reduces load on both the RPC node and your client's network connection. #### Compression example with curl Adding compression is simple with curl—just add the `Accept-Encoding: gzip` header and the `--compressed` flag: ```shell Shell theme={"system"} curl -X POST CHAINSTACK_SOLANA_URL \ -H "Content-Type: application/json" \ -H "Accept-Encoding: gzip" \ --compressed \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [ 329849011, { "encoding": "json", "transactionDetails": "full", "commitment": "finalized", "maxSupportedTransactionVersion": 1 } ] }' ``` When you run this command: 1. The `Accept-Encoding: gzip` header tells the server you can handle compressed responses 2. The server compresses the JSON data before sending it 3. The `--compressed` flag tells curl to automatically decompress the data on receipt 4. You see the normal JSON output, but the actual network transfer was much smaller #### Understanding the compression process Here's what happens under the hood: 1. **Request** — your client sends a request with `Accept-Encoding: gzip` 2. **Server processing** — the server generates the JSON response 3. **Compression** — the server compresses this data using gzip 4. **Transfer** — the compressed data (much smaller) is sent over the network 5. **Decompression** — your client decompresses the data 6. **Processing** — you work with the original JSON data Most HTTP libraries handle steps 1, 4, and 5 automatically when configured correctly. #### Example of handling the compressed (gzip) data manually For illustration purposes and to compare the actual size in compressed & decompressed state: ```shell Shell theme={"system"} curl -X POST CHAINSTACK_SOLANA_URL \ -H "Content-Type: application/json" \ -H "Accept-Encoding: gzip" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [329849011, {"encoding": "json", "maxSupportedTransactionVersion": 1}] }' \ --output block_data.gz ``` Decompress the `block_data.gz` file: ```shell Shell theme={"system"} gunzip -c block_data.gz > block_data.json ``` See the \~80% reduction in size when you compare `block_data.gz` to `block_data.json`. #### Compression in HTTP libraries Most modern HTTP libraries support compression automatically: * **Python requests** — add `headers={"Accept-Encoding": "gzip"}` or set `requests.get(..., stream=True)` * **Node.js** — most HTTP clients like Axios support this out of the box * **Rust** — libraries like reqwest have compression features Using compression is one of the simplest and most effective optimizations when working with Solana's `getBlock` RPC method, especially for blocks with many transactions. So use compression. ## json, jsonParsed, base58, base64 When using Solana's getBlock RPC method, you can request data in different encoding formats based on your specific needs. Note that when you are doing a `getBlock` call with `"encoding": "base58"` or `"encoding": "base64"`, you are getting the respective encoding *on the transaction level*, not the entire block. In other words, you will still get back a JSON response, it's only the transaction data that will be encoded in `base58` or `base64`. Let's explore each option: ### json (default) The `json` encoding provides transaction data in a standard JSON format with binary data encoded as base58 strings. ```bash Bash theme={"system"} curl -X POST CHAINSTACK_SOLANA_URL -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [ 149546741, { "encoding": "json", "transactionDetails": "full", "commitment": "finalized", "maxSupportedTransactionVersion": 1 } ] }' ``` **Best for**: General use cases where you need a balance of readability and performance. Binary data remains encoded but the overall structure is easily parseable. ### jsonParsed The `jsonParsed` encoding goes beyond standard JSON by attempting to decode instruction data into human-readable format: ```bash Bash theme={"system"} curl -X POST CHAINSTACK_SOLANA_URL -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [ 149546741, { "encoding": "jsonParsed", "transactionDetails": "full", "commitment": "finalized", "maxSupportedTransactionVersion": 1 } ] }' ``` **Best for**: * Debugging and analysis where you need to understand transaction contents * Decoding program instructions without additional parsing work * Applications that display transaction details to users **Limitations** — not all programs can be parsed as you need an IDL (similar to EVM's ABIs) or source code, and response size is larger than other encodings. ### base58 The `base58` encoding returns binary data for transactions as base58-encoded strings: ```bash Bash theme={"system"} curl -X POST CHAINSTACK_SOLANA_URL -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [ 149546741, { "encoding": "base58", "transactionDetails": "full", "commitment": "finalized", "maxSupportedTransactionVersion": 1 } ] }' ``` **Best for**: Compatibility with tools that expect base58 encoding, which is common in Solana's ecosystem. ### base64 The `base64` encoding returns binary data for transactions as base64-encoded strings: ```bash Bash theme={"system"} curl -X POST CHAINSTACK_SOLANA_URL -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [ 149546741, { "encoding": "base64", "transactionDetails": "full", "commitment": "finalized", "maxSupportedTransactionVersion": 1 } ] }' ``` **Best for**: * Performance-critical applications (base64 is more compact than base58) * Storage efficiency when saving transaction data * High-throughput systems processing many blocks ### Encoding comparison | Encoding | Size | Human Readability | Parsing Complexity | Use Case | | - | - | - | - | - | | json | Medium | Good | Low | General purpose | | jsonParsed | Largest | Best | Lowest | Analysis & debugging | | base58 | Medium-Large | Poor | Medium | Ecosystem compatibility | | base64 | Smallest | Poor | Medium | Performance & storage | ## Python example with json, jsonParsed, base58, base64 First, install the package:`pip install solana`. ``` import requests import time import json import os import base64 import binascii # Add base58 decoding support try: import base58 except ImportError: print("Installing base58 module...") import subprocess subprocess.check_call(["pip", "install", "base58"]) import base58 # Initialize the Solana RPC endpoint URL rpc_url = "CHAINSTACK_SOLANA_URL" def save_raw_response(response_data, slot, encoding): """Save raw RPC response data to a file""" # Create output directory if it doesn't exist os.makedirs('block_data', exist_ok=True) filename = f"block_data/block_{slot}_{encoding}.json" with open(filename, 'w') as f: json.dump(response_data, f, indent=2) print(f"Saved raw {encoding} response to {filename}") file_size = os.path.getsize(filename) / (1024 * 1024) # Size in MB print(f"File size: {file_size:.2f} MB") def make_rpc_request(method, params): """Make a JSON-RPC request to the Solana node""" headers = {"Content-Type": "application/json"} payload = { "jsonrpc": "2.0", "id": 1, "method": method, "params": params } response = requests.post(rpc_url, headers=headers, json=payload) return response.json() def get_block_with_encoding(slot, encoding="json"): """Fetch a block with specified encoding and return the raw response""" try: start_time = time.time() # Make direct RPC request params = [ slot, { "encoding": encoding, "maxSupportedTransactionVersion": 1, "transactionDetails": "full", "rewards": False } ] raw_response = make_rpc_request("getBlock", params) elapsed = time.time() - start_time if not raw_response.get("result"): print(f"Block {slot} not found with {encoding} encoding.") return None print(f"Fetched block {slot} with {encoding} encoding in {elapsed:.2f} seconds") # Save the raw response save_raw_response(raw_response, slot, encoding) return raw_response except Exception as e: print(f"Error fetching block with {encoding} encoding: {e}") return None def get_latest_slot(): """Get the latest finalized slot""" try: # Correct parameter format for getSlot - commitment should be in an object params = [{"commitment": "finalized"}] slot_resp = make_rpc_request("getSlot", params) if "result" not in slot_resp: print("Failed to get current slot") print(f"Error response: {slot_resp}") return None current_slot = slot_resp["result"] print(f"Current slot: {current_slot}") return current_slot except Exception as e: print(f"Error fetching latest slot: {e}") return None def inspect_transaction_format(response, encoding): """Inspect and print transaction format details for the first transaction""" if not response or "result" not in response: print(f"No result to inspect for {encoding}") return result = response["result"] if "transactions" not in result or not result["transactions"]: print(f"No transactions found in {encoding} response") return # Get the first transaction first_tx = result["transactions"][0] # Print transaction format details print(f"\n{encoding} Transaction Format Analysis:") print("-" * 40) # For base64 and base58 encodings, we need to handle the response differently if encoding in ["base64", "base58"]: print(f"Transaction structure type: {type(first_tx)}") print(f"Available fields: {list(first_tx.keys())}") # Check for specific encoding fields if "transaction" in first_tx: tx_data = first_tx["transaction"] print(f"Transaction data type: {type(tx_data)}") if isinstance(tx_data, list) and len(tx_data) >= 2: # The format is [encoded_data, encoding_type] encoded_str = tx_data[0] format_type = tx_data[1] print(f"Transaction is a {format_type} encoded array") print(f"First 50 chars of encoded data: {encoded_str[:50]}...") # Try to extract a small sample of the decoded data try: if format_type == "base64": sample = base64.b64decode(encoded_str[:100] + "=" * (4 - len(encoded_str[:100]) % 4)) elif format_type == "base58": sample = base58.b58decode(encoded_str[:100]) print(f"Sample of decoded bytes (hex): {sample.hex()[:60]}...") except Exception as e: print(f"Error decoding sample: {e}") elif isinstance(tx_data, str): print(f"Transaction is a string of length: {len(tx_data)}") print(f"First 50 chars: {tx_data[:50]}...") else: print(f"Transaction is not a string or list but: {type(tx_data)}") if isinstance(tx_data, dict): print(f"Keys: {list(tx_data.keys())}") else: # Original handling for JSON and jsonParsed if "transaction" in first_tx: tx = first_tx["transaction"] # Check if it's a string (likely base64) or an object if isinstance(tx, str): print(f"Transaction is a string (likely base64)") print(f"Length: {len(tx)} characters") print(f"First 100 chars: {tx[:100]}...") else: print(f"Transaction is an object/dict") print(f"Keys: {list(tx.keys()) if isinstance(tx, dict) else 'N/A'}") # If there's a message field, check its format if isinstance(tx, dict) and "message" in tx: msg = tx["message"] if isinstance(msg, str): print(f"Message is a string, length: {len(msg)}") print(f"First 100 chars: {msg[:100]}...") else: print(f"Message is an object with keys: {list(msg.keys()) if isinstance(msg, dict) else 'N/A'}") else: print(f"Transaction doesn't have expected 'transaction' field") print(f"Available fields: {list(first_tx.keys())}") print("-" * 40) # Fetch the latest slot slot_number = get_latest_slot() if slot_number: # Fetch the block with each encoding type print("\nFetching block with JSON encoding:") json_response = get_block_with_encoding(slot_number, encoding="json") print("\nFetching block with jsonParsed encoding:") jsonParsed_response = get_block_with_encoding(slot_number, encoding="jsonParsed") print("\nFetching block with base64 encoding:") base64_response = get_block_with_encoding(slot_number, encoding="base64") print("\nFetching block with base58 encoding:") base58_response = get_block_with_encoding(slot_number, encoding="base58") # Inspect the transaction formats to verify encoding differences if json_response: inspect_transaction_format(json_response, "json") if jsonParsed_response: inspect_transaction_format(jsonParsed_response, "jsonParsed") if base64_response: inspect_transaction_format(base64_response, "base64") if base58_response: inspect_transaction_format(base58_response, "base58") print("\nComparison complete. Check the block_data directory for the raw responses.") ``` Here's an example output: ```shell Shell theme={"system"} ├── [6.3M] block_329897953_base58.json ├── [6.3M] block_329897953_base64.json ├── [8.0M] block_329897953_json.json └── [ 16M] block_329897953_jsonParsed.json ``` ## Performance optimization Examples in Python. ### Limit concurrency and throttle requests When fetching multiple blocks, avoid sending too many requests simultaneously: ```python Python theme={"system"} import asyncio import concurrent.futures from solana.rpc.api import Client import json import os import pathlib async def fetch_block_with_solana(executor, client, slot, semaphore): # Use a semaphore to limit concurrency async with semaphore: try: # Run the synchronous client.get_block in a thread pool loop = asyncio.get_running_loop() response = await loop.run_in_executor( executor, lambda: client.get_block( slot, encoding="json", max_supported_transaction_version=1 ) ) # Solana-py returns a solders.rpc.responses.GetBlockResp object # Check if the value field exists (indicates success) if hasattr(response, 'value') and response.value is not None: return slot, response.value else: print(f"Error in response for slot {slot}") return slot, None except Exception as e: print(f"Exception for slot {slot}: {e}") return slot, None def save_block_to_file(slot, block, output_dir): """Save block data to a JSON file""" os.makedirs(output_dir, exist_ok=True) try: if hasattr(block, '__dict__'): block_dict = block.__dict__ else: block_dict = { 'blockhash': str(block.blockhash) if hasattr(block, 'blockhash') else None, 'blockHeight': block.block_height if hasattr(block, 'block_height') else None, 'blockTime': block.block_time if hasattr(block, 'block_time') else None, 'parentSlot': block.parent_slot if hasattr(block, 'parent_slot') else None, 'previousBlockhash': str(block.previous_blockhash) if hasattr(block, 'previous_blockhash') else None, 'transactions': [ { 'meta': tx.meta.__dict__ if hasattr(tx, 'meta') and hasattr(tx.meta, '__dict__') else None, 'transaction': str(tx.transaction) if hasattr(tx, 'transaction') else None, } for tx in block.transactions if hasattr(block, 'transactions') ] if hasattr(block, 'transactions') else [] } # Save to file file_path = pathlib.Path(output_dir) / f"block_{slot}.json" with open(file_path, 'w') as f: json.dump(block_dict, f, indent=2, default=str) print(f"Saved block {slot} to {file_path}") except Exception as e: print(f"Error saving block {slot}: {e}") async def get_multiple_blocks(rpc_url, slots, max_concurrency=5, output_dir="block_data"): # Create a semaphore to limit concurrent requests semaphore = asyncio.Semaphore(max_concurrency) # Create the Solana client client = Client(rpc_url) # Create a thread pool executor for running synchronous code with concurrent.futures.ThreadPoolExecutor() as executor: tasks = [fetch_block_with_solana(executor, client, slot, semaphore) for slot in slots] results = await asyncio.gather(*tasks) # Process and save each block blocks = {} for slot, block in results: if block is not None: blocks[slot] = block # Save to file save_block_to_file(slot, block, output_dir) return blocks # Example usage async def main(): rpc_url = "CHAINSTACK_SOLANA_URL" slots_to_fetch = [329849011, 329849012, 329849013, 329849014, 329849015] blocks = await get_multiple_blocks(rpc_url, slots_to_fetch, max_concurrency=3) print(f"\nSuccessfully fetched {len(blocks)} blocks") for slot, block in blocks.items(): if block: # Access the transactions field from the block object tx_count = len(block.transactions if hasattr(block, 'transactions') else []) print(f"Slot {slot}: {tx_count} transactions") # Run the async function if __name__ == "__main__": asyncio.run(main()) ``` As always, make sure you get your own range of blocks in `slots_to_fetch = [329849011, 329849012, 329849013, 329849014, 329849015]`. Recommended encodings: * For most use cases: `json` (good balance of size and parsing speed) * For human-readable data: `jsonParsed` (larger but provides decoded instruction data) * For binary efficiency: `base64` (efficient for storage and transmission) ### Use binary encoding for bulk requests Same script as above, but in line 18 instead of`encoding="json"` use `encoding="base64"`. ### Error handling Let's start with a working script and explain it below: ```python Python theme={"system"} """ Solana getBlock with retries implementation This script demonstrates how to fetch a Solana block with proper error handling and exponential backoff retries. """ import time from solana.rpc.api import Client def get_block_with_retries(client, slot, max_retries=3, backoff_factor=2): """Fetch a block with exponential backoff retries Args: client: Solana RPC client slot: The slot number to fetch max_retries: Maximum number of retry attempts backoff_factor: Multiplier for exponential backoff Returns: Block data or None if failed after retries """ retry_count = 0 base_wait_time = 1 # Start with 1 second wait while retry_count < max_retries: try: # Get the block response block_resp = client.get_block( slot, encoding="json", max_supported_transaction_version=1 ) # The response is a solders.rpc.responses.GetBlockResp object # We need to check if it has a value (success) or error if hasattr(block_resp, 'value') and block_resp.value is not None: return block_resp.value elif hasattr(block_resp, 'error'): error = block_resp.error # Handle specific error codes if possible if hasattr(error, 'code'): code = error.code message = getattr(error, 'message', 'Unknown error') if code == -32007: # Block not available (purged from ledger) print(f"Block {slot} not available in ledger storage") return None elif code == -32004: # Slot skipped print(f"Slot {slot} was skipped (no block produced)") return None elif code == -32603: # Internal error # This might be temporary, retry print(f"Internal error, will retry: {message}") elif code == 429: # Rate limit print("Rate limited, backing off significantly") retry_count += 1 time.sleep(base_wait_time * backoff_factor ** retry_count * 2) # Extra backoff for rate limits continue else: print(f"RPC error code {code}: {message}") else: # If we can't get a specific error code print(f"Error in response: {error}") else: # Null result print(f"Block {slot} returned null (not found or not confirmed)") return None except Exception as e: retry_count += 1 wait_time = base_wait_time * backoff_factor ** retry_count print(f"Error fetching block {slot}, retry {retry_count}/{max_retries} after {wait_time}s: {e}") if retry_count < max_retries: time.sleep(wait_time) else: print(f"Max retries reached for block {slot}") return None # If we get here, there was an error but not one we want to immediately retry on retry_count += 1 wait_time = base_wait_time * backoff_factor ** retry_count if retry_count < max_retries: print(f"Retrying after {wait_time}s...") time.sleep(wait_time) else: print(f"Max retries reached for block {slot}") return None return None # Shouldn't reach here, but just in case def main(): # Example usage rpc_url = "CHAINSTACK_SOLANA_URL" print(f"Connecting to Solana RPC: {rpc_url}") client = Client(rpc_url) slot_to_fetch = 329849011 # Example slot print(f"Fetching block at slot {slot_to_fetch}...") block = get_block_with_retries(client, slot_to_fetch) if block: # Print summary of the block transactions = getattr(block, 'transactions', []) # Safely extract signatures signatures = [] for tx in transactions: if hasattr(tx, 'transaction') and hasattr(tx.transaction, 'signatures'): if tx.transaction.signatures: signatures.append(tx.transaction.signatures[0]) print(f"\nBlock {slot_to_fetch} summary:") print(f" Blockhash: {getattr(block, 'blockhash', 'unknown')}") print(f" Parent Slot: {getattr(block, 'parent_slot', 'unknown')}") print(f" Block Time: {getattr(block, 'block_time', 'unknown')}") print(f" Block Height: {getattr(block, 'block_height', 'unknown')}") print(f" Transactions: {len(transactions)}") if signatures: print(f"\nFirst few transaction signatures:") for sig in signatures[:3]: print(f" {sig}") if len(signatures) > 3: print(f" ... and {len(signatures)-3} more") else: print("Failed to fetch block") if __name__ == "__main__": main() ``` #### Exponential backoff retry mechanism ```python Python theme={"system"} def get_block_with_retries(client, slot, max_retries=3, backoff_factor=2): retry_count = 0 base_wait_time = 1 # Start with 1 second wait while retry_count < max_retries: # ... [request code] # When an error occurs: wait_time = base_wait_time * backoff_factor ** retry_count time.sleep(wait_time) ``` The code implements classic exponential backoff where: * Wait time grows exponentially with each retry attempt * Initial wait is 1 second * Each subsequent wait is multiplied by backoff factor with retry count * This prevents overwhelming the server with rapid reconnection attempts #### Differential error handling ```python Python theme={"system"} # Handle specific error codes if possible if hasattr(error, 'code'): code = error.code message = getattr(error, 'message', 'Unknown error') if code == -32007: # Block not available (purged from ledger) print(f"Block {slot} not available in ledger storage") return None elif code == -32004: # Slot skipped print(f"Slot {slot} was skipped (no block produced)") return None elif code == -32603: # Internal error # This might be temporary, retry print(f"Internal error, will retry: {message}") elif code == 429: # Rate limit print("Rate limited, backing off significantly") retry_count += 1 time.sleep(base_wait_time * backoff_factor ** retry_count * 2) # Extra backoff for rate limits continue ``` The script intelligently handles different error types: * Non-retryable errors (like purged blocks) fail fast without wasting retries * Temporary errors proceed with standard backoff * Rate limit errors get special treatment with doubled backoff #### Enhanced rate limit handling ```python Python theme={"system"} elif code == 429: # Rate limit print("Rate limited, backing off significantly") retry_count += 1 time.sleep(base_wait_time * backoff_factor ** retry_count * 2) # Extra backoff for rate limits continue ``` Rate limits receive special handling: * Doubled backoff time compared to other errors * This helps prevent repeatedly hitting rate limits * The multiplier (2x) helps ensure the client stays under rate limits #### Comprehensive exception handling ```python Python theme={"system"} try: # Get the block response block_resp = client.get_block(...) # Various response validation checks except Exception as e: retry_count += 1 wait_time = base_wait_time * backoff_factor ** retry_count print(f"Error fetching block {slot}, retry {retry_count}/{max_retries} after {wait_time}s: {e}") if retry_count < max_retries: time.sleep(wait_time) else: print(f"Max retries reached for block {slot}") return None ``` The script catches all exceptions including: * Network errors * Timeout errors * Malformed response errors * Client library errors #### Response validation ```python Python theme={"system"} if hasattr(block_resp, 'value') and block_resp.value is not None: return block_resp.value elif hasattr(block_resp, 'error'): error = block_resp.error # Error handling logic else: # Null result print(f"Block {slot} returned null (not found or not confirmed)") return None ``` The code thoroughly validates responses before processing: * Checks for valid response structure * Handles null responses appropriately * Verifies response has expected attributes #### Detailed logging ```python Python theme={"system"} print(f"Error fetching block {slot}, retry {retry_count}/{max_retries} after {wait_time}s: {e}") # ... print(f"Max retries reached for block {slot}") ``` The script provides detailed logging: * Error messages with specific error codes and descriptions * Retry counts and wait times * Final outcomes (success or failure) * This aids in debugging and monitoring #### Parameterized retry configuration ```python Python theme={"system"} def get_block_with_retries(client, slot, max_retries=3, backoff_factor=2): ``` The retry mechanism is fully customizable: * Configurable maximum retries * Adjustable backoff factor * This allows tuning based on network conditions or application requirements These techniques together create a resilient implementation that gracefully handles various network issues, temporary failures, and rate limiting while providing clear feedback about what's happening during the process. ### Client-side caching Implement a client-side cache that stores previously fetched blocks in memory, allowing applications to: * Retrieve frequently accessed blocks without making additional RPC calls * Track cache performance with hit/miss statistics * Maintain a configurable maximum cache size This should reduce unnecessary calls. Example: ```python Python theme={"system"} #!/usr/bin/env python3 """ Solana Block Cache A utility for efficiently fetching and caching Solana blocks to reduce RPC calls. """ import time import json import requests from typing import Dict, Any, Optional, Union from solana.rpc.api import Client class BlockCache: """A simple cache for Solana blocks to reduce redundant RPC calls.""" def __init__(self, client: Client, max_size: int = 100): """ Initialize the block cache. Args: client: A Solana RPC client instance or URL string max_size: Maximum number of blocks to keep in cache """ if isinstance(client, str): self.rpc_url = client self.client = None else: self.rpc_url = client._provider.endpoint_uri self.client = client self.session = requests.Session() self.session.headers.update({ "Content-Type": "application/json", "Accept-Encoding": "gzip" }) self.cache: Dict[str, Any] = {} self.max_size = max_size self.hits = 0 self.misses = 0 self.request_id = 0 def get_block(self, slot: int, encoding: str = "json", tx_details: str = "full", max_supported_transaction_version: Optional[int] = 1) -> Optional[Dict[str, Any]]: """ Fetch a block, using cache if available. Args: slot: The slot number of the block to fetch encoding: Block encoding format ("json", "jsonParsed", "base64", etc.) tx_details: Transaction details level ("full", "signatures", "accounts", "none") max_supported_transaction_version: Maximum supported transaction version Returns: The block data or None if not found/error """ cache_key = f"{slot}:{encoding}:{tx_details}" # Return from cache if available if cache_key in self.cache: self.hits += 1 print(f"Cache hit for block {slot}") return self.cache[cache_key] # Fetch from RPC self.misses += 1 print(f"Cache miss for block {slot}, fetching from RPC...") try: self.request_id += 1 # Prepare parameters for the getBlock method params = { "encoding": encoding, "transactionDetails": tx_details, "maxSupportedTransactionVersion": max_supported_transaction_version } # Create the JSON-RPC payload payload = { "jsonrpc": "2.0", "id": self.request_id, "method": "getBlock", "params": [slot, params] } # Make the HTTP request response = self.session.post(self.rpc_url, json=payload, timeout=30) block_resp = response.json() # Get the result result = block_resp.get('result') # Store in cache if we got a result if result: # If cache is full, remove oldest entry if len(self.cache) >= self.max_size: oldest_key = next(iter(self.cache)) del self.cache[oldest_key] self.cache[cache_key] = result return result except Exception as e: print(f"Error fetching block {slot}: {e}") return None def clear_cache(self) -> None: """Clear the entire cache.""" self.cache = {} print("Cache cleared") def remove_from_cache(self, slot: int, encoding: str = "json", tx_details: str = "full") -> bool: """ Remove a specific block from the cache. Returns: True if the block was in the cache and removed, False otherwise """ cache_key = f"{slot}:{encoding}:{tx_details}" if cache_key in self.cache: del self.cache[cache_key] print(f"Removed block {slot} from cache") return True return False def get_stats(self) -> Dict[str, Union[int, float]]: """Get cache performance statistics.""" total_requests = self.hits + self.misses hit_rate = self.hits / total_requests if total_requests > 0 else 0 return { "size": len(self.cache), "max_size": self.max_size, "hits": self.hits, "misses": self.misses, "hit_rate": hit_rate, } def main(): """Example usage of BlockCache.""" rpc_url = "CHAINSTACK_SOLANA_URL" print("Initializing Solana Block Cache") block_cache = BlockCache(rpc_url) # Example: Fetch a block (cache miss) print("\nFetching block first time (should be cache miss):") block1 = block_cache.get_block(329849011) if block1: print(f"Block hash: {block1.get('blockhash')}") print(f"Block time: {block1.get('blockTime')}") print(f"Transaction count: {len(block1.get('transactions', []))}") # Example: Fetch the same block again (cache hit) print("\nFetching same block again (should be cache hit):") block2 = block_cache.get_block(329849011) # Fetch with different parameters (should be a cache miss) print("\nFetching same block with different parameters (should be cache miss):") block3 = block_cache.get_block(329849011, tx_details="signatures") # Print cache statistics print("\nCache statistics:") stats = block_cache.get_stats() for key, value in stats.items(): if key == "hit_rate": print(f"{key}: {value:.2%}") else: print(f"{key}: {value}") if __name__ == "__main__": main() ``` ### Block ranges Implement flexible block range handling through a range-first processing pattern — first identify all available blocks in the target range, then systematically process them in batches. ```python Python theme={"system"} #!/usr/bin/env python3 """ Solana Block Range Processing This script demonstrates how to process ranges of Solana blocks efficiently with batching and proper error handling. """ import time from solana.rpc.api import Client def get_block_with_retries(client, slot, max_retries=3, backoff_factor=2): """Fetch a block with exponential backoff retries Args: client: Solana RPC client slot: The slot number to fetch max_retries: Maximum number of retry attempts backoff_factor: Multiplier for exponential backoff Returns: Block data or None if failed after retries """ retry_count = 0 base_wait_time = 1 # Start with 1 second wait while retry_count < max_retries: try: # Get the block response block_resp = client.get_block( slot, encoding="json", max_supported_transaction_version=1 ) # The response is a solders.rpc.responses.GetBlockResp object # We need to check if it has a value (success) or error if hasattr(block_resp, 'value') and block_resp.value is not None: return block_resp.value elif hasattr(block_resp, 'error'): error = block_resp.error # Handle specific error codes if possible if hasattr(error, 'code'): code = error.code message = getattr(error, 'message', 'Unknown error') if code == -32007: # Block not available (purged from ledger) print(f"Block {slot} not available in ledger storage") return None elif code == -32004: # Slot skipped print(f"Slot {slot} was skipped (no block produced)") return None elif code == -32603: # Internal error # This might be temporary, retry print(f"Internal error, will retry: {message}") elif code == 429: # Rate limit print("Rate limited, backing off significantly") retry_count += 1 time.sleep(base_wait_time * backoff_factor ** retry_count * 2) # Extra backoff for rate limits continue else: print(f"RPC error code {code}: {message}") else: # If we can't get a specific error code print(f"Error in response: {error}") else: # Null result print(f"Block {slot} returned null (not found or not confirmed)") return None except Exception as e: retry_count += 1 wait_time = base_wait_time * backoff_factor ** retry_count print(f"Error fetching block {slot}, retry {retry_count}/{max_retries} after {wait_time}s: {e}") if retry_count < max_retries: time.sleep(wait_time) else: print(f"Max retries reached for block {slot}") return None # If we get here, there was an error but not one we want to immediately retry on retry_count += 1 wait_time = base_wait_time * backoff_factor ** retry_count if retry_count < max_retries: print(f"Retrying after {wait_time}s...") time.sleep(wait_time) else: print(f"Max retries reached for block {slot}") return None return None # Shouldn't reach here, but just in case def get_block_range(client, start_slot, end_slot=None, limit=500): """ Get a list of available block slots in a range. If end_slot is None, will use getBlocksWithLimit instead of getBlocks. Args: client: Solana RPC client start_slot: The starting slot number end_slot: The ending slot number (optional) limit: Maximum number of blocks to return when end_slot is None Returns: List of available block slots in the range """ try: if end_slot is None: # Use getBlocksWithLimit blocks_resp = client.get_blocks_with_limit(start_slot, limit) else: # Use getBlocks blocks_resp = client.get_blocks(start_slot, end_slot) # Handle the response which could be an object or a dict if hasattr(blocks_resp, 'value'): # Object response return blocks_resp.value or [] else: # Dict response return blocks_resp.get('result', []) except Exception as e: print(f"Error getting block range: {e}") return [] def process_block_range(client, start_slot, end_slot, batch_size=10): """Process blocks in a range in batches Args: client: Solana RPC client start_slot: The starting slot number end_slot: The ending slot number batch_size: Number of blocks to process in each batch Returns: Total number of transactions processed """ # First get all available block slots in the range print(f"Fetching block slots from {start_slot} to {end_slot}...") block_slots = get_block_range(client, start_slot, end_slot) print(f"Found {len(block_slots)} blocks to process") # Process in batches to avoid overwhelming the RPC node total_blocks = len(block_slots) total_transactions = 0 for i in range(0, total_blocks, batch_size): batch = block_slots[i:i+batch_size] batch_num = i//batch_size + 1 total_batches = (total_blocks + batch_size - 1)//batch_size print(f"Processing batch {batch_num}/{total_batches}...") # Process each block in the batch for slot in batch: block = get_block_with_retries(client, slot) if block: # Get transaction count safely transactions = getattr(block, 'transactions', []) tx_count = len(transactions) total_transactions += tx_count print(f" Slot {slot}: {tx_count} transactions") # Add a small delay between blocks to be nice to the RPC node time.sleep(0.1) # Add a delay between batches if i + batch_size < total_blocks: print(f"Waiting before next batch...") time.sleep(1) print(f"Processed {total_blocks} blocks with {total_transactions} total transactions") return total_transactions def process_recent_blocks(client, count=100): """Process the most recent blocks Args: client: Solana RPC client count: Number of recent blocks to process Returns: Total number of transactions processed """ # Get the current slot current_slot_resp = client.get_slot() # Handle either object or dict response if hasattr(current_slot_resp, 'value'): # Object response current_slot = current_slot_resp.value else: # Dict response current_slot = current_slot_resp.get('result') if current_slot: # Calculate the starting slot (count blocks back) start_slot = max(0, current_slot - count) print(f"Processing {count} recent blocks from {start_slot} to {current_slot}...") return process_block_range(client, start_slot, current_slot) else: print("Failed to get current slot") return 0 def main(): # Example usage rpc_url = "CHAINSTACK_SOLANA_URL" print(f"Connecting to Solana RPC: {rpc_url}") client = Client(rpc_url) # Process the 20 most recent blocks count = 20 print(f"Processing {count} recent blocks...") tx_count = process_recent_blocks(client, count=count) print(f"Total transactions in last {count} blocks: {tx_count}") if __name__ == "__main__": main() ``` ## Conclusion Working with Solana's `getBlock` RPC method efficiently requires understanding both what data you need and how to optimize your requests. By following the best practices outlined in this guide—using compression, limiting concurrency, using block ranges, requesting only what you need, and implementing proper error handling—you can build robust applications that interact with Solana blocks effectively. Remember these key takeaways: 1. Use the appropriate encoding and detail level for your use case — `json`, `jsonParsed`, `base58`, `base64`. 2. Always enable HTTP compression. 3. Implement client-side caching for frequently accessed blocks. 4. Use controlled concurrency and throttling for bulk operations. 5. Handle errors gracefully with retries and backoff. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Pinocchio vs Quasar program frameworks Source: https://docs.chainstack.com/docs/solana-pinocchio-vs-quasar Compare the Pinocchio and Quasar Solana program frameworks — efficiency, ergonomics, zero-copy, and audit status — and choose the right one for programs you deploy and test against your Chainstack node. **TLDR:** * Pinocchio and Quasar are both zero-copy, high-efficiency Solana program frameworks, but they optimize for different things. * Pinocchio is low-level and audited (p-token is built on it): maximum control, but you write the owner, signer, and discriminator checks yourself. * Quasar is a newer, beta, unaudited framework with Anchor-like ergonomics that keeps Pinocchio-level efficiency and is zero-copy by default. * Rule of thumb: reach for Quasar to write an efficient program quickly; reach for Pinocchio for byte-level control or an audited, battle-tested base. ## The framework spectrum Solana program frameworks sit on a spectrum that trades control and effort for ergonomics: * **Native** — a raw entry point and manual pointer work. Maximum control, no safety nets. * **Pinocchio** — a thin, zero-copy library: safe account and instruction views, entry-point macros, and CPI helpers, but no validation or routing. You write those. * **Quasar** — an Anchor-like macro layer (account validation, discriminators, PDA seeds, context structs) that compiles down to Pinocchio-tier efficiency and is zero-copy by design. * **Anchor** — the full-featured, most battle-tested framework: borsh serialization, 8-byte discriminators, IDL and client generation. The highest developer experience and the highest compute cost. This page focuses on the two middle options, Pinocchio and Quasar, since [Anchor has its own guide](/docs/solana-anchor-development). ## Pinocchio [Pinocchio](https://github.com/anza-xyz/pinocchio) is a zero-dependency, `no_std` program library from Anza (the Agave client team). It reads the runtime's serialized input in place through a single pointer — no copies — and exposes zero-copy account and instruction views, with an optional no-allocator mode for programs that need no heap. It is audited (by Zellic) and battle-tested: [p-token](/docs/solana-ptoken-batch-instruction), the production SPL Token replacement, is built on it. The trade-off is that Pinocchio gives you no safety nets. There is no framework-provided owner check, signer check, discriminator validation, or writable assertion — you implement all of them. You think in terms of instruction data, account data, and the owner field, not instructions and validated accounts. Pinocchio offers two entry-point macros: * `entrypoint!` — parses the whole input (all accounts and instruction data) before calling your function. Best for programs with many instructions, where that one-time parse is amortized. * `lazy_program_entrypoint!` — hands you a context and parses nothing until you ask for it, so you pay only for the accounts and data you touch. Best for programs with one or a few instructions; it dropped the memo program's compute by roughly 80–87%. Note that, unlike the standard entry point, it does not detect duplicate accounts for you. A minimal program routes on a discriminator byte and validates accounts by hand: ```rust Rust theme={"system"} // pinocchio = "0.11" use pinocchio::error::ProgramError; use pinocchio::{entrypoint, AccountView, Address, ProgramResult}; entrypoint!(process_instruction); pub fn process_instruction( program_id: &Address, accounts: &mut [AccountView], instruction_data: &[u8], ) -> ProgramResult { let [counter, authority, _rest @ ..] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; // You write these checks — the framework does not. if !authority.is_signer() { return Err(ProgramError::MissingRequiredSignature); } if counter.owner() != program_id { return Err(ProgramError::IncorrectProgramId); } match instruction_data.first() { Some(&0) => Ok(()), // increment the counter in place _ => Err(ProgramError::InvalidInstructionData), } } ``` > [!NOTE] > > Pinocchio's type names have shifted across releases — 0.11 uses `AccountView` and `Address`, where older versions used `AccountInfo` and `Pubkey`. Pin a version and follow [docs.rs/pinocchio](https://docs.rs/pinocchio/latest/pinocchio/) for the exact API. The no-allocator mode also forbids heap types (`String`, `Vec`) anywhere in the program, including transitively through dependencies. ## Quasar [Quasar](https://github.com/blueshift-gg/quasar) is a newer program framework from Blueshift. It brings Anchor-style ergonomics — `#[program]`, `#[account]`, `#[derive(Accounts)]`, and a context type — while compiling to Pinocchio-tier efficiency. Accounts are pointer-cast directly from the input buffer, so it is zero-copy and zero-allocation by default. It is not built on Pinocchio; both libraries sit on the same disaggregated Solana SDK crates. The same counter looks declarative — validation comes from the account constraints, not hand-written code: ```rust Rust theme={"system"} #![no_std] // Install the CLI with `cargo install --path cli`, then scaffold with `quasar init`. use quasar_lang::prelude::*; declare_id!("YourProgramID11111111111111111111111111111111"); // State account — 1-byte discriminator, PDA seeds, zero-copy. #[account(discriminator = 1, set_inner)] #[seeds(b"counter", authority: Address)] pub struct Counter { pub authority: Address, pub count: u64, } // Validated accounts — constraints replace the hand-written checks. #[derive(Accounts)] pub struct Increment { #[account(mut, address = Counter::seeds(authority.address()))] pub counter: Account, pub authority: Signer, } #[program] mod my_counter { use super::*; #[instruction(discriminator = 0)] pub fn increment(ctx: Ctx) -> Result<(), ProgramError> { ctx.accounts.counter.count += 1; Ok(()) } } ``` Quasar programs are `#![no_std]`. They use 1-byte configurable discriminators (not Anchor's 8-byte hash) and provide bounded, inline zero-copy `String` and `Vec` types whose maximum sizes are fixed at compile time — for example `String<32>` or `Vec`. ## Efficiency in context At this tier, the framework barely moves compute — both Pinocchio and Quasar compile to near-hand-written efficiency. Published benchmarks from Accelerate 2025 for a simple memo program put Anchor at roughly 649 compute units (down to \~281 when hand-optimized), Pinocchio at \~108, and hand-written assembly at \~104. p-token, built on Pinocchio, cut SPL Token compute by 88–95% and shrank the binary by about 40%. So the choice between Pinocchio and Quasar is not an efficiency contest. Quasar targets the same Pinocchio tier; in a like-for-like basic program it runs a handful of compute units above Pinocchio, and that small gap is precisely the owner and discriminator checks Quasar performs for you — the ones you would write by hand in Pinocchio. The real decision is control versus ergonomics at the same efficiency level. Against Anchor, both are dramatically cheaper. ## Which should you use? | | Pinocchio | Quasar | | - | - | - | | Style | Low-level library | Anchor-like framework | | You write validation | Yes — owner, signer, discriminator | No — declared via constraints | | Zero-copy | Yes | Yes, by default | | Audited | Yes (Zellic); powers p-token | No — beta | | Best for | Byte-level optimization, security-critical or audited bases, CPIs | Writing an efficient program quickly with low risk of mistakes | * Choose **Quasar** when you want to ship an efficient program fast and let the framework prevent common mistakes — and you can accept beta, unaudited software. * Choose **Pinocchio** when you need full control for byte-level optimization, or you want an audited, battle-tested foundation for a security-critical program. ## Caveats * Quasar is beta and unaudited — its README states it "has not been audited. APIs may change. Use at your own risk." It ships through its own CLI (`quasar init`) rather than versioned crates.io releases (the published `0.0.0` crate is a placeholder), so pin to a specific commit and expect breaking changes. Do not ship it to production without your own audit. Its 1-byte discriminators are also incompatible with Anchor's 8-byte scheme, which matters if you parse or migrate across both. * Pinocchio has no safety nets — a single missed owner, signer, or discriminator check is a vulnerability. Pin a version, since the API has shifted across releases. * An Anchor v2 built on a Pinocchio entry point is in development (not released as of this writing) and may reshape this comparison once it ships. ## Additional resources * [Anchor development](/docs/solana-anchor-development) — the full-featured framework this page contrasts against. * [p-token batch instruction](/docs/solana-ptoken-batch-instruction) — a production program built on Pinocchio. * [anza-xyz/pinocchio](https://github.com/anza-xyz/pinocchio) — source, docs, and examples. * [blueshift-gg/quasar](https://github.com/blueshift-gg/quasar) — source, and [quasar-lang.com](https://quasar-lang.com/docs) for the docs. # Solana: Priority fees for a Jupiter Swap in Python Source: https://docs.chainstack.com/docs/solana-priority-fees-for-a-jupiter-in-python Calculate and apply optimal priority fees for Jupiter swap transactions on Solana using Python with compute budget instructions through Chainstack. > **Part of the [How to land transactions on Solana](/docs/solana-how-to-land-transactions) guide.** **TLDR:** * Jupiter offers a quick way to swap tokens on Solana but lacks a Python SDK, so we query Jupiter’s public API for quotes and transaction data. * We calculate our priority fee by fetching recent block data via [`getRecentPrioritizationFees`](/reference/solana-getrecentprioritizationfees), then derive the median fee and multiply it by a user-defined factor. * Adding this priority fee to the raw Jupiter transaction data (plus the base fee of 5000 lamports) helps ensure faster block inclusion under congestion. * The entire flow uses both Jupiter endpoints (quote + swap) and a private Solana RPC (e.g., Chainstack) for the final transaction broadcast, giving you more control over performance and reliability. > [!WARNING] > > Jupiter retired the old `quote-api.jup.ag/v6` endpoints on October 1, 2025. This guide uses the current Swap API at `https://api.jup.ag/swap/v1`. The keyless `https://lite-api.jup.ag/swap/v1` works for low request rates; `api.jup.ag` takes an `X-API-Key` header for higher tiers (get a key at [portal.jup.ag](https://portal.jup.ag)). Jupiter also offers a newer consolidated [Swap API v2](https://dev.jup.ag); the v1 flow below is verified end to end. ## Main article With its cumulative volume in [USD billions](https://defillama.com/protocol/jupiter-aggregator#information), Jupiter is one of the top DEX aggregators on Solana. In this tutorial, we are going to have a look at how add our own prioritization fees to swap for a token pair through a Jupiter aggregator route. As Jupiter does not have a Python SDK at the time of this tutorial, it's more fun doing this in Python. For JavaScript, check out the [Jupiter developer docs](https://developers.jup.ag/docs/swap). ## Transaction fees on Solana The topic of transactions fees on Solana is both a nascent development and its own can of worms at the same time, so we are not going to do a deep dive into this. Here's however what you need to know in a succinct form for this tutorial: * Base fee — fixed 5000 lamports (0.000005 SOL) per transaction signature, which 99% of the time means 5000 lamports per transaction * Priority fee — a non-fixed amount that you can include in your transaction on top of the 5000 lamports base fee to get ahead of others in the queue for the block inclusion of the current leader. So in theory — the higher the priority fee, the more chances you have to skip ahead and become a part of the state of the chain than the others you are racing against. Let's implement adding a proper priority fee to our transaction based on an interaction with the Jupiter aggregator. Get yourself a Chainstack Solana node. ## Overview Here's how it's going to work: 1. Get a token pair price quote from the Jupiter API. 2. Get the priority fees data from the recent 150 blocks with the `getRecentPrioritizationFees` call from a Chainstack Solana node. 3. Calculate the priority fee to include with your transaction. 4. Submit the transaction through the Chainstack Solana node: 1. Transaction data (token pair exchange rate, liquidity pool) from the Jupiter API quote 2. Priority fee based on the raw chain data obtained through the Chainstack Solana node ## Implementation The flow is: estimate a priority fee from recent on-chain data, get a quote from Jupiter, build the swap transaction with that fee, then sign and send it through your own Solana node. Jupiter doesn't have an official Python SDK, so we call its REST API directly and sign with `solders`. Install the dependencies: ```shell Shell theme={"system"} pip install requests solders solana ``` The complete script: ```python jupiter_swap.py theme={"system"} import base64 import statistics import requests from solders.keypair import Keypair from solders.transaction import VersionedTransaction from solana.rpc.api import Client from solana.rpc.types import TxOpts # Your Chainstack Solana node and wallet RPC_ENDPOINT = "YOUR_CHAINSTACK_SOLANA_NODE" PRIVATE_KEY = "YOUR_PRIVATE_KEY" # base58-encoded # Jupiter Swap API. lite-api.jup.ag is keyless (low rate); api.jup.ag takes an # X-API-Key header for higher tiers — get a key at https://portal.jup.ag JUPITER = "https://api.jup.ag/swap/v1" INPUT_MINT = "So11111111111111111111111111111111111111112" # SOL OUTPUT_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" # USDC AMOUNT = 1_000_000 # 0.001 SOL, in lamports SLIPPAGE_BPS = 50 # 0.5% PRIORITY_MULTIPLIER = 1.1 # bump over the median fee wallet = Keypair.from_base58_string(PRIVATE_KEY) # 1) Estimate the priority fee from recent blocks. Use the median, not the max: # occasional astronomical fees on-chain can drain your wallet if you chase them. resp = requests.post(RPC_ENDPOINT, json={ "jsonrpc": "2.0", "id": 1, "method": "getRecentPrioritizationFees", "params": [[INPUT_MINT]], # account-aware: fees for the accounts your tx touches }).json() fees = [x["prioritizationFee"] for x in resp["result"]] median_fee = int(statistics.median(fees)) if fees else 0 max_lamports = int(median_fee * PRIORITY_MULTIPLIER) or 1_000 print(f"Median priority fee: {median_fee} microlamports/CU") # 2) Get a quote from Jupiter. quote = requests.get(f"{JUPITER}/quote", params={ "inputMint": INPUT_MINT, "outputMint": OUTPUT_MINT, "amount": AMOUNT, "slippageBps": SLIPPAGE_BPS, }).json() print(f"Quote: {AMOUNT} -> {quote['outAmount']} (out)") # 3) Build the swap transaction with the priority fee and a dynamic compute-unit limit. swap = requests.post(f"{JUPITER}/swap", json={ "quoteResponse": quote, "userPublicKey": str(wallet.pubkey()), "dynamicComputeUnitLimit": True, "prioritizationFeeLamports": { "priorityLevelWithMaxLamports": { "maxLamports": max_lamports, "priorityLevel": "veryHigh", } }, }).json() # 4) Sign the returned versioned transaction and send it through your node. raw = VersionedTransaction.from_bytes(base64.b64decode(swap["swapTransaction"])) signed = VersionedTransaction(raw.message, [wallet]) client = Client(RPC_ENDPOINT) sig = client.send_raw_transaction(bytes(signed), opts=TxOpts(skip_preflight=True)).value print(f"Sent: https://solscan.io/tx/{sig}") ``` A few notes on the current Jupiter API: * **Priority fee shape.** `prioritizationFeeLamports.priorityLevelWithMaxLamports` lets Jupiter pick a fee at the chosen `priorityLevel` (`medium`, `high`, or `veryHigh`) capped at `maxLamports`. Here we cap it at the median-derived value so we never overpay during a fee spike. * **`dynamicComputeUnitLimit: true`** makes Jupiter simulate the swap and set an accurate compute-unit limit, which both improves landing and avoids overpaying (the priority fee is `compute_unit_price × compute_unit_limit`). * **Versioned transactions.** Jupiter returns a base64 `VersionedTransaction`; sign it with `solders` and broadcast the raw bytes through your node. * **Why the median.** Fees over the recent window can include random astronomical outliers — basing your fee on the max can drain your wallet. The median (optionally bumped by `PRIORITY_MULTIPLIER`) is a safer baseline. Tune it for your needs. > [!NOTE] > > The companion repository [jupiter-swaps-priority-fees-python](https://github.com/chainstacklabs/jupiter-swaps-priority-fees-python) has the full project. The base fee on Solana is a fixed 5000 lamports per signature; the priority fee above is added on top. ## Conclusion Solana is a very fun and extremely lively ecosystem, so have fun building, keep and eye on the all changes and how the fee market develops — this can give you an edge. And use Chainstack, of course. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: PDAs and cross-program invocations Source: https://docs.chainstack.com/docs/solana-program-derived-addresses-and-cross-program-invocations How Solana PDAs and CPIs work — deriving deterministic addresses with seeds and bumps, creating PDA accounts, and calling other programs with Anchor. **TLDR:** * A Program Derived Address (PDA) is a 32-byte address derived deterministically from seeds and a program ID. No private key exists for it — only the deriving program can sign on its behalf. * PDAs enable deterministic addressing (same seeds = same address), program-owned authority (vault patterns), and user-scoped state (one account per user). * A Cross-Program Invocation (CPI) is when one program calls an instruction on another program during execution. Use `invoke` when no PDA signing is needed, `invoke_signed` when the calling program must sign as a PDA. * Anchor simplifies both with `seeds`/`bump` constraints for PDAs and `CpiContext` for CPIs. ## Program derived addresses On Solana, programs are stateless. All data lives in accounts, and accounts are identified by their 32-byte address. A regular keypair account has a public key (the address) and a private key (used to sign transactions). A PDA is different: it's an address that is guaranteed to **not** lie on the Ed25519 curve, which means no private key exists for it. Only the program that derived the PDA can authorize operations on it. This design enables two things that would be impossible with keypair accounts: 1. **Deterministic addressing** — the same seeds and program ID always produce the same address. You can derive a user's vault address from their wallet pubkey without storing it anywhere. 2. **Program-controlled authority** — the program can sign on behalf of the PDA during a cross-program invocation, making the PDA act as a trustless escrow, vault, or authority account. ### How derivation works PDA derivation uses SHA-256. The inputs are: 1. **Optional seeds** — up to 16 byte arrays (max 32 bytes each) that you define. These could be strings, public keys, integers, or a combination. 2. **Program ID** — the address of the program that owns the PDA. 3. **The string `"ProgramDerivedAddress"`** — a constant marker appended by the runtime. 4. **Bump seed** — a single byte (0–255) appended to the seeds to push the hash result off the Ed25519 curve. The algorithm hashes all of these together with SHA-256 and checks whether the result is a valid Ed25519 point. If it is on the curve (meaning a private key could exist), the bump is decremented and the hash is tried again. The first bump value that produces an off-curve address is the **canonical bump**. `findProgramAddress` starts at bump 255 and decrements until it finds a valid PDA. Always use the canonical bump — using a non-canonical bump creates a second valid address for the same seeds, which can lead to vulnerabilities. ### Limits | Limit | Value | | - | - | | Max seeds per derivation | 16 | | Max bytes per seed | 32 | | Bump range | 0–255 | | `create_program_address` cost | 1,500 CUs | | `find_program_address` worst-case cost | 1,500 + (1,500 × iterations) | | Max PDA signers per CPI | 16 | ### Deriving a PDA client-side Deriving a PDA is a read-only operation. It computes an address but does not create an account. ```typescript TypeScript theme={"system"} import { Connection, PublicKey } from "@solana/web3.js"; const CHAINSTACK_ENDPOINT = "CHAINSTACK_NODE_URL"; const connection = new Connection(CHAINSTACK_ENDPOINT); // The program that owns the PDA const programId = new PublicKey("11111111111111111111111111111111"); // Derive with a string seed const [pda, bump] = PublicKey.findProgramAddressSync( [Buffer.from("vault")], programId ); console.log(`PDA: ${pda.toBase58()}`); console.log(`Bump: ${bump}`); // Derive with a user-specific seed const userWallet = new PublicKey("B9Lf9z5BfNPT4d5KMeaBFx8x1G4CULZYR1jA2kmxRDka"); const [userPda, userBump] = PublicKey.findProgramAddressSync( [Buffer.from("user_data"), userWallet.toBuffer()], programId ); console.log(`User PDA: ${userPda.toBase58()}`); console.log(`User bump: ${userBump}`); ``` ```python Python theme={"system"} from solders.pubkey import Pubkey # The program that owns the PDA program_id = Pubkey.from_string("11111111111111111111111111111111") # Derive with a string seed pda, bump = Pubkey.find_program_address([b"vault"], program_id) print(f"PDA: {pda}") print(f"Bump: {bump}") # Derive with a user-specific seed user_wallet = Pubkey.from_string("B9Lf9z5BfNPT4d5KMeaBFx8x1G4CULZYR1jA2kmxRDka") user_pda, user_bump = Pubkey.find_program_address( [b"user_data", bytes(user_wallet)], program_id, ) print(f"User PDA: {user_pda}") print(f"User bump: {user_bump}") ``` ### Common seed patterns | Pattern | Seeds | Use case | | - | - | - | | Global singleton | `["global"]` | Single config account for the entire program | | Per-user account | `["user", user_pubkey]` | One account per user per program | | Per-user-per-token | `["vault", user_pubkey, mint_pubkey]` | Token vaults scoped to both user and mint | | Sequential record | `["order", user_pubkey, &order_id.to_le_bytes()]` | Numbered records per user | > [!WARNING] > > Seeds are concatenated before hashing. This means `["ab", "cd"]` and `["abcd"]` produce the same PDA. Use fixed-length seeds or a separator to avoid collisions. ### Real-world example: Associated Token Accounts The Associated Token Account (ATA) program is the most widely used PDA on Solana. Every ATA address is derived from three seeds: ```rust theme={"system"} Pubkey::find_program_address( &[ &wallet_address.to_bytes(), &token_program_id.to_bytes(), &token_mint_address.to_bytes(), ], &associated_token_program_id, ) ``` This means for any given wallet, token mint, and token program, there is exactly one deterministic ATA address. No lookup table needed — any client can derive it. > [!NOTE] > > Token-2022 mints use `TOKEN_2022_PROGRAM_ID` instead of `TOKEN_PROGRAM_ID` in the seeds. This produces a different ATA address for the same wallet and mint. Always use the correct token program ID for the mint you're working with. ```typescript TypeScript theme={"system"} import { PublicKey } from "@solana/web3.js"; import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from "@solana/spl-token"; const wallet = new PublicKey("B9Lf9z5BfNPT4d5KMeaBFx8x1G4CULZYR1jA2kmxRDka"); const usdcMint = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // The helper derives the PDA under the hood const ata = getAssociatedTokenAddressSync(usdcMint, wallet); console.log(`ATA: ${ata.toBase58()}`); // Equivalent manual derivation const [manualAta] = PublicKey.findProgramAddressSync( [ wallet.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), usdcMint.toBuffer(), ], ASSOCIATED_TOKEN_PROGRAM_ID, ); console.log(`Manual ATA: ${manualAta.toBase58()}`); console.log(`Match: ${ata.equals(manualAta)}`); ``` ```python Python theme={"system"} from solders.pubkey import Pubkey ASSOCIATED_TOKEN_PROGRAM_ID = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL") TOKEN_PROGRAM_ID = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA") wallet = Pubkey.from_string("B9Lf9z5BfNPT4d5KMeaBFx8x1G4CULZYR1jA2kmxRDka") usdc_mint = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") ata, bump = Pubkey.find_program_address( [bytes(wallet), bytes(TOKEN_PROGRAM_ID), bytes(usdc_mint)], ASSOCIATED_TOKEN_PROGRAM_ID, ) print(f"ATA: {ata}") print(f"Bump: {bump}") ``` ### Verifying a PDA on chain To verify that an on-chain account is the expected PDA, re-derive the address from the known seeds and program ID, then compare it to the account's address. Fetching account info alone does not prove PDA validity. ```typescript TypeScript theme={"system"} import { Connection, PublicKey } from "@solana/web3.js"; import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token"; const CHAINSTACK_ENDPOINT = "CHAINSTACK_NODE_URL"; const connection = new Connection(CHAINSTACK_ENDPOINT); // Known inputs const wallet = new PublicKey("vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg"); const usdcMint = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // Re-derive the expected ATA address from seeds const [expectedAta] = PublicKey.findProgramAddressSync( [wallet.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), usdcMint.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID, ); // Fetch the account and verify the address matches const accountInfo = await connection.getAccountInfo(expectedAta); if (accountInfo) { console.log(`ATA exists at expected PDA: ${expectedAta.toBase58()}`); console.log(`Owner program: ${accountInfo.owner.toBase58()}`); console.log(`Data length: ${accountInfo.data.length} bytes`); } else { console.log(`No account at derived ATA: ${expectedAta.toBase58()}`); } ``` ```python Python theme={"system"} from solana.rpc.api import Client from solders.pubkey import Pubkey client = Client("CHAINSTACK_NODE_URL") ASSOCIATED_TOKEN_PROGRAM_ID = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL") TOKEN_PROGRAM_ID = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA") wallet = Pubkey.from_string("vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg") usdc_mint = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") # Re-derive the expected ATA address from seeds expected_ata, bump = Pubkey.find_program_address( [bytes(wallet), bytes(TOKEN_PROGRAM_ID), bytes(usdc_mint)], ASSOCIATED_TOKEN_PROGRAM_ID, ) # Fetch and verify resp = client.get_account_info(expected_ata) if resp.value: info = resp.value print(f"ATA exists at expected PDA: {expected_ata}") print(f"Owner program: {info.owner}") print(f"Data length: {len(info.data)} bytes") else: print(f"No account at derived ATA: {expected_ata}") ``` ## Creating PDA accounts in Anchor Deriving a PDA and creating an account at that PDA are separate operations. Derivation just computes the address. Creating the account allocates on-chain space and pays rent. In Anchor, you use the `init` constraint with `seeds` and `bump` to create a PDA account. Under the hood, Anchor calls the System Program's `create_account` instruction via `invoke_signed`. ```rust theme={"system"} use anchor_lang::prelude::*; declare_id!("YourProgramId11111111111111111111111111111"); #[program] pub mod pda_example { use super::*; pub fn initialize(ctx: Context) -> Result<()> { let account = &mut ctx.accounts.user_account; account.user = ctx.accounts.user.key(); account.bump = ctx.bumps.user_account; account.data = 0; Ok(()) } pub fn update(ctx: Context, new_data: u64) -> Result<()> { ctx.accounts.user_account.data = new_data; Ok(()) } } #[derive(Accounts)] pub struct Initialize<'info> { #[account(mut)] pub user: Signer<'info>, #[account( init, payer = user, space = 8 + UserAccount::INIT_SPACE, seeds = [b"user_data", user.key().as_ref()], bump, )] pub user_account: Account<'info, UserAccount>, pub system_program: Program<'info, System>, } #[derive(Accounts)] pub struct Update<'info> { pub user: Signer<'info>, #[account( mut, seeds = [b"user_data", user.key().as_ref()], bump = user_account.bump, )] pub user_account: Account<'info, UserAccount>, } #[account] #[derive(InitSpace)] pub struct UserAccount { pub user: Pubkey, pub bump: u8, pub data: u64, } ``` Key points: * `init` — tells Anchor to create the account. Requires `payer` and `space`. * `seeds = [b"user_data", user.key().as_ref()]` — the PDA seeds. The address is deterministic from the user's wallet. * `bump` (without a value) — Anchor finds the canonical bump automatically. * `bump = user_account.bump` — on subsequent instructions, use the stored bump to save compute (avoids re-running `find_program_address`). * `space = 8 + UserAccount::INIT_SPACE` — 8 bytes for the Anchor discriminator + the struct size. The `InitSpace` derive macro calculates the struct size automatically. > [!TIP] > > Always store the bump seed in the account data. This saves 1,500 CUs on every subsequent instruction that accesses the PDA, because the program skips the `find_program_address` loop. ## Cross-program invocations A Cross-Program Invocation (CPI) is when one program calls an instruction on another program. This is what makes Solana programs composable — your program can transfer tokens, create accounts, or call any instruction on any deployed program. Solana provides two functions: | Function | When to use | PDA signing | | - | - | - | | `invoke` | All signers already signed the transaction | No | | `invoke_signed` | The calling program needs to sign as a PDA | Yes, via signer seeds | Under the hood, `invoke` just calls `invoke_signed` with an empty signer seeds array. Both use the same runtime syscall. ### CPI without PDA signing The simplest CPI: your program calls another program, and all required signers are already present in the transaction. Example: a program that wraps a SOL transfer through the System Program. ```rust theme={"system"} use anchor_lang::prelude::*; use anchor_lang::system_program::{transfer, Transfer}; declare_id!("YourProgramId11111111111111111111111111111"); #[program] pub mod sol_transfer { use super::*; pub fn transfer_sol(ctx: Context, amount: u64) -> Result<()> { let cpi_context = CpiContext::new( ctx.accounts.system_program.key(), Transfer { from: ctx.accounts.sender.to_account_info(), to: ctx.accounts.recipient.to_account_info(), }, ); transfer(cpi_context, amount)?; Ok(()) } } #[derive(Accounts)] pub struct TransferSol<'info> { #[account(mut)] pub sender: Signer<'info>, #[account(mut)] pub recipient: SystemAccount<'info>, pub system_program: Program<'info, System>, } ``` The Anchor `CpiContext::new` takes the target program and the accounts struct for the instruction. The `transfer` helper constructs and sends the CPI. ### CPI with PDA signing When your program owns a PDA (such as a vault), it can sign for that PDA during a CPI. This is the core pattern for escrows, vaults, and program-controlled token accounts. Example: a program-controlled vault that holds SOL and allows the program to release it. ```rust theme={"system"} use anchor_lang::prelude::*; use anchor_lang::system_program::{transfer, Transfer}; declare_id!("YourProgramId11111111111111111111111111111"); #[program] pub mod vault { use super::*; pub fn withdraw(ctx: Context, amount: u64) -> Result<()> { let recipient_key = ctx.accounts.recipient.key(); let bump = ctx.bumps.vault; let signer_seeds: &[&[&[u8]]] = &[&[ b"vault", recipient_key.as_ref(), &[bump], ]]; let cpi_context = CpiContext::new( ctx.accounts.system_program.key(), Transfer { from: ctx.accounts.vault.to_account_info(), to: ctx.accounts.recipient.to_account_info(), }, ) .with_signer(signer_seeds); transfer(cpi_context, amount)?; Ok(()) } } #[derive(Accounts)] pub struct Withdraw<'info> { #[account( mut, seeds = [b"vault", recipient.key().as_ref()], bump, )] pub vault: SystemAccount<'info>, #[account(mut)] pub recipient: SystemAccount<'info>, pub system_program: Program<'info, System>, } ``` The key difference from a regular CPI: 1. Build `signer_seeds` from the PDA's seeds plus the bump. 2. Use `CpiContext::new(...).with_signer(signer_seeds)` instead of just `CpiContext::new(...)`. 3. The runtime verifies the seeds produce the PDA address, then adds it as a valid signer for the inner instruction. ### CPI to the Token Program A common real-world CPI is transferring SPL tokens from a PDA-owned token account. This pattern is used in escrows, vesting contracts, and AMMs. ```rust theme={"system"} use anchor_lang::prelude::*; use anchor_spl::token::{self, Token, TokenAccount, Transfer}; declare_id!("YourProgramId11111111111111111111111111111"); #[program] pub mod token_vault { use super::*; pub fn release_tokens(ctx: Context, amount: u64) -> Result<()> { let authority_bump = ctx.bumps.vault_authority; let signer_seeds: &[&[&[u8]]] = &[&[ b"authority", &[authority_bump], ]]; let cpi_context = CpiContext::new( ctx.accounts.token_program.key(), Transfer { from: ctx.accounts.vault_token_account.to_account_info(), to: ctx.accounts.recipient_token_account.to_account_info(), authority: ctx.accounts.vault_authority.to_account_info(), }, ) .with_signer(signer_seeds); token::transfer(cpi_context, amount)?; Ok(()) } } #[derive(Accounts)] pub struct ReleaseTokens<'info> { #[account(mut)] pub admin: Signer<'info>, #[account( seeds = [b"authority"], bump, )] /// CHECK: PDA used as token authority pub vault_authority: UncheckedAccount<'info>, #[account( mut, token::mint = token_mint, token::authority = vault_authority, )] pub vault_token_account: Account<'info, TokenAccount>, #[account(mut)] pub recipient_token_account: Account<'info, TokenAccount>, pub token_mint: Account<'info, anchor_spl::token::Mint>, pub token_program: Program<'info, Token>, } ``` Key constraints: * `token::authority = vault_authority` — ensures the token account is controlled by the program's PDA, not an arbitrary authority. * `token::mint = token_mint` — binds the vault to a specific mint. Without this, an attacker could pass a vault token account for a different mint. * `admin: Signer` — ensures only an authorized caller can trigger the release. Without caller authorization, anyone could drain the vault. > [!WARNING] > > This example demonstrates the pattern. A production implementation should add additional checks such as an admin allowlist stored in a config PDA, amount caps, and timelocks. ### CPI limits | Limit | Value | | - | - | | Max instruction stack depth | 5 (9 with SIMD-0268) | | CPI invocation cost | 1,000 CUs (946 with SIMD-0339) | | Max PDA signers per CPI | 16 | | Max CPI instruction data | 10 KiB | | Max return data | 1,024 bytes | | Max CPI account infos | 128 (255 with SIMD-0339) | | Serialization cost | 1 CU per 250 bytes | > [!NOTE] > > Indirect reentrancy is not allowed. If program A calls program B, and B tries to call back into A, the transaction fails with `ReentrancyNotAllowed`. Direct self-recursion (A calling A) is allowed up to the stack depth limit. ## Calling a PDA from a client Once a program creates a PDA account, clients interact with it by deriving the same address and including it in transactions. ```typescript TypeScript theme={"system"} import { Connection, PublicKey, SystemProgram, Transaction, sendAndConfirmTransaction, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js"; const CHAINSTACK_ENDPOINT = "CHAINSTACK_NODE_URL"; const connection = new Connection(CHAINSTACK_ENDPOINT); // Your deployed program const programId = new PublicKey("YourProgramId11111111111111111111111111111"); // Derive the vault PDA const userWallet = Keypair.generate(); const [vaultPda, vaultBump] = PublicKey.findProgramAddressSync( [Buffer.from("vault"), userWallet.publicKey.toBuffer()], programId, ); console.log(`Vault PDA: ${vaultPda.toBase58()}`); console.log(`Vault bump: ${vaultBump}`); // Fund the PDA by transferring SOL to its address // Any account can receive SOL — the program controls withdrawals via invoke_signed const fundTx = new Transaction().add( SystemProgram.transfer({ fromPubkey: userWallet.publicKey, toPubkey: vaultPda, lamports: 0.1 * LAMPORTS_PER_SOL, }) ); fundTx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; fundTx.feePayer = userWallet.publicKey; fundTx.sign(userWallet); const sig = await connection.sendRawTransaction(fundTx.serialize()); console.log(`Fund tx: ${sig}`); ``` ```python Python theme={"system"} from solana.rpc.api import Client from solders.pubkey import Pubkey from solders.system_program import TransferParams, transfer from solders.transaction import Transaction from solders.message import Message client = Client("CHAINSTACK_NODE_URL") program_id = Pubkey.from_string("YourProgramId11111111111111111111111111111") # Derive the vault PDA user_wallet = Pubkey.from_string("B9Lf9z5BfNPT4d5KMeaBFx8x1G4CULZYR1jA2kmxRDka") vault_pda, vault_bump = Pubkey.find_program_address( [b"vault", bytes(user_wallet)], program_id, ) print(f"Vault PDA: {vault_pda}") print(f"Vault bump: {vault_bump}") ``` ## Common gotchas These are the issues developers hit most frequently, based on community discussions across Solana and Anchor developer channels. ### SOL transfer from data-bearing PDAs This is the single most common PDA mistake. If your PDA stores data (has a `#[account]` struct), it is **owned by your program, not the System Program**. Calling `system_program::transfer` to move SOL out of it fails with: ``` Transfer: `from` must not carry data ``` The System Program can only transfer from accounts it owns. For program-owned PDAs with data, manipulate lamports directly: ```rust theme={"system"} // Correct: direct lamport manipulation for program-owned accounts **ctx.accounts.recipient.try_borrow_mut_lamports()? += amount; **ctx.accounts.pda_with_data.try_borrow_mut_lamports()? -= amount; ``` Use `system_program::transfer` with `invoke_signed` only for PDAs that store **no data** (declared as `SystemAccount` in Anchor). ### Wrong bump field name in `ctx.bumps` The bump field name in `ctx.bumps` must match the **account field name** in your `#[derive(Accounts)]` struct, not the seed prefix: ```rust theme={"system"} #[derive(Accounts)] pub struct Withdraw<'info> { #[account( mut, seeds = [b"vault", recipient.key().as_ref()], bump, )] pub vault_account: SystemAccount<'info>, // field name is "vault_account" // ... } // In the instruction: let bump = ctx.bumps.vault_account; // correct — matches field name // NOT: ctx.bumps.vault // wrong — matches seed prefix ``` ### UnbalancedInstruction after lamport changes If you manually modify lamports on accounts (add to one, subtract from another) and then do a CPI, you get `UnbalancedInstruction`. The runtime checks that the total lamport sum across all instruction accounts stays constant when setting up the CPI. The fix: include **all** accounts whose lamports were modified in the CPI instruction accounts, even if the target program doesn't need them. ### Validating PDAs from other programs When your instruction receives a PDA owned by another program (Metaplex metadata, Raydium pool, etc.), use `seeds::program` to validate it: ```rust theme={"system"} #[account( seeds = [b"metadata", metadata_program.key().as_ref(), mint.key().as_ref()], seeds::program = metadata_program.key(), bump, )] pub metadata: UncheckedAccount<'info>, ``` Without `seeds::program`, Anchor derives the PDA using your program's ID, which produces a different address. ## Security considerations When working with PDAs and CPIs, follow these practices: * **Always use canonical bumps** — the bump returned by `findProgramAddress` / `find_program_address`. Non-canonical bumps create alternative valid PDAs for the same seeds, enabling account substitution attacks. * **Store and reuse bumps** — save the bump in account data at init time, then use `bump = account.bump` in subsequent instructions. This saves compute and avoids re-derivation. * **Validate PDA ownership** — use Anchor's `seeds` + `bump` constraints to verify accounts are the expected PDAs. Without this, an attacker could pass an arbitrary account. * **Check CPI account authority** — for token CPIs, use `token::authority = expected_pda` to ensure the token account is actually controlled by your program's PDA. * **Avoid seed collisions** — use distinct prefixes for different account types (`b"vault"`, `b"metadata"`, `b"config"`) and include discriminating keys (user pubkey, mint pubkey) to prevent collisions. * **Mind the CPI depth** — the max instruction stack depth is 5, meaning the entry instruction plus 4 nested CPIs. All nested CPIs share a single compute budget. Design program architecture to minimize CPI chains. * **Watch for reentrancy** — Solana blocks indirect reentrancy (A → B → A), but direct self-calls are allowed. If your program calls itself, ensure state is updated before the CPI to prevent double-processing. ## Further reading * [Solana documentation: Program derived addresses](https://solana.com/docs/core/pda) * [Solana documentation: Cross-program invocations](https://solana.com/docs/core/cpi) * [Anchor documentation: PDA constraints](https://www.anchor-lang.com/docs/basics/pda) * [Anchor documentation: CPIs](https://www.anchor-lang.com/docs/basics/cpi) * [Solana program examples: PDA](https://github.com/solana-developers/program-examples/tree/main/basics/program-derived-addresses) * [Solana program examples: CPI](https://github.com/solana-developers/program-examples/tree/main/basics/cross-program-invocation) ## Reference repos These are the source repositories we worked against while writing this guide. They stay closer to reality than docs — check them first when something here looks off. * [solana-developers/program-examples](https://github.com/solana-developers/program-examples) — canonical PDA and CPI worked examples; account derivations and seed patterns verified here * [solana-foundation/anchor](https://github.com/solana-foundation/anchor) — `find_program_address` and `invoke_signed` implementation; check here when bump or CPI authority rules look off * [solana-foundation/solana-bootcamp-2026](https://github.com/solana-foundation/solana-bootcamp-2026) — bootcamp projects that exercise PDA and CPI patterns end-to-end # Solana: batching token instructions with p-token Source: https://docs.chainstack.com/docs/solana-ptoken-batch-instruction Use the p-token batch instruction to bundle multiple Solana token operations into a single CPI, cutting compute costs. Build it in TypeScript and Rust against your Chainstack node. **TLDR:** * p-token is a compute-optimized, drop-in rewrite of the SPL Token program. It runs at the same program ID, so your existing code and Chainstack endpoints already use it — a transfer now costs \~76 compute units instead of \~4,645. * It adds a `batch` instruction that bundles several token operations into one token-program call. * Batching only pays off through cross-program invocation (CPI): it amortizes the fixed per-CPI base cost across all the bundled operations. * At the top level, batching is pure overhead — instructions there are already cheap, so send them separately. ## What p-token changes p-token is a [Pinocchio](https://github.com/anza-xyz/pinocchio)-based reimplementation of the SPL Token program from Anza, shipped through [SIMD-0266](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0266-efficient-token-program.md). It went live on Solana mainnet at epoch 971 and is fully backwards compatible. p-token activates as a feature-gate swap at the **same program ID** (`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`). Account layouts, instruction discriminators, and account structures are byte-for-byte identical to the old program. You do not change a single address, and transactions you send through your Chainstack node already run the optimized program. What you get is a large drop in compute units. The figures below are from the SIMD-0266 governance proposal: | Instruction | SPL Token (CU) | p-token (CU) | | - | - | - | | Transfer | 4,645 | 76 | | TransferChecked | 6,200 | 105 | | MintTo | 4,538 | 119 | | Burn | 4,753 | 126 | | InitializeAccount | 4,527 | 154 | | CloseAccount | 2,916 | 120 | | SyncNative | 3,045 | 61 | The upgrade also adds three new instructions: `batch`, `withdraw_excess_lamports`, and `unwrap_lamports`. This page focuses on `batch`. ## The batch instruction `batch` (discriminator `255`) lets you pack several token-program instructions into a single instruction. The wire format is a flat sequence: the discriminator, then one entry per sub-instruction, repeating until the data buffer is exhausted. There is no count prefix — the program loops until it runs out of bytes. Each sub-instruction entry is a 2-byte header followed by its data: * `numberOfAccounts` — how many accounts this sub-instruction consumes from the flat account list. * `dataLength` — the byte length of the instruction data that follows. * `data` — the raw token instruction bytes, including that instruction's own discriminator. All accounts for all sub-instructions are passed as one flat list, consumed left to right: sub-instruction 1 takes the first `numberOfAccounts` accounts, sub-instruction 2 takes the next batch, and so on. ## What you can batch Almost every token-program instruction can go into a batch — `transfer` and `transferChecked`, `mintTo` and `burn`, `approve` and `revoke`, `freeze` and `thaw`, the `initializeMint` and `initializeAccount` variants, `closeAccount`, `syncNative`, and `withdrawExcessLamports`. They execute sequentially, in the order you add them. Two things you cannot batch: * A batch itself — the `batch` instruction is not batchable, so no nesting. * Associated token account (ATA) creation — the Associated Token Account program is a separate program that in turn calls the token program, so its instructions cannot be folded into a token-program batch. If you want account creation inside the same batch, create a plain token account (the System program's create-account plus `initializeAccount`) rather than an ATA. There is no fixed cap on how many instructions one batch holds. Two Solana constraints bind instead: * The 255-account-reference limit — a single instruction can reference at most 255 accounts, and a batch counts accounts across all its sub-instructions (they are not deduplicated). Instructions that touch few accounts let you pack the most in. * The \~1,232-byte transaction size — each sub-instruction's header, data, and account references add up. This is the limit on a legacy or v0 transaction; [transaction v1](/docs/solana-transaction-v1) raises the envelope to 4,096 bytes. Which limit you hit first depends on the instruction. Account-light operations (a `revoke` touches two accounts) are bound by the account cap, so well over a hundred fit; a batch of plain transfers hits the transaction-size limit first, at around 70 per batch. ## When batching pays off The rule is simple: **`batch` is only worth it when you call the token program through CPI.** Every `invoke` call on Solana carries a fixed base cost of about 1,000 compute units, regardless of the instruction inside it. When a program transfers tokens twice, it pays that base cost twice — once per CPI. `batch` collapses those into a single CPI, so you pay the 1,000 CU base cost **once** and then only the (now tiny) cost of each token operation: * Two separate token CPIs — `2 × 1,000` base CU `+ 2 ×` operation cost. * One batch CPI — `1 × 1,000` base CU `+ 2 ×` operation cost. The savings grow with each operation you fold in. This matters because almost every DeFi program issues multiple token CPIs in a single instruction — an AMM transfers two tokens per swap, an LP deposit transfers one token and mints another. Folding those operations into one `batch` CPI eliminates the redundant per-CPI base costs. The effect compounds: bundling about a dozen token operations into a single CPI rather than a dozen separate CPIs has been measured to cut total compute roughly threefold — from around 18,000 CU to about 6,000. A batch's single CPI is not free, and its cost rises with the number of accounts it carries, but it is far cheaper than paying the base cost on every invocation. At the **top level** of a transaction there is no CPI, so there is nothing to amortize. A `batch` of top-level instructions just adds the wrapper overhead and runs slower than sending the instructions separately. Use `batch` from inside a program, not from your client, unless you have a specific reason to. In practice the gains are real but modest, so treat `batch` as a compute optimization, not a necessity — the big efficiency win on token operations came from p-token itself, which you already get for free. Reach for `batch` when your program issues two or more token instructions back-to-back through CPI with nothing else in between. Because they run sequentially in order, you cannot interleave other logic between batched operations. ## Batch via CPI in Rust For new programs, the [`pinocchio-token`](https://docs.rs/pinocchio-token/0.6.0/pinocchio_token/) crate gives you an idiomatic `Batch` builder and an `IntoBatch` trait implemented for every instruction type. ```toml Cargo.toml theme={"system"} [dependencies] pinocchio = "0.7" pinocchio-token = "0.6" ``` ```rust Rust theme={"system"} use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; use pinocchio_token::instructions::{Batch, IntoBatch, Transfer}; pub fn process_swap(accounts: &[AccountInfo]) -> ProgramResult { let [src_a, dst_a, src_b, dst_b, authority, _rest @ ..] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; // Pre-allocate the batch buffers on the stack. // Data buffer = header_data_len(N) + sum of each sub-instruction's data size. // A Transfer is 9 bytes (1 discriminator + 8 amount); here N = 2. let mut data_buf = [core::mem::MaybeUninit::::uninit(); Batch::header_data_len(2) + 9 + 9]; let mut ix_accounts_buf = [core::mem::MaybeUninit::uninit(); 6]; // 3 + 3 accounts let mut cpi_accounts_buf = [core::mem::MaybeUninit::uninit(); 4]; // unique accounts let mut batch = Batch::new(&mut data_buf, &mut ix_accounts_buf, &mut cpi_accounts_buf)?; // First transfer: src_a -> dst_a Transfer { from: src_a, to: dst_a, authority, amount: 1_000_000 }.into_batch(&mut batch)?; // Second transfer: src_b -> dst_b Transfer { from: src_b, to: dst_b, authority, amount: 500_000 }.into_batch(&mut batch)?; // One CPI for both transfers — the 1,000 CU base cost is paid only once. batch.invoke() } ``` Programs already built on the `spl-token` crate can still use `batch` by constructing the instruction data in the wire format above and issuing a normal `invoke` to the token program ID — it works because p-token runs at that same address. For new code, `pinocchio-token` is the cleaner path. > [!WARNING] > > `Batch::new` requires you to pre-size three stack buffers before building the batch. Get the data-buffer size wrong (`header_data_len(N)` plus each sub-instruction's data length) and you get a runtime `ProgramError`, not a compile error. ## Build a batch instruction in TypeScript If you do need to construct a `batch` from a client, the [`@solana-program/token`](https://www.npmjs.com/package/@solana-program/token) client exposes `getBatchInstruction`. This is a low-level API — you supply each sub-instruction's raw data and account count, and you attach the flat account list yourself. ```bash theme={"system"} npm install @solana-program/token @solana/kit ``` ```typescript TypeScript theme={"system"} import { getBatchInstruction } from "@solana-program/token"; // Encode a Transfer sub-instruction: discriminator (3) + amount (u64 LE) = 9 bytes. function encodeTransfer(amount: bigint): Uint8Array { const data = new Uint8Array(9); data[0] = 3; // Transfer discriminator new DataView(data.buffer).setBigUint64(1, amount, true); return data; } const batchIx = getBatchInstruction({ data: [ // Transfer uses 3 accounts: source, destination, authority. { numberOfAccounts: 3, instructionData: encodeTransfer(1_000_000n) }, { numberOfAccounts: 3, instructionData: encodeTransfer(500_000n) }, ], }); // Attach the flat account list in sub-instruction order: // [source1, destination1, authority1, source2, destination2, authority2] // as the instruction's accounts, then add batchIx to your transaction message // and send it through your Chainstack endpoint. ``` The client adds the per-sub-instruction length prefix for you — pass the raw instruction bytes (including each instruction's own discriminator) in `instructionData`. Remember that the account count per entry must match the accounts you append, in order. ## Caveats and gotchas * No instruction-name logs — p-token does not emit the `Program log: Instruction: Transfer` lines the old program logged, because logging cost \~103 CU and was a large share of each instruction's total. If you parse [logsSubscribe](/reference/logssubscribe-solana) output or [Geyser](/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js) streams, parse instruction data instead of instruction-name logs. * Indexers must look inside batches — a `batch` is a single top-level instruction (discriminator `255`) that hides its real operations as sub-instructions. To catch every transfer or mint, descend into discriminator-`255` instructions and walk their sub-instructions; scanning only top-level instructions will miss anything bundled in a batch. * 255-byte data cap per sub-instruction — the `dataLength` header is a single byte, so each bundled instruction's data must be 255 bytes or less. Transfers (9 bytes) are far under this, but parameter-heavy instructions can approach it. * Account ordering is positional — accounts are consumed in sub-instruction order from one flat list. A misordered list is a silent bug, not an error. * Token-2022 has no equivalent — `batch` exists in p-token only. Token-2022 (`TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`) is a separate program with no batch mechanism. ## The other new instructions * `withdraw_excess_lamports` — moves the lamports above the rent-exempt minimum out of a token account, mint, or multisig. After Solana's rent reduction, this is how token accounts and mints created at the old rate release the difference. See [Solana: Rent reduction and reclaiming excess rent](/docs/solana-rent-reduction-reclaim-excess-rent). * `unwrap_lamports` — moves lamports directly out of a wrapped-SOL token account to a destination, without the create-and-close dance. ## Additional resources * [Transferring SPL tokens on Solana](/docs/transferring-spl-tokens-on-solana-typescript) — the single-transfer baseline batching builds on. * [Compute budget](/docs/solana-compute-budget) — set compute unit limits and prices for your transactions. * [Listening to programs using Geyser and Yellowstone gRPC](/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js) — where the no-logs change matters most. * [SIMD-0266: efficient token program](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0266-efficient-token-program.md) — the proposal behind p-token. * [solana-program/token](https://github.com/solana-program/token) — p-token source and the generated clients. # Solana: Rent reduction and reclaiming excess rent Source: https://docs.chainstack.com/docs/solana-rent-reduction-reclaim-excess-rent SIMD-0437 lowers Solana's rent-exempt minimum in five steps. Check the current rate through your endpoint and reclaim the excess lamports from token accounts, mints, stake accounts, and your own program's PDAs. **TLDR:** * [SIMD-0437](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0437-incremental-rent-reduction.md) lowers the rent rate from 6,960 to 696 lamports per byte in five feature-gated steps. Steps 1 and 2 are active on Mainnet, so the rate is 5,080 lamports per byte and a token account needs 1,488,440 lamports instead of 2,039,280. * Existing accounts keep their balances. Anything above the new minimum is excess that the account's authority can withdraw, and each later step frees more. * Token accounts and mints, under both the Token and Token-2022 programs, release it through the `WithdrawExcessLamports` instruction. Stake accounts release it through a normal stake withdrawal. A PDA releases it only through an instruction in the program that owns it. * If the rate is ever reset to 6,960 under SIMD-0438, accounts you have reclaimed stay valid as long as they do not grow or change owner. * Read minimums from `getMinimumBalanceForRentExemption` rather than hardcoding them. ## Main article Every Solana account holds a minimum balance, its rent-exempt minimum, which is refunded in full when the account closes. The minimum is the account's size plus 128 bytes of metadata, multiplied by the rate in lamports per byte: ```text theme={"system"} minimum balance = (128 + data size in bytes) × lamports per byte ``` The rate was 6,960 lamports per byte from Solana's launch until September 2026. SIMD-0437 lowers it to 696 in five steps, each behind its own feature gate. A step activates only after a risk review of the previous one, and the [SIMD-0438](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0438-rent-increase-safeguard.md) feature can reset the rate to 6,960 if state grows too fast. > [!NOTE] > > To follow along, you need a Solana node endpoint. See [Solana tooling](/docs/solana-tooling) to get set up. ## Reduction schedule | Step | Feature | Lamports per byte | Empty account (0 bytes) | Mint (82 bytes) | Token account (165 bytes) | Stake account (200 bytes) | | - | - | - | - | - | - | - | | Before SIMD-0437 | — | 6,960 | 890,880 | 1,461,600 | 2,039,280 | 2,282,880 | | 1 | `4a6f7o7iTcA8hRDCrPLkSatnt5Ykxiu36wo5p1Tt12wC` | 6,333 | 810,624 | 1,329,930 | 1,855,569 | 2,077,224 | | 2 | `61BtM7BkDEE8Yq5fskEVAQT9mYA8qCejJWoLe5apqg81` | 5,080 | 650,240 | 1,066,800 | 1,488,440 | 1,666,240 | | 3 | `rntCigrTppP5JdZz7K8TyN9sMzLdAcXp8SejYpVpX6D` | 2,575 | 329,600 | 540,750 | 754,475 | 844,600 | | 4 | `rntD7invRBswCAdKtRsh1G4psKjrPdS3BKqtnA78C7N` | 1,322 | 169,216 | 277,620 | 387,346 | 433,616 | | 5 | `rntTjNZ9boq8owDxjGVFHPfWNQPDaKiM5JcjxmDGg47` | 696 | 89,088 | 146,160 | 203,928 | 228,288 | All amounts are in lamports. On Mainnet, step 1 activated at slot 444,096,000 on September 3, 2026, and step 2 at slot 446,256,000 on September 11, 2026. Solana expects steps 3–5 with Agave 4.4 in November 2026. The SIMD-0438 reset is gated by `rnt8ZQpz2HYhX3DkYBDGjJS1a36mYq69oXka7JrhEdi`. ## Check the current rate Query the six features through your endpoint: ```bash theme={"system"} solana feature status \ 4a6f7o7iTcA8hRDCrPLkSatnt5Ykxiu36wo5p1Tt12wC \ 61BtM7BkDEE8Yq5fskEVAQT9mYA8qCejJWoLe5apqg81 \ rntCigrTppP5JdZz7K8TyN9sMzLdAcXp8SejYpVpX6D \ rntD7invRBswCAdKtRsh1G4psKjrPdS3BKqtnA78C7N \ rntTjNZ9boq8owDxjGVFHPfWNQPDaKiM5JcjxmDGg47 \ rnt8ZQpz2HYhX3DkYBDGjJS1a36mYq69oXka7JrhEdi \ --url YOUR_CHAINSTACK_ENDPOINT | head -7 ``` On Mainnet, after step 2: ```text theme={"system"} Feature | Status | Activation Slot | Description 4a6f7o7iTcA8hRDCrPLkSatnt5Ykxiu36wo5p1Tt12wC | active since epoch 1028 | 444096000 | SIMD-0437-1: Set lamports per byte to 6333 61BtM7BkDEE8Yq5fskEVAQT9mYA8qCejJWoLe5apqg81 | active since epoch 1033 | 446256000 | SIMD-0437-2: Set lamports per byte to 5080 rnt8ZQpz2HYhX3DkYBDGjJS1a36mYq69oXka7JrhEdi | inactive | NA | SIMD-0438: Reset lamports per byte to legacy value of 6960 rntCigrTppP5JdZz7K8TyN9sMzLdAcXp8SejYpVpX6D | inactive | NA | SIMD-0437-3: Set lamports per byte to 2575 rntD7invRBswCAdKtRsh1G4psKjrPdS3BKqtnA78C7N | inactive | NA | SIMD-0437-4: Set lamports per byte to 1322 rntTjNZ9boq8owDxjGVFHPfWNQPDaKiM5JcjxmDGg47 | inactive | NA | SIMD-0437-5: Set lamports per byte to 696 ``` The minimum for a given size comes from [getMinimumBalanceForRentExemption](/reference/solana-getminimumbalanceforrentexemption), which the CLI wraps as `solana rent`: ```bash theme={"system"} solana rent 165 --lamports --url YOUR_CHAINSTACK_ENDPOINT ``` ```text theme={"system"} Rent-exempt minimum: 1488440 lamports ``` > [!NOTE] > > `solana-test-validator` 4.3.0 starts at 6,960 lamports per byte, even with `--clone-feature-set`, which marks the rent features active without applying their rates. A reclaim script finds no excess on a local validator, so test it on Devnet, where steps 1 and 2 are active as on Mainnet. ## What happens to existing accounts The reduction does not move any lamports. An account created before a step keeps its balance, and everything above the new minimum for its size becomes excess. How you withdraw the excess depends on the program that owns the account: | Account | Owner | How to reclaim | | - | - | - | | Wallet | System Program | Nothing to reclaim. The whole balance is yours, and a new wallet account needs only the lower minimum. | | Token account, mint, or multisig | Token or Token-2022 program | `WithdrawExcessLamports`, signed by the account's authority | | Wrapped SOL account | Token program | Not supported. Close the account to recover its rent. | | Stake account | Stake program | `solana withdraw-stake`, signed by the withdraw authority | | PDA of your program | Your program | An instruction in your program that moves the excess | | PDA of another program | That program | Only if the program provides an instruction for it | If SIMD-0438 ever resets the rate, the protocol keeps reclaimed accounts valid. Under [SIMD-0392](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0392-rent-increase-adaptations.md), active on Mainnet since slot 443,664,000, a transaction can leave an account below the current minimum as long as the account does not grow, does not change owner, and does not lose lamports below its starting balance. Only new allocations pay the higher rate. ## Reclaim from token accounts and mints The `WithdrawExcessLamports` instruction moves an account's lamports above its current minimum to a destination you choose. Who signs depends on the account: * Token account — the owner. * Mint — the mint authority. A mint without a mint authority accepts the mint account's own keypair instead. * Multisig — the multisig's signers. This script finds the wallet's token accounts under both token programs, adds any mints passed as arguments, and withdraws the excess from each one in batches of 20: ```bash theme={"system"} npm install @solana/kit @solana-program/token @solana-program/token-2022 ``` ```javascript reclaim.mjs theme={"system"} import { address, appendTransactionMessageInstructions, createKeyPairSignerFromBytes, createSolanaRpc, createSolanaRpcSubscriptions, createTransactionMessage, getSignatureFromTransaction, pipe, sendAndConfirmTransactionFactory, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from "@solana/kit"; import { TOKEN_PROGRAM_ADDRESS, getWithdrawExcessLamportsInstruction } from "@solana-program/token"; import { TOKEN_2022_PROGRAM_ADDRESS } from "@solana-program/token-2022"; import { readFileSync } from "node:fs"; const rpc = createSolanaRpc("YOUR_CHAINSTACK_ENDPOINT"); const rpcSubscriptions = createSolanaRpcSubscriptions("YOUR_CHAINSTACK_WSS_ENDPOINT"); const keypairBytes = new Uint8Array(JSON.parse(readFileSync("YOUR_KEYPAIR_PATH", "utf8"))); const owner = await createKeyPairSignerFromBytes(keypairBytes); // The current rent-exempt minimum for each account size. const minimums = new Map(); async function minimumBalance(space) { if (!minimums.has(space)) { minimums.set(space, await rpc.getMinimumBalanceForRentExemption(space).send()); } return minimums.get(space); } // Token accounts that the wallet owns, under both token programs. const candidates = []; for (const programId of [TOKEN_PROGRAM_ADDRESS, TOKEN_2022_PROGRAM_ADDRESS]) { const { value } = await rpc .getTokenAccountsByOwner(owner.address, { programId }, { encoding: "jsonParsed" }) .send(); for (const { pubkey, account } of value) { // Wrapped SOL accounts are not supported. Close them to recover their rent. if (account.data.parsed.info.isNative) continue; candidates.push({ source: pubkey, account }); } } // Mints that the wallet is the mint authority of, passed as arguments. for (const mint of process.argv.slice(2)) { const { value: account } = await rpc.getAccountInfo(address(mint), { encoding: "base64" }).send(); candidates.push({ source: address(mint), account }); } const instructions = []; let total = 0n; for (const { source, account } of candidates) { const excess = account.lamports - (await minimumBalance(account.space)); if (excess <= 0n) continue; console.log(`${source}: ${excess} lamports`); total += excess; instructions.push( getWithdrawExcessLamportsInstruction( { source, destination: owner.address, authority: owner }, { programAddress: account.owner }, ), ); } console.log(`Reclaimable: ${total} lamports from ${instructions.length} accounts`); // Send up to 20 withdrawals per transaction. const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions }); for (let i = 0; i < instructions.length; i += 20) { const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const message = pipe( createTransactionMessage({ version: 0 }), (m) => setTransactionMessageFeePayerSigner(owner, m), (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), (m) => appendTransactionMessageInstructions(instructions.slice(i, i + 20), m), ); const transaction = await signTransactionMessageWithSigners(message); await sendAndConfirm(transaction, { commitment: "confirmed" }); console.log("Signature:", getSignatureFromTransaction(transaction)); } ``` Run it with the addresses of mints for which your wallet is the mint authority, or with no arguments for token accounts only: ```bash theme={"system"} node reclaim.mjs Cta7RoJvr8PJ7L7xDUEx9WAMu2TCvV4VmiZKEE1h1hDM 7965FTkGwwZD1pMi1hmCQY7yaV7jJ39dJMpFthNv8s5K ``` ```text theme={"system"} QAxn6PHjrHXy9tKowjqFxLAFCaXkEUEYQ13uCmr5zfv: 550840 lamports 7qEdMgtNGzUGr6zQwQRS8cnquqNfnF69B9Epycb8wp3W: 550840 lamports 48Zw7z7v41DFgLQMmEzUUCzcWPrk7NEd95USSsDeNR7J: 560240 lamports Cta7RoJvr8PJ7L7xDUEx9WAMu2TCvV4VmiZKEE1h1hDM: 394800 lamports 7965FTkGwwZD1pMi1hmCQY7yaV7jJ39dJMpFthNv8s5K: 394800 lamports Reclaimable: 2451520 lamports from 5 accounts Signature: 5fsr25YqbhyPYBxJT5yxD2kzCPUJcq5boDENDdzshNrgqiRW615QWkPAMBr15y5oKcEvSAYEfEFWXze2eM94VYxk ``` The five accounts in this run are two Token program accounts, a Token-2022 account, a Token program mint, and a Token-2022 mint, all funded at the rate before SIMD-0437. One transaction reclaimed 2,451,520 lamports for a 5,000-lamport fee. A second run reports `Reclaimable: 0 lamports from 0 accounts` until the next step activates. `WithdrawExcessLamports` fails on a wrapped SOL account with `Instruction does not support native tokens`, and on a mint without a mint authority with `Account does not support specified authority type` unless the mint account itself signs. ## Reclaim from stake accounts The stake program computes a stake account's reserve from the current rate, so the excess on an older stake account can be withdrawn like any other unstaked balance. `solana stake-account` shows the reserve that the stake program applies: ```bash theme={"system"} solana stake-account YOUR_STAKE_ACCOUNT --url YOUR_CHAINSTACK_ENDPOINT ``` ```text theme={"system"} Balance: 0.00228288 SOL Rent Exempt Reserve: 0.00166624 SOL Stake account is undelegated Stake Authority: 8diQptTPMvxFooDa7TfWSkbjbxB5Yg4RR3EbBCa6yWou Withdraw Authority: 8diQptTPMvxFooDa7TfWSkbjbxB5Yg4RR3EbBCa6yWou ``` This undelegated account holds the reserve from before SIMD-0437, so 616,640 lamports are excess at step 2. Withdraw them with the withdraw authority: ```bash theme={"system"} solana withdraw-stake YOUR_STAKE_ACCOUNT YOUR_WALLET_ADDRESS 0.00061664 \ --url YOUR_CHAINSTACK_ENDPOINT ``` For a delegated account, the withdrawable amount is the balance minus the delegated stake and the current reserve. A withdrawal above that fails with `insufficient funds for instruction`. > [!NOTE] > > The `meta.rentExemptReserve` field in parsed stake account data always reads `2282880`, the value before SIMD-0437. The stake program writes that fixed value for compatibility and ignores it, so compute the reserve with `getMinimumBalanceForRentExemption(200)`. ## Reclaim from your program's PDAs Only the program that owns an account can debit it, so excess rent on your program's PDAs stays there until your program moves it. An instruction that has already checked the authority's signature and the PDA's seeds can transfer the excess directly: ```rust src/lib.rs theme={"system"} // `vault` is a PDA owned by this program; `authority` receives the excess. if vault.owner != program_id { return Err(ProgramError::IncorrectProgramId); } let minimum = Rent::get()?.minimum_balance(vault.data_len()); let excess = vault.lamports().saturating_sub(minimum); **vault.try_borrow_mut_lamports()? -= excess; **authority.try_borrow_mut_lamports()? += excess; Ok(()) ``` The PDA keeps exactly the current minimum for its size. Programs that you do not control need their own instruction for this, and many programs have none. ## When to reclaim Each step makes more of the same balance withdrawable, and every reclaim costs a transaction fee of 5,000 lamports for up to 20 accounts in this script. A token account created before SIMD-0437 frees 550,840 lamports at step 2 and 1,835,352 lamports in total after step 5. Running the reclaim after each activation collects the excess as it appears. Waiting until step 5 collects all of it in one pass. ## Additional resources * [SIMD-0437: incremental rent reduction](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0437-incremental-rent-reduction.md) * [SIMD-0438: rent increase safeguard](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0438-rent-increase-safeguard.md) * [SIMD-0392: rent increase adaptations](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0392-rent-increase-adaptations.md) * [Reduced rent](https://solana.com/upgrades/reduced-rent) on solana.com * [Solana: p-token batch instruction](/docs/solana-ptoken-batch-instruction) # Solana: Rebuild your programs for sBPFv3 Source: https://docs.chainstack.com/docs/solana-sbpfv3-program-migration SIMD-0500 makes sBPFv3 the only format Solana accepts for program deployments and upgrades. Check which format your programs use, rebuild with Agave 4.3.0 or Anchor 1.2, fix hand-declared syscalls, and deploy through your Chainstack endpoint. **TLDR:** * [SIMD-0500](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0500-disable-deployment-of-sbpf-v0-v1-v2.md) makes sBPFv3 the only bytecode format a Solana cluster accepts when you deploy, upgrade, or finalize a program. It is expected on Mainnet with Agave 4.4 in November 2026. * Programs already on chain keep running in the format they were built in. The next upgrade of a v0, v1, or v2 program has to ship a v3 build, and the program ID stays the same. * `cargo build-sbf` still builds sBPFv0 unless you pass `--arch v3`. Anchor 1.2.0 and later build v3 by default. * A program that declares syscalls in its own `extern "C"` block fails to link as v3. Declare them through `solana-define-syscall` instead. * `solana-test-validator` 4.3.0 already enforces SIMD-0500, so a default v0 build fails to deploy to a local validator today. ## Main article Every Solana program is compiled to sBPF bytecode before it is deployed. Mainnet runs programs in four sBPF versions, v0 to v3, and most deployed programs are still v0. SIMD-0500 stops new v0, v1, and v2 code from reaching the chain so that the older formats can eventually be retired. The rule is gated behind the `B8JJXCy5amZyWG9r7EnUYLwzXSXTxG7GZ1qZ1qggo83g` feature. sBPFv3 deployment itself has been enabled on Mainnet since slot 428,976,000 on June 26, 2026, under the `5cC3foj77CWun58pC51ebHFUWavHWKarWyR5UUik7dnC` feature, so you can rebuild and upgrade now. > [!NOTE] > > To follow along with the deploy and call examples, you need a Solana node endpoint. See [Solana tooling](/docs/solana-tooling) to get set up. ## What sBPFv3 changes sBPFv3 is the combination of three proposals: * Static syscalls ([SIMD-0178](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0178-static-syscalls.md)) — a syscall is an ordinary `call` instruction whose immediate value is the murmur3 hash of the syscall name, fixed at link time. Validators no longer patch syscall addresses into a copy of the program when they load it. * Stricter ELF headers ([SIMD-0189](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0189-sbpf-stricter-elf-headers.md)) — a v3 file carries no dynamic linking sections and no `PT_DYNAMIC` program header. * eBPF ISA compatibility ([SIMD-0377](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0377-ebpf-isa-compatibility.md)) — the instruction set matches the eBPF that the LLVM BPF backend generates. The version is stored in the `e_flags` field of the ELF header: `0x0` for v0 and `0x3` for v3. For most programs, the source code does not change and rebuilding with a current toolchain is the whole migration. The exception is a program that declares syscalls by hand. See [Hand-declared syscalls fail to link](#hand-declared-syscalls-fail-to-link). ## What SIMD-0500 blocks Once the feature is active, the upgradeable loader rejects a v0, v1, or v2 ELF in three instructions: | Action | Loader instruction | CLI command | v0, v1, or v2 ELF | | - | - | - | - | | Deploy | `DeployWithMaxDataLen` | `solana program deploy` | Rejected | | Upgrade | `Upgrade` | `solana program deploy --program-id` | Rejected | | Finalize | `SetAuthority` with no new authority | `solana program set-upgrade-authority --final` | Rejected | | Execute | — | — | Runs as before | Writing a buffer, extending a program, and closing an account are not affected. A rejected deployment fails in simulation: ```text theme={"system"} Error: Deploying program failed: RPC response error -32002: Transaction simulation failed: Error processing Instruction 1: invalid account data for instruction; 7 log messages: Program 11111111111111111111111111111111 invoke [1] Program 11111111111111111111111111111111 success Program BPFLoaderUpgradeab1e11111111111111111111111 invoke [1] Program 11111111111111111111111111111111 invoke [2] Program 11111111111111111111111111111111 success Detected sbpf_version required by the executable which are not enabled Program BPFLoaderUpgradeab1e11111111111111111111111 failed: invalid account data for instruction ``` By then, the CLI has already written the program to a buffer account, so a rejected deployment leaves the buffer and its rent behind. List and reclaim your buffers with: ```bash theme={"system"} solana program show --buffers --url YOUR_CHAINSTACK_ENDPOINT solana program close --buffers --url YOUR_CHAINSTACK_ENDPOINT ``` ## Check the activation status Query both features through your endpoint: ```bash theme={"system"} solana feature status \ 5cC3foj77CWun58pC51ebHFUWavHWKarWyR5UUik7dnC \ B8JJXCy5amZyWG9r7EnUYLwzXSXTxG7GZ1qZ1qggo83g \ --url YOUR_CHAINSTACK_ENDPOINT | head -3 ``` On Mainnet, before SIMD-0500 activates: ```text theme={"system"} Feature | Status | Activation Slot | Description 5cC3foj77CWun58pC51ebHFUWavHWKarWyR5UUik7dnC | active since epoch 993 | 428976000 | SIMD-0178, SIMD-0189 and SIMD-0377: Enable deployment and execution of SBPFv3 programs B8JJXCy5amZyWG9r7EnUYLwzXSXTxG7GZ1qZ1qggo83g | inactive | NA | SIMD-0500: Disable deployment of SBPF v0, v1 and v2 programs ``` ## Check which format a program uses For a program you built, read the ELF header of the `.so` file: ```bash theme={"system"} readelf -h target/deploy/hello_sbpf.so | grep Flags ``` ```text theme={"system"} Flags: 0x0 ``` `0x0` is v0, and `0x3` is v3. `readelf` comes with GNU binutils on Linux. On macOS, use the `llvm-readelf` that `cargo build-sbf` installs with its platform tools, for example `~/.cache/solana/v1.57/platform-tools/llvm/bin/llvm-readelf` for Agave 4.3.0. For a program that is already deployed, dump its ELF through your endpoint and read the same field: ```bash theme={"system"} solana program dump YOUR_PROGRAM_ID program.so --url YOUR_CHAINSTACK_ENDPOINT readelf -h program.so | grep Flags ``` Any program that shows `0x0`, `0x1`, or `0x2` needs a v3 build before its next upgrade. ## Install the toolchain Install the Agave 4.3.0 release of the Solana CLI: ```bash theme={"system"} sh -c "$(curl -sSfL https://release.anza.xyz/v4.3.0/install)" cargo build-sbf --version ``` ```text theme={"system"} cargo-build-sbf 4.3.0 platform-tools v1.57 ``` > [!NOTE] > > `cargo build-sbf` needs the `cargo` that rustup installs. A `cargo` from another package manager, such as Homebrew, fails with `error: no such command: +1.95.0-sbpf-solana-v1.57`. Solana lists these minimum versions for sBPFv3 builds: | Component | Minimum version | | - | - | | Solana CLI (Agave) | 4.3.0 | | `solana-program` crates | 2.3.0 | | Anchor | 0.30.2, 0.31.2, 0.32.2, or 1.2.0 | | Pinocchio | 0.10 | ## Rebuild a native or Pinocchio program This minimal program logs a message with `msg!`: ```toml Cargo.toml theme={"system"} [package] name = "hello_sbpf" version = "0.1.0" edition = "2024" [dependencies] solana-program = "5.1.0" [lib] crate-type = ["cdylib", "lib"] ``` ```rust src/lib.rs theme={"system"} use solana_program::{ account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey, }; entrypoint!(process_instruction); pub fn process_instruction( _program_id: &Pubkey, _accounts: &[AccountInfo], _instruction_data: &[u8], ) -> ProgramResult { msg!("Hello, sBPFv3!"); Ok(()) } ``` Build it for v3: ```bash theme={"system"} cargo build-sbf --arch v3 readelf -h target/deploy/hello_sbpf.so | grep Flags ``` ```text theme={"system"} Flags: 0x3 ``` The same command builds a Pinocchio program. Pinocchio 0.11 re-exports the `solana-define-syscall` declarations as `pinocchio::syscalls`, so a program that calls syscalls through that module links as v3 without changes. ## Rebuild an Anchor program Anchor 1.2.0 and later pass `--arch v3` to `cargo build-sbf`, so `anchor build` produces a v3 program: ```bash theme={"system"} avm install 1.2.1 avm use 1.2.1 anchor build ``` Update `anchor-lang` in your program's `Cargo.toml` to the same version as the CLI. On Anchor 0.30.2, 0.31.2, and 0.32.2, `anchor build` still produces v0. Arguments after `--` go to `cargo build-sbf`, but Anchor also passes them to the IDL build, which rejects `--arch`. Build the program and the IDL in two steps: ```bash theme={"system"} anchor build --no-idl -- --arch v3 mkdir -p target/idl target/types anchor idl build -o target/idl/PROGRAM_NAME.json -t target/types/PROGRAM_NAME.ts ``` `anchor idl build` does not create its output directories, which is why the `mkdir` step comes first. ## Hand-declared syscalls fail to link A v0 program can declare a syscall in its own `extern "C"` block, because the loader resolves the name when it loads the program: ```rust src/lib.rs theme={"system"} use solana_program::{account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey}; unsafe extern "C" { fn sol_log_(message: *const u8, len: u64); } entrypoint!(process_instruction); pub fn process_instruction( _program_id: &Pubkey, _accounts: &[AccountInfo], _instruction_data: &[u8], ) -> ProgramResult { let message = "Hello, sBPFv3!"; unsafe { sol_log_(message.as_ptr(), message.len() as u64) }; Ok(()) } ``` sBPFv3 has no load-time resolution, so `cargo build-sbf --arch v3` fails at the link step: ```text theme={"system"} error: linking with `rust-lld` failed: exit status: 1 = note: rust-lld: error: undefined symbol: sol_log_ ``` Take the declaration from `solana-define-syscall` instead: ```bash theme={"system"} cargo add solana-define-syscall ``` Import the syscall from `solana_define_syscall::definitions` and keep the rest of the program as it is: ```rust src/lib.rs theme={"system"} use solana_define_syscall::definitions::sol_log_; use solana_program::{account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey}; entrypoint!(process_instruction); pub fn process_instruction( _program_id: &Pubkey, _accounts: &[AccountInfo], _instruction_data: &[u8], ) -> ProgramResult { let message = "Hello, sBPFv3!"; unsafe { sol_log_(message.as_ptr(), message.len() as u64) }; Ok(()) } ``` For a syscall that `definitions` does not include, declare it with the `define_syscall!` macro instead of an `extern "C"` block. The macro emits the hash-based call for v3 and an `extern "C"` declaration for older versions. In macro form, the `sol_log_` declaration is: ```rust theme={"system"} use solana_define_syscall::define_syscall; define_syscall!(fn sol_log_(message: *const u8, len: u64)); ``` Agave 4.3.0 catches a hand-declared syscall at build time, and older toolchains do not. With Agave 4.2.2, `cargo build-sbf --arch v3` builds the `extern "C"` version without an error, and the 4.2.2 CLI deploys it. The unresolved call never reaches the syscall, so every invocation fails with `exceeded max BPF to BPF call depth`. The 4.3.0 CLI refuses such a file before it sends anything, with `Verifier error: Invalid function at instruction (local pre-flight)`. > [!WARNING] > > Build and deploy v3 programs with Agave 4.3.0 or later. ## Deploy through your Chainstack endpoint Deploy a new program: ```bash theme={"system"} solana program deploy target/deploy/hello_sbpf.so --url YOUR_CHAINSTACK_ENDPOINT ``` ```text theme={"system"} Program Id: 56SsH1vms2jVCKTxYkBVrCfu8D3Bpc4PY6HAGkoyoDqd Signature: 3hB4frof2FEaLnWAYW4NGN9G5xs4KQ9NELhvdkiStXpabxg8Qk5LAEqX1rn6Mk6LHBnhCS2ZxT8CcunV8C8Kx4QD ``` To upgrade an existing program, pass its address with `--program-id` and sign with its upgrade authority, which is your default keypair unless you set `--upgrade-authority`. Upgrading a v0 program with a v3 build keeps the program ID, so clients and PDAs derived from it are unaffected: ```bash theme={"system"} solana program deploy target/deploy/hello_sbpf.so \ --program-id YOUR_PROGRAM_ID \ --url YOUR_CHAINSTACK_ENDPOINT ``` ### Call the program This script sends one instruction to the program with [Solana Kit](https://github.com/anza-xyz/kit) and prints the transaction logs: ```bash theme={"system"} npm install @solana/kit ``` ```javascript call.mjs theme={"system"} import { address, appendTransactionMessageInstruction, createKeyPairSignerFromBytes, createSolanaRpc, createSolanaRpcSubscriptions, createTransactionMessage, getSignatureFromTransaction, pipe, sendAndConfirmTransactionFactory, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from "@solana/kit"; import { readFileSync } from "node:fs"; const rpc = createSolanaRpc("YOUR_CHAINSTACK_ENDPOINT"); const rpcSubscriptions = createSolanaRpcSubscriptions("YOUR_CHAINSTACK_WSS_ENDPOINT"); const keypairBytes = new Uint8Array(JSON.parse(readFileSync("YOUR_KEYPAIR_PATH", "utf8"))); const payer = await createKeyPairSignerFromBytes(keypairBytes); const programAddress = address(process.argv[2]); const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const message = pipe( createTransactionMessage({ version: 0 }), (m) => setTransactionMessageFeePayerSigner(payer, m), (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), (m) => appendTransactionMessageInstruction({ programAddress }, m), ); const transaction = await signTransactionMessageWithSigners(message); await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(transaction, { commitment: "confirmed", }); const signature = getSignatureFromTransaction(transaction); const result = await rpc .getTransaction(signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0 }) .send(); console.log("Signature:", signature); console.log(result.meta.logMessages.join("\n")); ``` ```bash theme={"system"} node call.mjs 56SsH1vms2jVCKTxYkBVrCfu8D3Bpc4PY6HAGkoyoDqd ``` ```text theme={"system"} Signature: 2DtWUuTzGC8nSsmsudn32ELgbo7jiMGLVYtX13pFmN8EU5VeSunsfNq4eXYcmYpu4zVWaZTfrtUxPoGp6oMSsVYm Program 56SsH1vms2jVCKTxYkBVrCfu8D3Bpc4PY6HAGkoyoDqd invoke [1] Program log: Hello, sBPFv3! Program 56SsH1vms2jVCKTxYkBVrCfu8D3Bpc4PY6HAGkoyoDqd consumed 145 of 200000 compute units Program 56SsH1vms2jVCKTxYkBVrCfu8D3Bpc4PY6HAGkoyoDqd success ``` A program becomes callable in the slot after its deployment. A call sent in the same slot fails with `Program is not deployed`. ## Test locally `solana-test-validator` 4.3.0 starts with every feature active, SIMD-0500 included, so it rejects a v0 build with the same error as above. To deploy a v0 build locally, start the validator with the feature turned off: ```bash theme={"system"} solana-test-validator --reset \ --deactivate-feature B8JJXCy5amZyWG9r7EnUYLwzXSXTxG7GZ1qZ1qggo83g ``` To rehearse the Mainnet upgrade with SIMD-0500 active, dump your current program and load it into the local validator at genesis, which bypasses the deployment check. Set the upgrade authority to the address of the keypair you deploy with, then upgrade the program with your v3 build: ```bash theme={"system"} solana program dump YOUR_PROGRAM_ID current.so --url YOUR_CHAINSTACK_ENDPOINT solana-test-validator --reset \ --upgradeable-program YOUR_PROGRAM_ID current.so YOUR_UPGRADE_AUTHORITY_ADDRESS ``` In a second terminal: ```bash theme={"system"} solana program deploy target/deploy/hello_sbpf.so --program-id YOUR_PROGRAM_ID --url localhost ``` The preloaded v0 program runs until the upgrade, the upgrade with the v3 build succeeds, and an upgrade with a v0 build fails. ## Verified builds [solana-verify](https://github.com/solana-foundation/solana-verifiable-build) picks its Docker build image from the Solana version of your project. Programs that don't depend on `solana-program`, such as Pinocchio programs, set the version in the root `Cargo.toml`: ```toml Cargo.toml theme={"system"} [workspace.metadata.cli] solana = "4.3.0" ``` `solana-verify build` and `solana-verify verify-from-repo` take an `--arch` flag that defaults to `v0`. Pass `--arch v3` to both, so the verified build matches the v3 program on chain. ## Readiness checklist **1. Inventory your programs** Dump each upgradeable program you own with `solana program dump` and read its `Flags` field. Anything other than `0x3` needs a v3 build before its next upgrade. **2. Update the toolchain** Install Agave 4.3.0 and, for Anchor projects, Anchor 1.2.0 or later. **3. Build for v3** Use `cargo build-sbf --arch v3`, or `anchor build` on Anchor 1.2.0 and later. Confirm `Flags: 0x3` before you deploy. **4. Replace hand-declared syscalls** Move every `extern "C"` syscall declaration to `solana-define-syscall`. **5. Rehearse the upgrade locally** Preload the current program into `solana-test-validator` and upgrade it with the v3 build. **6. Upgrade before you have to** sBPFv3 deployment is already enabled on Mainnet. Upgrading now leaves no v0 program to block a later hotfix. ## Additional resources * [SIMD-0500: disable deployment of SBPF v0, v1, and v2](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0500-disable-deployment-of-sbpf-v0-v1-v2.md) * [sBPFv3 programs](https://solana.com/upgrades/sbpfv3-programs) on solana.com * [Solana: Anchor development](/docs/solana-anchor-development) * [Solana: Pinocchio vs Quasar](/docs/solana-pinocchio-vs-quasar) * [Solana tooling](/docs/solana-tooling) # Solana: Borsh, Bincode, and Wincode serialization Source: https://docs.chainstack.com/docs/solana-serialization-borsh-bincode-wincode Compare Borsh, Bincode, and Wincode serialization on Solana, decode account data fetched from your Chainstack node, and choose the right format for your program. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * Solana accounts store raw bytes — to read them, you need to know how the owning program serialized its data. * Borsh is the canonical, deterministic, cross-language format used by Anchor and most clients. * Bincode is the Rust-only format the validator has historically used internally; it is now unmaintained and being phased out. * Wincode is Anza's bincode-compatible replacement with zero-copy reads, already shipping inside Agave — it is the reason recent gRPC streams are faster. ## Why serialization matters on Solana A Solana account is just an owner, some lamports, and an opaque byte array in its `data` field. The runtime does not know or care what those bytes mean — the program that owns the account decides how to lay them out. When you call [getAccountInfo or getMultipleAccounts](/docs/solana-getaccountinfo-getmultipleaccounts) against your Chainstack node, you get those bytes back as base64. Turning them into a typed struct is deserialization, and you have to use the same format the program used to write them. Three serialization formats dominate the Solana ecosystem, and they are not interchangeable on the wire: * Borsh — the canonical format for on-chain program data and clients. * Bincode — the format the validator uses internally for its own wire protocol and snapshots. * Wincode — Anza's high-performance, bincode-compatible successor. Picking the wrong one gives you garbage, not an error, so it pays to know which is which. ### Borsh Borsh — Binary Object Representation Serializer for Hashing — is deterministic and canonical: a given value always serializes to exactly one byte sequence, and two different values can never produce the same bytes. That property is why it is the default for on-chain account data and why it has a [formal, language-agnostic specification](https://borsh.io/) with implementations in Rust, TypeScript, Python, Go, C++, and more. It is the default in [Anchor](/docs/solana-anchor-development) and across the SPL. Integers are little-endian, dynamic containers (`Vec`, `String`) use a 4-byte length prefix, and enum variants use a single-byte discriminant. Use Borsh whenever your data crosses a language boundary — for example, a TypeScript or Python indexer reading account data from your Chainstack node. ### Bincode Bincode is a Rust-only binary format with no formal specification and no maintained cross-language implementations. The Solana validator has historically used it for its internal wire protocol (gossip, repair, shreds), transaction serialization, and on-disk snapshots. > [!WARNING] > > Bincode is unmaintained as of [RUSTSEC-2025-0141](https://rustsec.org/advisories/RUSTSEC-2025-0141) (January 7, 2026). The advisory recommends migrating to Wincode. Avoid bincode for new code — you will still encounter it in existing Solana data structures, but the ecosystem is actively moving off it. ### Wincode [Wincode](https://github.com/anza-xyz/wincode) is a serialization library from Anza, the team behind the Agave validator. It was built to replace bincode on the validator's hot paths, and it is **byte-for-byte wire-compatible with bincode's default configuration** — so it is a drop-in performance upgrade, not a new protocol. Its headline feature is true zero-copy deserialization: for fixed-layout `#[repr(C)]` structs of primitive fields, you get typed references directly into the original byte buffer with no allocation and no copy. That is exactly the pattern you want when parsing high volumes of raw account data, such as in a Geyser pipeline. Wincode is audited by Neodyme, OtterSec, and Asymmetric Research, and it is the format [RUSTSEC-2025-0141](https://rustsec.org/advisories/RUSTSEC-2025-0141) points to as the bincode successor. Because it matches bincode, Wincode writes 8-byte length prefixes for `Vec` and `String`, where Borsh writes 4-byte prefixes — so Borsh-encoded and Wincode-encoded bytes are not interchangeable. Decode data with the same library that wrote it. ## Decode account data from your Chainstack node The most common task is reading account data your program wrote with Borsh. First, get a Solana RPC endpoint: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Then fetch the account and deserialize its bytes. The schema must match your on-chain struct's field order exactly. ```typescript TypeScript theme={"system"} import * as borsh from "borsh"; import { Connection, PublicKey } from "@solana/web3.js"; // Schema must match the on-chain struct field-for-field, in order. const schema = { struct: { amount: "u64", nonce: "u32", }, }; const connection = new Connection("YOUR_CHAINSTACK_ENDPOINT"); const pubkey = new PublicKey("YourProgramAccountAddressHere"); const accountInfo = await connection.getAccountInfo(pubkey); if (!accountInfo) throw new Error("Account not found"); // web3.js returns account data as a Buffer, already base64-decoded. const decoded = borsh.deserialize(schema, accountInfo.data) as { amount: bigint; nonce: number; }; console.log(decoded.amount); // u64 values come back as BigInt console.log(decoded.nonce); ``` ```python Python theme={"system"} from borsh_construct import CStruct, U32, U64 from solana.rpc.api import Client from solders.pubkey import Pubkey # Schema must match the on-chain struct field-for-field, in order. schema = CStruct( "amount" / U64, "nonce" / U32, ) client = Client("YOUR_CHAINSTACK_ENDPOINT") pubkey = Pubkey.from_string("YourProgramAccountAddressHere") account = client.get_account_info(pubkey).value if account is None: raise ValueError("Account not found") decoded = schema.parse(bytes(account.data)) print(decoded.amount) print(decoded.nonce) ``` > [!NOTE] > > Anchor programs prepend an 8-byte discriminator (a hash of the account name) to the data. Skip the first 8 bytes before deserializing, or use the [Anchor client](/docs/solana-anchor-development), which handles it for you. ### Not everything is Borsh: SPL Token accounts A common trap: SPL Token accounts are **not** Borsh-encoded. The Token program uses the Solana-native `Pack` trait — a fixed-size, C-style layout (a token account is exactly 165 bytes, with the owner at offset 32). This is why `memcmp` filters work at fixed byte offsets. Trying to Borsh-decode a token account will fail or return nonsense. You have two clean options: * Pass `encoding: "jsonParsed"` to [`getAccountInfo`](/reference/solana-getaccountinfo) and let your Chainstack node return a structured JSON object with `mint`, `owner`, and `tokenAmount`. * Decode the binary layout client-side with `@solana/spl-token`, which uses fixed-offset buffer layouts, not Borsh. ```typescript TypeScript theme={"system"} import { AccountLayout } from "@solana/spl-token"; import { Connection, PublicKey } from "@solana/web3.js"; const connection = new Connection("YOUR_CHAINSTACK_ENDPOINT"); const info = await connection.getAccountInfo(new PublicKey("TokenAccountAddressHere")); if (!info) throw new Error("Account not found"); const token = AccountLayout.decode(info.data); // fixed 165-byte layout, not Borsh console.log(token.amount); // bigint console.log(new PublicKey(token.owner).toBase58()); ``` The rule of thumb: Anchor and most custom programs use Borsh; the Token program and other native programs use packed `Pack` layouts. When in doubt, `jsonParsed` lets the node do the work for well-known programs. ## Serialize and deserialize in Rust On the program and validator side, all three formats are Rust libraries doing the same job. Here is the same struct round-tripped through each. ```toml Cargo.toml theme={"system"} borsh = { version = "1", features = ["derive"] } bincode = { version = "2", features = ["derive"] } wincode = { version = "0.5", features = ["derive"] } ``` ```rust Borsh theme={"system"} use borsh::{BorshSerialize, BorshDeserialize, to_vec, from_slice}; #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] struct Transfer { amount: u64, nonce: u32, } let original = Transfer { amount: 1_000_000, nonce: 42 }; let bytes: Vec = to_vec(&original)?; let decoded: Transfer = from_slice(&bytes)?; assert_eq!(original, decoded); ``` ```rust Bincode theme={"system"} use bincode::{Encode, Decode, config}; #[derive(Encode, Decode, PartialEq, Debug)] struct Transfer { amount: u64, nonce: u32, } let cfg = config::standard(); let original = Transfer { amount: 1_000_000, nonce: 42 }; let bytes: Vec = bincode::encode_to_vec(&original, cfg)?; // decode_from_slice returns (value, bytes_read) let (decoded, _len): (Transfer, usize) = bincode::decode_from_slice(&bytes, cfg)?; assert_eq!(original, decoded); ``` ```rust Wincode theme={"system"} use wincode::{SchemaWrite, SchemaRead}; #[derive(SchemaWrite, SchemaRead, PartialEq, Debug)] struct Transfer { amount: u64, nonce: u32, } let original = Transfer { amount: 1_000_000, nonce: 42 }; let bytes: Vec = wincode::serialize(&original)?; let decoded: Transfer = wincode::deserialize(&bytes)?; assert_eq!(original, decoded); ``` To serialize straight into a destination instead of allocating an intermediate `Vec`, use `wincode::serialize_into(dst, src)` — note the writer comes first. To serialize Solana's `Pubkey` (address) with Wincode, enable the `wincode` feature on `solana-pubkey` or `solana-address`; it is off by default. ### Zero-copy reads with Wincode For high-throughput parsing, Wincode can hand you a typed reference straight into the byte buffer — no allocation, no copy. There is no `ZeroCopy` derive: derive `SchemaRead` (and `SchemaWrite`) on a `#[repr(C)]` struct whose fields are all zero-copy eligible, and the impl is generated for you. Add `#[wincode(assert_zero_copy)]` to make the compiler reject the type at definition if it is not eligible. ```rust Rust theme={"system"} use wincode::{SchemaWrite, SchemaRead, ZeroCopy}; // Layout matches a token-like account: mint (32), owner (32), amount (8). #[derive(SchemaWrite, SchemaRead)] #[wincode(assert_zero_copy)] #[repr(C)] struct TokenAccountData { mint: [u8; 32], owner: [u8; 32], amount: u64, } // `use wincode::ZeroCopy` brings from_bytes / from_bytes_mut into scope. // Read a typed reference into the buffer — zero allocations, zero copies. let account: &TokenAccountData = TokenAccountData::from_bytes(raw)?; println!("{}", account.amount); // Or borrow mutably and edit the buffer in place. let account: &mut TokenAccountData = TokenAccountData::from_bytes_mut(raw_mut)?; account.amount += 500; // writes directly into the underlying bytes ``` Zero-copy is strict about layout. A type qualifies only if it is `#[repr(C)]` (or `#[repr(transparent)]`), contains no `String`, `Vec`, or `Option`, and has no implicit padding. Padding is the subtle trap: a struct of two `u64` fields followed by a single `u8` is not eligible, because the trailing byte leaves the struct misaligned — you would have to pad it out to the next 8-byte boundary. Anything variable-length, like a `String` or `Vec`, can never be zero-copy, since its size is not known from the type alone. In practice you rarely make a whole account zero-copy. The pattern is to keep one full struct for the complete data and a separate, slimmer `#[repr(C)]` view for the hot path. A counter bump, for example, reads and writes only the counter through the zero-copy view — skipping the full deserialize-then-reserialize, which is where the largest compute savings come from. ## Which format should you use? | | Borsh | Bincode | Wincode | | - | - | - | - | | Maintained by | NEAR | Unmaintained | Anza | | Primary use on Solana | On-chain data, clients, Anchor | Validator internals (legacy) | Validator internals (current) | | Cross-language | Yes (TS, Python, Go, more) | No (Rust only) | No (Rust only) | | Deterministic and canonical | Yes | Version-dependent | Yes, for fixed layouts | | Zero-copy reads | No | No | Yes (`#[repr(C)]` structs) | | Formal spec | Yes | No | No (bincode-compatible) | | Status | Stable, recommended | Retired ([RUSTSEC-2025-0141](https://rustsec.org/advisories/RUSTSEC-2025-0141)) | Active, replacing bincode | In practice: * Building an on-chain program, or a client or indexer that reads its data — use Borsh. Anchor gives you this by default, and it keeps your TypeScript and Python tooling interoperable. * Writing a performance-critical native program or low-level tooling that parses raw account bytes at volume — reach for Wincode and its zero-copy reads. * Maintaining existing code that uses bincode — plan a migration to Wincode. The wire formats are compatible for default types, so the move does not break data already on disk or in flight. ## Performance and Geyser streams Wincode's payoff is in compute units (CU) on-chain and CPU cycles in the validator. The community [`febo/tide`](https://github.com/febo/tide) benchmark measures the CU cost of reading and updating one field of a 160-byte, token-like account on-chain. The numbers are workload-specific, but the ordering is consistent and dramatic: | Approach | CU | | - | - | | `transmute` (theoretical floor) | 35 | | Wincode (zero-copy) | 35 | | Wincode (standard) | 62 | | Bincode 2 | 214 | | Borsh | 617 | | Bincode 1 | 8,127 | This is not just an on-chain concern. Anza moved the validator's hot serialization paths to Wincode in Agave 3.1 and broadened it in Agave 4.0. Independent benchmarks attribute single-digit-millisecond latency improvements on account and transaction gRPC streams to the change. If you run Geyser workloads against Chainstack [Trader Nodes](/docs/solana-trader-nodes) or [Yellowstone gRPC](/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js), this lands as lower stream latency as the fleet runs newer Agave — with no change required on your side. > [!NOTE] > > Wincode does not change how you consume Geyser data. The [Yellowstone gRPC](/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js) boundary is Protocol Buffers, so your subscriber code is unaffected. The benefit is faster validator-internal serialization, which shows up as lower latency. The raw `account.data` you receive is still encoded by whichever program owns the account — so the decoding rules above (Borsh, `Pack`, and so on) still apply. ## Additional resources * [getAccountInfo vs getMultipleAccounts](/docs/solana-getaccountinfo-getmultipleaccounts) — fetch the bytes you decode here. * [Anchor development](/docs/solana-anchor-development) — the framework that handles Borsh and discriminators for you. * [Listening to programs using Geyser and Yellowstone gRPC](/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js) — stream account and transaction data at scale. * [Borsh specification](https://borsh.io/) — the canonical, cross-language format spec. * [anza-xyz/wincode](https://github.com/anza-xyz/wincode) — the bincode-compatible successor from Anza. # Solana: Subscriptions and allowances Source: https://docs.chainstack.com/docs/solana-subscriptions-and-allowances Build native onchain recurring payments and delegated spending on Solana in Python — the Subscription Authority PDA model, the three delegation types, and a full create-plan, subscribe, and collect lifecycle against a Chainstack node. **TLDR:** * On June 2, 2026, the Solana Foundation shipped **Subscriptions & Allowances** — an audited, open-source reference program (program ID `De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44`) that makes recurring billing and delegated spending native to Solana, live on mainnet and devnet. * The trick it solves: an SPL token account can have **only one delegate**. The program routes every arrangement through a per-(user, mint) **Subscription Authority** PDA that holds the single `u64::MAX` approval, then gates each transfer through individual delegation PDAs — so one token account can power unlimited simultaneous subscriptions and allowances. * It exposes three models: **fixed delegations** (allowances), **recurring delegations**, and **subscription plans**. * There is no Python SDK — the official TypeScript client is even Codama-generated — so we build the instructions by hand in Python with `solders` and run the full create-plan, subscribe, and collect lifecycle against a Chainstack Solana devnet node. ## What shipped On June 2, 2026, the Solana Foundation released [Subscriptions & Allowances](https://solana.com/news/subscriptions-and-allowances): a single shared program that any team can build on for recurring billing and delegated spending, instead of stitching together off-chain billing infrastructure. It is a Solana Foundation reference program — repo [`solana-program/subscriptions`](https://github.com/solana-program/subscriptions) — written in [Pinocchio](/docs/solana-pinocchio-vs-quasar) (a lightweight `no_std` framework), with IDL and clients generated by Codama, audited by Cantina, and deployed under a Squads multisig upgrade authority. Design partners at launch include Helius (API-tier billing), Confirmo, Dynamic, Majority, Mesh, and Meow. This guide explains the one idea that makes the whole thing work, disambiguates it from the two adjacent primitives people confuse it with, and then walks the full lifecycle in Python against a Chainstack node. ## The one-delegate problem Solana's SPL Token program already lets you hand spending authority to someone else. The `Approve` instruction records a **delegate** and an **allowance** on your token account: the delegate can move up to that many tokens out of your account without you signing each transfer. `Revoke` clears it. This is the primitive behind every "approve then pull" flow on Solana. It has one hard limit, stated plainly in the program's architecture notes: > Solana's SPL Token delegate model allows only **one delegate per token account**. Approving a new delegate silently overwrites the previous one. So a single USDC account can authorize exactly one service at a time. Want to subscribe to two newsletters and give an AI agent a budget, all from the same balance? You can't — the third approval wipes the first two. This constraint has been an open request since 2022 ([solana-program-library issue #3858](https://github.com/solana-labs/solana-program-library/issues/3858)). The Subscriptions program fixes it with one level of indirection. ## Not to be confused with Before the mechanics, clear up three things that sound alike: | Primitive | What it is | Limit | | - | - | - | | **Approve delegate** (`spl-token`) | The raw SPL Token instruction that sets one delegate + allowance on a token account | One delegate per account | | **Spend permissions** | Solana's product framing of that same approve-delegate primitive for builders | Same — one delegate per account | | **Subscriptions program** | A program *built on top of* approve-delegate that multiplexes many policy-controlled delegations behind a single PDA delegate | Unlimited simultaneous arrangements | "Spend permissions" on Solana is not a separate mechanism — it is the approve-delegate primitive described from a payments angle. The Subscriptions program is the thing that removes the one-delegate ceiling. ## How it works For each `(user, mint)` pair, the program derives a **Subscription Authority** (SA) PDA: ```text theme={"system"} seeds = ["SubscriptionAuthority", user_pubkey, mint_pubkey] ``` On initialization, the program calls SPL `Approve` so the SA PDA becomes the user's **single delegate, with a `u64::MAX` allowance**. From the token program's point of view there is exactly one delegate — no conflict with the one-delegate rule. The SA itself can't move anything. It only transfers when a separate **delegation PDA** authorizes a specific amount to a specific party. Those delegation PDAs are where all the real limits live: ```text theme={"system"} delegation (fixed/recurring) = ["delegation", subscription_authority, delegator, delegatee, nonce] subscription = ["subscription", plan_pda, subscriber] ``` So the SPL allowance is a blunt "yes, everything" switch, and the program is the policy engine in front of it. You can create as many delegation PDAs as you like against the one SA — each with its own cap, cadence, expiry, and authorized puller. That is how a single token account supports unlimited concurrent subscriptions and allowances while staying exactly as safe as a normal approve-delegate (the SA can never move funds without an active delegation that permits it). > [!NOTE] > > Each delegation PDA records the SA's `init_id` (the slot at which the SA was created). Every transfer checks it against the live SA. Closing and re-creating the SA in a later slot changes `init_id`, which invalidates old delegations — a coarse kill switch. To stop one specific delegation immediately, revoke that delegation PDA. ## The three models One program, three delegation types: | Model | Who creates it | Cap behavior | Who can pull | | - | - | - | - | | **Fixed delegation** (allowance) | The token owner | One total `amount`, decremented per transfer; optional expiry | The delegatee only | | **Recurring delegation** | The token owner | `amount_per_period` that resets each period; skipped periods don't stack | The delegatee only | | **Subscription plan** | The merchant | Plan-defined `amount` per `period_hours`, resets each period | Plan owner + up to 4 whitelisted pullers | Fixed and recurring delegations are pull arrangements the **user** sets up for a specific delegatee — ideal for allowances and payroll. Subscription plans flip the direction: a **merchant** publishes immutable terms once, and any number of users subscribe to the same plan, each getting their own subscription PDA. The merchant (or a whitelisted puller) collects each cycle. Subscription plan terms (`amount`, `period_hours`, and an on-chain `created_at` fingerprint) are immutable and are snapshotted into each subscriber's PDA at subscribe time. A merchant can't change the price out from under existing subscribers — and can't delete-and-recreate a plan at the same address with new terms, because the snapshot would no longer match (you'd get a `PlanTermsMismatch`). ## Token-2022 and stablecoins The program works with both SPL Token and Token-2022 mints, but Token-2022 extensions that would make delegated transfers unsafe are a problem. > [!WARNING] > > The program's README and the official docs state that seven extensions are rejected at SA initialization: `ConfidentialTransfer`, `NonTransferable`, `PermanentDelegate`, `TransferHook`, `TransferFee`, `MintCloseAuthority`, and `Pausable`. That is the **design intent**, and it's the right mental model for what mints are safe. > > The shipped code is narrower: the runtime check (`validate_mint_extensions`) currently rejects only a mint with a **configured `TransferHook`** (an inert, permanently-unconfigured hook is allowed). The `ConfidentialTransfer` error code is even marked "reserved for backwards compatibility." So don't rely on the program to bounce a fee-bearing or permanent-delegate mint for you — treat the seven-item list as the set of mints you should not use, and pick your mint deliberately. In practice: * **USDC works** — it's a plain mint with no problematic extensions. (Solana's announcement copy says the program works "including confidential transfers"; that contradicts both the README and the code, so don't build on it.) * **PYUSD does not** — PayPal's Solana mint carries Token-2022 extensions (permanent delegate / transfer fee) that the design rules out for delegated pulls. We'll use a plain SPL mint for the walkthrough. ## Prerequisites * Python 3.10+ * `pip install solana solders` (the `solana` package ships the `spl.token` helpers we use for mint setup) * A Chainstack Solana **devnet** node endpoint * Some devnet SOL from the [Chainstack faucet](https://faucet.chainstack.com/solana-testnet-faucet) for fees and rent To get a devnet endpoint: in the [Chainstack console](https://console.chainstack.com/), deploy a Solana devnet node and copy its HTTPS endpoint. We'll refer to it as `CHAINSTACK_DEVNET` below. ## Build it in Python There is no Python SDK for this program, and the official TypeScript client is Codama-generated (and gitignored in the repo) — so the cleanest path in Python is to build the instructions ourselves. The program makes this pleasant: every instruction is a **single leading discriminator byte** followed by a fixed, C-packed, little-endian payload — no Borsh length prefixes, no 8-byte Anchor hashes. **1. Constants and helpers** The program IDs, PDA derivation, ATA derivation, and a small instruction-data encoder. ```python theme={"system"} import time import struct from solana.rpc.api import Client from solders.keypair import Keypair from solders.pubkey import Pubkey from solders.instruction import Instruction, AccountMeta from solders.message import MessageV0 from solders.transaction import VersionedTransaction from solders.system_program import transfer, TransferParams from spl.token.client import Token from spl.token.constants import TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID from solana.rpc.commitment import Confirmed CHAINSTACK_DEVNET = "YOUR_CHAINSTACK_SOLANA_DEVNET_ENDPOINT" PROGRAM_ID = Pubkey.from_string("De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44") SYS_PROGRAM = Pubkey.from_string("11111111111111111111111111111111") # The program's "anchor:event" CPI authority is a constant PDA. EVENT_AUTHORITY = Pubkey.find_program_address([b"event_authority"], PROGRAM_ID)[0] # Pin the client to "confirmed" so reads and confirmations don't wait on finalization. client = Client(CHAINSTACK_DEVNET, commitment=Confirmed) # --- PDA derivation (seeds taken verbatim from the program) --- def subscription_authority_pda(user: Pubkey, mint: Pubkey) -> tuple[Pubkey, int]: return Pubkey.find_program_address( [b"SubscriptionAuthority", bytes(user), bytes(mint)], PROGRAM_ID ) def plan_pda(owner: Pubkey, plan_id: int) -> tuple[Pubkey, int]: return Pubkey.find_program_address( [b"plan", bytes(owner), plan_id.to_bytes(8, "little")], PROGRAM_ID ) def subscription_pda(plan: Pubkey, subscriber: Pubkey) -> tuple[Pubkey, int]: return Pubkey.find_program_address( [b"subscription", bytes(plan), bytes(subscriber)], PROGRAM_ID ) def ata(owner: Pubkey, mint: Pubkey) -> Pubkey: return Pubkey.find_program_address( [bytes(owner), bytes(TOKEN_PROGRAM_ID), bytes(mint)], ASSOCIATED_TOKEN_PROGRAM_ID, )[0] def meta(pubkey: Pubkey, signer: bool, writable: bool) -> AccountMeta: return AccountMeta(pubkey=pubkey, is_signer=signer, is_writable=writable) def send(ixs: list[Instruction], payer: Keypair, signers: list[Keypair]) -> str: blockhash = client.get_latest_blockhash().value.blockhash msg = MessageV0.try_compile(payer.pubkey(), ixs, [], blockhash) tx = VersionedTransaction(msg, signers) sig = client.send_raw_transaction(bytes(tx)).value client.confirm_transaction(sig, commitment=Confirmed) return str(sig) ``` **2. Instruction builders** Each builder returns a `solders.Instruction`. The account order and discriminators come straight from the program's instruction handlers. ```python theme={"system"} # Instruction discriminators (single leading byte). INIT_SUBSCRIPTION_AUTHORITY = 0 CREATE_PLAN = 7 SUBSCRIBE = 11 TRANSFER_SUBSCRIPTION = 10 def ix_init_subscription_authority(user: Pubkey, mint: Pubkey) -> Instruction: sa, _ = subscription_authority_pda(user, mint) accounts = [ meta(user, signer=True, writable=True), meta(sa, signer=False, writable=True), meta(mint, signer=False, writable=False), meta(ata(user, mint), signer=False, writable=True), meta(SYS_PROGRAM, signer=False, writable=False), meta(TOKEN_PROGRAM_ID, signer=False, writable=False), ] return Instruction(PROGRAM_ID, bytes([INIT_SUBSCRIPTION_AUTHORITY]), accounts) def ix_create_plan( merchant: Pubkey, mint: Pubkey, plan_id: int, amount: int, period_hours: int, end_ts: int = 0, metadata_uri: str = "", ) -> Instruction: plan, _ = plan_pda(merchant, plan_id) # PlanData (456 bytes): plan_id u64, mint, terms{amount u64, period_hours u64, # created_at i64}, end_ts i64, destinations[4], pullers[4], metadata_uri[128]. # created_at is sent as 0 and overwritten on-chain with the current clock. data = bytearray([CREATE_PLAN]) data += struct.pack(" Instruction: plan, plan_bump = plan_pda(merchant, plan_id) sub, _ = subscription_pda(plan, subscriber) sa, _ = subscription_authority_pda(subscriber, mint) # The subscriber consents to the exact live plan terms. Read them from chain # so a stale signed transaction can't bind the user to different terms. amount, period_hours, created_at = read_plan_terms(plan) init_id = read_sa_init_id(sa) # SubscribeData (73 bytes): plan_id u64, plan_bump u8, expected_mint, # expected_amount u64, expected_period_hours u64, expected_created_at i64, # expected_sa_init_id i64. data = bytearray([SUBSCRIBE]) data += struct.pack(" Instruction: plan, _ = plan_pda(merchant, plan_id) sub, _ = subscription_pda(plan, subscriber) sa, _ = subscription_authority_pda(subscriber, mint) # TransferData (72 bytes): amount u64, delegator (the subscriber), mint. data = bytearray([TRANSFER_SUBSCRIPTION]) data += struct.pack(" int: # SubscriptionAuthority: disc(1) user(32) mint(32) payer(32) bump(1) init_id(i64). data = client.get_account_info(sa).value.data return int.from_bytes(data[98:106], "little", signed=True) def read_plan_terms(plan: Pubkey) -> tuple[int, int, int]: # Plan: disc(1) owner(32) bump(1) status(1) then PlanData at offset 35. # PlanData: plan_id(35:43) mint(43:75) amount(75:83) period_hours(83:91) # created_at(91:99) ... data = client.get_account_info(plan).value.data amount = int.from_bytes(data[75:83], "little") period_hours = int.from_bytes(data[83:91], "little") created_at = int.from_bytes(data[91:99], "little", signed=True) return amount, period_hours, created_at ``` **3. Fund the wallets** We need two wallets — a merchant and a subscriber — and some devnet SOL for fees and rent. Managed devnet nodes have `requestAirdrop` disabled, so we fund the merchant from the [Chainstack faucet](https://faucet.chainstack.com/solana-testnet-faucet) and then send the subscriber a little SOL from the merchant. ```python theme={"system"} merchant = Keypair() subscriber = Keypair() print("Fund this merchant address from the Chainstack faucet, then continue:") print(merchant.pubkey()) ``` Paste the merchant address into the [Chainstack Solana faucet](https://faucet.chainstack.com/solana-testnet-faucet) to receive 1 devnet SOL, then run the rest: ```python theme={"system"} # Wait until the merchant is funded. while client.get_balance(merchant.pubkey()).value == 0: time.sleep(2) # Send the subscriber 0.2 SOL for rent and fees. send( [transfer(TransferParams( from_pubkey=merchant.pubkey(), to_pubkey=subscriber.pubkey(), lamports=200_000_000, ))], payer=merchant, signers=[merchant], ) print("subscriber funded:", client.get_balance(subscriber.pubkey()).value / 1e9, "SOL") ``` **4. Create a mint and give the subscriber a balance** A fresh 6-decimal SPL mint, an ATA for each wallet, and 100 test tokens for the subscriber to be billed against. We use a plain SPL mint so there are no Token-2022 extensions to worry about. ```python theme={"system"} # Create a 6-decimal mint with the merchant as mint authority. token = Token.create_mint( conn=client, payer=merchant, mint_authority=merchant.pubkey(), decimals=6, program_id=TOKEN_PROGRAM_ID, ) MINT = token.pubkey # Create ATAs and mint 100 test tokens to the subscriber. token.create_associated_token_account(subscriber.pubkey()) token.create_associated_token_account(merchant.pubkey()) token.mint_to( dest=ata(subscriber.pubkey(), MINT), mint_authority=merchant, amount=100_000_000, # 100.0 tokens at 6 decimals ) print("mint:", MINT) ``` **5. Initialize the subscriber's Subscription Authority** This creates the SA PDA and approves it as the `u64::MAX` delegate on the subscriber's token account. The subscriber must sign — a sponsor can pay rent but cannot approve on someone else's account. ```python theme={"system"} sig = send( [ix_init_subscription_authority(subscriber.pubkey(), MINT)], payer=subscriber, signers=[subscriber], ) print("init SA:", sig) ``` **6. Merchant publishes a plan** A plan billing 1 token per day. `period_hours` is the billing period, bounded to 1–8760 (up to one year); we pass `24`. ```python theme={"system"} PLAN_ID = 1 sig = send( [ix_create_plan( merchant=merchant.pubkey(), mint=MINT, plan_id=PLAN_ID, amount=1_000_000, # 1.0 token per period period_hours=24, # daily metadata_uri="https://example.com/plan.json", )], payer=merchant, signers=[merchant], ) print("create plan:", sig) ``` **7. Subscriber subscribes** `ix_subscribe` reads the live plan terms and the SA `init_id` from chain and binds them into the instruction as consent fields. The program rejects the subscribe if the live plan disagrees. ```python theme={"system"} sig = send( [ix_subscribe(subscriber.pubkey(), merchant.pubkey(), MINT, PLAN_ID)], payer=subscriber, signers=[subscriber], ) print("subscribe:", sig) ``` **8. Merchant collects the first payment** The merchant pulls one period's amount from the subscriber into the merchant's token account. The program enforces that the caller is the plan owner (or a whitelisted puller), the receiver is allowed, and the per-period cap isn't exceeded. ```python theme={"system"} sig = send( [ix_transfer_subscription( caller=merchant.pubkey(), subscriber=subscriber.pubkey(), merchant=merchant.pubkey(), mint=MINT, plan_id=PLAN_ID, amount=1_000_000, # this period's charge receiver=merchant.pubkey(), )], payer=merchant, signers=[merchant], ) print("collect:", sig) bal = client.get_token_account_balance(ata(merchant.pubkey(), MINT)).value print("merchant balance:", bal.ui_amount_string) ``` A second collect inside the same 24-hour period fails with `AmountExceedsPeriodLimit` — the cap resets only when the period rolls over. Skipped periods don't accumulate. That's the full lifecycle: one SA approval, one published plan, any number of subscribers, and a pull each cycle — all onchain, no billing backend. ## What's next This guide covered the user-facing lifecycle. Two follow-ups build on it: * [Indexing subscription events](/docs/solana-indexing-subscription-events-geyser) with Yellowstone gRPC — the program emits create, cancel, and pull events as Anchor-compatible inner-instruction CPIs. Streaming them through a Chainstack Geyser endpoint gives you live revenue, churn, and failed-collection dashboards without polling. * [Capped onchain budgets](/docs/solana-agent-allowances-x402) for AI agents — the fixed-delegation model is the natural way to hand an autonomous agent a spending allowance, and it pairs with x402 for per-call agentic commerce. ## Conclusion Subscriptions & Allowances turns recurring revenue from custom infrastructure into a shared, audited onchain primitive. The whole design rests on one idea — route every arrangement through a single Subscription Authority delegate and enforce the real limits in per-delegation PDAs — which is what lifts the one-delegate-per-account ceiling that blocked this for years. Because there's no Python SDK, building the instructions by hand is also the best way to understand exactly what the program checks on every pull. Point the script at a Chainstack devnet node, run it top to bottom, and you have a working subscription in a few seconds. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Surfpool quick-start Source: https://docs.chainstack.com/docs/solana-surfpool Run a local Solana network with real mainnet state in milliseconds using Surfpool — copy-on-read fork, surfnet cheatcodes, and test-validator compatibility. **TLDR:** * Surfpool is a local Solana network with **real mainnet state on demand** — a copy-on-read fork means any account you touch is lazy-fetched from mainnet, no snapshot dumps required. * Anchor v1.0.0 (2026-04-02) makes Surfpool the **default validator** for `anchor test` and `anchor localnet`. If you ran `anchor init` today, your `Anchor.toml` already has a `[surfpool]` section — but you still need to install the `surfpool` binary separately (see install below). * It exposes a full Solana JSON-RPC server and adds **26 `surfnet_*` cheatcode methods** — set any account, mint any token, time-travel, pause the clock, profile any transaction. * Internally it wraps [LiteSVM](/docs/solana-litesvm-testing) for execution, so it boots in sub-second time and runs on a Raspberry Pi. Self-description: *"Surfpool is to Solana what Anvil is to Ethereum."* * All commands and cheatcode signatures in this guide match [txtx/surfpool v1.1.2](https://github.com/txtx/surfpool/releases/tag/v1.1.2) as of April 2026. ## Where Surfpool fits Solana's modern testing pyramid trades fidelity for speed. Surfpool owns the **integration** tier — the layer above pure unit testing, the layer below a real testnet. | Tier | Tool | What you get | What you give up | | - | - | - | - | | Unit (single instruction) | [Mollusk](https://github.com/anza-xyz/mollusk) | CU-accurate benchmarking, no bank state | No sysvars, no CPI across programs | | Unit (multi-instruction) | [LiteSVM](/docs/solana-litesvm-testing) | Full bank, sysvars, CPI, fastest iteration | No RPC, no network effects, no mainnet state | | **Integration** | **Surfpool** | **Full RPC, mainnet fork, cheatcodes** | **No MEV/contention simulation, single validator only** | | End-to-end | Devnet / mainnet | Real network | Slow, flaky, real money | Surfpool is not a replacement for LiteSVM — it wraps it. Reach for LiteSVM when you want to hammer pure program logic in a tight red-green loop. Reach for Surfpool when you need a real RPC endpoint, real mainnet account state, or the full Solana JSON-RPC surface your client SDK expects. ### What Surfpool replaces | Incumbent | How Surfpool compares | | - | - | | `solana-test-validator` | **Drop-in replacement.** Full RPC compatibility, but boots in sub-second time and can fork any mainnet account on demand. Works on a Raspberry Pi. | | Manual `--clone` of mainnet accounts | No pre-dumping. Call any RPC method that touches a mainnet address — Surfpool fetches the account the first time you ask for it and caches it locally. | ## Install The installer script is the recommended path: ```bash theme={"system"} curl -sL https://run.surfpool.run/ | bash surfpool --version ``` Alternatives: ```bash cargo theme={"system"} cargo install surfpool-cli ``` ```bash docker theme={"system"} docker run --rm surfpool/surfpool --version ``` ```bash from-source theme={"system"} git clone https://github.com/txtx/surfpool.git cd surfpool cargo surfpool-install ``` > [!WARNING] > > **Avoid the Linux snap.** Snapcraft's `surfpool` package lags \~6 months behind GitHub (v0.9.5 vs v1.1.2 at time of writing). The installer script and `cargo install surfpool-cli` always give you the latest release. > [!WARNING] > > **Windows + Docker Studio unreachable.** Surfpool Studio binds to `0.0.0.0` inside the container and the browser cannot reach it in some Windows setups ([issue #616](https://github.com/txtx/surfpool/issues/616), open). Native WSL2 works; pure Docker Desktop on Windows does not. ## First run ```bash theme={"system"} surfpool start ``` By default this: * Starts a JSON-RPC server on `127.0.0.1:8899` (same as `solana-test-validator`) * Starts a WebSocket server on `127.0.0.1:8900` * Opens **Surfpool Studio** (a local dashboard) on a browser port * Enters an interactive TUI showing block production, transaction throughput, and account activity Pass `--no-tui` to stream logs instead of the dashboard, or `--no-studio` if you don't want the browser UI. Point any Solana tool at the local endpoint — it speaks standard Solana RPC: ```bash theme={"system"} solana config set -u http://127.0.0.1:8899 solana balance ``` ## Fork mainnet with Chainstack The killer feature is **copy-on-read mainnet fork**. Any account you read is fetched from the datasource the first time you touch it, cached locally, and treated as local state from then on. ```bash theme={"system"} surfpool start \ --rpc-url https://solana-mainnet.core.chainstack.com/YOUR_KEY ``` Now local RPC calls see real mainnet state: ```bash theme={"system"} # USDC mint is real — Surfpool fetched it the moment you asked. solana account EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v # Jupiter aggregator program too. solana account JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 ``` You can also pre-airdrop SOL to arbitrary pubkeys at boot: ```bash theme={"system"} surfpool start \ --rpc-url https://solana-mainnet.core.chainstack.com/YOUR_KEY \ --airdrop 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM \ --airdrop-amount 1000000000 ``` > [!NOTE] > > The `SURFPOOL_DATASOURCE_RPC_URL` environment variable is equivalent to `--rpc-url`. Set it once in your shell profile to avoid retyping the endpoint. ## Cheatcodes: 26 `surfnet_*` RPC methods Surfpool's JSON-RPC server accepts all standard Solana methods (`getAccountInfo`, `sendTransaction`, etc.) plus 26 `surfnet_*` extensions that let you mutate local state in ways the real network won't allow. Complete list, verified from [source](https://github.com/txtx/surfpool/blob/v1.1.2/crates/core/src/rpc/surfnet_cheatcodes.rs): **Accounts:** `setAccount`, `setTokenAccount`, `cloneProgramAccount`, `resetAccount`, `offlineAccount`, `setSupply`, `setProgramAuthority`, `writeProgram` **Transaction profiling:** `profileTransaction`, `getTransactionProfile`, `getProfileResultsByTag` **IDL:** `registerIdl`, `getActiveIdl` **Clock:** `timeTravel`, `pauseClock`, `resumeClock` **Network:** `resetNetwork`, `exportSnapshot`, `getSurfnetInfo`, `getLocalSignatures` **Account streaming:** `streamAccount`, `streamAccounts`, `getStreamedAccounts` **Cheatcode control:** `enableCheatcode`, `disableCheatcode` **Scenarios:** `registerScenario` Four worked examples below cover the ones you will reach for on day one. ### Set any account Overwrite any on-chain account with arbitrary lamports, data, and owner. The `AccountUpdate` struct is all-optional — send only the fields you want to change. ```bash theme={"system"} curl -s http://127.0.0.1:8899 \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "surfnet_setAccount", "params": [ "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", { "lamports": 5000000000, "owner": "11111111111111111111111111111111" } ] }' ``` The `data` field, if provided, takes a **hex-encoded** byte string — despite what some documentation suggests, the v1.1.2 implementation only accepts hex. Sending base64 or base58 returns `Invalid hex data provided`. Omit the field to leave data untouched, or pass `"data": ""` to clear it. ### Mint tokens to any wallet (no authority required) The cheatcode that saves the most time in DeFi tests. Give any wallet any token balance without caring about mint authority. ```bash theme={"system"} curl -s http://127.0.0.1:8899 \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "surfnet_setTokenAccount", "params": [ "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", { "amount": 1000000000, "state": "initialized" } ] }' ``` After this call, the owner has 1,000 USDC (6 decimals) on the local network. The mint's real authority is irrelevant — Surfpool writes the token account directly. > [!NOTE] > > Include `"state": "initialized"` or the newly-created token account is usable as an address but reports a zero balance. This is a quiet footgun — the cheatcode succeeds either way. ### Time travel Advance the clock to any slot, epoch, or unix timestamp. Useful for vesting, auctions, staking warmup/cooldown. ```bash theme={"system"} curl -s http://127.0.0.1:8899 \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "surfnet_timeTravel", "params": [{ "absoluteTimestamp": 2000000000000 }] }' ``` > [!WARNING] > > **`absoluteTimestamp` is milliseconds, not seconds.** Pass `Date.now() + delta_ms` or the equivalent. Supplying a seconds-based unix timestamp silently fails with `Cannot travel to past timestamp` because Surfpool interprets it as far earlier than the current simulated clock. Parameter shape also accepts `{ "absoluteSlot": }` or `{ "absoluteEpoch": }`. Slot and epoch are absolute values — they must be strictly greater than the current ones. Pair with `surfnet_pauseClock` / `surfnet_resumeClock` to freeze block production. ### Profile a transaction `surfnet_profileTransaction` executes the transaction against a temporary state snapshot — the execution is real, but state changes are **not committed** to the local network. It captures pre/post account snapshots and per-instruction CU metrics for analysis. ```bash theme={"system"} curl -s http://127.0.0.1:8899 \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "surfnet_profileTransaction", "params": [ "", "swap-v0-baseline" ] }' ``` The response contains every account the instruction touched, pre/post lamport + data snapshots under `instructionProfiles`, and a unique `key` UUID. The second positional parameter is an optional tag string — not a config object. Call `surfnet_getProfileResultsByTag` later to compare runs that share a tag. ## Anchor integration Anchor v1.0.0 made Surfpool the default test validator (PR [#4106](https://github.com/solana-foundation/anchor/pull/4106)). A fresh `anchor init` scaffolds a `[surfpool]` section in `Anchor.toml`: ```toml theme={"system"} [surfpool] startup_wait = 5000 shutdown_wait = 2000 rpc_port = 8899 ws_port = 8900 online = true datasource_rpc_url = "https://solana-mainnet.core.chainstack.com/YOUR_KEY" airdrop_addresses = ["9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"] block_production_mode = "clock" slot_time = 400 ``` > [!WARNING] > > **`online = true` is required for mainnet fork to activate.** Anchor invokes Surfpool in offline mode by default. If your tests fail with `AccountNotFound` for mainnet addresses you expected to exist, set `online = true` and provide a `datasource_rpc_url`. This is the single most common footgun, by design — Anchor optimizes CI speed, you opt into network fetches. Run the full suite: ```bash theme={"system"} anchor test ``` To fall back to the legacy `solana-test-validator`: ```bash theme={"system"} anchor test --validator legacy ``` ### Migrating from `[test.validator.clone]` Legacy Anchor projects pre-dump mainnet accounts via the `[test.validator.clone]` section. Surfpool makes that unnecessary — lazy fetch replaces pre-cloning. The old pattern: ```toml theme={"system"} # Pre-v1.0 Anchor.toml [test.validator] url = "https://api.mainnet-beta.solana.com" [[test.validator.clone]] address = "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4" ``` Becomes: ```toml theme={"system"} [surfpool] online = true datasource_rpc_url = "https://solana-mainnet.core.chainstack.com/YOUR_KEY" # No clone list — any account you touch is fetched on demand. ``` If you still want to pre-warm specific accounts at boot (e.g., to avoid a first-call latency spike in benchmarks), call `surfnet_setAccount` from a test-setup step after fetching the bytes from mainnet RPC yourself. `surfnet_cloneProgramAccount` is not a general warmer — its signature is `(source_program_id, destination_program_id)` and it only copies executable program accounts. ## Pitfalls ### Offline mode is the default As above — `online = false` in `Anchor.toml` means no mainnet fetch, no matter what `datasource_rpc_url` you set. This trips up everyone migrating from a manual `solana-test-validator --clone` workflow. ### No transaction contention or MEV simulation Every transaction Surfpool sees succeeds in priority order as submitted. There is no priority-fee reordering, no sandwich attack simulation, no write-lock contention ([issue #529](https://github.com/txtx/surfpool/issues/529), open). For MEV-sensitive logic, test on a real cluster or use dedicated MEV simulation tooling. ### Single validator only No multi-node support ([issue #448](https://github.com/txtx/surfpool/issues/448), open). Tests that need cross-validator behavior (e.g., leader rotation, gossip-dependent protocols) need a real cluster. ### CI version pinning The installer script always installs the latest version. Anchor's `SURFPOOL_CLI_VERSION` env var only busts caches — it doesn't pin ([issue #4160](https://github.com/solana-foundation/anchor/issues/4160), closed as resolved in Anchor v1.0, though version pinning for the installer itself is still manual). For reproducible CI, pin to a specific `cargo install surfpool-cli --version` or a pinned Docker tag. ### Snap package is stale `sudo snap install surfpool` fetches v0.9.5 (2025-07-21) — roughly 6 months behind GitHub. Use the installer script or cargo. ### `anchor deploy` cluster-node timeout Running `anchor deploy` against a running Surfpool sometimes fails with `Failed to find any cluster node info for upcoming leaders, timeout: 20s` ([Solana SE #23339](https://solana.stackexchange.com/questions/23339)). The validator is up but has not emitted leader-schedule info yet. Wait a few slots after `surfpool start` before invoking `anchor deploy`, or prefer `anchor test` (which waits for Surfpool to be ready before deploying). ### No MCP-less non-interactive mode `surfpool start` always boots with the TUI unless you pass `--no-tui`. For CI, always combine `--no-tui --no-studio`. ## When not to use Surfpool * You need MEV or priority-fee contention simulation. Use a real cluster. * You need multi-validator / leader-rotation tests. Use a real cluster. * You need reproducible CI with a locked version before installer pinning ships. Use a Docker tag or pinned `cargo install`. * You are doing pure unit testing and don't need a real RPC. [LiteSVM](/docs/solana-litesvm-testing) is 10× faster per test. * You want CU-accurate single-instruction benchmarking. [Mollusk](https://github.com/anza-xyz/mollusk) is purpose-built. ## See also * [Solana: Mainnet fork tests with the Surfpool Kit plugin](/docs/solana-surfpool-kit-mainnet-fork-tests) — run Surfpool inside a TypeScript test suite and fork Mainnet from your Chainstack endpoint. * [Solana: Fast unit testing with LiteSVM](/docs/solana-litesvm-testing) — the unit tier below Surfpool; Surfpool wraps LiteSVM internally. * [Solana: Anchor development](/docs/solana-anchor-development) — the framework Surfpool is now the default test backend for. * [Surfpool docs](https://docs.surfpool.run) — authoritative CLI, RPC, cheatcode reference. * [Surfpool cheatcodes reference](https://docs.surfpool.run/rpc/cheatcodes) — full JSON-RPC signature for every `surfnet_*` method. * [Anchor v1.0.0 release notes](https://github.com/solana-foundation/anchor/releases/tag/v1.0.0) — the PR that made Surfpool default. * [Helius: Introducing Surfpool](https://www.helius.dev/blog/surfpool) — conceptual intro and lazy-fork explanation. * [Blueshift: Surfpool 101](https://learn.blueshift.gg/en/courses/testing-with-surfpool/surfpool-101) — interactive course. ## Reference repos These are the source repositories we worked against while writing this guide. They stay closer to reality than docs — check them first when something here looks off. * [txtx/surfpool](https://github.com/txtx/surfpool) — Surfpool source; `crates/core/src/rpc/surfnet_cheatcodes.rs` is the authoritative cheatcode surface — check here when signatures look off * [solana-foundation/anchor](https://github.com/solana-foundation/anchor) — Anchor 1.0 is Surfpool-native; `[surfpool]` section semantics and `online = true` behavior live here * [anza-xyz/mollusk](https://github.com/anza-xyz/mollusk) — alternative lightweight runtime referenced when Surfpool's overlay model isn't the right fit # Solana: Mainnet fork tests with the Surfpool Kit plugin Source: https://docs.chainstack.com/docs/solana-surfpool-kit-mainnet-fork-tests Run TypeScript tests against Solana Mainnet state forked from your Chainstack endpoint with the Surfpool Kit plugin: fund wallets with cheatcodes, send SPL token transfers locally, profile compute units, and move the clock forward. **TLDR:** * The Surfpool Kit plugin, `@solana/surfpool/kit`, starts a local Surfnet inside your test process and connects a [Solana Kit](https://github.com/anza-xyz/kit) client to it, with a funded payer and typed cheatcodes. * Pass your Chainstack Solana Mainnet endpoint as `remoteRpcUrl`, and the Surfnet forks Mainnet: each account is copied from your endpoint the first time something reads it, and every transaction runs locally. Without `remoteRpcUrl`, the Surfnet is offline and Mainnet accounts don't exist in it. * The test suite in this guide reads the real USDC mint, gives a wallet 1,000 USDC with a cheatcode, sends a transfer, profiles compute units without sending, and moves the clock forward 30 days. A run takes about 2 seconds and sends 12 read requests to your endpoint. * Set `slotTimeMs: 400` if your code depends on slots or epochs. With the default 1 ms slot, moving the clock forward 30 days advances 6,000 epochs instead of 15. ## Main article [Surfpool](/docs/solana-surfpool) is a local Solana network that forks Mainnet on demand. The [Solana: Surfpool quick-start](/docs/solana-surfpool) covers the `surfpool` CLI. This guide covers the Kit plugin, which runs the same Surfnet inside a Node.js process, so a TypeScript test suite can start a fresh fork for each test file and drive it through a Kit client. The fork is lazy. Your tests use real Mainnet accounts, such as token mints, programs, and pools, without a clone list or a snapshot, and nothing they do reaches Mainnet. > [!NOTE] > > To follow along, you need a Solana Mainnet node endpoint. See [Solana tooling](/docs/solana-tooling) to get set up. ## Requirements * Node.js 20.18 or later, which `@solana/kit` 8 requires. * macOS (Apple silicon or Intel) or Linux x64 with glibc. `@solana/surfpool` ships its native Surfnet build only for these platforms. On other platforms, run the `surfpool` CLI and connect to it with the plugin's attach mode, described in the [Surfpool Kit plugin documentation](https://solana.com/docs/tools/surfpool/sdk/kit-plugin). ## Set up the project ```bash theme={"system"} mkdir surfpool-fork-tests && cd surfpool-fork-tests npm init -y npm install --save-dev @solana/kit @solana/surfpool @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana/sysvars @solana-program/token ``` This guide uses `@solana/kit` 8.4.0, `@solana/surfpool` 1.6.1, `@solana/kit-plugin-rpc` 0.18.0, `@solana/kit-plugin-signer` 0.18.0, `@solana/sysvars` 8.4.0, and `@solana-program/token` 0.17.0. Two of these packages need care: * `@solana/surfpool` 1.6.1 requires the 0.18 line of `@solana/kit-plugin-rpc` and `@solana/kit-plugin-signer`. Installing all the packages in one command lets npm pick 0.18. Requesting 0.19 explicitly fails with `ERESOLVE`. * `@solana/sysvars` provides `fetchSysvarClock`. Kit 8 doesn't re-export it from `@solana/kit`. ## How the fork reaches your endpoint The Surfnet calls your Chainstack endpoint only to read state: * At startup, it reads the genesis hash, epoch info, the epoch schedule, and one sysvar account. The local clock starts at your endpoint's current slot and epoch. * The first time your code or a transaction reads an account, the Surfnet fetches it with `getAccountInfo` or `getMultipleAccounts`. Later reads use the local copy, so the copy doesn't follow changes on Mainnet. * Transactions run only in the local Surfnet. The Surfnet never sends a transaction to your endpoint. Each Surfnet starts with a new payer funded with 10 SOL, available as `client.payer`. ## Write the test suite The suite uses the Node.js built-in test runner, so it needs no test framework. It reads your endpoint from the `CHAINSTACK_ENDPOINT` environment variable. ```javascript fork.test.mjs theme={"system"} import assert from "node:assert/strict"; import { after, before, describe, it } from "node:test"; import { address, appendTransactionMessageInstructions, createClient, createSolanaRpc, createTransactionMessage, fetchEncodedAccount, generateKeyPairSigner, getBase64EncodedWireTransaction, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from "@solana/kit"; import { surfpool } from "@solana/surfpool/kit"; import { fetchSysvarClock } from "@solana/sysvars"; import { TOKEN_PROGRAM_ADDRESS, fetchMint, fetchToken, findAssociatedTokenPda, getCreateAssociatedTokenIdempotentInstructionAsync, getTransferCheckedInstruction, tokenProgram, } from "@solana-program/token"; const CHAINSTACK_ENDPOINT = process.env.CHAINSTACK_ENDPOINT; const USDC_MINT = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); const ONE_USDC = 1_000_000n; async function usdcAccountOf(owner) { const [tokenAccount] = await findAssociatedTokenPda({ owner, mint: USDC_MINT, tokenProgram: TOKEN_PROGRAM_ADDRESS, }); return tokenAccount; } describe("USDC on a Solana Mainnet fork", () => { const mainnet = createSolanaRpc(CHAINSTACK_ENDPOINT); let client; before(async () => { client = await createClient() .use(surfpool({ surfnet: { remoteRpcUrl: CHAINSTACK_ENDPOINT, slotTimeMs: 400 } })) .use(tokenProgram()); }); after(() => client.surfnet.stop()); it("reads the USDC mint from mainnet", async () => { const forked = await fetchMint(client.rpc, USDC_MINT); const live = await fetchMint(mainnet, USDC_MINT); assert.equal(forked.data.decimals, 6); assert.deepEqual(forked.data.mintAuthority, live.data.mintAuthority); }); it("transfers USDC without touching mainnet", async () => { await client.cheatcodes .setTokenAccount(client.payer.address, USDC_MINT, { amount: 1_000n * ONE_USDC }) .send(); const recipient = await generateKeyPairSigner(); await client.token.instructions .transferToATA({ mint: USDC_MINT, authority: client.payer, recipient: recipient.address, amount: 250n * ONE_USDC, decimals: 6, }) .sendTransaction(); const payerAccount = await fetchToken(client.rpc, await usdcAccountOf(client.payer.address)); const recipientAddress = await usdcAccountOf(recipient.address); const recipientAccount = await fetchToken(client.rpc, recipientAddress); assert.equal(payerAccount.data.amount, 750n * ONE_USDC); assert.equal(recipientAccount.data.amount, 250n * ONE_USDC); const onMainnet = await fetchEncodedAccount(mainnet, recipientAddress); assert.equal(onMainnet.exists, false); }); it("profiles a transfer without sending it", async (t) => { await client.cheatcodes .setTokenAccount(client.payer.address, USDC_MINT, { amount: 1_000n * ONE_USDC }) .send(); const recipient = await generateKeyPairSigner(); const recipientAddress = await usdcAccountOf(recipient.address); const createRecipientAccount = await getCreateAssociatedTokenIdempotentInstructionAsync({ payer: client.payer, owner: recipient.address, mint: USDC_MINT, }); const transfer = getTransferCheckedInstruction({ source: await usdcAccountOf(client.payer.address), mint: USDC_MINT, destination: recipientAddress, authority: client.payer, amount: 250n * ONE_USDC, decimals: 6, }); const { value: latestBlockhash } = await client.rpc.getLatestBlockhash().send(); const message = pipe( createTransactionMessage({ version: 0 }), (m) => setTransactionMessageFeePayerSigner(client.payer, m), (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), (m) => appendTransactionMessageInstructions([createRecipientAccount, transfer], m), ); const transaction = await signTransactionMessageWithSigners(message); const profile = await client.cheatcodes .profileTransaction(getBase64EncodedWireTransaction(transaction), "usdc-transfer") .send(); assert.equal(profile.transactionProfile.errorMessage, null); t.diagnostic(`compute units: ${profile.transactionProfile.computeUnitsConsumed}`); const afterProfile = await fetchEncodedAccount(client.rpc, recipientAddress); assert.equal(afterProfile.exists, false); }); it("moves the clock forward 30 days", async (t) => { const start = await fetchSysvarClock(client.rpc); const thirtyDays = 30n * 24n * 60n * 60n; await client.cheatcodes .timeTravel({ absoluteTimestamp: (start.unixTimestamp + thirtyDays) * 1000n }) .send(); const end = await fetchSysvarClock(client.rpc); assert.equal(end.unixTimestamp - start.unixTimestamp, thirtyDays); t.diagnostic(`epoch ${start.epoch} -> ${end.epoch}, slot ${start.slot} -> ${end.slot}`); }); }); ``` ### Start and stop the fork The `before` hook builds the client. `surfpool()` starts the Surfnet and installs `client.rpc`, `client.payer`, `client.cheatcodes`, and `client.surfnet`. The `surfnet` options go to the native Surfnet: `remoteRpcUrl` turns on the fork, and `slotTimeMs: 400` matches Mainnet's slot time. `tokenProgram()` from `@solana-program/token` adds the Token program's instructions and accounts as `client.token`. The `after` hook calls `client.surfnet.stop()`, which closes the Surfnet's local HTTP and WebSocket servers and frees their ports. Node.js runs each test file in its own process, so each file gets its own Surfnet. ### Read Mainnet state The first test reads the USDC mint twice: through the fork and directly from your endpoint. The forked copy has the same decimals and mint authority as the account on Mainnet. ### Fund a wallet and send a transfer The `setTokenAccount` cheatcode writes the payer's USDC token account, creating it if needed, with a balance of 1,000 USDC. No faucet or swap is involved, and the USDC mint authority doesn't sign anything. `client.token.instructions.transferToATA` creates the recipient's associated token account and transfers 250 USDC. `sendTransaction()` plans the transaction, adds a `SetComputeUnitLimit` instruction with an estimated limit, signs it with the payer, and sends it to the Surfnet. The test then checks both balances, and checks that the recipient's token account doesn't exist on Mainnet. ### Profile compute units The `profileTransaction` cheatcode runs a signed transaction against the fork and returns its compute units, logs, and account states, without changing any account. The test confirms that the recipient's token account still doesn't exist after profiling. The test builds this transaction by hand rather than with `planTransaction()`. A planned transaction carries a `SetComputeUnitLimit` instruction with a limit of 0, which `sendTransaction()` replaces with an estimate when it sends. Profiling a planned transaction as it is fails with `Computational budget exceeded`. ### Move the clock The `timeTravel` cheatcode takes `absoluteTimestamp` in milliseconds and moves the Clock sysvar to that time. Use it to test vesting schedules, lockups, and other time-based logic. The Surfnet converts the time difference into slots using `slotTimeMs`. With `slotTimeMs: 400`, moving 30 days forward advances about 6.48 million slots and 15 epochs, as on Mainnet. With the default of 1 ms, the same move advances about 2.59 billion slots and 6,000 epochs. ## Run the tests ```bash theme={"system"} CHAINSTACK_ENDPOINT=YOUR_CHAINSTACK_ENDPOINT node --test ``` On Node.js 24, the output looks like this: ```text theme={"system"} ▶ USDC on a Solana Mainnet fork ✔ reads the USDC mint from mainnet (444.605625ms) ✔ transfers USDC without touching mainnet (471.001583ms) ✔ profiles a transfer without sending it (298.677291ms) ℹ compute units: 13630 ✔ moves the clock forward 30 days (8.218292ms) ℹ epoch 1054 -> 1069, slot 455644639 -> 462124638 ✔ USDC on a Solana Mainnet fork (1873.32875ms) ℹ tests 4 ℹ suites 1 ℹ pass 4 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 2014.683083 ``` The compute units change from run to run because each run uses a new recipient. Creating the recipient's associated token account searches for a valid address, and each extra search step costs 1,500 compute units. ## Why a Mainnet account is not found `surfpool()` without `remoteRpcUrl` starts an offline Surfnet. It still has a funded payer, but no Mainnet accounts, so reading the USDC mint fails: ```text theme={"system"} Account not found at address: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v ``` If a test that worked against Mainnet state fails with `Account not found`, check that `remoteRpcUrl` is set and that `CHAINSTACK_ENDPOINT` holds your endpoint. ## See also * [Solana: Surfpool quick-start](/docs/solana-surfpool) — the `surfpool` CLI, Anchor integration, and the full cheatcode list. * [Solana: Fast unit testing with LiteSVM](/docs/solana-litesvm-testing) — unit tests without RPC or Mainnet state. * [Surfpool Kit plugin documentation](https://solana.com/docs/tools/surfpool/sdk/kit-plugin) — attach mode, the standalone cheatcodes client, and every plugin option. * [solana-foundation/surfpool](https://github.com/solana-foundation/surfpool) — Surfpool source; the Node.js package and Kit plugin live in `crates/sdk-node`. # Solana: Token extensions (Token-2022) Source: https://docs.chainstack.com/docs/solana-token-extensions Understand Solana Token Extensions — the Token-2022 program adding transfer fees, confidential transfers, metadata, and soulbound tokens to SPL tokens. **TLDR:** * The Token Extensions Program (Token-2022) is a superset of the original SPL Token Program with optional extensions you enable at mint creation time. * Extensions add features like transfer fees, on-chain metadata, confidential transfers, non-transferable (soulbound) tokens, permanent delegates, and transfer hooks. * Token-2022 tokens work with the same RPC methods (`getTokenAccountsByOwner`, `getTokenSupply`, etc.) but require the Token-2022 program ID for filtering. * Most extensions cannot be added after mint creation — plan your token design upfront. ## Why Token Extensions matter The original SPL Token Program covers basic mint, transfer, and burn operations. But real-world token use cases need more: * **Transfer fees** — protocol-level fee on every transfer, collected automatically on-chain * **Confidential transfers** — ZK-encrypted balances and transfer amounts for privacy * **On-chain metadata** — store token name, symbol, and URI directly on the mint account (no Metaplex dependency) * **Non-transferable tokens** — soulbound credentials, achievements, identity tokens * **Permanent delegate** — compliance-oriented freeze/clawback capability * **Transfer hooks** — programmable logic that runs on every transfer (royalties, allow-lists, custom validation) The Token-2022 program ID is `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`. ## Available extensions Extensions are enabled at mint or token account creation time. Most cannot be added later. ### Mint extensions | Extension | What it does | | - | - | | TransferFeeConfig | Automatic fee on every transfer, collected in withheld accounts | | MintCloseAuthority | Allows closing the mint account to reclaim rent | | InterestBearingConfig | Tokens accrue interest over time (UI display, not actual minting) | | PermanentDelegate | An authority that can transfer or burn tokens from any account | | NonTransferable | Tokens cannot be transferred (soulbound) | | TransferHook | Calls a custom program on every transfer (CPI-based) | | MetadataPointer + TokenMetadata | On-chain metadata stored directly on the mint | | GroupPointer + TokenGroup | Collection-like grouping for tokens | | ConfidentialTransferMint | Enables ZK-encrypted balances and transfers | | DefaultAccountState | New token accounts start as frozen (require explicit thaw) | | Pausable | Admin can pause all transfers globally | | ScaledUiAmount | Rebasing token display (like stETH) | ### Account extensions | Extension | What it does | | - | - | | ImmutableOwner | Token account owner cannot be changed (enabled by default for ATAs) | | MemoTransfer | Inbound transfers require a memo | | CpiGuard | Blocks certain token operations when invoked via CPI | ## Create a token with extensions This example creates a mint with transfer fees and on-chain metadata. ### Prerequisites ```bash theme={"system"} npm install @solana/web3.js @solana/spl-token @solana/spl-token-metadata bs58 dotenv ``` ### Code ```typescript TypeScript theme={"system"} import { Connection, Keypair, SystemProgram, Transaction, sendAndConfirmTransaction, } from "@solana/web3.js"; import { ExtensionType, TOKEN_2022_PROGRAM_ID, createInitializeMintInstruction, createInitializeTransferFeeConfigInstruction, getMintLen, createMintToInstruction, createAssociatedTokenAccountInstruction, getAssociatedTokenAddressSync, } from "@solana/spl-token"; import { createInitializeInstruction, createInitializeMetadataPointerInstruction, pack, TokenMetadata, TYPE_SIZE, LENGTH_SIZE, } from "@solana/spl-token-metadata"; import bs58 from "bs58"; import "dotenv/config"; const connection = new Connection(process.env.SOLANA_RPC!); const payer = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY!)); const mintKeypair = Keypair.generate(); const mint = mintKeypair.publicKey; const decimals = 6; const transferFeeBasisPoints = 100; // 1% const maxFee = BigInt(1_000_000); // 1 token max fee // Metadata for the token const metadata: TokenMetadata = { mint: mint, name: "My Hackathon Token", symbol: "HACK", uri: "https://example.com/metadata.json", additionalMetadata: [], }; async function createTokenWithExtensions() { // Calculate space needed for mint + extensions const extensions = [ExtensionType.TransferFeeConfig, ExtensionType.MetadataPointer]; const mintLen = getMintLen(extensions); const metadataLen = TYPE_SIZE + LENGTH_SIZE + pack(metadata).length; const totalLen = mintLen + metadataLen; const lamports = await connection.getMinimumBalanceForRentExemption(totalLen); const transaction = new Transaction().add( // Create account SystemProgram.createAccount({ fromPubkey: payer.publicKey, newAccountPubkey: mint, space: mintLen, lamports, programId: TOKEN_2022_PROGRAM_ID, }), // Initialize metadata pointer (points to the mint itself) createInitializeMetadataPointerInstruction( mint, payer.publicKey, // metadata pointer authority mint, // metadata account (self-referencing) TOKEN_2022_PROGRAM_ID ), // Initialize transfer fee extension createInitializeTransferFeeConfigInstruction( mint, payer.publicKey, // transfer fee config authority payer.publicKey, // withdraw withheld authority transferFeeBasisPoints, maxFee, TOKEN_2022_PROGRAM_ID ), // Initialize mint createInitializeMintInstruction( mint, decimals, payer.publicKey, null, // no freeze authority TOKEN_2022_PROGRAM_ID ), // Initialize on-chain metadata createInitializeInstruction({ programId: TOKEN_2022_PROGRAM_ID, mint: mint, metadata: mint, // metadata stored on the mint itself name: metadata.name, symbol: metadata.symbol, uri: metadata.uri, mintAuthority: payer.publicKey, updateAuthority: payer.publicKey, }) ); const sig = await sendAndConfirmTransaction(connection, transaction, [ payer, mintKeypair, ]); console.log("Token created:", mint.toBase58()); console.log("Transaction:", sig); } createTokenWithExtensions(); ``` ## Query Token-2022 accounts via RPC When querying Token-2022 accounts, use the Token-2022 program ID in the `programId` filter: ```bash cURL theme={"system"} curl -X POST YOUR_CHAINSTACK_ENDPOINT -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "id": 1, "method": "getTokenAccountsByOwner", "params": [ "OWNER_PUBKEY", { "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" }, { "encoding": "jsonParsed" } ] }' ``` The `jsonParsed` response for Token-2022 accounts includes an `extensions` array showing the active extensions and their configuration. This is the same [getTokenAccountsByOwner](/reference/solana-gettokenaccountsbyowner) method — the only difference is the program ID filter. ## Key considerations * **Plan extensions at creation** — most extensions cannot be added after the mint is initialized. * **Incompatible extensions** — some combinations don't work together (e.g., `NonTransferable` + `TransferFeeConfig`). * **Rent costs** — extensions increase account size and therefore rent. Calculate space with `getMintLen([...extensions])`. * **Wallet support** — major wallets (Phantom, Backpack, Solflare) support Token-2022 tokens. Some older wallets may not. * **DEX support** — Jupiter, Raydium, and Orca support Token-2022 tokens with most extensions. ## Additional resources * [Solana Token Extensions documentation](https://solana.com/docs/tokens/extensions) * [Token-2022 program source](https://github.com/solana-program/token-2022) * [Transferring SPL tokens on Solana](/docs/transferring-spl-tokens-on-solana-typescript) — for classic SPL Token transfers * [getTokenAccountsByOwner](/reference/solana-gettokenaccountsbyowner) — query token accounts with `jsonParsed` encoding # Solana tooling Source: https://docs.chainstack.com/docs/solana-tooling Modern Solana development tooling for Chainstack nodes — Solana CLI, @solana/kit, Anchor 1.0, LiteSVM, Surfpool, solana-py, and more. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Get started with a [reliable Solana RPC endpoint](https://chainstack.com/build-better-with-solana/), then plug it into the tools below. ## Solana CLI (Agave) The Solana Foundation one-liner installs the full toolkit — Agave CLI, Anchor, and Surfpool — in a single step. ```bash theme={"system"} curl --proto '=https' --tlsv1.2 -sSfL https://solana-install.solana.workers.dev | bash ``` Alternatively, install just the Agave CLI: ```bash theme={"system"} sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)" ``` > [!NOTE] > > The client is called **Agave** (forked from Solana Labs by Anza), but the binary is still named `solana` for ecosystem compatibility. `solana --version` prints `solana-cli 3.x.x (... client:Agave)`. Upgrades now go through `agave-install update` (the old `solana-install` binary was renamed). Point the CLI at your Chainstack node: ```bash theme={"system"} solana config set --url YOUR_CHAINSTACK_ENDPOINT ``` > [!NOTE] > > This is only needed on **Trader Nodes**, whose WebSocket endpoint is on a different host than the HTTPS one — the WSS host carries a `ws-` prefix (`nd-….p2pify.com` for HTTPS, `ws-nd-….p2pify.com` for WSS). Because `solana config set` derives the WebSocket URL by swapping only the protocol, set it explicitly on a [Trader Node](/docs/trader-node): > > ```bash theme={"system"} > solana config set --ws YOUR_CHAINSTACK_WSS_ENDPOINT > ``` > > On **Global Nodes**, HTTPS and WebSocket share the same host (for example, `solana-mainnet.core.chainstack.com`), so the derived WebSocket URL is correct and no `--ws` flag is needed. Example: ```bash theme={"system"} $ solana block-height 413787151 ``` Full command reference: [Solana CLI docs](https://docs.anza.xyz/cli). ## QUIC support `sendTransaction` on a Chainstack Solana node forwards over QUIC to the current and next leaders. No client configuration required. For high landing rate under contention, use [Solana Trader Nodes](/docs/solana-trader-nodes) — `sendTransaction` is routed through bloXroute's staked [Trader API](https://docs.bloxroute.com/solana/trader-api-v2), all other RPC calls stay on the standard endpoint. See also the [Stake-Weighted QoS guide](https://solana.com/developers/guides/advanced/stake-weighted-qos). ## JSON-RPC API Any HTTP client works. Use [curl](https://curl.haxx.se) or [Postman](https://www.getpostman.com) for ad-hoc calls; see the full [JSON-RPC reference](https://solana.com/docs/rpc) for all methods. ```bash theme={"system"} curl -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"getBalance","params":["23dQfKhhsZ9RA5AAn12KGk21MB784PmTB3gfKRwdBNHr"],"id":1}' \ YOUR_CHAINSTACK_ENDPOINT ``` where `YOUR_CHAINSTACK_ENDPOINT` is your node HTTPS endpoint. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## gRPC: Yellowstone Geyser Real-time streaming runs over gRPC through the [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin), a paid add-on you install on the node. Its endpoint appears under **Yellowstone gRPC** in the node's **Access and credentials** view, separate from the HTTPS and WSS endpoints. The endpoint listens on port `443` with TLS and authenticates with the token in the `x-token` metadata header rather than in the URL. The console shows it as `https://yellowstone-solana-mainnet.core.chainstack.com:443` on Global Nodes and as a bare hostname with no scheme or port on Dedicated Nodes. The endpoint form depends on the client: `@triton-one/yellowstone-grpc` (TypeScript) and `yellowstone-grpc-client` (Rust) need the scheme and port, as in `https://HOST:443`, while the Python `grpc` package, Go, and `grpcurl` take `HOST:443` with no scheme. `HOST` is the hostname alone — `yellowstone-solana-mainnet.core.chainstack.com` on Global Nodes. See [Connect to your endpoint](/docs/yellowstone-grpc-geyser-plugin#connect-to-your-endpoint) for per-client examples, and [gRPC access via x-token](/docs/authentication-methods-for-different-scenarios#grpc-access-via-x-token) for the authentication details. ## JavaScript and TypeScript: @solana/kit [`@solana/kit`](https://github.com/anza-xyz/kit) is the modern Solana JavaScript SDK — a tree-shakeable, functional API that replaces the class-based `@solana/web3.js` v1. It is the direct successor to what briefly shipped as `@solana/web3.js@2.x`. ```bash theme={"system"} npm install @solana/kit ``` Fetch an account balance against your Chainstack endpoint: ```typescript theme={"system"} import { address, createSolanaRpc, lamports } from "@solana/kit"; const rpc = createSolanaRpc("YOUR_CHAINSTACK_ENDPOINT"); const balance = await rpc .getBalance(address("23dQfKhhsZ9RA5AAn12KGk21MB784PmTB3gfKRwdBNHr")) .send(); console.log(balance.value); // lamports as bigint ``` ### Per-program instruction builders Kit exposes only the RPC layer. Typed instruction builders for each Solana program live in per-program packages under the [`solana-program`](https://github.com/solana-program) org — install them alongside Kit as you need them: ```bash theme={"system"} npm install @solana/kit @solana-program/system @solana-program/token ``` ### Migrating from @solana/web3.js v1 `@solana/web3.js` v1 is on a [maintenance branch](https://github.com/solana-foundation/solana-web3.js/tree/maintenance/v1.x), which still ships protocol compatibility updates such as [transaction v1](/docs/solana-transaction-v1) read support in 1.99.0. For existing v1 apps, the [`@solana/web3-compat`](https://solana.com/docs/frontend/web3-compat) bridge lets you keep your existing imports while progressively adopting Kit primitives: ```bash theme={"system"} npm install @solana/web3-compat @solana/kit ``` ## React dApps: create-solana-dapp The official scaffolding CLI bootstraps a full dApp (Next.js + Tailwind + wallet connection + Anchor example) in one command: ```bash theme={"system"} npm create solana-dapp@latest # or: pnpm create solana-dapp@latest # or: bun create solana-dapp@latest ``` Templates are maintained at [solana-foundation/templates](https://github.com/solana-foundation/templates). The default scaffold uses `@solana/kit` + [`@solana/react-hooks`](https://github.com/solana-foundation/framework-kit) (framework-kit) for wallet and RPC wiring. ## Programs: Anchor 1.0 [Anchor v1.0.0](https://www.anchor-lang.com) shipped on 2026-04-02. Install via the Anchor Version Manager: ```bash theme={"system"} cargo install --git https://github.com/solana-foundation/anchor avm --force avm install latest avm use latest ``` > [!WARNING] > > **Breaking rename in v1.0:** The TypeScript package was renamed from `@coral-xyz/anchor` to `@anchor-lang/core`. The Rust crate remains `anchor-lang`. Existing `@coral-xyz/anchor` imports continue to resolve to the 0.32.x line but will not receive v1.0 features. Anchor v1.0 also makes [Surfpool the default validator](https://github.com/solana-foundation/anchor/pull/4106) for `anchor test` and `anchor localnet`. See [Solana: Anchor development](/docs/solana-anchor-development) for an end-to-end walkthrough. ## Client codegen: Codama [Codama](https://github.com/codama-idl/codama) (the successor to Kinobi) generates typed JavaScript and Rust clients from a program IDL. The standard workflow is: ``` Anchor / Shank macros → IDL → Codama → TypeScript / Rust clients ``` ```bash theme={"system"} pnpm install codama codama init ``` This is how the `@solana-program/*` packages are produced. Any program you ship should emit an IDL and pipe it through Codama to generate first-class client types. ## Local testing Three tools cover the modern Solana testing pyramid: | Tier | Tool | Guide | | - | - | - | | Unit (in-process, multi-instruction) | **LiteSVM** | [Solana: Fast unit testing with LiteSVM](/docs/solana-litesvm-testing) | | Unit (single-instruction, CU benchmarks) | [Mollusk](https://github.com/anza-xyz/mollusk) | — | | Integration (full RPC, mainnet fork) | **Surfpool** | [Solana: Surfpool quick-start](/docs/solana-surfpool) | LiteSVM boots an in-process SVM for fast unit tests. Surfpool wraps LiteSVM with a full JSON-RPC server and a copy-on-read mainnet fork — Anchor 1.0 invokes it as the default validator. Mollusk, from Anza, focuses on single-instruction CU profiling. ## Python: Solana.py and solders [Solana.py](https://github.com/michaelhly/solana-py) is the main RPC client: ```bash theme={"system"} pip install solana ``` ```python theme={"system"} from solana.rpc.api import Client from solders.pubkey import Pubkey client = Client("YOUR_CHAINSTACK_ENDPOINT") print(client.get_balance(Pubkey.from_string("23dQfKhhsZ9RA5AAn12KGk21MB784PmTB3gfKRwdBNHr"))) ``` > [!NOTE] > > Solana.py now uses [solders](https://github.com/kevinheavey/solders) under the hood for its core types (`Keypair`, `Transaction`, `Pubkey`, etc.) — `pip install solana` installs solders transitively. Solders also ships the Python bindings for LiteSVM (`from solders.litesvm import LiteSVM`). ## AI-assisted development The Solana Foundation publishes an official [solana-dev-skill](https://github.com/solana-foundation/solana-dev-skill) — a Claude Code skill that teaches agents the current Solana stack (Kit, Anchor 1.0, Codama, LiteSVM/Surfpool, security patterns, version compatibility). Install it into Claude Code: ```bash theme={"system"} npx skills add https://github.com/solana-foundation/solana-dev-skill ``` Once installed, the skill activates automatically on Solana-related prompts. It covers program development (Anchor and Pinocchio), client work with Kit and framework-kit, testing with LiteSVM and Surfpool, IDL codegen with Codama, and common error troubleshooting. For the broader AI tooling ecosystem — Cursor rules, MCP servers, agent kits — see the Solana Foundation's [awesome-solana-ai](https://github.com/solana-foundation/awesome-solana-ai) catalog. ## Wallets ### Backpack [Backpack](https://backpack.app) is one of the few Solana wallets that lets you set a custom RPC endpoint — essential if you want your wallet to inherit the reliability of your own node rather than share a public endpoint during congestion. To point Backpack at your Chainstack Solana node: 1. Open Backpack. 2. Click the account icon > **Settings**. 3. Click **Solana** > **RPC connection** > **Custom**. 4. Enter your Chainstack Solana HTTPS endpoint and click **Update**. ## Reference repos These are the source repositories we worked against while writing this guide. They stay closer to reality than docs — check them first when something here looks off. * [anza-xyz/kit](https://github.com/anza-xyz/kit) — `@solana/kit` v6 source; canonical for RPC types and transaction-builder semantics * [solana-foundation/anchor](https://github.com/solana-foundation/anchor) — Anchor 1.0 source; framework behavior cross-checked against master * [solana-foundation/solana-dev-skill](https://github.com/solana-foundation/solana-dev-skill) — Solana Foundation's Claude Code skill; opinionated stack defaults cross-checked against * [solana-foundation/templates](https://github.com/solana-foundation/templates) — `create-solana-dapp` scaffolder templates * [codama-idl/codama](https://github.com/codama-idl/codama) — IDL → typed-client codegen referenced in the "generate clients from IDL" section # Solana Trader Nodes with Warp transactions Source: https://docs.chainstack.com/docs/solana-trader-nodes Use Solana Trader Nodes with Warp transactions for fast propagation through bloXroute's high-speed relay network with staked connection access. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). [Solana Trader Nodes](https://www.youtube.com/embed/BCGqfXjHDac) > [!NOTE] > > The Warp transactions feature is available starting from the [paid plans](https://chainstack.com/pricing/). > > Warp transactions (not requests, just the transactions) consumed are billed separately. For details, see [Pricing](https://chainstack.com/pricing/) with the [extra usage](/docs/manage-your-billing#manage-the-extra-usage-setting) setting enabled. See also the [Solana Warp transactions](https://chainstack.com/solana-trader-nodes/) page for a more general overview. Note that Warp transactions only work over the HTTP protocol. The WebSocket protocol is not supported. To enable Warp transactions, install the [Warp transactions](/docs/warp-transactions) add-on on your Trader or Global Node's **Add-ons** tab. ## Overview Chainstack Solana Trader Nodes with Warp transactions offer the following benefits: * All requests, except `sendTransaction`, go through the highly reliable & balanced Chainstack Solana node. The `sendTransaction` request is routed through the [bloXroute Trader API](https://bloxroute.com/products/solana-trader-api/). * No need to craft custom transaction instructions or change your regular trading setup if it's using standard Solana nodes. * All `sendTransaction` requests (i.e., all transactions) are sent to the current leader through a staked validator connection. * Transactions land competitively in the first blocks via SwQoS. See [Speed metrics](#speed-metrics) for current measured data. * Front-running protection is not enabled, as it would reduce transaction processing speed. * You can use [priority fees](/docs/solana-how-to-priority-fees-faster-transactions) to increase the chances of getting earlier placement within a block and in earlier blocks. * Warp transactions (not requests, just the transactions) consumed are billed separately. For details, see [Pricing](https://chainstack.com/pricing/) with the [extra usage](/docs/manage-your-billing#manage-the-extra-usage-setting) setting enabled. ## No need for custom setups If you have a trading project setup that's using regular Solana nodes, to transform your project to a high-speed one with all the benefits, all you need to do is swap the Solana node endpoint to a Chainstack node with Warp transactions enabled (available on both Trader Nodes and Global Nodes). Just doing that immediately routes your transactions through the staked SwQoS relay, with no custom transaction crafting on your side. All your project requests will be going through the Chainstack Solana nodes with the exception of the `sendTransaction` requests that will be routed through the bloXroute infrastructure. This means that you get the best of both worlds at zero additional effort from you: * If you were using bloXroute alone, you'd have to craft your custom non-standard transaction that'd have to include instructions for memos and tips to successfully propagate and land through bloXroute. * If you were using a regular Solana node setup, you'd get no benefits of bloXroute solution. ## SwQoS Transactions get the SwQoS landing rate as they are submitted through some of the biggest SwQoS Solana validators. You can read more on what SwQoS is: [A Guide to Stake-weighted Quality of Service on Solana](https://solana.com/developers/guides/advanced/stake-weighted-qos). And check the bloXroute SwQoS infrastructure: [What are the benefits of sending transactions through bloXroute Solana Trader API](https://docs.bloxroute.com/solana/trader-api-v2/wiki/faq#q-what-are-the-benefits-of-sending-transactions-through-bloxroute-solana-trader-api-1). ## Speed metrics The speed metrics are tricky, especially on an extremely fast chain like Solana. These are easily influenced by latency (e.g., how far your bot is from the actual node and routing gate location), priority fees, and so on. Note that the metrics below are done **without high priority fees** to provide you with a base expectation to work from. Landing rates depend heavily on your bot's proximity to the node and routing gate, the priority fees you set, and live network conditions, so a single fixed number isn't meaningful. For current, continuously-measured Solana transaction-landing rates — Warp compared against other providers, refreshed every 15 minutes — see the [Chainstack Compare dashboard](https://compare.chainstack.com/). ## No front running protection The transactions do not have the front running protection enabled. Enabling front running protection would limit the number validators that can get the transaction (to protect it from front running), which in turn will delay the transaction. Not to sacrifice the speed, the front running protection is disabled. That said, utilizing the bloXroute infrastructure for your transactions puts in you in the very top of users and greatly reduces the front running opportunities for your adversaries. ## Priority fees for extra speed Using priority fees increases your chances of being included in the first two blocks, including getting in the first block and getting early in order of transactions in the block. You can also check our guides for priority fees: > [card] [Solana: Priority fees for a Jupiter swap in Python](/docs/solana-priority-fees-for-a-jupiter-in-python) > [card] [Solana: Estimate Priority Fees with getRecentPrioritizationFees](/docs/solana-estimate-priority-fees-getrecentprioritizationfees) > [card] [Solana: How to use Priority Fees to unlock faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) # Solana: Transaction v1 explained Source: https://docs.chainstack.com/docs/solana-transaction-v1 What Solana's transaction v1 format changes — the 4096-byte limit, the transactionConfig field, no address lookup tables — and how to keep your RPC reads working. **TLDR:** * Transaction v1 activated on Solana Mainnet at the first slot of epoch 1035 (slot 447,120,000), on September 15, 2026. * The per-transaction size limit goes from 1,232 to 4,096 bytes, which is what makes ZK proofs, large multisigs, and wide instruction sets fit into a single transaction. * Reading is the breaking part: [getBlock](/reference/solana-getblock), [getTransaction](/reference/gettransaction), and [blockSubscribe](/reference/blocksubscribe-solana) need `maxSupportedTransactionVersion: 1` or they stop returning transaction data. * Compute unit limits, the loaded accounts data size limit, and the priority fee move out of Compute Budget Program instructions and into a `transactionConfig` field on the message. * The priority fee in `transactionConfig` is a total in lamports, not micro-lamports per compute unit. * A v1 transaction carries no address lookup tables. Every address travels inline, up to 64 of them. * Legacy and v0 transactions keep working exactly as before, and blocks now mix all three versions. ## Main article Transaction v1 is a new wire format for Solana transactions, specified in [SIMD-0385](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0385-transaction-v1.md). It is gated behind the `txv1aq4pp281K9um3tnPgkfX8UqtFT6wcVW3hNezGLL` feature account, which records slot 447,120,000 as its activation slot — the first slot of epoch 1035. Sending a v1 transaction is opt-in. Reading one is not: mainnet blocks now mix legacy, v0, and v1 transactions, so any client that reads blocks or transactions has to declare that it understands v1. > [!NOTE] > > To follow along with the examples, you need a Solana node endpoint. See [Solana tooling](/docs/solana-tooling) to get set up. ## What changed on the wire Three things are visible from the raw bytes: * Version byte — a v1 transaction starts with `0x81` at offset zero, so you can identify the format from a single byte. Legacy and v0 transactions start with their compact signature count instead. * Signatures — they move from the head of the transaction to the tail, which is what frees offset zero for the version byte. * Size — the maximum raw transaction size is 4,096 bytes, up from 1,232. The size limit is enforced per encoding when you submit through [sendTransaction](/reference/solana-sendtransaction): | Encoding | Maximum raw size | Maximum encoded size | | - | - | - | | `base64` | 4,096 bytes | 5,464 bytes | | `base58` | 1,232 bytes | 1,683 bytes | > [!WARNING] > > Base58 submission is still capped at the old 1,232-byte limit. A v1 transaction larger than that has to be submitted as `base64`, or `sendTransaction` rejects it with `-32602` and a `too large` message. ## Reading v1 transactions over RPC `maxSupportedTransactionVersion` declares the highest transaction version your client can parse. It is a ceiling, not a filter — declaring `1` returns legacy, v0, and v1 transactions alike, so raising it never costs you data. It has to be the JSON integer `1`. The string `"1"` fails request validation with `-32602`. | Method | With `maxSupportedTransactionVersion: 1` | At a lower value, or omitted | | - | - | - | | [getBlock](/reference/solana-getblock) | Returns the block | The whole call fails with `-32015` | | [getTransaction](/reference/gettransaction) | Returns the transaction | Fails with `-32015` on a v1 transaction | | [blockSubscribe](/reference/blocksubscribe-solana) | Streams blocks | Notifications keep arriving, each with `block: null` and an `err` of `{"UnsupportedTransactionVersion": }` | A `blockSubscribe` subscription at a lower version returns no JSON-RPC error. The subscription stays open and advances slot by slot, and its notifications carry `block: null` instead of block data. Calls that return no transaction bodies are unaffected. Setting `transactionDetails` to `signatures` or `none` on `getBlock` or `blockSubscribe` works at any version, and [getSignaturesForAddress](/reference/solana-getsignaturesforaddress) has no version parameter at all. ```shell getBlock theme={"system"} curl -X POST CHAINSTACK_SOLANA_URL \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [ SLOT_NUMBER, { "encoding": "json", "transactionDetails": "full", "rewards": false, "maxSupportedTransactionVersion": 1 } ] }' ``` ```shell blockSubscribe theme={"system"} wscat -c CHAINSTACK_SOLANA_WSS_URL > {"jsonrpc":"2.0","id":1,"method":"blockSubscribe","params":["all",{"commitment":"confirmed","encoding":"json","transactionDetails":"full","showRewards":false,"maxSupportedTransactionVersion":1}]} ``` ### Client library versions Reading v1 transactions through a JavaScript SDK needs `@solana/web3.js` 1.99.0 or later, or `@solana/kit` 7.1.0 or later. Earlier `@solana/web3.js` releases throw a validation error on any `getBlock`, `getTransaction`, or `getParsedTransaction` response that contains a v1 transaction, even with `maxSupportedTransactionVersion: 1`. Earlier `@solana/kit` releases return `version` as the bigint `1n` instead of the number `1`. ## transactionConfig replaces compute budget scanning In legacy and v0 transactions, the compute unit limit, the compute unit price, and the loaded accounts data size limit arrive as [Compute Budget Program](/docs/solana-compute-budget) instructions that you have to find and decode. A v1 message carries them in a `transactionConfig` field instead: ```json theme={"system"} "transactionConfig": { "computeUnitLimit": 117372, "heapSize": null, "loadedAccountsDataSizeLimit": 14097421, "priorityFee": 839349 } ``` * `computeUnitLimit` — the compute unit ceiling for the transaction. * `loadedAccountsDataSizeLimit` — the account data budget, in bytes. * `heapSize` — the requested heap size, or `null` for the default. * `priorityFee` — the priority fee, as a total in lamports. It can be `null`. Every v1 transaction carries the field, and no legacy or v0 transaction does. If your indexer derives compute limits or fees by scanning for Compute Budget instructions, it reads zeros on v1 traffic unless you read `transactionConfig` first. ### The priority fee is a total, not a rate In legacy and v0, `SetComputeUnitPrice` is a rate in micro-lamports per compute unit. In v1, `transactionConfig.priorityFee` is the whole priority fee in lamports. The fee a v1 transaction pays is exactly: ``` fee = 5000 * numRequiredSignatures + priorityFee ``` To compare a v0 rate against a v1 total, multiply the rate by the compute unit limit and divide by 1,000,000. ## No address lookup tables in v1 A v1 message has no `addressTableLookups` field. Every address the transaction touches travels inline as a full 32-byte public key, and a v1 transaction can carry up to 64 of them. The 4,096-byte size limit leaves room for the addresses that a v0 transaction moves into a lookup table. [Address lookup tables](/docs/solana-address-lookup-tables) remain fully supported on v0, which is not deprecated, so an account-heavy workload can stay on v0 with a table or move to v1 without one. ## What stays the same * Legacy and v0 transactions are unchanged and still produced. * Signatures, blockhashes, and the 150-block expiry window work as before. * The base fee is still 5,000 lamports per required signature. * Account and program semantics do not change. Only the envelope does. ## Readiness checklist **1. Raise maxSupportedTransactionVersion to 1** Set it on every `getBlock`, `getTransaction`, and `blockSubscribe` call, as the integer `1`. Without it, `getBlock` and `getTransaction` fail with `-32015` on v1 data, and `blockSubscribe` notifications carry `block: null`. **2. Upgrade the JavaScript SDK** Use `@solana/web3.js` 1.99.0 or later, or `@solana/kit` 7.1.0 or later. See [Client library versions](#client-library-versions). **3. Read limits from transactionConfig** Where you scan for Compute Budget instructions, check for `transactionConfig` first and fall back to the instruction scan for legacy and v0. **4. Normalize priority fees** Treat `transactionConfig.priorityFee` as lamports. Convert v0 rates with `price * computeUnitLimit / 1000000` before comparing. **5. Handle versions 0, 1, and legacy in one pipeline** A single block holds all three. Branch on the `version` field rather than assuming a shape. **6. Submit large transactions as base64** Anything over 1,232 raw bytes cannot go out as base58. **7. Drop lookup tables from v1 builds** A v1 transaction cannot reference one. Place the accounts inline, within the 64-address limit. ## Additional resources * [SIMD-0385: transaction V1 format](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0385-transaction-v1.md) * [Larger transaction sizes](https://solana.com/upgrades/larger-transaction-sizes) on solana.com * [Solana: understanding the compute budget](/docs/solana-compute-budget) * [Solana: address lookup tables](/docs/solana-address-lookup-tables) * [Solana: optimize your getBlock performance](/docs/solana-optimize-your-getblock-performance) # Solana: Associating transactions with block and block time using Yellowstone gRPC Source: https://docs.chainstack.com/docs/solana-transactions-block-time-yellowstone-grpc Join Solana transactions to their block and block time in a single Yellowstone gRPC stream, without a second stream or a per-block getBlockTime call. > **Part of the [How to land transactions on Solana](/docs/solana-how-to-land-transactions) guide.** **TLDR:** * A Yellowstone gRPC transaction update carries the `slot`, but not the block time. Block time arrives only on a block-metadata update. * Subscribe to `transactions`, `blocks_meta`, and `slots` on one stream, then join on `slot`: buffer transactions by slot, and stamp them with the block time when that slot's `block_meta` arrives. * This is far cheaper than the alternatives — a second full-block stream re-sends every transaction, and a per-block `getBlockTime` adds an RPC round trip to every slot. * Run at `confirmed` commitment, drop dead and skipped slots so the buffer can't leak, and answer the server's pings. ## The problem If you stream Solana transactions over [Yellowstone gRPC Geyser](/docs/yellowstone-grpc-geyser-plugin), each transaction update tells you which `slot` it landed in, but it does not include the block's timestamp. The transaction message simply has no block-time field — this is by design. That leaves a common question: how do you attach a block time to every transaction without either committing a second stream to block data or calling `getBlockTime` once per block? Both work, and both are expensive. There is a cheaper way that uses a single stream and no extra RPC calls. ## The approach A single `SubscribeRequest` can carry several filters at once, and the server multiplexes all of their updates onto one stream. You subscribe to three filters: * `transactions` — the transactions you care about. Each update carries the `slot` and the transaction, but no block time. * `blocks_meta` — one small message per block that *does* carry `block_time`, along with `blockhash`, `block_height`, `parent_slot`, and `executed_transaction_count`. It does not carry the transactions, so it stays tiny. * `slots` — the slot lifecycle, so you can tell when a slot is dead or skipped and will never produce a block. The join key is `slot`. A block-metadata update for slot N normally arrives *after* all of slot N's transactions, because the validator emits block metadata when the slot finishes replaying. That ordering is convenient, but it is best-effort rather than a contractual guarantee — so instead of assuming it, correlate by `slot` in both directions: 1. Keep two maps keyed by `slot`: transactions waiting for their block time, and block metadata waiting for late transactions. 2. When a transaction arrives, stamp and emit it if its `block_meta` is already cached; otherwise buffer it. 3. When the `block_meta` for slot N arrives, cache it and flush every transaction buffered for slot N. 4. Evict a slot from both maps once it goes `SLOT_DEAD` or the confirmed tip passes it (a skipped slot). > [!TIP] > > This pattern uses one filter each of three different types in a single subscribe request, well within the per-type filter limits. See [Yellowstone gRPC Geyser plugin filter limits](/docs/yellowstone-grpc-geyser-plugin#filter-limits) for the full limits. ## Why not the alternatives | Approach | Latency | Bandwidth and cost | When to use it | | - | - | - | - | | `transactions` + `blocks_meta` join (this guide) | Lowest — block time trails the transactions by less than a slot | Minimal — one small `block_meta` per block, plus your filtered transactions | Real-time pipelines that need a block time on each transaction | | Full `blocks` filter | Atomic, but waits for server-side block reconstruction | Heavy — full transaction bodies are re-sent, megabytes per busy block | You need whole blocks (transactions, accounts, and entries) in one message | | `getBlock` or `getBlockTime` per block | A synchronous round trip per slot | Extra RPC calls, credits, and rate-limit exposure | History older than the replay window, or one-off lookups | The full `blocks` filter is the only update type that bundles transactions and block time atomically, but it re-transports every transaction body, so it wastes bandwidth if you already receive transactions from the `transactions` filter. A per-block `getBlockTime` call defeats the purpose of a push stream. For historical data, `getBlock` returns the block time together with the transactions in a single call — see [Solana: optimize your getBlock performance](/docs/solana-optimize-your-getblock-performance). ## Prerequisites > [!TIP] > > Geyser is a paid add-on available from the Growth plan, and it runs on Solana mainnet only. See [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) to enable it on your node. Install the client libraries and generate the Python stubs from the Yellowstone proto definitions: ```bash theme={"system"} pip install grpcio grpcio-tools base58 requests # fetch the proto definitions curl -O https://raw.githubusercontent.com/rpcpool/yellowstone-grpc/master/yellowstone-grpc-proto/proto/geyser.proto curl -O https://raw.githubusercontent.com/rpcpool/yellowstone-grpc/master/yellowstone-grpc-proto/proto/solana-storage.proto # generate geyser_pb2.py and geyser_pb2_grpc.py python -m grpc_tools.protoc -I. \ --python_out=. --grpc_python_out=. \ geyser.proto solana-storage.proto ``` You authenticate with an `x-token` metadata header. Take the Geyser endpoint and token from your node's details on Chainstack. ## Build the subscription Set the commitment to `confirmed`, add the three filters, and exclude votes and failed transactions to cut the volume you process: ```python theme={"system"} import geyser_pb2 as pb def build_request(from_slot=None): req = pb.SubscribeRequest() req.commitment = pb.CONFIRMED # transactions: at least one constraint is required (see the warning below) txs = req.transactions["txs"] txs.vote = False txs.failed = False # to track specific programs, add: txs.account_include.append("") # block metadata: an empty filter means every block req.blocks_meta["meta"].SetInParent() # slot lifecycle: filter_by_commitment=False delivers every status, incl. dead req.slots["slots"].filter_by_commitment = False if from_slot is not None: req.from_slot = from_slot return req ``` Chainstack's Yellowstone rejects an unconstrained transactions filter. A filter with no `vote`, `failed`, `account_include`, `account_exclude`, `account_required`, or `signature` constraint returns: ```text theme={"system"} failed to create filter: Subscribe on full stream with `any` is not allowed, at least one filter required ``` Setting `vote = False` already satisfies this. ## Join transactions to block time Open the stream and run the buffer-and-flush loop. Buffer each transaction by `slot`, and flush when that slot's `block_meta` arrives: ```python theme={"system"} import queue import base58 import grpc import geyser_pb2 as pb import geyser_pb2_grpc as pb_grpc ENDPOINT = "yellowstone-solana-mainnet.core.chainstack.com:443" X_TOKEN = "YOUR_X_TOKEN" SKIP_MARGIN = 50 # slots def requests(initial, outbox): # keep the send half-stream open so we can answer the server's pings yield initial while True: item = outbox.get() if item is None: return yield item def emit(sig, slot, block_time, blockhash): print(f"{sig} slot={slot} block_time={block_time} block={blockhash[:8]}") channel = grpc.secure_channel( ENDPOINT, grpc.ssl_channel_credentials(), options=[ ("grpc.max_receive_message_length", 64 * 1024 * 1024), # lift the 4 MB default ("grpc.keepalive_time_ms", 30_000), ], ) stub = pb_grpc.GeyserStub(channel) outbox = queue.Queue() responses = stub.Subscribe( requests(build_request(), outbox), metadata=(("x-token", X_TOKEN),) ) buffer = {} # slot -> [signature, ...] awaiting block_meta meta_by_slot = {} # slot -> block_meta, cached so late transactions still get stamped hi_confirmed = 0 # highest confirmed slot seen for update in responses: kind = update.WhichOneof("update_oneof") if kind == "transaction": slot = update.transaction.slot sig = base58.b58encode(bytes(update.transaction.transaction.signature)).decode() meta = meta_by_slot.get(slot) if meta is not None: # block_meta already arrived: stamp now block_time = meta.block_time.timestamp if meta.HasField("block_time") else None emit(sig, slot, block_time, meta.blockhash) else: buffer.setdefault(slot, []).append(sig) elif kind == "block_meta": meta = update.block_meta meta_by_slot[meta.slot] = meta # cache for any later transactions block_time = meta.block_time.timestamp if meta.HasField("block_time") else None for sig in buffer.pop(meta.slot, []): emit(sig, meta.slot, block_time, meta.blockhash) elif kind == "slot": s = update.slot if s.status == pb.SLOT_DEAD: # no block will ever be produced buffer.pop(s.slot, None) meta_by_slot.pop(s.slot, None) elif s.status == pb.SLOT_CONFIRMED: hi_confirmed = max(hi_confirmed, s.slot) # evict slots the confirmed tip has passed: skipped txs and stale cached metas for slot in [k for k in buffer if k + SKIP_MARGIN < hi_confirmed]: buffer.pop(slot, None) for slot in [k for k in meta_by_slot if k + SKIP_MARGIN < hi_confirmed]: meta_by_slot.pop(slot, None) elif kind == "ping": outbox.put(pb.SubscribeRequest(ping=pb.SubscribeRequestPing(id=1))) ``` Every transaction is now emitted with the `block_time` and `blockhash` of the block it landed in, from a single stream and with no extra RPC calls. ## Getting it right ### Choose the right commitment Run the association at `confirmed`. At `processed`, both the transaction and its block metadata can be delivered on a fork that the cluster later abandons, so the pairing you compute may belong to a dropped block. At `confirmed`, the slot has been voted on by a supermajority of stake, which makes the pairing reliable. Use `processed` only when you need the lowest possible latency and can treat associations as provisional. ### Drop dead and skipped slots A buffered slot that never receives a `block_meta` would otherwise sit in memory forever. Two cases cause this: * Dead slots — the `slots` filter delivers a `SLOT_DEAD` status. Discard anything buffered or cached for that slot. * Skipped slots — no block is produced and no status pinpoints the gap, so evict the slot from both maps once the confirmed tip moves past it by a safe margin (`slot + SKIP_MARGIN < hi_confirmed` in the loop above). For why slots and blocks diverge, see [Solana: understanding the difference between blocks and slots](/docs/understanding-the-difference-between-blocks-and-slots-on-solana). ### Completeness and executed\_transaction\_count `block_meta.executed_transaction_count` is the validator's total count of executed transactions for the block, and it includes vote and failed transactions. On Solana, vote transactions alone are roughly three-quarters of block activity, so if you stream with `vote = False` (or filter any other way), your received count will be far lower than `executed_transaction_count`. Use the count as a completeness check only against an unfiltered set. In practice, the arrival of `block_meta` is itself the signal that the slot is sealed. > [!NOTE] > > The `entries_count` field is present on block metadata but is unreliable on standard validators, so do not use it as a completeness signal. ## Harden and recover ### Answer pings and lift the message cap The server sends periodic pings; reply with a `SubscribeRequestPing` on the same stream so that an idle load balancer does not drop the connection. That is why the example keeps the send half-stream open with a queue. Raise the gRPC receive limit above its 4 MB default to avoid truncation on busy slots. ### Recover after a disconnect with from\_slot Track the last slot you cleanly flushed. On reconnect, set `from_slot` to that slot to replay the gap, then de-duplicate the overlap by signature and slot. The replay buffer is a reconnection-recovery mechanism, not a historical backfill: on Global Nodes it holds approximately the last 100 slots (around a minute). Requesting an older slot returns: ```text theme={"system"} broadcast from is not available, last available: ``` For a deeper replay window, [Dedicated Nodes](/docs/dedicated-node) can be configured with a larger buffer. For data older than the buffer, fall back to `getBlock`, which returns the block time together with the transactions in one call. See [Replay depth with from\_slot](/docs/yellowstone-grpc-geyser-plugin#replay-depth-with-from_slot). ## Verify the join You can confirm the block time you stamped over gRPC is correct by cross-checking a captured slot against JSON-RPC. Call `getBlock` for the slot and compare: ```python theme={"system"} import requests def get_block(rpc_url, slot): payload = { "jsonrpc": "2.0", "id": 1, "method": "getBlock", "params": [slot, { "encoding": "json", "transactionDetails": "signatures", "rewards": False, "maxSupportedTransactionVersion": 1, "commitment": "confirmed", }], } return requests.post(rpc_url, json=payload, timeout=30).json()["result"] ``` For a correctly joined slot, `getBlock`'s `blockTime` equals the `block_time` you received on `block_meta`, its `blockhash` matches, and the length of its `signatures` list equals `executed_transaction_count` — which confirms the count includes votes and failed transactions. ## Caveats * `block_time` is an estimate. Solana derives it from a stake-weighted mean of validator-submitted timestamps, with capped drift, so treat it as accurate to the second rather than as an exact or security-critical clock. See [Solana: understanding block time](/docs/solana-understanding-block-time). * The [Alpenglow consensus upgrade](/docs/solana-alpenglow-consensus) is on the horizon. It collapses the commitment levels and changes how the cluster clock is derived — and it moves votes off-chain, which would reshape the `executed_transaction_count` math above. The stream mechanism in this guide — block time on `block_meta`, joined by `slot` — is expected to carry over, but revisit the block-time derivation when Alpenglow reaches mainnet. ## Full example > [accordion] Complete join.py > > ```python theme={"system"} > import queue > > import base58 > import grpc > > import geyser_pb2 as pb > import geyser_pb2_grpc as pb_grpc > > ENDPOINT = "yellowstone-solana-mainnet.core.chainstack.com:443" > X_TOKEN = "YOUR_X_TOKEN" > SKIP_MARGIN = 50 # slots > > > def build_request(from_slot=None): > req = pb.SubscribeRequest() > req.commitment = pb.CONFIRMED > txs = req.transactions["txs"] > txs.vote = False > txs.failed = False > req.blocks_meta["meta"].SetInParent() > req.slots["slots"].filter_by_commitment = False > if from_slot is not None: > req.from_slot = from_slot > return req > > > def requests(initial, outbox): > yield initial > while True: > item = outbox.get() > if item is None: > return > yield item > > > def emit(sig, slot, block_time, blockhash): > print(f"{sig} slot={slot} block_time={block_time} block={blockhash[:8]}") > > > def main(): > channel = grpc.secure_channel( > ENDPOINT, > grpc.ssl_channel_credentials(), > options=[ > ("grpc.max_receive_message_length", 64 * 1024 * 1024), > ("grpc.keepalive_time_ms", 30_000), > ], > ) > stub = pb_grpc.GeyserStub(channel) > outbox = queue.Queue() > responses = stub.Subscribe( > requests(build_request(), outbox), metadata=(("x-token", X_TOKEN),) > ) > > buffer = {} > meta_by_slot = {} > hi_confirmed = 0 > > for update in responses: > kind = update.WhichOneof("update_oneof") > > if kind == "transaction": > slot = update.transaction.slot > sig = base58.b58encode( > bytes(update.transaction.transaction.signature) > ).decode() > meta = meta_by_slot.get(slot) > if meta is not None: > block_time = ( > meta.block_time.timestamp if meta.HasField("block_time") else None > ) > emit(sig, slot, block_time, meta.blockhash) > else: > buffer.setdefault(slot, []).append(sig) > > elif kind == "block_meta": > meta = update.block_meta > meta_by_slot[meta.slot] = meta > block_time = ( > meta.block_time.timestamp if meta.HasField("block_time") else None > ) > for sig in buffer.pop(meta.slot, []): > emit(sig, meta.slot, block_time, meta.blockhash) > > elif kind == "slot": > s = update.slot > if s.status == pb.SLOT_DEAD: > buffer.pop(s.slot, None) > meta_by_slot.pop(s.slot, None) > elif s.status == pb.SLOT_CONFIRMED: > hi_confirmed = max(hi_confirmed, s.slot) > for slot in [k for k in buffer if k + SKIP_MARGIN < hi_confirmed]: > buffer.pop(slot, None) > for slot in [k for k in meta_by_slot if k + SKIP_MARGIN < hi_confirmed]: > meta_by_slot.pop(slot, None) > > elif kind == "ping": > outbox.put(pb.SubscribeRequest(ping=pb.SubscribeRequestPing(id=1))) > > > if __name__ == "__main__": > main() > ``` ## Related * [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) — enable the add-on, limits, and replay depth * [Solana: understanding block time](/docs/solana-understanding-block-time) — how block time is derived and its drift rules * [Solana: understanding the difference between blocks and slots](/docs/understanding-the-difference-between-blocks-and-slots-on-solana) * [Solana: optimize your getBlock performance](/docs/solana-optimize-your-getblock-performance) * [Solana: listening to programs using Geyser and Yellowstone gRPC (Node.js)](/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js) — the Node.js counterpart * [Solana Geyser Python tutorial](https://github.com/chainstacklabs/grpc-geyser-tutorial) and the [Yellowstone repository](https://github.com/rpcpool/yellowstone-grpc) # Solana: Create a token and vesting program Source: https://docs.chainstack.com/docs/solana-tutorial-creating-a-token-and-vesting-the-token-in-your-program Mint a custom SPL token on Solana devnet, deploy the Bonfida vesting program, and create a token vesting schedule with timed release using Chainstack. **TLDR:** * This tutorial walks you through creating a token on Solana’s devnet, deploying an audited vesting program (Bonfida), and vesting your token. * You’ll mint a custom SPL token, derive token accounts, deploy the vesting program with Rust, create a vesting schedule (amounts plus release times), and finally unlock tokens once they vest. * Tools used include the Solana CLI, SPL token CLI, and cargo commands for building and deploying the vesting program. * The exact procedure also applies to mainnet – just switch endpoints and make sure you have enough SOL for fees. ## Main article This tutorial guides you through creating a token, deploying a vesting program, and vesting the created token in the program instance on the Solana network. In short, you will do the following: * Create a token using the default [token program](https://spl.solana.com/token). * Deploy an audited [vesting program](https://github.com/Bonfida/token-vesting). * Vest the created token using the deployed vesting program. In the process, you will gain first-hand knowledge of accounts, the default and non-default programs on Solana and how to interact with them. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Solana RPC endpoint](https://chainstack.com/build-better-with-solana/). * [Rust](https://www.rust-lang.org/tools/install) to work with the vesting program. * [Solana tool suite](https://docs.solana.com/cli/install-solana-cli-tools) to create accounts and interact with the network. * [SPL token CLI](https://spl.solana.com/token#command-line-utility) to interact with the default [token program](https://spl.solana.com/token). ## Overview To get from zero to your own instance of vested tokens on the devnet, do the following: 1. With Chainstack, create a public chain project. 2. With Chainstack, join Solana devnet. 3. With Chainstack, access your Solana node credentials. 4. Set up the [Solana tool suite](https://docs.solana.com/cli/install-solana-cli-tools) to work through your Chainstack-managed devnet node. 5. With the Solana tool suite, generate two accounts—the account that will deploy the program and vest the tokens and the account that will receive the tokens. 6. Fund the generated accounts with devnet SOL through a [faucet ](https://solfaucet.com/). 7. Create a token. 8. Derive token accounts for the two created accounts. 9. Mint the token. 10. Deploy a vesting [program](https://github.com/Bonfida/token-vesting). 11. Create a vesting instance. 12. Check the vesting instance state. 13. Release the vested tokens. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the devnet See Join a [public network](/docs/manage-your-networks). ### Get your Solana node access and credentials See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Set up the Solana tools suite with the Chainstack node In your project directory, run: ```bash Shell theme={"system"} solana config set --url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your Solana node endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). You are now on the devnet, interacting with the network through your Chainstack node. ### Generate two accounts In your project directory, run: ```bash Shell theme={"system"} solana-keygen new --outfile ~/wallet/keypair.json ``` where `~/wallet/keypair.json` is any directory and name to store your wallet as a password-protected JSON file. Make sure you generate two accounts: one account will create and hold the tokens, the other account will be the destination for the vested tokens. ### Fund the accounts Both accounts need to pay fees to run transactions on the network. Fund each of the created accounts with the [faucet](https://solfaucet.com/). ### Create a token At this point, you have two funded accounts, but none of the accounts is set as the default signer for operations. Set one of the accounts as the default signer: ```bash Shell theme={"system"} solana config set --keypair ~/wallet/keypair1.json ``` where `~/wallet/keypair1.json` is the path to one of your wallets that you created earlier. Check that the default account is set: ```bash Shell theme={"system"} solana address ``` Create a token: ```bash Shell theme={"system"} spl-token create-token ``` The `create-token` command will call the default [token program](https://explorer.solana.com/address/TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA?cluster=devnet) on the devnet through your Chainstack node and return the address of your new token. The token has the default 9 decimals. ### Derive token accounts for the token Before you operate with the created token, and even before you can mint it, you must derive a token account for each of the accounts for the created token. In brief: * At this point, you have two accounts. You funded the accounts with the devnet SOL. Each of the accounts can only hold SOL. * To be able to hold the tokens, each of the accounts must derive the token account from the main account address and the token address. See also Solana documentation: [Associated Token Account Program](https://spl.solana.com/associated-token-account). Derive the token account for each of your accounts: ```bash Shell theme={"system"} spl-token create-account TOKEN_ADDRESS ``` where TOKEN\_ADDRESS is the address of the token you created. Remember to switch between your accounts with the `solana config set --keypair WALLET_PATH` command. ### Mint the token Make sure you are on the account that created the token by running `solana address`. Mint the token: ```bash Shell theme={"system"} spl-token mint TOKEN_ADDRESS 1000 ``` where TOKEN\_ADDRESS is the address of the token you created. This will mint 1,000 tokens to your account. Congratulations! At this point, you have the following: * A token created through the default token program on the Solana devnet * Two main accounts funded with SOL * Two token accounts mapped to the two main accounts and associated with your created token * One of the token accounts holds 1,000 9-decimal units of your created token and the other token account holds nil. What you are going to do next is: * Deploy a custom vesting program on the devnet. * Use the deployed program to create a vesting instance. * Via the vesting instance, you will transfer an amount of tokens from the token account that holds 1,000 tokens to the token account that holds no tokens. ### Deploy the vesting program You are going to deploy the audited [Bonfida vesting program](https://github.com/Bonfida/token-vesting)—the go-to vesting program for many projects on Solana. Note that you are going to deploy the program for tutorial purposes as, similar to the token program, you would normally need just the address of the deployed program to create your own instance of the program. In other words, you do not need to deploy a program every time you need a separate instance of it with your program parameters. In your project directory, clone the [token vesting repository](https://github.com/Bonfida/token-vesting). Change to the `program/` directory and run: ```bash Shell theme={"system"} cargo build-sbf ``` This will build the program bytecode that you can deploy to a Solana network. See also Solana documentation: [Program overview](https://docs.solana.com/developing/on-chain-programs/overview). Deploy the program: ```bash Shell theme={"system"} solana deploy PROGRAM_BYTECODE ``` This will run the deployment and return the address of the vesting program on the Solana devnet. ### Create a vesting instance You need to build the CLI tools to interact with the program and create a vesting instance. Change to the `cli/` directory and run: ```bash Shell theme={"system"} cargo build ``` Create a vesting instance: ```bash Shell theme={"system"} echo "RUST_BACKTRACE=1 ./target/debug/vesting-contract-cli \ --url YOUR_CHAINSTACK_ENDPOINT \ --program_id PROGRAM_ADDRESS \ create \ --mint_address TOKEN_ADDRESS \ --source_owner SOURCE_WALLET_PATH \ --source_token_address SOURCE_ACCOUNT_TOKEN_ADDRESS \ --destination_token_address DESTINATION_ACCOUNT_TOKEN_ADDRESS \ --amounts AMOUNT,! \ --release-times TIME,! \ --payer SOURCE_WALLET_PATH" \ --verbose | bash ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your Solana node endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). * PROGRAM\_ADDRESS — the address of the vesting program that you deployed on the devnet. * TOKEN\_ADDRESS — the address of the token that you created. * SOURCE\_WALLET\_PATH — path to the wallet file of the account that holds the tokens. * SOURCE\_ACCOUNT\_TOKEN\_ADDRESS — the derived token account that holds the tokens. * DESTINATION\_ACCOUNT\_TOKEN\_ADDRESS — the derived token account to which the tokens will be released. * AMOUNT — the amount of tokens to release in base form. For example, 1 unit of the default 9-decimal token is `1000000000`. * TIME — the timestamp in Unix format when the vested amount becomes releasable. Example: ```bash Shell theme={"system"} echo "RUST_BACKTRACE=1 ./target/debug/vesting-contract-cli \ --url YOUR_CHAINSTACK_ENDPOINT \ --program_id MwAy4o479mqPc3ViAz68N7rcjxcT5cdbn8i4b3xZ6WD \ create \ --mint_address C1Swjv4cQ2nEujJ4QeYDBsWSuSVPRXX4QkrtmF6UtdJK \ --source_owner ~/wallet/keypair1.json \ --source_token_address CYMrd3NHGF46gxftaZJRS1DaT1boALVFAmUhJYdS76vW \ --destination_token_address AcFTnytDRgQhdKK5QSxjAByjcwz5jaUnkTQG6FK5F7bP \ --amounts 1000000000,2000000000,! \ --release-times 1643587200,1646006400,! \ --payer ~/wallet/keypair1.json" \ --verbose | bash ``` The example creates a vesting instance for token `C1Swjv4cQ2nEujJ4QeYDBsWSuSVPRXX4QkrtmF6UtdJK` to become releasable: * 1 unit on January 31, 2022, 00:00:00 UTC. * 2 units on February 28, 2022, 00:00:00 UTC. Creating a vesting instance will return the seed. The seed can be shared publicly and is used to check the vesting state. ### Check the vesting instance You can check the state of the vesting instance to see the predefined release times and the amounts remaining to be released. Check the vesting state: ```bash Shell theme={"system"} echo "RUST_BACKTRACE=1 ./target/debug/vesting-contract-cli \ --url YOUR_CHAINSTACK_ENDPOINT \ --program_id PROGRAM_ADDRESS \ info \ --seed SEED " | bash ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your Solana node endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). * PROGRAM\_ADDRESS — the address of the vesting program that you deployed on the devnet. * SEED — the seed of the vesting instance that you received earlier. ### Release the vested tokens Anyone on the network willing to pay the release transaction fee can call the release of the tokens. Call the release of tokens: ```bash Shell theme={"system"} echo "RUST_BACKTRACE=1 ./target/debug/vesting-contract-cli \ --url YOUR_CHAINSTACK_ENDPOINT \ --program_id PROGRAM_ADDRESS \ unlock \ --seed SEED \ --payer WALLET_PATH" | bash ``` where * ENDPOINT — your Solana node endpoint. See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). * PROGRAM\_ADDRESS — the address of the vesting program that you deployed on the devnet. * WALLET\_PATH — path to any wallet that you have unlocked locally. Anyone can call the release of tokens. If the tokens have an unreleased amount at a timestamp in the past, they will be released to the destination account. Check the token balance of a derived token account: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenAccountBalance", "params": ["AcFTnytDRgQhdKK5QSxjAByjcwz5jaUnkTQG6FK5F7bP"]}' ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your Solana node endpoint * `AcFTnytDRgQhdKK5QSxjAByjcwz5jaUnkTQG6FK5F7bP` — the derived token account that can hold the tokens ## Conclusion This tutorial guided you through the basics of creating a token using the default token program, deploying your own program, and interacting with the program on the Solana network. You also learned what programs and derived token accounts are. This tutorial uses the devnet, however, the exact same instructions and sequence will work on the mainnet as well. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Understanding Block Time Source: https://docs.chainstack.com/docs/solana-understanding-block-time Understand Solana block time mechanics including slot duration, leader schedules, skip rates, and how they affect transaction confirmation latency. **TLDR:** * Solana’s slot-based timeline can be confusing—remember slots are scheduled intervals (\~400ms), while blocks only exist if produced in those intervals. * Timestamps on Solana are derived from validator-submitted UTC times, with built-in drift rules (25% fast, 150% slow) to maintain accurate on-chain time. * For critical time logic, rely on the on-chain `Clock` sysvar (`unix_timestamp`) rather than local or client clocks. * Don’t confuse slots (intervals) with block heights (successful block productions), and keep drift and missed slots in mind when designing dApps. ## Main article If you are coming from EVM, or pretty much any other blockchain, you have to realize that Solana's **block time** is actually an integral part of the consensus and thus differs notably from everything else. To follow along with the RPC examples in this guide, you'll want a [Chainstack Solana RPC](https://chainstack.com/build-better-with-solana/) endpoint. ## Key concepts * **Slots** — scheduled intervals (\~400ms each) designated for validators to attempt block production. Due to network conditions, not all slots produce blocks. Which creates even more confusion. See [Solana: Understanding the difference between blocks and slots](/docs/understanding-the-difference-between-blocks-and-slots-on-solana). * **Blocks** — actual data structures containing transactions, state changes, and related metadata. Blocks only exist if validators successfully produce them. * **Timestamps** — Solana derives block timestamps via consensus rather than including explicit timestamps in block headers. Validators periodically submit their UTC timestamps; the network calculates a stake-weighted mean to set each block’s timestamp. * **Drift and accuracy** — Solana caps timestamp drift (up to 25% forward and 150% backward — see further in the article for an example) from the expected progression to safeguard against manipulation or malicious validators. ## Validator impact Validators use Proof of History (PoH) internally for timing. They vote on blocks, including UTC timestamps. Missed or skipped slots result in gaps without blocks or timestamps, creating a divergence between the slot and block counts. ## Off-chain time retrieval via JSON-RPC ### Get block time Retrieve estimated Unix timestamp for a given slot: ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"getBlockTime","params":[SLOT_NUMBER]}' \ CHAINSTACK_SOLANA_URL ``` Replace `SLOT_NUMBER` with the desired slot and `CHAINSTACK_SOLANA_URL` with your Solana node endpoint. ### Get detailed block information Retrieve block details including block time and hash: ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"getBlock","params":[SLOT_NUMBER, {"encoding":"json","maxSupportedTransactionVersion":1}]}' \ CHAINSTACK_SOLANA_URL ``` Replace `SLOT_NUMBER` and `CHAINSTACK_SOLANA_URL` with your Solana node endpoint. ### Get latest slot Check the latest slot number: ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"getSlot"}' \ CHAINSTACK_SOLANA_URL ``` ### Get latest block height Retrieve the latest block height (actual blocks produced): ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"getBlockHeight"}' \ CHAINSTACK_SOLANA_URL ``` ## On-chain time: `Clock` sysvar Solana smart contracts access current time via the `Clock` sysvar: * `unix_timestamp` — consensus-agreed timestamp of the current slot, updated every slot. Contracts rely on this timestamp for logic like auctions or expirations. ## Time stamp drift example check A practical example using the real timestamp. Tmestamp drift is the allowable deviation in block timestamps from the expected time, ensuring that validators maintain a synchronized blockchain clock despite network variability. Solana enforces asymmetric drift limits: * [a 25% fast drift](https://github.com/anza-xyz/agave/blob/a99bbba064268ff89842c3ebd2b1b36d2e7c0384/runtime/src/stake_weighted_timestamp.rs#L17) — the time difference between slots can be up to 25% less than the expected 0.4 seconds, allowing a minimum of 0.3 seconds per slot instead of 0.4 seconds * [a 150% slow drift](https://github.com/anza-xyz/agave/blob/a99bbba064268ff89842c3ebd2b1b36d2e7c0384/runtime/src/stake_weighted_timestamp.rs#L18) — the time difference between slots can be up to 150% more than the expected 0.4 seconds, allowing a maximum of 1.0 second per slot instead of 0.4 seconds Let's take [Epoch 763](https://solscan.io/epoch/763) as an example to illustrate how timestamp drift can be calculated and enforced. Epoch details: * Epoch — [763]() * First slot — [329616000](https://solscan.io/block/329616000) * First block timestamp — `1743118653` (Unix timestamp for Thursday, March 27, 2025, 23:37:33 UTC) * Current slot — [329849011](https://solscan.io/block/329849011) * Actual timestamp for Slot [329849011](https://solscan.io/block/329849011) — `1743211690` (Saturday, March 29, 2025, 01:28:10 UTC) Let's figure out if this actual timestamp is okay under Solana’s rules. Step 1: how many slots passed? * Slots passed = Current slot - Starting slot * 329849011 - 329616000 = 233011 slots Step 2: how much time should have passed? * Each slot takes 0.4 seconds. * Total expected time = 233011 × 0.4 = 93204 seconds (rounding for simplicity). Step 3: What’s the expected timestamp? * Expected timestamp = Starting timestamp + Expected time * `1743118653 + 93204 = 1743211857` So, if everything was perfect, the timestamp at slot `329849011` should be around `1743211857` (remember we also rounded up). Step 4: what’s the allowed ange? The timestamp can drift: * 25% fast drift — it can be 25% earlier (faster). * 150% slow drift — it can be 150% later (slower). Let’s calculate these limits: * Expected time = 93204 seconds * Fast drift — 25% of 93204 = 0.25 × 93204 = 23301 seconds earlier * Slow drift — 150% of 93204 = 1.5 × 93204 = 139806 seconds later So: * Earliest allowed timestamp: 1743211857 - 23301 = 1743188556 * Latest allowed timestamp: 1743211857 + 139806 = 1743351664 The timestamp must be between **1743188556** and **1743351664**. Step 5: is the actual timestamp okay? * Actual timestamp = **1743211690** * Check: Is 1743211690 between 1743188556 and 1743351664? **Yes**. It’s fine. This mechanism allows for normal variations in clock synchronization while preventing extreme outliers from disrupting consensus timing. ## Best practices for time-sensitive logic * **Use on-chain time** — always utilize the on-chain timestamp (`Clock::get()?.unix_timestamp`) for critical on-chain logic. Do not depend on client-side or local times. * **Anticipate delays** — understand that Solana timestamps can slightly lag during network congestion or validator outages. Include buffer times in applications where precision is crucial. * **Blocks vs. slots** — remember that transaction expiration is based on blocks (typically 150 blocks), not slots. Missed slots don’t affect the block-based expiry window. ## Common pitfalls * **Misinterpreting slots and blocks** — Slots represent intervals; blocks represent successful data production. The difference can span millions. Again, see [Solana: Understanding the difference between blocks and slots](/docs/understanding-the-difference-between-blocks-and-slots-on-solana). * **Incorrect expiry calculations** — Ensure calculations use block counts (150 blocks) for transactions validity, not slot counts. Same reference here [Solana: Understanding the difference between blocks and slots](/docs/understanding-the-difference-between-blocks-and-slots-on-solana). * **RPC method confusion** — be aware that [`getBlock`](/reference/solana-getblock) takes a slot number but returns block-specific information, including block height. Again, [Solana: Understanding the difference between blocks and slots](/docs/understanding-the-difference-between-blocks-and-slots-on-solana). Understanding these nuances of Solana’s block and slot dynamics ensures effective development, precise transaction management, and robust dApp functionality. And don't forget to code your `getBlock` calls efficiently — see [Solana: optimize your getBlock performance](/docs/solana-optimize-your-getblock-performance). > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Solana: Upgrade a program from another program Source: https://docs.chainstack.com/docs/solana-upgrade-program-from-program Make a PDA the upgrade authority of a Solana program and upgrade it through a cross-program invocation. Covers which loader instructions a program can call, extending ProgramData for a larger build, closing PDA-owned buffers, and handing the authority back. **TLDR:** * A program can hold another program's upgrade authority through a PDA and upgrade it with a cross-program invocation (CPI) to the upgradeable loader. Multisig and governance programs use this to put upgrades behind their own approval rules. * Through CPI, a program can call only the loader's `Upgrade`, `SetAuthority`, `SetAuthorityChecked`, and `Close` instructions. Writing a buffer, deploying a new program, and extending a program stay top-level instructions that your wallet sends. * `ExtendProgram` needs no authority signature, so your wallet can grow a PDA-controlled program before a larger build goes in. Each extension adds at least 10,240 bytes. * A buffer handed to the PDA can only be closed by the PDA. Give the upgrading program a close instruction, or the buffer's rent stays locked. * Once SIMD-0500 activates, the buffer has to hold an sBPFv3 build, whoever signs the upgrade. See [Solana: Rebuild your programs for sBPFv3](/docs/solana-sbpfv3-program-migration). ## Main article Every program deployed with the upgradeable loader, `BPFLoaderUpgradeab1e11111111111111111111111`, has an upgrade authority: the one address that can replace its code. The authority does not have to be a wallet. Set it to a program derived address (PDA), and only the program that derives the PDA can sign for it, which it does with `invoke_signed`. That program decides when an upgrade goes through. This guide builds an upgrader program that holds a PDA authority, hands a second program to it, and upgrades that program to a larger build through CPI. The examples use Devnet through a Chainstack endpoint. > [!NOTE] > > To follow along, you need a Solana node endpoint and the Agave 4.3.0 toolchain. See [Solana tooling](/docs/solana-tooling) and [Install the toolchain](/docs/solana-sbpfv3-program-migration#install-the-toolchain). ## What a program can call on the loader The runtime filters CPIs to the upgradeable loader by instruction: | Loader instruction | Through CPI | Purpose | | - | - | - | | `InitializeBuffer` | Rejected | Create a buffer for a new build | | `Write` | Rejected | Upload bytes into a buffer | | `DeployWithMaxDataLen` | Rejected | Deploy a new program | | `Upgrade` | Allowed | Replace a program's code with a buffer's | | `SetAuthority` | Allowed | Change a program's or buffer's authority | | `Close` | Allowed | Close a buffer or program and reclaim its lamports | | `ExtendProgram` | Rejected | Grow a program's ProgramData account | | `SetAuthorityChecked` | Allowed | Change an authority, with the new authority signing | A rejected CPI fails the calling program before the loader runs: ```text theme={"system"} Program 6kCVB6cmBk9cXDfUgVvbcVj7q22EcPJGGqPFiN9Htzav failed: Program BPFLoaderUpgradeab1e11111111111111111111111 not supported by inner instructions ``` So the work splits in two. Your wallet uploads the new build to a buffer and extends the program when the build grows. The upgrader program signs the upgrade itself. ## Write the upgrader program The upgrader derives its authority PDA from the seed `upgrade_authority` and the admin's address. Each admin gets a separate PDA, and the upgrader signs with it only when that admin signs the transaction. It has three instructions: * `0` — upgrade the target program from a buffer that the PDA owns. * `1` — hand the upgrade authority back to the admin wallet. * `2` — close a buffer that the PDA owns and return its lamports to the admin. ```toml Cargo.toml theme={"system"} [package] name = "upgrader" version = "0.1.0" edition = "2024" [dependencies] solana-program = "5.1.0" solana-sdk-ids = "3.1.0" [lib] crate-type = ["cdylib", "lib"] ``` ```rust src/lib.rs theme={"system"} use solana_program::{ account_info::{AccountInfo, next_account_info}, entrypoint, entrypoint::ProgramResult, instruction::{AccountMeta, Instruction}, program::invoke_signed, program_error::ProgramError, pubkey::Pubkey, }; use solana_sdk_ids::bpf_loader_upgradeable::ID as LOADER_V3; entrypoint!(process_instruction); const AUTHORITY_SEED: &[u8] = b"upgrade_authority"; // Loader-v3 instruction tags, serialized as little-endian u32. const LOADER_UPGRADE: [u8; 4] = [3, 0, 0, 0]; const LOADER_CLOSE: [u8; 4] = [5, 0, 0, 0]; const LOADER_SET_AUTHORITY_CHECKED: [u8; 4] = [7, 0, 0, 0]; pub fn process_instruction( program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { let accounts_iter = &mut accounts.iter(); let admin = next_account_info(accounts_iter)?; let authority = next_account_info(accounts_iter)?; // The authority PDA is derived from the admin's address, so only that admin can sign with it. if !admin.is_signer { return Err(ProgramError::MissingRequiredSignature); } let (expected_authority, bump) = Pubkey::find_program_address(&[AUTHORITY_SEED, admin.key.as_ref()], program_id); if authority.key != &expected_authority { return Err(ProgramError::InvalidSeeds); } let signer_seeds: &[&[u8]] = &[AUTHORITY_SEED, admin.key.as_ref(), &[bump]]; match instruction_data.first() { // 0: upgrade the target program from a buffer that the PDA owns. Some(0) => { let program = next_account_info(accounts_iter)?; let programdata = next_account_info(accounts_iter)?; let buffer = next_account_info(accounts_iter)?; let rent = next_account_info(accounts_iter)?; let clock = next_account_info(accounts_iter)?; let upgrade = Instruction::new_with_bytes( LOADER_V3, &LOADER_UPGRADE, vec![ AccountMeta::new(*programdata.key, false), AccountMeta::new(*program.key, false), AccountMeta::new(*buffer.key, false), AccountMeta::new(*admin.key, false), // receives the buffer's lamports AccountMeta::new_readonly(*rent.key, false), AccountMeta::new_readonly(*clock.key, false), AccountMeta::new_readonly(*authority.key, true), ], ); invoke_signed( &upgrade, &[ programdata.clone(), program.clone(), buffer.clone(), admin.clone(), rent.clone(), clock.clone(), authority.clone(), ], &[signer_seeds], ) } // 1: hand the upgrade authority back to the admin wallet. Some(1) => { let programdata = next_account_info(accounts_iter)?; let hand_back = Instruction::new_with_bytes( LOADER_V3, &LOADER_SET_AUTHORITY_CHECKED, vec![ AccountMeta::new(*programdata.key, false), AccountMeta::new_readonly(*authority.key, true), AccountMeta::new_readonly(*admin.key, true), ], ); invoke_signed( &hand_back, &[programdata.clone(), authority.clone(), admin.clone()], &[signer_seeds], ) } // 2: close a buffer that the PDA owns and return its lamports to the admin. Some(2) => { let buffer = next_account_info(accounts_iter)?; let close = Instruction::new_with_bytes( LOADER_V3, &LOADER_CLOSE, vec![ AccountMeta::new(*buffer.key, false), AccountMeta::new(*admin.key, false), AccountMeta::new_readonly(*authority.key, true), ], ); invoke_signed( &close, &[buffer.clone(), admin.clone(), authority.clone()], &[signer_seeds], ) } _ => Err(ProgramError::InvalidInstructionData), } } ``` The program writes each loader instruction as its 4-byte tag, a little-endian `u32`. The instruction builders in `solana-loader-v3-interface` need that crate's `wincode` feature, which takes this program from 17 KB to about 50 KB and raises the rent to deploy it about threefold. Build it for sBPFv3: ```bash theme={"system"} cargo build-sbf --arch v3 ``` ## Write the program to upgrade The target is a minimal program in a crate named `greeter`. Version 1 logs a fixed message: ```toml Cargo.toml theme={"system"} [package] name = "greeter" version = "0.1.0" edition = "2024" [dependencies] solana-program = "5.1.0" [lib] crate-type = ["cdylib", "lib"] ``` ```rust src/lib.rs theme={"system"} use solana_program::{ account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey, }; entrypoint!(process_instruction); pub fn process_instruction( _program_id: &Pubkey, _accounts: &[AccountInfo], _instruction_data: &[u8], ) -> ProgramResult { msg!("Greeter version 1"); Ok(()) } ``` Version 2 formats its output, which pulls in more code and makes the build larger: ```rust src/lib.rs theme={"system"} use solana_program::{ account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey, }; entrypoint!(process_instruction); pub fn process_instruction( _program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { msg!( "Greeter version 2: {} accounts, {} bytes of data", accounts.len(), instruction_data.len() ); Ok(()) } ``` Build version 1 with `cargo build-sbf --arch v3` and keep the version 2 source for later. ## Deploy both programs Deploy the upgrader and version 1 of the target: ```bash theme={"system"} solana program deploy upgrader/target/deploy/upgrader.so --url YOUR_CHAINSTACK_ENDPOINT solana program deploy greeter/target/deploy/greeter.so --url YOUR_CHAINSTACK_ENDPOINT ``` ```text theme={"system"} Program Id: GpL5HDHkWq92CCN6mrYnNqVy2qRBmDF3hPCenqV3gHMX Program Id: EdnRnFuQkAKiK1Xb9enrx7pE1N4wN8mTP21A72EYfhX5 ``` ## Hand the upgrade authority to the PDA Derive the PDA for your wallet: ```bash theme={"system"} solana find-program-derived-address YOUR_UPGRADER_PROGRAM_ID \ string:upgrade_authority pubkey:YOUR_WALLET_ADDRESS ``` ```text theme={"system"} CgviYcgMm668ZeHFh3MEE5HXAmc3HEMU6kDJ4c1WNkMr ``` Make it the target's upgrade authority. By default, the CLI requires the new authority to sign, which a PDA cannot do, so skip that check: ```bash theme={"system"} solana program set-upgrade-authority YOUR_TARGET_PROGRAM_ID \ --new-upgrade-authority YOUR_AUTHORITY_PDA \ --skip-new-upgrade-authority-signer-check \ --url YOUR_CHAINSTACK_ENDPOINT ``` ```text theme={"system"} Account Type: Program Authority: CgviYcgMm668ZeHFh3MEE5HXAmc3HEMU6kDJ4c1WNkMr ``` From here on, your wallet can no longer upgrade the target directly. Only the upgrader can. ## Prepare the new build Build version 2 and upload it to a buffer, then give the buffer to the same PDA. The loader requires the buffer and the program to have the same authority: ```bash theme={"system"} cargo build-sbf --arch v3 solana program write-buffer greeter/target/deploy/greeter.so --url YOUR_CHAINSTACK_ENDPOINT solana program set-buffer-authority YOUR_BUFFER_ADDRESS \ --new-buffer-authority YOUR_AUTHORITY_PDA \ --url YOUR_CHAINSTACK_ENDPOINT ``` ```text theme={"system"} Buffer: 218ZhdtyMtVAom7P6ZA5uGjq3A2819r914bHmQj3aJ4W Account Type: Buffer Authority: CgviYcgMm668ZeHFh3MEE5HXAmc3HEMU6kDJ4c1WNkMr ``` ### Extend the program when the build grows A program's ProgramData account holds its current build and nothing more: after the version 1 deployment, `solana program show` reports a `Data Length` of 9,152 bytes, the size of that `.so` file. Version 2 is 14,592 bytes. An upgrade to a larger build fails with `ProgramData account not large enough` until you extend the program. `ExtendProgram` cannot go through CPI, and it does not need the upgrade authority to sign, so your wallet sends it. Every extension adds at least 10,240 bytes, the minimum set by [SIMD-0431](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0431-minimum-extend-program-size.md): ```bash theme={"system"} solana program extend YOUR_TARGET_PROGRAM_ID 10240 --url YOUR_CHAINSTACK_ENDPOINT ``` ```text theme={"system"} Extended Program Id EdnRnFuQkAKiK1Xb9enrx7pE1N4wN8mTP21A72EYfhX5 by 10240 bytes ``` The loader treats an extension like a deployment in that slot. An upgrade in the same slot fails with `Program was deployed in this block already`, so wait for the next slot before you upgrade. ## Upgrade through the upgrader This client sends the upgrader's instructions with [Solana Kit](https://github.com/anza-xyz/kit). It derives the authority PDA and the target's ProgramData address, then passes the accounts each instruction reads: ```bash theme={"system"} npm install @solana/kit ``` ```javascript upgrade.mjs theme={"system"} import { AccountRole, address, appendTransactionMessageInstruction, createKeyPairSignerFromBytes, createSolanaRpc, createSolanaRpcSubscriptions, createTransactionMessage, getAddressEncoder, getProgramDerivedAddress, getSignatureFromTransaction, pipe, sendAndConfirmTransactionFactory, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from "@solana/kit"; import { readFileSync } from "node:fs"; const rpc = createSolanaRpc("YOUR_CHAINSTACK_ENDPOINT"); const rpcSubscriptions = createSolanaRpcSubscriptions("YOUR_CHAINSTACK_WSS_ENDPOINT"); const UPGRADER = address("YOUR_UPGRADER_PROGRAM_ID"); const TARGET = address("YOUR_TARGET_PROGRAM_ID"); const LOADER_V3 = address("BPFLoaderUpgradeab1e11111111111111111111111"); const SYSVAR_RENT = address("SysvarRent111111111111111111111111111111111"); const SYSVAR_CLOCK = address("SysvarC1ock11111111111111111111111111111111"); const keypairBytes = new Uint8Array(JSON.parse(readFileSync("YOUR_KEYPAIR_PATH", "utf8"))); const admin = await createKeyPairSignerFromBytes(keypairBytes); const encodeAddress = getAddressEncoder().encode; const [authority] = await getProgramDerivedAddress({ programAddress: UPGRADER, seeds: ["upgrade_authority", encodeAddress(admin.address)], }); const [programData] = await getProgramDerivedAddress({ programAddress: LOADER_V3, seeds: [encodeAddress(TARGET)], }); function actionAccounts(action, buffer) { switch (action) { case "upgrade": return [ 0, [ { address: TARGET, role: AccountRole.WRITABLE }, { address: programData, role: AccountRole.WRITABLE }, { address: address(buffer), role: AccountRole.WRITABLE }, { address: SYSVAR_RENT, role: AccountRole.READONLY }, { address: SYSVAR_CLOCK, role: AccountRole.READONLY }, ], ]; case "hand-back": return [1, [{ address: programData, role: AccountRole.WRITABLE }]]; case "close-buffer": return [2, [{ address: address(buffer), role: AccountRole.WRITABLE }]]; default: throw new Error(`Unknown action: ${action}`); } } const [action, buffer] = process.argv.slice(2); const [discriminator, accounts] = actionAccounts(action, buffer); const instruction = { programAddress: UPGRADER, accounts: [ { address: admin.address, role: AccountRole.WRITABLE_SIGNER, signer: admin }, { address: authority, role: AccountRole.READONLY }, ...accounts, { address: LOADER_V3, role: AccountRole.READONLY }, ], data: new Uint8Array([discriminator]), }; const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const message = pipe( createTransactionMessage({ version: 0 }), (m) => setTransactionMessageFeePayerSigner(admin, m), (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), (m) => appendTransactionMessageInstruction(instruction, m), ); const transaction = await signTransactionMessageWithSigners(message); await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(transaction, { commitment: "confirmed", }); const signature = getSignatureFromTransaction(transaction); const result = await rpc .getTransaction(signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0 }) .send(); console.log("Signature:", signature); console.log(result.meta.logMessages.join("\n")); ``` Run the upgrade with the buffer address: ```bash theme={"system"} node upgrade.mjs upgrade 218ZhdtyMtVAom7P6ZA5uGjq3A2819r914bHmQj3aJ4W ``` ```text theme={"system"} Signature: 46YCoGfAJ6NchL2rwhQa5BwrWEp5nD8fiW3gDP1KRZYZP51F5fEZMBZWDoSBwxbDcRZjmENsFRRotgb5jQ1cFgnu Program GpL5HDHkWq92CCN6mrYnNqVy2qRBmDF3hPCenqV3gHMX invoke [1] Program BPFLoaderUpgradeab1e11111111111111111111111 invoke [2] Upgraded program EdnRnFuQkAKiK1Xb9enrx7pE1N4wN8mTP21A72EYfhX5 Program BPFLoaderUpgradeab1e11111111111111111111111 success Program GpL5HDHkWq92CCN6mrYnNqVy2qRBmDF3hPCenqV3gHMX consumed 10899 of 200000 compute units Program GpL5HDHkWq92CCN6mrYnNqVy2qRBmDF3hPCenqV3gHMX success ``` The upgrade keeps the program ID, closes the buffer, and returns the buffer's lamports to the admin. Calling the target with the `call.mjs` script from [Call the program](/docs/solana-sbpfv3-program-migration#call-the-program) now runs version 2: ```text theme={"system"} Signature: 3fgRquCqHE7vZqMJAbENzYc4zX516Pr3WPURHGJFZxuVQHvogqS9uBhX5ZwdBjR7a5WXMP7wHRxZ4XiQZmaPJabF Program EdnRnFuQkAKiK1Xb9enrx7pE1N4wN8mTP21A72EYfhX5 invoke [1] Program log: Greeter version 2: 0 accounts, 0 bytes of data Program EdnRnFuQkAKiK1Xb9enrx7pE1N4wN8mTP21A72EYfhX5 consumed 766 of 200000 compute units Program EdnRnFuQkAKiK1Xb9enrx7pE1N4wN8mTP21A72EYfhX5 success ``` ## Close buffers that the PDA owns A buffer that you give to the PDA and never use for an upgrade still holds rent. Your wallet cannot close it, and `solana program close` fails with `Buffer account authority Some() does not match Some()`. Close it through the upgrader instead: ```bash theme={"system"} node upgrade.mjs close-buffer YOUR_BUFFER_ADDRESS ``` ```text theme={"system"} Signature: 3Mh7uSAtTY82nuNTcsWmL5detEJv68S4azGiwkgK2gnq3dUhhYki9opx4zgip6U19cffMTqckztarC6PAWgMYgnU Program GpL5HDHkWq92CCN6mrYnNqVy2qRBmDF3hPCenqV3gHMX invoke [1] Program BPFLoaderUpgradeab1e11111111111111111111111 invoke [2] Closed Buffer 3eZWWrBStyRTLDyRkEe71Tr6XuwDnnz6ydCQd5Cca4W7 Program BPFLoaderUpgradeab1e11111111111111111111111 success Program GpL5HDHkWq92CCN6mrYnNqVy2qRBmDF3hPCenqV3gHMX consumed 9440 of 200000 compute units Program GpL5HDHkWq92CCN6mrYnNqVy2qRBmDF3hPCenqV3gHMX success ``` ## Hand the authority back To return the target to direct wallet control, call the hand-back instruction. It uses `SetAuthorityChecked`, which requires the new authority to sign. The admin signs the transaction, so the authority cannot go to an address nobody controls: ```bash theme={"system"} node upgrade.mjs hand-back ``` ```text theme={"system"} Signature: huL6KTJuuZkMebnKHS5mmaSHB6hHg7muojDbzcsVkgRUphMTBWk6RonU1ACvcj2UUbiFT8PZTUon4sMuS3q1akb Program GpL5HDHkWq92CCN6mrYnNqVy2qRBmDF3hPCenqV3gHMX invoke [1] Program BPFLoaderUpgradeab1e11111111111111111111111 invoke [2] New authority 8diQptTPMvxFooDa7TfWSkbjbxB5Yg4RR3EbBCa6yWou Program BPFLoaderUpgradeab1e11111111111111111111111 success Program GpL5HDHkWq92CCN6mrYnNqVy2qRBmDF3hPCenqV3gHMX consumed 9484 of 200000 compute units Program GpL5HDHkWq92CCN6mrYnNqVy2qRBmDF3hPCenqV3gHMX success ``` ## Errors | Message in the logs | Cause | | - | - | | `Program BPFLoaderUpgradeab1e11111111111111111111111 not supported by inner instructions` | The program called a loader instruction that CPI does not allow. Send it as a top-level instruction. | | `ProgramData account not large enough` | The new build is larger than the ProgramData account. Run `solana program extend` first. | | `ExtendProgram requires a minimum of 10240 additional bytes or to extend to maximum size` | The extension is smaller than 10,240 bytes. | | `Program was deployed in this block already` | The upgrade landed in the same slot as a deployment or an extension. Send it again in a later slot. | | `Buffer and upgrade authority don't match` | The buffer's authority is not the PDA. Run `solana program set-buffer-authority`. | | `Incorrect upgrade authority provided` | The PDA that signed is not the program's upgrade authority, for example because a different admin called the upgrader. | | `Detected sbpf_version required by the executable which are not enabled` | The buffer holds a v0, v1, or v2 build on a cluster where SIMD-0500 is active. Rebuild with `--arch v3`. | | `missing signature for supplied pubkey` | `solana program set-upgrade-authority` was run without `--skip-new-upgrade-authority-signer-check` for a PDA. | ## Before you use this in production * Gate the upgrade instruction. The example accepts any upgrade the admin signs, while a production upgrader checks multisig approvals, a timelock, or a governance vote before it calls `Upgrade`. * Check what the buffer holds. The upgrader installs whatever bytes the buffer contains, so compare the buffer's hash with your verified build, for example with `solana-verify get-buffer-hash`. * Protect the upgrader itself. Whoever controls the upgrader's own upgrade authority can replace its rules, so put that authority behind the same controls or make the upgrader immutable. ## Additional resources * [Solana: Rebuild your programs for sBPFv3](/docs/solana-sbpfv3-program-migration) * [Solana: Program derived addresses and cross-program invocations](/docs/solana-program-derived-addresses-and-cross-program-invocations) * [SIMD-0431: minimum extend program size](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0431-minimum-extend-program-size.md) * [Agave CPI loader filter](https://github.com/anza-xyz/agave/blob/v4.3.0/program-runtime/src/cpi.rs) — `check_authorized_program` in Agave 4.3.0 # Solana: Zero-knowledge proofs with Noir and Groth16 Source: https://docs.chainstack.com/docs/solana-zk-proofs Write zero-knowledge circuits in Noir, prove them with Groth16, and verify them on-chain on Solana using the alt_bn128 syscalls and Sunspot toolchain. **TLDR:** * Solana has the primitives to verify Groth16 zero-knowledge proofs on-chain — the `alt_bn128` syscalls handle BN254 pairing, and `sol_poseidon` handles the hashing. Verification typically costs 170K–500K compute units depending on circuit complexity and public-input count; budget explicitly. * Noir is the cleanest way to write a circuit in 2026, but its default backend (Barretenberg/UltraHonk) does not produce Groth16 proofs — you must use [Sunspot](https://github.com/reilabs/sunspot) to compile Noir ACIR to Gnark-Groth16 and generate a Solana verifier program. * The Poseidon in Noir's stdlib is *not* byte-compatible with the `sol_poseidon` syscall. Use the [noir-lang/poseidon](https://github.com/noir-lang/poseidon) library instead — it matches circomlib, which is what the syscall implements. * This guide walks two circuits end to end: a trivial `x != y` assertion to exercise the pipeline, and a sparse-Merkle-tree blacklist exclusion proof that demonstrates the real-world pattern of a Solana program doing a CPI to the verifier. * All code follows [solana-foundation/noir-examples](https://github.com/solana-foundation/noir-examples) (the canonical reference) and [reilabs/sunspot](https://github.com/reilabs/sunspot) as of April 2026. ## Why verify zero-knowledge proofs on Solana A zero-knowledge proof lets a prover convince a verifier that some computation was performed correctly, without revealing the private inputs. On Solana, this unlocks patterns that are structurally impossible with plain accounts: * **Private membership** — prove you hold a credential or belong to a set without revealing which one. * **Off-chain computation with on-chain settlement** — execute heavy logic off-chain, post a succinct proof, pay verification cost in constant time. * **Confidential state transitions** — prove a transition is valid without publishing the full pre-state. * **Signature aggregation and threshold schemes** — verify complex cryptography (BLS, ECDSA, Schnorr) inside a circuit and produce a single Groth16 proof. Solana runs Groth16 verification in roughly 170,000–500,000 compute units, depending on circuit complexity and the number of public inputs. Simple circuits fit within the default 200,000-CU per-instruction budget; heavier circuits need an explicit `SetComputeUnitLimit`. Either way, verification cost is bounded and predictable, which is what makes ZK on Solana usable as a primitive rather than a novelty. Production deployments already rely on this path. Light Protocol's [ZK Compression](https://www.zkcompression.com/) proves the validity of compressed-account state transitions using the same `alt_bn128` syscalls, on mainnet, every slot. > [!NOTE] > > This guide assumes basic Solana program development experience. If you are new to Solana programs, start with [Solana: Anchor development](/docs/solana-anchor-development) and [Solana: Program derived addresses and cross-program invocations](/docs/solana-program-derived-addresses-and-cross-program-invocations). ### What this guide is not Three distinct things share the "ZK on Solana" label. This guide covers one of them: | Topic | What it is | Covered here | | - | - | - | | **Custom Groth16 verification** | Deploy your own on-chain verifier program, write circuits in Noir, prove Groth16, verify via `alt_bn128` syscalls | ✅ This guide | | **ZK Compression** | Light Protocol's mainnet state-compression layer. Uses Groth16 under the hood but exposed as a managed service, not a circuit-writing flow | ❌ See [ZK Compression docs](https://www.zkcompression.com/) | | **Token-2022 Confidential Transfers / ZK ElGamal Proof Program** | Native Solana proof program for encrypted token balances, using ElGamal and Bulletproofs, not Groth16 | ❌ See [Agave ZK ElGamal docs](https://docs.anza.xyz/runtime/zk-docs/zk-elgamal-proof) | Conflating these is the single most common source of confusion in existing community discussion. If you are building a confidential token transfer, you want the third. If you are building compressed accounts, the second. If you are writing your own circuit with custom constraints and want to verify the resulting proof on-chain, read on. ## The stack There are five moving parts. Understanding what each one does and where the boundaries are will save you a lot of debugging. | Piece | Role | Where it runs | | - | - | - | | [Noir](https://noir-lang.org/) | Write the circuit in a Rust-like DSL. Compiles to ACIR (an arithmetic-circuit intermediate representation). | Off-chain, in your editor | | [Sunspot](https://github.com/reilabs/sunspot) | Translate ACIR to Gnark's constraint system (CCS), run Groth16 setup, generate proofs, and build a Solana verifier program with the verifying key baked in. | Off-chain, CLI tool | | [Gnark](https://github.com/Consensys/gnark) | The Groth16 proving backend Sunspot uses under the hood. | Off-chain, library | | `alt_bn128` syscalls | BN254 pairing check and elliptic-curve arithmetic. What the on-chain verifier calls to validate the proof. | On-chain, Agave runtime | | `sol_poseidon` syscall | Poseidon hash over BN254. Used when your Solana program needs to recompute a hash that was also computed inside the circuit. | On-chain, Agave runtime | The end-to-end flow is: ``` ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ Noir source │──▶│ nargo compile│──▶│ sunspot │──▶│ sunspot │ │ main.nr │ │ (ACIR bytes) │ │ compile │ │ setup │ └─────────────┘ └──────────────┘ │ (CCS) │ │ (pk, vk) │ └──────────────┘ └─────────────┘ │ ▼ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ Solana │◀──│ solana deploy│◀──│ sunspot │◀──│ sunspot │ │ verifier │ │ (.so) │ │ deploy │ │ prove │ │ program │ │ │ │ (verifier) │ │ (.proof) │ └─────────────┘ └──────────────┘ └──────────────┘ └─────────────┘ ``` ### Why Noir's default backend is not enough Noir compiles to ACIR. The default Noir backend — Barretenberg's `bb` CLI, distributed with `nargo` — produces **UltraHonk** proofs. UltraHonk and Groth16 both use the BN254 curve, but UltraHonk is a KZG-based proving system and its verifier needs polynomial-commitment operations that Solana does not expose as syscalls. The `alt_bn128` syscalls cover G1/G2 arithmetic and pairing — enough for Groth16 — but not the multi-point KZG openings UltraHonk relies on. That gap, not the curve, is why you must route Noir through Sunspot to target Solana today. Sunspot fills this gap. It reads the ACIR bytecode Noir emits, translates it to Gnark's constraint system, runs a Groth16 setup over BN254, and generates a Solana program that uses `alt_bn128` pairing to verify the resulting proofs. This is the only production path from Noir to Solana today. > [!WARNING] > > Sunspot has not been audited. From the project's README: **"Sunspot has not been audited yet and is provided as-is. We make no guarantees to its safety or reliability."** Additionally, `sunspot setup` performs a Groth16 trusted setup **with no MPC ceremony or toxic-waste mitigation** — whoever runs it holds the trapdoor. For production use, you must either run a proper multi-party ceremony (for example using Gnark's `phase2` tooling or a Perpetual Powers of Tau contribution) or accept that your verifier's soundness rests on the setup operator's honesty. ## Prerequisites * A Chainstack [Solana node endpoint](/docs/solana-development) — devnet is fine for this guide; mainnet if you want to test production paths. * [Solana CLI](https://solana.com/docs/intro/installation) 3.x or later. * [Rust](https://www.rust-lang.org/tools/install) 1.94.0 or later (via rustup, not Homebrew — the Homebrew Rust is often too old). * [Node.js](https://nodejs.org/) 18+ and [pnpm](https://pnpm.io/). * [Go](https://go.dev/dl/) 1.24+ (required by Sunspot). * [just](https://github.com/casey/just) (task runner used by the reference repo). ### Pin a coherent toolchain Noir, Sunspot, and the examples repo all move fast. Versions drift. The combination known to work end-to-end as of April 2026 is: * Noir (`nargo`) — `1.0.0-beta.18` (Sunspot's hard requirement). * Sunspot — latest `main` branch. * Solana CLI — 3.1.x or later. * Gnark — pulled automatically by Sunspot's Go module. ```bash theme={"system"} # Install noirup curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash source ~/.bashrc # or your shell's rc file # Pin the Noir version noirup -v 1.0.0-beta.18 # Verify nargo --version # Expected: nargo version = 1.0.0-beta.18 ``` ### Install Sunspot ```bash theme={"system"} git clone https://github.com/reilabs/sunspot.git ~/sunspot cd ~/sunspot/go go build -o sunspot . sudo mv sunspot /usr/local/bin/ # Sunspot needs to know where the verifier-bin crate lives export GNARK_VERIFIER_BIN="$HOME/sunspot/gnark-solana/crates/verifier-bin" echo 'export GNARK_VERIFIER_BIN="$HOME/sunspot/gnark-solana/crates/verifier-bin"' >> ~/.zshrc # Verify sunspot --help ``` ### Clone the examples repo All code in this guide lives in the Solana Foundation's [noir-examples](https://github.com/solana-foundation/noir-examples) repo. Clone it and follow along. ```bash theme={"system"} git clone https://github.com/solana-foundation/noir-examples.git cd noir-examples just install-all ``` ## Tutorial 1: the `one` circuit — prove `x != y` The simplest possible circuit: given a secret `x` and a public `y`, prove that `x != y`. The circuit leaks nothing about `x` beyond the fact that it is not equal to `y`. ### The circuit `circuits/one/src/main.nr`: ```rust theme={"system"} fn main(x: u64, y: pub u64) { assert(x != y); } #[test] fn test_main() { main(1, 2); } ``` Two things to notice: * `x: u64` — a private input. Its value is part of the witness but never revealed. * `y: pub u64` — a public input. Its value is passed to the verifier alongside the proof and is checked against what the circuit committed to. The `assert` becomes a constraint in the arithmetic circuit. When you generate a proof, that constraint must be satisfied by the witness, or proof generation fails. ### The input file `circuits/one/Prover.toml`: ```toml theme={"system"} x = "42" y = "100" ``` Nargo reads `Prover.toml` to populate the witness when you run `nargo execute`. ### Compile, execute, prove From `circuits/one/`: ```bash theme={"system"} # 1. Compile Noir → ACIR bytecode nargo compile # Produces target/one.json (the ACIR) # 2. Execute the circuit with the inputs from Prover.toml to produce a witness nargo execute # Produces target/one.gz (the witness) # 3. Translate ACIR to Gnark's constraint system sunspot compile target/one.json # Produces target/one.ccs # 4. Run the Groth16 trusted setup sunspot setup target/one.ccs # Produces target/one.pk (proving key) and target/one.vk (verifying key) # 5. Generate the Groth16 proof sunspot prove target/one.json target/one.gz target/one.ccs target/one.pk # Produces target/one.proof and target/one.pw (public witness) ``` The outputs you care about after this pipeline are three files: * `target/one.proof` — 388 bytes for a simple circuit, the Groth16 proof itself. * `target/one.pw` — the public witness (public inputs, serialized). * `target/one.vk` — the verifying key, which gets compiled into the on-chain verifier program. > [!TIP] > > Proof size in Groth16 is constant — it does not grow with circuit complexity. The same \~388 bytes proves a trivial assertion or a signature-verification circuit with thousands of constraints. This is the property that makes Groth16 practical on Solana despite the compute-unit budget. ### Build and deploy the verifier ```bash theme={"system"} # Build a Solana program with the verifying key baked in sunspot deploy target/one.vk # Output file names derive from the verifying-key basename: # target/one.so and target/one-keypair.json # Deploy to devnet solana program deploy target/one.so \ --program-id target/one-keypair.json \ --url devnet ``` The resulting program has one job: given instruction data of the form `proof || public_witness`, validate the Groth16 proof against the baked-in verifying key. It returns success or an error — no accounts are written, no state is modified. ### Verify on-chain from a TypeScript client The reference client in `circuits/one/client/` uses `@solana/kit`. The core of it is straightforward: ```typescript theme={"system"} import { createSolanaRpc, createSolanaRpcSubscriptions, createTransactionMessage, appendTransactionMessageInstructions, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, sendAndConfirmTransactionFactory, type Address, } from "@solana/kit"; import { getSetComputeUnitLimitInstruction } from "@solana-program/compute-budget"; import fs from "fs"; // Run from circuits/one/client/ — artifacts live one directory up. const proof = fs.readFileSync("../target/one.proof"); const publicWitness = fs.readFileSync("../target/one.pw"); const instructionData = Buffer.concat([proof, publicWitness]); const rpc = createSolanaRpc("YOUR_CHAINSTACK_RPC_ENDPOINT"); const rpcSubscriptions = createSolanaRpcSubscriptions("YOUR_CHAINSTACK_WSS_ENDPOINT"); const verifyInstruction = { programAddress: VERIFIER_PROGRAM_ID as Address, accounts: [], data: new Uint8Array(instructionData), }; // Sunspot-generated verifiers consume 170K–500K CU depending on circuit // complexity. The default per-instruction budget is 200K; set an explicit // limit to cover the verifier plus caller overhead. const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); const tx = appendTransactionMessageInstructions( [ getSetComputeUnitLimitInstruction({ units: 500_000 }), verifyInstruction, ], setTransactionMessageLifetimeUsingBlockhash( latestBlockhash, setTransactionMessageFeePayerSigner(wallet, createTransactionMessage({ version: 0 })), ), ); const signed = await signTransactionMessageWithSigners(tx); const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions }); await sendAndConfirm(signed, { commitment: "confirmed" }); ``` The entire on-chain interaction is a single instruction with an empty accounts list and a data blob that is just `proof || witness`. The verifier program handles deserialization and pairing check internally. Run the reference client: ```bash theme={"system"} just verify-one ``` You should see the transaction succeed. Try changing `x` and `y` in `Prover.toml` to the same value and re-run `nargo execute` — witness generation fails immediately because the `assert(x != y)` constraint is violated, so no `.gz` witness file is produced and `sunspot prove` cannot even run. ## Tutorial 2: sparse-Merkle-tree exclusion proof The `one` circuit exercises the pipeline but doesn't do anything useful. The `smt_exclusion` circuit is the real-world pattern: prove that your public key is **not** in a blacklist, without revealing anything else. The pattern generalizes. Any time you have an allow-list or deny-list and you want users to prove their status without leaking which user they are, this is the shape of the solution. ### How the exclusion proof works The set of "blacklisted" public keys is stored as a sparse Merkle tree of depth 254 (one bit per position in the BN254 scalar field). Each leaf position corresponds to a Poseidon hash of a public key. An empty leaf has value `0`; an occupied leaf has a non-zero value. The root of this tree is committed on-chain. To prove your public key is not blacklisted, you: 1. Hash your public key with Poseidon to get the leaf position. 2. Provide the sibling hashes along the Merkle path from that position to the root. 3. Prove inside the circuit that the leaf value at that position is `0` and that the path reconstructs to the committed root. If your key is in the tree, the leaf value will be non-zero and the assertion fails — proof generation is impossible. ### The Poseidon coordinate trap This is the part that will bite you if you are not careful. Noir's standard library ships a Poseidon implementation at `std::hash::poseidon`. Circomlib — the canonical circuit library in the ZK ecosystem — ships a different Poseidon with the same BN254 parameters but a different output convention. Specifically, the sponge permutation produces a 3-element state, and the two implementations disagree on which coordinate is the "hash output": * Noir's stdlib `poseidon` follows the Poseidon paper — output is `state[1]`. * Circomlib's `poseidon` — output is `state[0]`. The Solana `sol_poseidon` syscall is implemented by [light-poseidon](https://github.com/Lightprotocol/light-poseidon), which is circomlib-compatible. So `sol_poseidon` returns `state[0]`. **If you use Noir's stdlib Poseidon inside your circuit and then try to recompute the hash on-chain with `sol_poseidon`, the values will differ.** The proof verifies correctly (the circuit's internal hash checks out), but any on-chain logic that depends on matching the hash to something your program computes separately will fail. > [!WARNING] > > Do **not** use `std::hash::poseidon` from Noir's stdlib when you need the hash to match `sol_poseidon`. Use the [noir-lang/poseidon](https://github.com/noir-lang/poseidon) external library instead. It is maintained by the Noir team and is explicitly circomlib-compatible, matching what the syscall produces. In `Nargo.toml`: ```toml theme={"system"} [package] name = "smt_exclusion" type = "bin" authors = [""] [dependencies] poseidon = { tag = "v0.1.1", git = "https://github.com/noir-lang/poseidon" } ``` In the circuit: ```rust theme={"system"} use dep::poseidon::poseidon::bn254::hash_2 as poseidon_hash; fn poseidon_hash_2(left: Field, right: Field) -> Field { poseidon_hash([left, right]) } ``` The reference circuit includes a unit test that locks this invariant down: ```rust theme={"system"} #[test] fn test_poseidon_circom_compatible() { let h12 = poseidon_hash_2(1, 2); let expected: Field = 0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a; assert(h12 == expected, "Poseidon hash does not match circomlibjs"); } ``` The magic constant `0x115cc0f5...` is the circomlibjs hash of `[1, 2]`. If a future Noir or library update drifts the convention, this test catches it immediately. Every ZK circuit you write that needs on-chain hash matching should pin a value like this. ### The circuit Abbreviated, the `smt_exclusion` circuit (`circuits/smt_exclusion/src/main.nr`): ```rust theme={"system"} use dep::poseidon::poseidon::bn254::hash_2 as poseidon_hash; global TREE_DEPTH: u32 = 254; global EMPTY_LEAF: Field = 0; fn poseidon_hash_2(left: Field, right: Field) -> Field { poseidon_hash([left, right]) } // Split a 32-byte pubkey into two 16-byte halves and hash them. // This is the "pubkey to leaf index" function, matched by `sol_poseidon` // on-chain. pub fn pubkey_to_index(pubkey: [u8; 32]) -> Field { let low = bytes16_to_field(pubkey, 0); let high = bytes16_to_field(pubkey, 16); poseidon_hash_2(low, high) } fn compute_merkle_root( leaf: Field, path_bits: [u1; N], siblings: [Field; N], ) -> Field { let mut current = leaf; for i in 0..N { let (left, right) = if path_bits[i] == 0 { (current, siblings[i]) } else { (siblings[i], current) }; current = poseidon_hash_2(left, right); } current } fn main( smt_root: pub Field, // Public: committed on-chain pubkey_hash: pub Field, // Public: bound to the signer on-chain pubkey: [u8; 32], // Private: the actual pubkey siblings: [Field; TREE_DEPTH], // Private: the Merkle path leaf_value: Field, // Private: must be 0 for exclusion ) { // 1. Prove pubkey hashes to the claimed public hash. let computed_hash = pubkey_to_index(pubkey); assert(computed_hash == pubkey_hash, "Pubkey hash mismatch"); // 2. Path through the tree is determined by the pubkey hash bits. let path_bits: [u1; TREE_DEPTH] = computed_hash.to_le_bits(); // 3. Prove the leaf at this position is empty. assert(leaf_value == EMPTY_LEAF, "Exclusion failed: leaf is not empty"); // 4. Prove the path reconstructs to the committed root. let computed_root = compute_merkle_root(leaf_value, path_bits, siblings); assert(computed_root == smt_root, "Root mismatch: invalid merkle proof"); } ``` Two public inputs (`smt_root`, `pubkey_hash`) and three private inputs (`pubkey`, `siblings`, `leaf_value`). The proof attests that the prover knows a `pubkey` whose Poseidon hash matches the public `pubkey_hash`, and that this key's position in the tree rooted at the public `smt_root` holds an empty leaf. Run the tests and generate a proof: ```bash theme={"system"} cd circuits/smt_exclusion nargo test just setup-smt ``` ### The on-chain verifier — same as before Running `sunspot deploy target/smt_exclusion.vk` produces a Solana program that does one thing: verify a Groth16 proof against the baked-in verifying key. Deploy it the same way as before. This is what you would call directly from a client if the only thing your application needs is "prove the user is not blacklisted". But in most real systems the ZK check is one gate among several — for example, the user is not blacklisted **and** they are transferring no more than `N` lamports. That is where CPI comes in. ### A calling program that does CPI to the verifier The richer pattern in `circuits/smt_exclusion/on_chain_program/` is a regular Solana program that: 1. Holds an admin-settable SMT root in a PDA state account. 2. Accepts a `TRANSFER_SOL` instruction with instruction data `[amount || proof || public_witness]`. 3. Verifies the public witness matches the stored SMT root and the signer's Poseidon hash. 4. Does a CPI to the ZK verifier program. 5. If verification succeeds, transfers the lamports. The relevant slice of the program (`on_chain_program/src/lib.rs`): ```rust theme={"system"} use solana_poseidon::{hashv, Endianness, Parameters}; use solana_program::{ instruction::Instruction, program::invoke, pubkey::Pubkey, }; pub const ZK_VERIFIER_PROGRAM_ID: Pubkey = solana_program::pubkey!("548u4SFWZMaRWZQqdyAgm66z7VRYtNHHF2sr7JTBXbwN"); fn process_transfer_sol(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult { // Instruction data layout: 8 (amount) + 388 (proof) + 76 (witness) if data.len() != 8 + 388 + 76 { return Err(ExclusionError::InvalidDataLength.into()); } let amount = u64::from_le_bytes(data[0..8].try_into().unwrap()); let proof_data = &data[8..8 + 388]; let witness_data = &data[8 + 388..]; // 1. Check the witness commits to the correct SMT root. let witness_smt_root = &witness_data[12..44]; if witness_smt_root != stored_smt_root { return Err(ExclusionError::SmtRootMismatch.into()); } // 2. Recompute the signer's pubkey_hash with sol_poseidon and check // the witness commits to the same value. SIMD-0359 requires every // input slice to be exactly 32 bytes; zero-pad the 16-byte halves. let pubkey_bytes = sender.key.as_ref(); let mut low_padded = [0u8; 32]; low_padded[..16].copy_from_slice(&pubkey_bytes[0..16]); let mut high_padded = [0u8; 32]; high_padded[..16].copy_from_slice(&pubkey_bytes[16..32]); let computed_hash = hashv( Parameters::Bn254X5, Endianness::LittleEndian, &[&low_padded, &high_padded], ) .map_err(|_| ExclusionError::PoseidonHashFailed)?; // sol_poseidon with LittleEndian outputs little-endian bytes; // gnark emits big-endian in the witness. Reverse before comparing. let computed_bytes = computed_hash.to_bytes(); let mut computed_be = [0u8; 32]; for i in 0..32 { computed_be[i] = computed_bytes[31 - i]; } let witness_pubkey_hash = &witness_data[44..76]; if witness_pubkey_hash != computed_be { return Err(ExclusionError::PubkeyHashMismatch.into()); } // 3. CPI to the ZK verifier program. Accounts list is empty — // the verifier has no state, only the baked-in verifying key. let mut verifier_data = Vec::with_capacity(388 + 76); verifier_data.extend_from_slice(proof_data); verifier_data.extend_from_slice(witness_data); let verify_ix = Instruction { program_id: ZK_VERIFIER_PROGRAM_ID, accounts: vec![], data: verifier_data, }; invoke(&verify_ix, &[])?; // 4. Verification succeeded — run the rest of the logic. invoke( &system_instruction::transfer(sender.key, recipient.key, amount), &[sender.clone(), recipient.clone(), system_program.clone()], )?; Ok(()) } ``` This is the actual structure of a production ZK-gated operation. Four things to lock in from this code: * **The verifier is stateless** — the CPI passes an empty accounts list. The verifier program reads only its baked-in verifying key and the instruction data. * **Bind the proof to on-chain state.** Your program must recompute the expected public inputs (SMT root, signer hash, whatever else is public) and check they match what the witness claims. Otherwise a user could submit a valid proof for a different tree or different signer. * **Endianness conversion is required.** The `sol_poseidon` syscall with `Endianness::LittleEndian` produces little-endian bytes; Gnark emits big-endian in the public witness. Reverse the computed bytes before comparing, or you will get spurious mismatches. * **Every `sol_poseidon` input must be exactly 32 bytes.** [SIMD-0359](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0359-poseidon-enforce-input-length.md) is active on devnet (since epoch 1014) and testnet (since epoch 897). Before activation, the runtime silently zero-extended short inputs; after activation, anything other than exactly 32 bytes returns `PoseidonSyscallError::Unexpected`. The code above zero-pads 16-byte pubkey halves to 32 bytes; match the padding in both the client-side Poseidon computation and anywhere else you call the syscall. * **Compute-unit budget.** Groth16 verification alone fits in \~200K CU, but the calling program also does hashing, state reads, and the transfer. Measure with `compute-budget` and set the limit explicitly from the client. ### The client flow The TypeScript client (`circuits/smt_exclusion/client/`) builds the witness by (1) hashing the sender's pubkey with a circomlib-compatible Poseidon in JavaScript, (2) building the Merkle path against the current SMT state, (3) writing `Prover.toml`, (4) shelling out to `nargo execute` and `sunspot prove`, and (5) submitting the transaction. ```typescript theme={"system"} import { generateProof } from "./proof.helper"; const proofResult = generateProof(circuitConfig, { smt_root: currentRoot, pubkey_hash: pubkeyHash, pubkey: pubkeyBytes, siblings: merklePath, leaf_value: "0", // proving non-inclusion }); // Instruction layout matches process_instruction's dispatch: // data[0] = opcode (2 = TRANSFER_SOL) // data[1..9] = amount (u64 LE) // data[9..397] = Groth16 proof (388 bytes) // data[397..] = public witness (76 bytes) const TRANSFER_SOL_OPCODE = 2; const transferData = Buffer.concat([ Buffer.from([TRANSFER_SOL_OPCODE]), toLeBytes(amountLamports, 8), proofResult.proof, proofResult.publicWitness, ]); ``` Run the full end-to-end flow: ```bash theme={"system"} just test-transfer-smt ``` The script initializes the state account, sets an SMT root, generates a proof that the signer is not in the tree, submits the transfer-with-proof instruction, and confirms the recipient received the SOL. ## Using your Chainstack endpoint Point the reference client at your own Chainstack node rather than the default public RPC. In any of the client scripts, replace the RPC URL: ```typescript theme={"system"} const RPC_URL = process.env.RPC_URL || "YOUR_CHAINSTACK_RPC_ENDPOINT"; const WS_URL = process.env.WS_URL || "YOUR_CHAINSTACK_WSS_ENDPOINT"; ``` Then: ```bash theme={"system"} export RPC_URL="https://solana-devnet.core.chainstack.com/YOUR_KEY" export WS_URL="wss://solana-devnet.core.chainstack.com/YOUR_KEY" just verify-one just test-transfer-smt ``` For deploys: ```bash theme={"system"} solana config set --url "$RPC_URL" solana program deploy target/verifier.so ``` See [Solana: Development](/docs/solana-development) for endpoint setup. ## Production checklist Before shipping a ZK-gated Solana program to mainnet, work through this list. **1. Run a proper trusted-setup ceremony** `sunspot setup` is a 1-of-1 ceremony with no toxic-waste mitigation. For any real deployment, replace the verifying key with one produced by a multi-party computation. Gnark supports phase2 contributions; pull `.pk`/`.vk` from a ceremony where at least one honest participant destroyed their randomness. Re-run `sunspot deploy` with the post-ceremony `.vk`. **2. Lock the verifier program** Once deployed, make the verifier program immutable (`solana program set-upgrade-authority --final`). An upgradeable verifier means whoever holds the upgrade authority can swap in a different verifying key and accept forged proofs. **3. Treat feature-gate activations as release blockers** Solana's ZK-adjacent syscalls and programs have been disabled and re-enabled before — the ZK ElGamal Proof Program was disabled at epoch 805 after a Fiat-Shamir transcript bug, and SIMD-0359 silently changed the input-length contract for `sol_poseidon`. Subscribe to the [Agave feature gate tracker](https://github.com/anza-xyz/agave/wiki/Feature-Gate-Tracker-Schedule) and re-test your deployed programs on devnet before any feature gate activates on mainnet. A working deploy today can fail silently tomorrow if a feature gate tightens input validation. **4. Bind public inputs to on-chain state** The proof only attests that the circuit's constraints are satisfied given the public inputs. Your calling program must verify those public inputs match real state — the stored SMT root, the signer's pubkey, the current epoch, etc. Without this, a valid proof for any tree or any signer is accepted. **5. Set and measure compute-unit budgets explicitly** The verifier alone consumes \~200K CU. Any calling program adds more. Never rely on the default; set `SetComputeUnitLimit` from the client and confirm the transaction actually succeeds under that budget on mainnet, not just localnet. **6. Pin dependency versions in both Rust and Noir** Noir, Sunspot, and the Poseidon library all iterate. A minor-version update can change constraint counts, round constants, or byte layouts. Commit `Nargo.toml`, `Cargo.toml`, and `Cargo.lock`. Re-run the setup ceremony if any of these change. **7. Unit-test the hash invariants** Include a `test_poseidon_circom_compatible`-style test that hashes a known input and asserts equality with a pre-computed expected value. This catches silent drift in the Poseidon library. **8. Audit the circuit** Groth16 verifies that the circuit's constraints are satisfied — it does not verify that your circuit says what you meant it to say. A bug in your circuit logic is silent and total. Circuit audits are a distinct specialty; firms like Reilabs, ZK Security, and zkSecurity do them. ## Common pitfalls ### Proof generation fails with "constraint not satisfied" The witness does not satisfy the circuit's constraints. Most often this means your off-chain computation produced inputs that disagree with the circuit's `assert`s — for example, the Merkle path you built does not reconstruct to the root you claimed, or the leaf value at the claimed position is not what you said. Turn on Sunspot's verbose output and the specific failing constraint will be reported. ### Proof verifies in `sunspot verify` but fails on-chain Two likely causes: * **Endianness.** Gnark emits public witnesses in big-endian; the `sol_poseidon` syscall with `Endianness::LittleEndian` returns little-endian bytes. If your calling program compares them without reversing one side, they will never match. * **Public-input binding mismatch.** The circuit hashed one set of bytes; your program hashed a different set. Check that you split the pubkey the same way (16 low + 16 high bytes) on both sides and that the byte order of each half matches. ### Hash from the circuit does not match `sol_poseidon` output You used Noir's stdlib `std::hash::poseidon`. Switch to the [noir-lang/poseidon](https://github.com/noir-lang/poseidon) library (`dep::poseidon::poseidon::bn254::hash_N`). See the coordinate-trap section above. ### Verifier CU usage exceeds 200K The baseline is under 200K for a simple verifier, but large public-witness sizes or additional hashing in the caller push it over. Set the limit explicitly from the client with `SetComputeUnitLimit`. If you need to stay under 200K strictly, keep the number of public inputs small and avoid per-byte hashing in the caller. ### Program fails with stack overflow during verification Groth16 verifying keys and proof structs, if instantiated on the stack inside a Solana program, can exceed the 4 KB SBF stack limit. The Sunspot-generated verifier avoids this internally, but if you are writing a calling program that also does crypto work (hashing, building witness buffers), keep large byte arrays on the heap via `Vec` or in account data — never stack-allocate a `[u8; 4096]` or larger. Anchor 0.31 and later emit stack-size warnings for this pattern even when the runtime usage is fine. ### Transaction exceeds the 1232-byte size limit A Sunspot-generated Groth16 proof is 324–388 bytes depending on the circuit; the underlying compressed BN254 proof is 256 bytes, but Sunspot's wire format adds framing. Public witnesses are small (44 bytes for `one`, 76 bytes for `smt_exclusion`), but grow with the number of public inputs. If your calling program passes proof + witness as CPI data on top of an already-large instruction, you will hit Solana's \~1232-byte transaction limit on a legacy or v0 transaction. The direct fix is [transaction v1](/docs/solana-transaction-v1), which raises the limit to 4096 bytes — proofs are the workload it was sized for. On v0, the options are to use address lookup tables to shave signer/account overhead, split large public-witness bytes into a data account written in a separate transaction, or reduce the circuit's public inputs by hashing them into a single commitment. ### `convert_endianness` is not available on-chain `solana_program::alt_bn128::compression::convert_endianness` is gated `#[cfg(not(target_os = "solana"))]` — it exists only off-chain. If you are converting Gnark big-endian output to alt\_bn128 little-endian expectations, do the conversion in the client before submitting. If you need equivalent logic on-chain, copy the byte-reversal into your program (it is a simple per-component reverse, not a syscall). ### `sunspot deploy` produces an empty or broken `.so` `GNARK_VERIFIER_BIN` is not set, or points at the wrong directory. It must point to the `gnark-solana/crates/verifier-bin` directory inside your Sunspot clone. ### Calling program's `sol_poseidon` returns `Unexpected` [SIMD-0359](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0359-poseidon-enforce-input-length.md) (active on devnet epoch 1014+, testnet epoch 897+, at time of writing pending on mainnet-beta) requires every input slice to the `sol_poseidon` syscall to be **exactly 32 bytes** — the byte length of the BN254 scalar field modulus. Before activation, shorter inputs were silently zero-extended. After activation, they return `PoseidonSyscallError::Unexpected`. If your program hashes a pubkey by splitting it into two 16-byte halves, zero-pad each half to 32 bytes before passing them to `hashv`: ```rust theme={"system"} let mut low_padded = [0u8; 32]; low_padded[..16].copy_from_slice(&pubkey_bytes[0..16]); let mut high_padded = [0u8; 32]; high_padded[..16].copy_from_slice(&pubkey_bytes[16..32]); let computed_hash = hashv( Parameters::Bn254X5, Endianness::LittleEndian, &[&low_padded, &high_padded], )?; ``` The zero-padding matches the runtime's pre-SIMD-0359 behavior, so proofs generated against circuits that hash the same 16-byte halves (treated as Field elements, which are 32-byte) continue to verify. Apply the same padding wherever your client-side code computes Poseidon hashes that need to match the on-chain syscall. ### Noir stdlib version mismatch `nargo` version must match what Sunspot expects. Sunspot's README pins beta.18 as of April 2026. If you update Noir without updating Sunspot, ACIR serialization may change and `sunspot compile` will fail with an opaque parse error. Use `noirup -v 1.0.0-beta.18` to stay pinned. ## Where this stack is heading Several in-flight Solana features will change the ZK landscape. * **[SIMD-0302](https://github.com/solana-foundation/solana-improvement-documents/pull/302)** — standalone BN254 G2 arithmetic syscalls. Currently G2 is only accessible via the G1+G2 pairing operation. Pending devnet activation in Agave v4.0.0-beta. * **[SIMD-0388](https://github.com/solana-foundation/solana-improvement-documents/pull/388)** — BLS12-381 syscalls. Adds a second pairing-friendly curve alongside BN254, enabling proving systems that target BLS12-381. Does not directly unlock UltraHonk on Solana (UltraHonk uses BN254 but needs KZG polynomial-commitment operations that remain absent), but broadens the curve surface for future ZK work. Pending mainnet. * **[ZK Compression V2](https://www.zkcompression.com/)** — Light Protocol's production ZK infrastructure, shipping on mainnet as of March 2026. Uses the same Groth16-via-`alt_bn128` path described in this guide, at production scale. Until KZG support lands, the Noir → Sunspot → Groth16 → `alt_bn128` path is the only production route from Noir to Solana. ### When to reach for a zkVM instead Noir is a constraint-writing DSL: you describe a small arithmetic circuit and prove statements about it. This is the right tool for compact, repeat-use circuits — Merkle membership, signature verification, range proofs. If your proof target is "run this arbitrary Rust (or C, or Python-like) program and prove it executed correctly", a zkVM is the better fit: * **[SP1](https://github.com/succinctlabs/sp1-solana)** — write Rust, run in a RISC-V zkVM, generate a STARK that is recursively wrapped in Groth16 for on-chain verification. Good for general-purpose off-chain computation. * **[Bonsol](https://github.com/Bonsol-Collective/bonsol)** — RISC Zero on Solana; similar shape to SP1 but a different proving stack. The rule of thumb: if you would be comfortable writing the computation as a handful of explicit constraints, use Noir + Sunspot. If the computation is a program that happens to need a proof, use a zkVM. ## Further reading * [solana-foundation/noir-examples](https://github.com/solana-foundation/noir-examples) — the canonical reference repo; three circuits with pre-deployed devnet verifiers. * [reilabs/sunspot](https://github.com/reilabs/sunspot) — Noir → Gnark-Groth16 → Solana toolchain. * [Lightprotocol/groth16-solana](https://github.com/Lightprotocol/groth16-solana) — the lower-level crate for Groth16 verification on Solana. Use this directly if you are not going through Sunspot (for example, generating proofs with snarkjs). * [Lightprotocol/light-poseidon](https://github.com/Lightprotocol/light-poseidon) — the circomlib-compatible Poseidon implementation behind `sol_poseidon`. * [Noir documentation](https://noir-lang.org/docs/) — circuit syntax and stdlib reference. * [Groth16 paper](https://eprint.iacr.org/2016/260) — the proving system itself. * [Solana ZK primitives](https://docs.anza.xyz/runtime/zk-docs/zk-elgamal-proof) — Agave runtime's ZK documentation (covers ZK ElGamal specifically; `alt_bn128` and `sol_poseidon` are documented in the general syscall reference). ## Where to get help Custom Groth16 verification on Solana is an emerging area — there is no active #zk channel to ping, and Noir-specific Solana discussion is near-zero on Discord, Telegram, and Reddit as of April 2026. When you get stuck, the productive channels are: * [groth16-solana GitHub issues](https://github.com/Lightprotocol/groth16-solana/issues) — direct line to Light Protocol maintainers for low-level verification questions. * [reilabs/sunspot GitHub issues](https://github.com/reilabs/sunspot/issues) — for Noir → Gnark → Solana pipeline bugs. * [Noir Discord](https://discord.gg/aztec) — circuit-writing and Noir-language questions (Aztec-run, not Solana-specific). * [Solana Stack Exchange](https://solana.stackexchange.com/) — has a small but growing corpus of Groth16 / alt\_bn128 / stack-overflow-during-verify questions. Searching for `alt_bn128` turns up the most useful prior answers. * [Light Protocol Discord](https://discord.gg/J3zB8ZgnJv) — ZK Compression and the broader Groth16-on-Solana community. ## Reference repos These are the source repositories we worked against while writing this guide. They stay closer to reality than docs — check them first when something here looks off. * [solana-foundation/noir-examples](https://github.com/solana-foundation/noir-examples) — Solana Foundation's Noir + Solana examples; the reference we built the guide against * [Lightprotocol/groth16-solana](https://github.com/Lightprotocol/groth16-solana) — on-chain Groth16 verifier; verification-key serialization and proof formats verified here * [Lightprotocol/light-poseidon](https://github.com/Lightprotocol/light-poseidon) — Solana Poseidon syscall wrapper and hasher * [reilabs/sunspot](https://github.com/reilabs/sunspot) — Noir → Solana tooling used in the build and deploy steps * [noir-lang/poseidon](https://github.com/noir-lang/poseidon) — Poseidon circuit library for Noir; referenced for hash-function parameter compatibility # Sonic methods Source: https://docs.chainstack.com/docs/sonic-methods Complete reference of available Sonic blockchain RPC methods. Includes eth_, debug_, trace_, and web3_ API methods with availability status for developers. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | ftm\_currentEpoch | | | | trace\_block | | | | trace\_transaction | | | | trace\_filter | | | | trace\_get | | | | dag\_getEvent | | | | dag\_getEventPayload | | | | dag\_getHeads | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # Sonic: Swap farming for points walkthrough in Python Source: https://docs.chainstack.com/docs/sonic-swap-farming-for-points-walkthrough-in-python Learn to automate token swaps on Sonic testnet DEX using Python. Farm points with round-robin swapping between ONYX, CORAL, and OBS tokens on Uniswap V2 fork. **TLDR:** * This tutorial demonstrates an investigative approach to automate token swaps on an unverified Uniswap V2–based DEX. * It shows how to retrieve the router and LP addresses by decompiling the bytecode, scanning transactions, and decoding input data. * Once identified, a round-robin swapping script is built in Python to continuously swap among multiple tokens, including handling approvals. * By automating these swaps, developers can farm points and streamline their testnet usage. ## Introduction On the Sonic testnet, you can do daily tasks that include swaps of a set of the official ERC-20 tokens dispersed from the Sonic faucet. The DEX has the UI but no instructions on how to automate the swaps (obviously). The contract involved in the swap is not verified, which means we have no contract source and don't have the contract ABI, which in turn adds another obstacle in achieving our objective. This guide is a quick & fun walkthrough on how to approach the problem, investigate it, and ultimately achieve the automatic swaps. ## The problem Going to the [swap, account, faucet](https://testnet.soniclabs.com/account) (all in one place) page, you can see that what you can do there is: * Request different testnet coins (CORAL, ONYX, and so on) * Swap them in the interface But you of course don't want to do any of that manually, so let's FAFO and try to automate the coin swapping. Will implement a round-robin approach in which can just continuously swap coins to farm points. ## Investigate Once you have requested all the coins for swapping, do a sample swap. As a reminder, here's he [swap, account, faucet](https://testnet.soniclabs.com/account). The sample swap transaction will reveal an unverified contract address for which you have no source (which is understandable — why publish it and let people farm). Here's the address: [0x086D426f8B653b88a2d6D03051C8b4aB8783Be2b](https://testnet.sonicscan.org/address/0x086d426f8b653b88a2d6d03051c8b4ab8783be2b) . The explorer is a paid implementation by the etherscan, a team that produces the best explorers in the world. It was very nice of the Sonic team do run an etherscan version, which is extremely convenient in everything, **including a built-in decompiler**. So just go ahead on the [Contract tab](https://testnet.sonicscan.org/address/0x086d426f8b653b88a2d6d03051c8b4ab8783be2b#code), hit the **Decompile Bytecode** button. This will produce a bunch of output, including a familiar name—`UniswapV2Library`. So, it's Uniswap V2 fork, which means `0x086D426f8B653b88a2d6D03051C8b4aB8783Be2b` is likely the router address. And any token exchange going through the router does the actual exchange on an LP address for the token pair. And we already know the token addresses, since they are in the faucet and you can request them. And you can also look through all the exchanges going on there on the address: [0x086D426f8B653b88a2d6D03051C8b4aB8783Be2b](https://testnet.sonicscan.org/address/0x086d426f8b653b88a2d6d03051c8b4ab8783be2b) and see that the function signature used for the swaps is `0xddba27a7`. To be able to script the actual swaps, we need the LP addresses of the tokens. Here's what we know so far: * Router (a modification of Uniswap V2): `0x086D426f8B653b88a2d6D03051C8b4aB8783Be2b` * Token addresses on the faucet: * DIAM: `0x30BF3761147Ef0c86E2f84c3784FBD89E7954670` * CORAL: `0xAF93888cbD250300470A1618206e036E11470149` * FLE: `0x9Fa14D267d331c9E8BB7979bcDC212136915eCE8` * MACH: `0x50971F8978C431D560ff658a83a8a03fdf199055` * OBS: `0x3e6eE2F3f33766294C7148bc85c7d145E70cBD9A` * ONYX: `0xE73c4f6A0A3B0EF8337fD080b76C08172b3eB958` * A token swap involves at least four addresses: * Router * TokenA * TokenB * LP address Knowing that, let's create a Python script that scan through blocks for transactions the router address and our token addresses and prints the LP address associated with each swap: ```python Python theme={"system"} import json import sys from web3 import Web3 from eth_abi import decode from hexbytes import HexBytes # Sonic Blaze Testnet configuration RPC_URL = "SONIC_BLAZE_NODE" ROUTER_ADDRESS = "0x086d426f8b653b88a2d6d03051c8b4ab8783be2b" SWAP_METHOD_ID = "0xddba27a7" # Method ID for the swap function SWAP_METHOD_ID_BYTES = bytes.fromhex(SWAP_METHOD_ID[2:]) # Convert to bytes for comparison # Define start block directly in the script from which to start scanning START_BLOCK = 24069000 # Replace with your desired start block # Token addresses to track TOKENS = { "DIAM": "0x30BF3761147Ef0c86E2f84c3784FBD89E7954670", "CORAL": "0xAF93888cbD250300470A1618206e036E11470149", "FLE": "0x9Fa14D267d331c9E8BB7979bcDC212136915eCE8", "MACH": "0x50971F8978C431D560ff658a83a8a03fdf199055", "OBS": "0x3e6eE2F3f33766294C7148bc85c7d145E70cBD9A", "ONYX": "0xE73c4f6A0A3B0EF8337fD080b76C08172b3eB958" } # Normalize addresses to lowercase for comparison ROUTER_ADDRESS = ROUTER_ADDRESS.lower() TOKENS = {k: v.lower() for k, v in TOKENS.items()} # Connect to the Sonic Blaze Testnet w3 = Web3(Web3.HTTPProvider(RPC_URL)) if not w3.is_connected(): print("Failed to connect to the Sonic Blaze Testnet") sys.exit(1) print(f"Connected to Sonic Blaze Testnet. Chain ID: {w3.eth.chain_id}") def extract_addresses_from_input(input_data): """ Extract all Ethereum addresses from the input data. This is a more robust approach that doesn't rely on specific offsets. """ addresses = [] # Remove '0x' prefix if present if input_data.startswith('0x'): data = input_data[2:] else: data = input_data # Scan through the data looking for potential addresses # An Ethereum address is 20 bytes (40 hex chars) for i in range(0, len(data) - 40, 2): # Step by 2 because each byte is 2 hex chars # Check if this could be an address (look for leading zeros that pad addresses in ABI encoding) potential_address_with_padding = data[i:i+64] if len(potential_address_with_padding) == 64 and potential_address_with_padding.startswith('000000000000000000000000'): address = '0x' + potential_address_with_padding[24:64] # Basic validation: check if it looks like an address if all(c in '0123456789abcdefABCDEF' for c in address[2:]): addresses.append(address.lower()) return addresses def decode_swap_input(input_data): """ Decode the input data of a swap transaction """ try: # Extract all addresses from the input data addresses = extract_addresses_from_input(input_data) if not addresses: print("No addresses found in input data") return None # Filter addresses to find our tokens token_addresses = [] for addr in addresses: if addr.lower() in TOKENS.values(): token_addresses.append(addr.lower()) # The last address is typically the LP address lp_address = None for addr in reversed(addresses): if addr.lower() not in TOKENS.values(): lp_address = addr.lower() break return { "path": token_addresses, "lp_address": lp_address } except Exception as e: print(f"Error decoding input data: {e}") return None def scan_blocks(start_block): """ Scan blocks for swap transactions involving the specified tokens. Continues until LP addresses are found for all tokens. """ # Track LP addresses for each token token_lp_map = {token_addr: set() for token_addr in TOKENS.values()} # Keep track of tokens that still need LP addresses tokens_remaining = set(TOKENS.values()) print(f"Scanning blocks starting from {start_block}...") print(f"Looking for LP addresses for {len(tokens_remaining)} tokens...") block_num = start_block while tokens_remaining: if block_num % 100 == 0: print(f"Processing block {block_num}...") try: block = w3.eth.get_block(block_num, full_transactions=True) for tx in block.transactions: # Check if transaction is to the router contract if hasattr(tx, 'to') and tx.to and tx.to.lower() == ROUTER_ADDRESS: # Check if it's a swap transaction by comparing the first 4 bytes (method ID) if tx.input[:4] == SWAP_METHOD_ID_BYTES: # Get transaction hash tx_hash = tx.hash.hex() decoded = decode_swap_input(tx.input.hex()) if decoded and decoded["lp_address"] and decoded["path"]: # Check if any of our tracked tokens are in the path for token_addr in decoded["path"]: if token_addr in tokens_remaining: token_name = next((name for name, addr in TOKENS.items() if addr == token_addr), token_addr) # Add LP address to the map token_lp_map[token_addr].add(decoded["lp_address"]) # Print only if this is the first LP address found for this token if len(token_lp_map[token_addr]) == 1: print(f"Block {block_num}, TX {tx_hash}: Found LP for {token_name}: {decoded['lp_address']}") # Remove token from remaining list tokens_remaining.remove(token_addr) print(f"Remaining tokens to find: {len(tokens_remaining)}") # Move to the next block block_num += 1 except Exception as e: print(f"Error processing block {block_num}: {e}") # Continue to the next block even if there's an error block_num += 1 print(f"Found LP addresses for all tokens after scanning to block {block_num-1}") return token_lp_map def main(): token_lp_map = scan_blocks(START_BLOCK) print("\n--- Summary of LP Addresses ---") for token_addr, lp_addresses in token_lp_map.items(): token_name = next((name for name, addr in TOKENS.items() if addr == token_addr), token_addr) if lp_addresses: print(f"\n{token_name} ({token_addr}):") for lp in lp_addresses: print(f" LP: {lp}") else: print(f"\n{token_name} ({token_addr}): No LP addresses found") if __name__ == "__main__": main() ``` Make sure you put: * `RPC_URL` — the actual Sonic Blaze testnet endpoint URL that you can get from Chainstack. * `START_BLOCK` — go for eg 300 blocks in the past The script will keep running until it finds the LP addresses. It will print something like this: ```shell Shell theme={"system"} Connected to Sonic Blaze Testnet. Chain ID: 57054 Scanning blocks starting from 24069000... Looking for LP addresses for 6 tokens... Processing block 24069000... Block 24069018, TX 02fd4c396e9d146eab07b6427a712644fac14cdbc85ea52615f5144484df7c81: Found LP for CORAL: 0x0db7087e050ef022ec1d0432e983ff42506cc990 Remaining tokens to find: 5 Block 24069039, TX b4995dabb7c74ea1bf01834b6c1b305b426905d27b9bd82dc2a55c44050069d7: Found LP for MACH: 0xf88e70b3e1f848c55781297329093e8b15969908 Remaining tokens to find: 4 Block 24069078, TX 9a9680eb2487b8d6d4bd226a624104bdec76a2dff08ae2d90219aa68f2e70642: Found LP for FLE: 0xce86c8d81d24dcfe54d29409afeffde81852b8ad Remaining tokens to find: 3 Block 24069085, TX a86f4b4f3d46e0a1fae4c369ae5d2541cc43c5d83ee889205d98da5283d401fa: Found LP for ONYX: 0x7d5be487743f73264d6aa4ae202b6103078cd1a8 Remaining tokens to find: 2 Processing block 24069100... Processing block 24069200... Block 24069235, TX c6bcd8d9d783aef33819343b46924b33f67b9115a5ab45b5eaa56500caaf1174: Found LP for DIAM: 0x9785f7336ccfdd863ffc8179761f51f81e45bdf4 Remaining tokens to find: 1 Processing block 24069300... Processing block 24069400... Processing block 24069500... Processing block 24069600... Block 24069659, TX 3a0f5f962ec06a8b91dd99128311f1e2ecc760de48fb1b7732bd04c8db43f6c7: Found LP for OBS: 0xd7d04d8a33b6e6eb42a2e0e273e0fe1f23f818fd Remaining tokens to find: 0 Found LP addresses for all tokens after scanning to block 24069659 --- Summary of LP Addresses --- DIAM (0x30bf3761147ef0c86e2f84c3784fbd89e7954670): LP: 0x9785f7336ccfdd863ffc8179761f51f81e45bdf4 CORAL (0xaf93888cbd250300470a1618206e036e11470149): LP: 0x0db7087e050ef022ec1d0432e983ff42506cc990 FLE (0x9fa14d267d331c9e8bb7979bcdc212136915ece8): LP: 0xce86c8d81d24dcfe54d29409afeffde81852b8ad MACH (0x50971f8978c431d560ff658a83a8a03fdf199055): LP: 0xf88e70b3e1f848c55781297329093e8b15969908 OBS (0x3e6ee2f3f33766294c7148bc85c7d145e70cbd9a): LP: 0xd7d04d8a33b6e6eb42a2e0e273e0fe1f23f818fd ONYX (0xe73c4f6a0a3b0ef8337fd080b76c08172b3eb958): LP: 0x7d5be487743f73264d6aa4ae202b6103078cd1a8 ``` ## Implementation Now that we have the LP addresses, and we know this is actually a modified Uniswap V2 contract, let's implement a round-robin swap: 1. It swaps 1 ONYX to 1 CORAL. LP Address: `0x7D5bE487743F73264D6aA4Ae202B6103078cD1a8` 2. Then it swaps 1 CORAL to 1 OBS. LP Address: `0xD7D04d8A33b6E6EB42a2e0e273e0fe1F23f818fD` 3. Then it swaps 1 OBS to 1 ONYX. LP Addresss: `0xCE1c63381b03bd5f227C1cCfa71c5E93154f336F` 4. And it starts all over again (1 ONYX to 1 CORAL and so on) Also don't forget that we need token approvals and token approval checks to do the actual swaps. ```python Python theme={"system"} import json import time import sys from web3 import Web3 from eth_abi import encode from hexbytes import HexBytes import requests # Sonic Blaze Testnet configuration RPC_URL = "SONIC_BLAZE_NODE" ROUTER_ADDRESS = "0x086d426f8b653b88a2d6d03051c8b4ab8783be2b" SWAP_METHOD_ID = "0xddba27a7" # Method ID for the swap signature ROUTER_ABI = [ { "inputs": [ {"name": "amountIn", "type": "uint256"}, {"name": "path_offset", "type": "uint256"}, {"name": "to_offset", "type": "uint256"}, {"name": "path", "type": "address[]"}, {"name": "lp", "type": "address[]"} ], "name": "swap", "outputs": [{"name": "amounts", "type": "uint256[]"}], "stateMutability": "nonpayable", "type": "function", "function_selector": "0xddba27a7" } ] # Token addresses TOKENS = { "ONYX": "0xE73c4f6A0A3B0EF8337fD080b76C08172b3eB958", "CORAL": "0xAF93888cbD250300470A1618206e036E11470149", "OBS": "0x3e6eE2F3f33766294C7148bc85c7d145E70cBD9A" } # LP addresses for each swap pair LP_ADDRESSES = { "ONYX_CORAL": "0x7D5bE487743F73264D6aA4Ae202B6103078cD1a8", "CORAL_OBS": "0xD7D04d8A33b6E6EB42a2e0e273e0fe1F23f818fD", "OBS_ONYX": "0xCE1c63381b03bd5f227C1cCfa71c5E93154f336F" } # Swap configuration # IMPORTANT: Replace with your own private key PRIVATE_KEY = "PRIVATE_KEY" CYCLES = 4 # Number of swap cycles (0 for infinite) AMOUNT = 1 # Amount to swap each time DELAY = 10 # Delay between swaps in seconds # ERC20 ABI for token balance checking and approvals ERC20_ABI = [ { "constant": True, "inputs": [{"name": "_owner", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "balance", "type": "uint256"}], "type": "function" }, { "constant": False, "inputs": [ {"name": "_spender", "type": "address"}, {"name": "_value", "type": "uint256"} ], "name": "approve", "outputs": [{"name": "", "type": "bool"}], "type": "function" }, { "constant": True, "inputs": [ {"name": "_owner", "type": "address"}, {"name": "_spender", "type": "address"} ], "name": "allowance", "outputs": [{"name": "", "type": "uint256"}], "type": "function" } ] # Connect to the Sonic Blaze Testnet w3 = Web3(Web3.HTTPProvider(RPC_URL)) if not w3.is_connected(): print("Failed to connect to the Sonic Blaze Testnet") sys.exit(1) # Get the chain ID CHAIN_ID = w3.eth.chain_id print(f"Connected to Sonic Blaze Testnet. Chain ID: {CHAIN_ID}") # Convert all addresses to checksum format ROUTER_ADDRESS = Web3.to_checksum_address(ROUTER_ADDRESS) TOKENS = {k: Web3.to_checksum_address(v) for k, v in TOKENS.items()} LP_ADDRESSES = {k: Web3.to_checksum_address(v) for k, v in LP_ADDRESSES.items()} # Derive wallet address from private key account = w3.eth.account.from_key(PRIVATE_KEY) WALLET_ADDRESS = account.address print(f"Using wallet: {WALLET_ADDRESS}") def get_token_balance(token_name): """ Get the balance of a specific token for the wallet """ token_address = TOKENS[token_name] token_contract = w3.eth.contract(address=token_address, abi=ERC20_ABI) balance_wei = token_contract.functions.balanceOf(WALLET_ADDRESS).call() balance_eth = w3.from_wei(balance_wei, 'ether') return balance_eth def check_all_balances(): """ Check and display balances for all tokens """ print("\n--- Current Token Balances ---") for token_name in TOKENS.keys(): balance = get_token_balance(token_name) print(f"{token_name}: {balance}") print("-----------------------------") def check_and_approve_token(token_name, amount): """ Check if the router has enough allowance to spend the token If not, approve the router to spend the token """ token_address = TOKENS[token_name] token_contract = w3.eth.contract(address=token_address, abi=ERC20_ABI) # Convert amount to Wei amount_wei = w3.to_wei(amount, 'ether') # Check current allowance current_allowance = token_contract.functions.allowance(WALLET_ADDRESS, ROUTER_ADDRESS).call() if current_allowance < amount_wei: print(f"Approving {token_name} for the router...") # Approve a large amount to avoid frequent approvals # Using max uint256 value (2^256 - 1) max_amount = 2**256 - 1 # Build the approval transaction tx = token_contract.functions.approve( ROUTER_ADDRESS, max_amount ).build_transaction({ 'from': WALLET_ADDRESS, 'gas': 100000, 'gasPrice': w3.eth.gas_price, 'nonce': w3.eth.get_transaction_count(WALLET_ADDRESS), 'chainId': CHAIN_ID }) # Sign and send the transaction signed_tx = w3.eth.account.sign_transaction(tx, PRIVATE_KEY) tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) print(f"Approval transaction sent: {tx_hash.hex()}") # Wait for the transaction to be mined receipt = w3.eth.wait_for_transaction_receipt(tx_hash) if receipt.status == 1: print(f"{token_name} approved successfully!") else: print(f"Failed to approve {token_name}!") return False return True def create_swap_transaction(from_token, to_token, amount, lp_address): # Convert amount to Wei (assuming 18 decimals for all tokens) amount_wei = w3.to_wei(amount, 'ether') # Create the path array with the token addresses path = [TOKENS[from_token], TOKENS[to_token]] # Function selector function_selector = SWAP_METHOD_ID[2:] # Remove '0x' prefix # Encode parameters # 1. amountIn (uint256) amount_hex = f"{amount_wei:064x}" # 2. path_offset (uint256) - fixed at 0x60 (96 in decimal) path_offset_hex = "0000000000000000000000000000000000000000000000000000000000000060" # 3. to_offset (uint256) - fixed at 0xc0 (192 in decimal) to_offset_hex = "00000000000000000000000000000000000000000000000000000000000000c0" # 4. path_length (uint256) - 2 tokens path_length_hex = "0000000000000000000000000000000000000000000000000000000000000002" # 5. path tokens (address[]) path_tokens_hex = "" for token in path: path_tokens_hex += f"000000000000000000000000{token[2:].lower()}" # 6. lp_length (uint256) - 1 LP lp_length_hex = "0000000000000000000000000000000000000000000000000000000000000001" # 7. lp_address (address) lp_address_hex = f"000000000000000000000000{lp_address[2:].lower()}" # Combine all parts data = f"0x{function_selector}{amount_hex}{path_offset_hex}{to_offset_hex}{path_length_hex}{path_tokens_hex}{lp_length_hex}{lp_address_hex}" # Debug print print(f"Transaction data: {data}") print(f"From token: {TOKENS[from_token]}") print(f"To token: {TOKENS[to_token]}") print(f"LP address: {lp_address}") # Create transaction dictionary tx = { 'from': WALLET_ADDRESS, 'to': ROUTER_ADDRESS, 'value': 0, 'gas': 500000, 'gasPrice': w3.eth.gas_price, 'nonce': w3.eth.get_transaction_count(WALLET_ADDRESS), 'data': data, 'chainId': CHAIN_ID } return tx def sign_and_send_transaction(tx): """ Sign and send a transaction """ try: # Sign the transaction signed_tx = w3.eth.account.sign_transaction(tx, PRIVATE_KEY) # Send the transaction tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction) print(f"Transaction sent: {tx_hash.hex()}") # Wait for the transaction to be mined receipt = w3.eth.wait_for_transaction_receipt(tx_hash) if receipt.status == 1: print("Transaction successful!") else: print("Transaction failed!") # Try to get more information about the failure try: # Try to get the transaction trace trace_data = { "jsonrpc": "2.0", "method": "trace_transaction", "params": [tx_hash.hex()], "id": 1 } # Make a direct RPC call to get trace data response = requests.post(RPC_URL, json=trace_data) if response.status_code == 200: trace_result = response.json() if 'result' in trace_result and trace_result['result']: for trace in trace_result['result']: if 'error' in trace: print(f"Transaction error: {trace['error']}") else: print("No trace data available") else: print(f"Failed to get trace data: {response.status_code}") except Exception as e: print(f"Error getting trace data: {e}") return receipt except Exception as e: print(f"Error sending transaction: {e}") return None def perform_round_robin_swaps(cycles=CYCLES, amount=AMOUNT, delay=DELAY): """ Perform continuous round-robin swaps between ONYX, CORAL, and OBS Args: cycles: Number of complete cycles to perform (0 for infinite) amount: Amount of tokens to swap each time delay: Delay in seconds between swaps """ # Define the swap sequence swap_sequence = [ {"from": "ONYX", "to": "CORAL", "lp": LP_ADDRESSES["ONYX_CORAL"]}, {"from": "CORAL", "to": "OBS", "lp": LP_ADDRESSES["CORAL_OBS"]}, {"from": "OBS", "to": "ONYX", "lp": LP_ADDRESSES["OBS_ONYX"]} ] # Approve all tokens before starting print("Checking and approving tokens...") for token_name in TOKENS.keys(): if not check_and_approve_token(token_name, amount): print(f"Failed to approve {token_name}. Aborting.") return # Wait a bit after approvals to ensure they're processed print("Waiting 5 seconds for approvals to be fully processed...") time.sleep(5) cycle_count = 0 # Check initial balances check_all_balances() try: while cycles == 0 or cycle_count < cycles: print(f"\n--- Starting cycle {cycle_count + 1} ---") for swap in swap_sequence: from_token = swap['from'] to_token = swap['to'] # Check balance before swap from_balance_before = get_token_balance(from_token) to_balance_before = get_token_balance(to_token) print(f"\nSwapping {amount} {from_token} to {to_token} via LP {swap['lp']}") print(f"Balance before swap: {from_token}: {from_balance_before}, {to_token}: {to_balance_before}") # Check if we have enough balance if from_balance_before < amount: print(f"Insufficient {from_token} balance. Skipping this swap.") continue # Create and send the swap transaction tx = create_swap_transaction(from_token, to_token, amount, swap['lp']) receipt = sign_and_send_transaction(tx) # Check balance after swap from_balance_after = get_token_balance(from_token) to_balance_after = get_token_balance(to_token) print(f"Balance after swap: {from_token}: {from_balance_after}, {to_token}: {to_balance_after}") print(f"Change: {from_token}: {from_balance_after - from_balance_before}, {to_token}: {to_balance_after - to_balance_before}") # Wait between swaps if delay > 0: print(f"Waiting {delay} seconds before next swap...") time.sleep(delay) cycle_count += 1 print(f"Completed cycle {cycle_count}") # Check all balances at the end of each cycle check_all_balances() except KeyboardInterrupt: print("\nSwap process interrupted by user") # Show final balances check_all_balances() except Exception as e: print(f"\nError during swap process: {e}") # Show balances even if there was an error check_all_balances() def main(): print("=== Sonic Token Round-Robin Swap ===") print("This script will continuously swap between ONYX, CORAL, and OBS tokens") print(f"Configured for {CYCLES} cycles, {AMOUNT} tokens per swap, {DELAY}s delay between swaps") # Perform the swaps perform_round_robin_swaps() print("\nSwap process completed") if __name__ == "__main__": main() ``` Make sure you put: * `RPC_URL` — the actual Sonic Blaze testnet endpoint URL that you can get from Chainstack. * `PRVATE_KEY` — the key that holds the tokens you are going to swap. * `CYCLES` — how many cycles to run. `0` for infinite. * `AMOUNT` — the amount of each token to keep swapping. * `DELAY` — if you want to add a bit of a delay there and not keep blasting the swaps like a bot (which you are). ## Conclusion And there you have it. We've walked you through on how to make your life easier though FAFO. And honestly the sort of people that can investigate and automate this for points are the type of people that network onboarders may want — active and capable developers. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Sonic tooling Source: https://docs.chainstack.com/docs/sonic-tooling Sonic blockchain development tools guide. Connect using MetaMask, web3.py, ethers.js, Hardhat, Remix IDE, and Foundry for smart contract deployment. Get started with a [reliable Sonic RPC endpoint](https://chainstack.com/build-better-with-sonic/) to use the tools below. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Sonic nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Sonic nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.JsonRpcProvider(urlInfo.url, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * NETWORK\_ID — Sonic network ID: * Mainnet: `146` * Blaze Testnet: `57054` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Sonic nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Stable methods Source: https://docs.chainstack.com/docs/stable-methods Complete reference of Stable JSON-RPC methods supported on Chainstack, with per-method availability for the eth, debug, txpool, net, and web3 namespaces. The trace namespace is not served. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). Stable is an EVM-compatible layer 1. Chain ID is `988`. Chainstack nodes run the `stabled` client, which is built on the Cosmos SDK and serves an Ethereum-compatible JSON-RPC surface on top of it. The availability below applies to Stable Mainnet on Global Nodes. ## The trace namespace is not served > [!WARNING] > > Stable serves `debug_*` but not `trace_*`. All nine Parity-style trace methods return `-32601`: `trace_block`, `trace_transaction`, `trace_call`, `trace_callMany`, `trace_filter`, `trace_get`, `trace_rawTransaction`, `trace_replayBlockTransactions`, and `trace_replayTransaction`. > > If you are porting code from Ethereum, Arc, or World Chain, swap the calls before you deploy: > > | Instead of | Use | > | - | - | > | `trace_block` | `debug_traceBlockByNumber` with `{"tracer": "callTracer"}` | > | `trace_transaction` | `debug_traceTransaction` with `{"tracer": "callTracer"}` | > | `trace_call` | `debug_traceCall` with `{"tracer": "callTracer"}` | `rpc_modules` is available on Stable and reports the served namespaces accurately: `debug`, `eth`, `net`, `rpc`, `txpool`, and `web3`. The available tracing methods are `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceCall`, all of which need a [paid plan](https://chainstack.com/pricing/) — see [Debug and trace APIs](/docs/debug-and-trace-apis#stable). The native tracers `callTracer`, `prestateTracer`, `4byteTracer`, and `noopTracer` are enabled; `flatCallTracer` and `muxTracer` are not — see [Available native tracers](/docs/limits#available-native-tracers). ## Pending transactions are accepted but never delivered `txpool_status` responds on Stable, but it always reports an empty pool. Across 30 samples taken every 4 seconds over two minutes, `txpool_status` returned `pending: 0x0, queued: 0x0` every time, while 52 transactions confirmed in new blocks over the same window. The pool is not exposed through the EVM JSON-RPC layer. `txpool_content`, `txpool_contentFrom`, and `txpool_inspect` return `-32601`, because Global Nodes and Trader Nodes do not serve them on any EVM chain. `eth_newPendingTransactionFilter` and `eth_subscribe("newPendingTransactions")` both hand back an ID and then deliver nothing. Do not build a pending-transaction feed on Stable. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ## Filters on a load-balanced endpoint A [Global Node](/docs/global-elastic-node) endpoint is load balanced across backends, and a filter created by `eth_newFilter` or `eth_newBlockFilter` lives on the single backend that created it. A follow-up `eth_getFilterChanges` that lands on a different backend returns `-32000 filter not found`. Poll with [eth\_getLogs](/reference/ethereum-getlogs) over an explicit block range instead, or use a WSS subscription, which holds one connection to one backend for its lifetime. ## Cosmos SDK behavior to expect The Cosmos SDK foundation shows through in three places: * `eth_getProof` returns a Cosmos IAVL proof, not an Ethereum Merkle-Patricia proof, so it is not a drop-in for Ethereum state-proof verification. It also needs an explicit block number — passing `latest` returns `-32000 proof queries at height <= 2 are not supported`. * `debug_printBlock` and `debug_intermediateRoots` return `-32000 profiling disabled in the debug namespace`. * `eth_createAccessList` returns `-32603 method handler crashed` for every request shape tested. Since Stable charges a flat base fee, an access list buys you nothing here anyway. `eth_simulateV1` is not implemented. `eth_blobBaseFee` is not either — Stable takes no blob transactions. ## Block time and archive billing Stable produces a block roughly every 0.7 seconds. The standard EVM rule — 127 or more blocks behind the tip bills as archive — therefore covers only about 90 seconds of history, against roughly 25 minutes on Ethereum. On Stable, expect most historical queries to bill as archive. ## Method availability | Method | Availability | Comment | | - | - | - | | debug\_accountRange | | returns `-32601` | | debug\_chaindbProperty | | returns `-32601` | | debug\_codeByHash | | returns `-32601` | | debug\_dumpBlock | | returns `-32601` | | debug\_getAccessibleState | | returns `-32601` | | debug\_getBadBlocks | | returns `-32601` | | debug\_getModifiedAccountsByHash | | returns `-32601` | | debug\_getModifiedAccountsByNumber | | returns `-32601` | | debug\_getRawBlock | | | | debug\_getRawHeader | | returns `-32601` | | debug\_getRawReceipts | | returns `-32601` | | debug\_getRawTransaction | | returns `-32601` | | debug\_getTrieFlushInterval | | returns `-32601` | | debug\_intermediateRoots | | returns `-32000 profiling disabled in the debug namespace` | | debug\_preimage | | returns `-32601` | | debug\_printBlock | | returns `-32000 profiling disabled in the debug namespace` | | debug\_storageRangeAt | | returns `-32601` | | debug\_traceBadBlock | | returns `-32601` | | debug\_traceBlock | | takes an RLP-encoded block | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | eth\_accounts | | returns an empty array — no unlocked accounts on a hosted node | | eth\_blobBaseFee | | returns `-32601` | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_coinbase | | returns `-32601` | | eth\_createAccessList | | returns `-32603 method handler crashed` for every request shape tested | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | returns `-32601` | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | filters are node-local — see [Filters on a load-balanced endpoint](#filters-on-a-load-balanced-endpoint) | | eth\_getFilterLogs | | | | eth\_getHeaderByHash | | | | eth\_getHeaderByNumber | | | | eth\_getLogs | | | | eth\_getProof | | pass an explicit block number — `latest` returns `-32000 proof queries at height <= 2 are not supported`. The proof is a Cosmos IAVL proof, not an Ethereum Merkle-Patricia proof | | eth\_getRawTransactionByHash | | returns `-32601` | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionBySenderAndNonce | | returns `-32601` | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleByBlockHashAndIndex | | always null — Stable has no uncles | | eth\_getUncleByBlockNumberAndIndex | | always null — Stable has no uncles | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_hashrate | | returns `-32601` | | eth\_maxPriorityFeePerGas | | | | eth\_mining | | returns `-32601` | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | accepts the filter but never returns a transaction — see [Pending transactions](#pending-transactions-are-accepted-but-never-delivered) | | eth\_protocolVersion | | | | eth\_sendRawTransaction | | | | eth\_sendRawTransactionSync | | returns `-32601` | | eth\_simulateV1 | | returns `-32601` | | eth\_subscribe | | WSS. `newHeads` and `logs` deliver. `newPendingTransactions` subscribes but delivers nothing; `syncing` returns `-32600 syncing subscription is not implemented` | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | WSS | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | rpc\_modules | | | | trace\_block | | returns `-32601` | | trace\_call | | returns `-32601` | | trace\_callMany | | returns `-32601` | | trace\_filter | | returns `-32601` | | trace\_get | | returns `-32601` | | trace\_rawTransaction | | returns `-32601` | | trace\_replayBlockTransactions | | returns `-32601` | | trace\_replayTransaction | | returns `-32601` | | trace\_transaction | | returns `-32601` | | txpool\_content | | not available on Global Nodes and Trader Nodes — see [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access) | | txpool\_contentFrom | | not available on Global Nodes and Trader Nodes | | txpool\_inspect | | not available on Global Nodes and Trader Nodes | | txpool\_status | | responds, but the pool always reads empty | | web3\_clientVersion | | | | web3\_sha3 | | | # Stable tooling Source: https://docs.chainstack.com/docs/stable-tooling Connect MetaMask, ethers.js, viem, and web3.py to a Stable node on Chainstack. Chain configuration, the USDT0 gas token and its two decimal counts, and the behaviors that affect tooling. Stable is EVM-compatible, so the standard Ethereum libraries work against a Stable node without modification. The differences are in the chain configuration and in a handful of methods the client does not implement. ## Chain configuration | Property | Value | | - | - | | Network | Stable Mainnet | | Chain ID | `988` | | Native gas currency | USDT0 | | Decimals | `18` | | Block time | \~0.7 seconds | | RPC URL | your Chainstack [Stable endpoint](/docs/manage-your-node#view-node-access-and-credentials) | | Multicall3 | `0xcA11bde05977b3631167028862bE2a173976CA11` | | Explorer | [stablescan.xyz](https://stablescan.xyz) | > [!WARNING] > > **Two different USDT0 decimal counts on one chain.** The native gas currency carries **18 decimals**, so gas balances are wei-denominated exactly as on Ethereum and `formatEther` and `from_wei(..., "ether")` are the correct helpers. > > The USDT0 **ERC-20 token** at `0x779Ded0c9e1022225f8E0630b35a9b54bE713736` reports **6 decimals**. Format that one with `formatUnits(value, 6)`, not `formatEther`. > > Both are called USDT0, so the mistake is easy to make and puts you out by a factor of 1012 in whichever direction you got it wrong. See [Stable methods](/docs/stable-methods) for per-method availability and [Debug and trace APIs](/docs/debug-and-trace-apis#stable) for the tracing namespaces. ## MetaMask Add Stable Mainnet as a custom network. On Chainstack, get your [Stable endpoint](/docs/manage-your-node#view-node-access-and-credentials), then in MetaMask select **Add a custom network** and fill in: * Network name — Stable * Default RPC URL — your Chainstack Stable endpoint * Chain ID — `988` * Currency symbol — USDT0 * Block explorer URL — `https://stablescan.xyz` ## ethers.js Install [ethers.js](https://docs.ethers.org/): ```shell Shell theme={"system"} npm install ethers ``` ```javascript index.js theme={"system"} const { JsonRpcProvider, formatEther } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function main() { const network = await provider.getNetwork(); console.log("Chain ID:", network.chainId.toString()); const block = await provider.getBlockNumber(); console.log("Block:", block); const balance = await provider.getBalance("0x71B7c43c004D99A1091A532808b8E9c812eC51F8"); console.log("Balance:", formatEther(balance), "USDT0"); } main(); ``` ## viem Stable ships in viem's bundled chain list as `stable`, so you do not need `defineChain`: ```shell Shell theme={"system"} npm install viem ``` ```javascript index.mjs theme={"system"} import { createPublicClient, http, formatEther, formatUnits } from "viem"; import { stable } from "viem/chains"; const client = createPublicClient({ chain: stable, transport: http("CHAINSTACK_NODE_URL"), }); console.log("Chain ID:", await client.getChainId()); console.log("Block:", await client.getBlockNumber()); // Native USDT0 — 18 decimals. const balance = await client.getBalance({ address: "0x71B7c43c004D99A1091A532808b8E9c812eC51F8", }); console.log("Gas balance:", formatEther(balance), "USDT0"); // The USDT0 ERC-20 — 6 decimals. const totalSupply = await client.readContract({ address: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", abi: [{ name: "totalSupply", type: "function", inputs: [], outputs: [{ type: "uint256" }], stateMutability: "view", }], functionName: "totalSupply", }); console.log("USDT0 token supply:", formatUnits(totalSupply, 6)); ``` Pass your Chainstack endpoint to `http()` as above. The bundled chain definition carries a public RPC URL, and `http()` with no argument would use that instead of your node. ## web3.py Install [web3.py](https://web3py.readthedocs.io/): ```shell Shell theme={"system"} pip install web3 ``` ```python main.py theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider("CHAINSTACK_NODE_URL")) print("Connected:", web3.is_connected()) print("Chain ID:", web3.eth.chain_id) print("Block:", web3.eth.block_number) address = Web3.to_checksum_address("0x71b7c43c004d99a1091a532808b8e9c812ec51f8") balance = web3.eth.get_balance(address) print("Gas balance:", web3.from_wei(balance, "ether"), "USDT0") ``` > [!NOTE] > > web3.py rejects lowercase addresses with `InvalidAddress`. Wrap any address you did not get from the library itself in `Web3.to_checksum_address()`, as above. ## Stable behaviors that affect tooling **No `trace_*` namespace.** All nine Parity-style trace methods return `-32601`. Any library helper or indexer that reaches for `trace_block` or `trace_transaction` will fail. Use `debug_traceBlockByNumber` and `debug_traceTransaction` with the `callTracer` instead — see [Stable methods](/docs/stable-methods#the-trace-namespace-is-not-served) for the full swap table. **The transaction pool reads empty.** `txpool_status` returns successfully and reports nothing pending, and `txpool_content`, `txpool_contentFrom`, and `txpool_inspect` return `-32601`. Pending-transaction subscriptions and filters hand back an ID and then deliver no transactions. Do not build a pending-transaction feed on Stable. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). **Three methods behave unlike Geth.** `eth_getProof` returns a Cosmos IAVL proof rather than an Ethereum Merkle-Patricia proof, and needs an explicit block number — passing `latest` returns `-32000 proof queries at height <= 2 are not supported`. `eth_createAccessList` returns `-32603 method handler crashed`; since Stable charges a flat base fee, an access list buys you nothing here anyway. `eth_simulateV1` is not implemented. **`eth_getLogs` is capped at 10,000 blocks.** Exceeding it returns `-32000 maximum [from, to] blocks distance: 10000`, and a query that matches too much returns `-32000 query returned more than 10000 results`. Paginate on both. See [EVM range limits](/docs/limits#evm-range-limits). **Filters are node-local.** A [Global Node](/docs/global-elastic-node) endpoint is load balanced across backends, and a filter created by `eth_newFilter` or `eth_newBlockFilter` lives on the backend that created it. A follow-up `eth_getFilterChanges` that lands elsewhere returns `-32000 filter not found`. Poll [eth\_getLogs](/reference/ethereum-getlogs) over an explicit block range instead, or hold a WSS subscription, which keeps one connection to one backend for its lifetime. # Starknet methods Source: https://docs.chainstack.com/docs/starknet-methods Complete Starknet RPC API methods reference. Includes starknet_ and pathfinder_ methods for blocks, transactions, storage, and smart contracts. See also [interactive Starknet API call examples](/reference/getting-started-starknet). Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which Starknet methods are full vs archive, see [Request units — Starknet method scope](/docs/request-units#starknet-method-scope). | Method | Availability | Comment | | - | - | - | | starknet\_specVersion | | | | starknet\_getBlockWithTxHashes | | | | starknet\_getBlockWithTxs | | | | starknet\_getBlockWithReceipts | | | | starknet\_getStateUpdate | | | | starknet\_getStorageAt | | | | starknet\_getStorageProof | | | | starknet\_getTransactionStatus | | | | starknet\_getTransactionByHash | | | | starknet\_getTransactionByBlockIdAndIndex | | | | starknet\_getTransactionReceipt | | | | starknet\_getClass | | | | starknet\_getClassHashAt | | | | starknet\_getClassAt | | | | starknet\_getBlockTransactionCount | | | | starknet\_call | | | | starknet\_estimateFee | | | | starknet\_estimateMessageFee | | | | starknet\_blockNumber | | | | starknet\_blockHashAndNumber | | | | starknet\_chainId | | | | starknet\_syncing | | | | starknet\_getEvents | | | | starknet\_getNonce | | | | starknet\_traceTransaction | | | | starknet\_simulateTransactions | | | | starknet\_traceBlockTransactions | | | | starknet\_addInvokeTransaction | | | | starknet\_addDeclareTransaction | | | | starknet\_addDeployAccountTransaction | | | | pathfinder\_version | | | | starknet\_subscribeNewHeads | | WebSocket only | | starknet\_subscribeEvents | | WebSocket only | | starknet\_subscribeNewTransactions | | WebSocket only | | starknet\_subscribeNewTransactionReceipts | | WebSocket only | | starknet\_subscribeTransactionStatus | | WebSocket only | | starknet\_unsubscribe | | WebSocket only | | pathfinder\_subscribe | | Removed in Pathfinder 0.18.0 — use `starknet_subscribeNewHeads` | | pathfinder\_unsubscribe | | Removed in Pathfinder 0.18.0 — use `starknet_unsubscribe` | # Starknet tooling Source: https://docs.chainstack.com/docs/starknet-tooling Starknet development tools guide using Pathfinder client. Includes Starknet.js, Starknet.py, Starkli CLI, and JSON-RPC API examples for blockchain interaction. Get started with a [reliable Starknet RPC endpoint](https://chainstack.com/build-better-with-starknet/) to use the tools below. Pathfinder currently supports querying the Starknet network for state. For the full list of available queries, see [Pathfinder API](https://github.com/equilibriumco/pathfinder#api). ## JSON-RPC API Use [curl](https://curl.haxx.se) or [Postman](https://www.getpostman.com). ```bash cURL theme={"system"} curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":"0","method":"METHOD","params":[PARAMS]}' YOUR_CHAINSTACK_ENDPOINT ``` where * METHOD — a supported JSON-RPC API method * PARAMS — request parameters if any * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ## Starknet.js 1. Install Starknet.js. See [Starknet.js guide](https://www.starknetjs.com). 2. Use `RpcProvider` to connect to your Starknet node and get the latest block: ```javascript theme={"system"} import { RpcProvider } from 'starknet'; const provider = new RpcProvider({ nodeUrl: 'YOUR_CHAINSTACK_ENDPOINT', }) provider.getBlockNumber().then(console.log); ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password. See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Starknet.py 1. Install Starknet.py. See [Starknet.py guide](https://Starknetpy.readthedocs.io/en/latest/installation.html). 2. Use `FullNodeClient` to connect to your Starknet node and retrieve a transaction by hash: ```python theme={"system"} from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.networks import MAINNET node_url = "YOUR_CHAINSTACK_ENDPOINT" full_node_client = FullNodeClient(node_url=node_url, net=MAINNET) call_result = full_node_client.get_transaction_sync(tx_hash="0x50c1941ab13ccc5d9785ceda1d0d7a47be01865eecb795a62f4e589ddca0258") print(call_result) ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password. See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Starkli Rust-based CLI tool to interact with the Starknet blockchain. 1. Install Starkli. See [Starkli Book](https://book.starkli.rs/installation). 2. In a terminal run commands with the `--rpc` flag: ```bash theme={"system"} starkli block-number --rpc YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password. See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). Find more JSON-RPC methods on the [Starkli](https://book.starkli.rs/providers) documentation. # Starknet: NFT contract with Nile and L1-L2 messaging Source: https://docs.chainstack.com/docs/starknet-tutorial-an-nft-contract-with-nile-and-l1-l2-reputation-messaging Build Starknet NFT contract with L1-L2 messaging using Nile, Cairo, and OpenZeppelin. Deploy cross-chain reputation system between Ethereum and Starknet. > [!WARNING] > > ### Deprecation notice > > This guide is for historical reference. It builds on [Nile](https://github.com/OpenZeppelin/nile), which OpenZeppelin archived in December 2023, and on the Starknet Goerli testnet, which no longer exists. For current Starknet development, see [Starknet tooling](/docs/starknet-tooling). **TLDR:** * Starknet employs an account-based model and a distinct L1–L2 messaging architecture to enable cross-chain interactions with Ethereum. * This tutorial walks through deploying a Starknet-based NFT contract (with a reputation system) and a corresponding L1 contract on Ethereum Sepolia. * You’ll mint NFTs on L2, send messages from L2 to L1 (to withdraw reputation points), and send messages back from L1 to L2 (to raise an NFT’s reputation). * Tools used include Cairo, OpenZeppelin’s Nile, the Starknet CLI, Remix, and MetaMask (for L1) plus ArgentX (for L2). ## Main article The objective of this tutorial is twofold: * Get you familiar with the foundational Starknet concepts: the account model and the L1 \<-> L2 messaging. * Do a hands-on walkthrough with the tooling available on Starknet, the smart contracts, and the communication between the L1 Ethereum network and the L2 Starknet network. For the tutorial, you will implement an NFT contract with a simple reputation points system. You will run the NFT contract on the L2 Starknet testnet, and you will be able to interact with the L2 reputation system through the L1 Ethereum Sepolia testnet. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Starknet RPC endpoint](https://chainstack.com/build-better-with-starknet/). * [Cairo](https://docs.cairo-lang.org/) to compile Starknet contracts and to interact with the network through the Starknet CLI. * [OpenZeppelin Nile](https://github.com/OpenZeppelin/nile) to create and deploy an NFT contract. * [OpenZeppelin Cairo contracts](https://github.com/OpenZeppelin/cairo-contracts/) to use the ERC-721 contract library in Cairo. * [MetaMask](https://metamask.io/) to deploy the L1 contract and interact with the contract. * [ArgentX](https://www.argent.xyz/argent-x/) to interact with the L2 NFT contract. ## Overview To get from zero to a deployed NFT contract with a reputation system on L2 and its counterpart on L1, do the following: 1. With Chainstack, create a public chain project (A public chain project- a project to join public networks). 2. With Chainstack, deploy an Ethereum Sepolia testnet node. 3. With Chainstack, deploy a Starknet testnet node. 4. With Chainstack, access your Ethereum Sepolia testnet node credentials and your Starknet testnet node credentials. 5. Set up your MetaMask with the Ethereum Sepolia testnet node. 6. Deploy the L1 contract on the Ethereum Sepolia testnet and verify the contract. 7. With the Starknet CLI, deploy an account on the Starknet testnet. 8. With Nile, deploy the L2 NFT contract on the Starknet network. 9. Mint an NFT on L2. 10. Increase and decrease the reputation of the minted NFT on L2. 11. On L1, withdraw the reputation points of the minted L2 NFT. 12. On L1, increase the reputation of the minted L2 NFT. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the Ethereum Sepolia and Starknet testnets For the L1 \<-> L2 messaging, you need access to both an Ethereum network and a Starknet network. See [Join a public network](/docs/manage-your-networks). ### Get the node access and credentials See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Set up MetaMask You will deploy the L1 contract with Remix through MetaMask. To set up, see [Ethereum tools: MetaMask](/docs/ethereum-tooling#metamask). ### Set up an account on Starknet Unlike on other EVM-based networks, accounts on Starknet are abstract and decoupled from the signer. Accounts are contracts and can contain any code—for example, an account can be multi-signature or have any other mechanisms built in. For this tutorial, you will deploy the standard account that comes with the Starknet CLI and is based on the OpenZeppelin account contract implementation. To deploy an account: ```bash Shell theme={"system"} new_account --wallet starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount --network=alpha-sepolia ``` Fund the deployed account with [L2 Goerli ETH](https://faucet.sepolia.starknet.io/). ### Set up the L2 NFT contract There will be three contracts in total to do the L1 \<-> L2 messaging for this tutorial: * Starknet L1 core contract — the default Starknet contract deployed on L1. See the [official core contract addresses](https://docs.starknet.io/documentation/tools/important_addresses/). * Starknet L2 NFT contract — your tutorial contract in Cairo that you will deploy on the Starknet testnet. This contract will communicate with your Ethereum L1 contract. * Ethereum L1 contract — your tutorial contract in Solidity that you will deploy on the Ethereum Sepolia testnet. This contract will: * Consume messages from your L2 contract through the Starknet L1 core contract. * Send messages to your L2 contract. You are going to use the OpenZeppelin ERC721 Mintable Burnable contract and modify it to add the L1 \<-> L2 reputation messaging. Initialize the project with Nile: ```bash Shell theme={"system"} nile init ``` Install the OpenZeppelin contract libraries: ```bash Shell theme={"system"} pip install cairo-nile openzeppelin-cairo-contracts ``` In the `contracts/` directory, create a contract called `NFT_with_reputation.cairo`: ```js JavaScript theme={"system"} %lang starknet from openzeppelin.token.erc721.ERC721_Mintable_Burnable import constructor ``` If you were going to just create a simple NFT contract on the Starknet network, this would have been enough. But you are going to add to the NFT contract a reputation system that will communicate with the L1 contract. Update your `NFT_with_reputation.cairo` contract with the following code: ```js JavaScript theme={"system"} %lang starknet from openzeppelin.token.erc721.ERC721_Mintable_Burnable import constructor from starkware.cairo.common.alloc import alloc from starkware.cairo.common.cairo_builtins import HashBuiltin from starkware.starknet.common.messages import send_message_to_l1 const L1_CONTRACT_ADDRESS = ( L1_MESSAGING_CONTRACT) const MESSAGE_REP_DOWN = 0 # A mapping from a nftId (L1 Ethereum address) to their reputation. @storage_var func reputation(nftId : felt) -> (res : felt): end @view func get_rep{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( nftId : felt ) -> (reputation : felt): let (res) = reputation.read(nftId=nftId) return (res) end @external func rep_up{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( nftId : felt, points : felt ): let (res) = reputation.read(nftId=nftId) reputation.write(nftId, res + points) return () end @external func rep_down{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( nftId : felt, points : felt ): let (res) = reputation.read(nftId=nftId) tempvar new_reputation = res - points # Update the new reputation. reputation.write(nftId, new_reputation) # Send the rep down message. let (message_payload : felt*) = alloc() assert message_payload[0] = MESSAGE_REP_DOWN assert message_payload[1] = nftId assert message_payload[2] = points send_message_to_l1(to_address=L1_CONTRACT_ADDRESS, payload_size=3, payload=message_payload) return () end @l1_handler func repUp{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( from_address : felt, nftId : felt, points : felt ): # Make sure the message was sent by the intended L1 contract. assert from_address = L1_CONTRACT_ADDRESS # Read the current reputation. let (res) = reputation.read(nftId=nftId) # Compute and update the new reputation. tempvar new_reputation = res + points reputation.write(nftId, new_reputation) return () end ``` where L1\_MESSAGING\_CONTRACT is the address of the L1 contract that you will deploy later. The contract implementation is the following: * The NFT part of the contract is based on the OpenZeppelin ERC721 Mintable Burnable library. * `nftId` is used to identify the NFT token ID when it is minted. * `rep_up` can be called by any contract on L2 to increase the reputation of the NFT. * `rep_down` can be called by any contract on L2 to decrease the reputation of the NFT and send the reputation points as a message to the L1 contract. * `get_rep` can be called by any contract on L2 to get the current reputation of the NFT. * `l1_handler` processes the message from the L1 contract. You will deploy the L2 contract after you deploy the L1 contract. ### Set up and deploy the L1 contract The L1 contract will communicate with your L2 contract through the [L1 Starknet core contract](https://docs.starknet.io/documentation/tools/important_addresses/). In [Remix](https://remix.ethereum.org/), create the `L1L2ERC721Rep.sol` contract: ```solidity solidity theme={"system"} pragma solidity ^0.8.7; interface IStarknetCore { /** Sends a message to an L2 contract. Returns the hash of the message. */ function sendMessageToL2( uint256 toAddress, uint256 selector, uint256[] calldata payload ) external returns (bytes32); /** Consumes a message that was sent from an L2 contract. Returns the hash of the message. */ function consumeMessageFromL2(uint256 fromAddress, uint256[] calldata payload) external returns (bytes32); } /** Contract for L1 <-> L2 interaction between an L2 Starknet contract and this L1 solidity contract. */ contract L1L2ERC721Rep { // The Starknet core contract. IStarknetCore starknetCore; mapping(uint256 => uint256) public nftIdIdleReputationPoints; uint256 constant MESSAGE_REP_DOWN = 0; // The selector of the "repUp" l1_handler. uint256 constant REPUP_SELECTOR = 481301234104709516967081079511443560691305293629011359495317790738588668414; /** Initializes the contract state. */ constructor(IStarknetCore starknetCore_) { starknetCore = starknetCore_; } function withdrawToIdle( uint256 l2ContractAddress, uint256 nftId, uint256 points ) external { // Construct the rep down message's payload. uint256[] memory payload = new uint256[](3); payload[0] = MESSAGE_REP_DOWN; payload[1] = nftId; payload[2] = points; // Consume the message from the Starknet core contract. // This will revert the (Ethereum) transaction if the message does not exist. starknetCore.consumeMessageFromL2(l2ContractAddress, payload); // Update the L1 reputation points of the NFT. nftIdIdleReputationPoints[nftId] += points; } function repUp( uint256 l2ContractAddress, uint256 nftId, uint256 points ) external { require(points < 2**64, "Invalid points."); require(points <= nftIdIdleReputationPoints[nftId], "Insufficient nftId's reputation points."); // Update the L1 reputation points of the NFT. nftIdIdleReputationPoints[nftId] -= points; // Construct the repUp message's payload. uint256[] memory payload = new uint256[](2); payload[0] = nftId; payload[1] = points; // Send the message to the Starknet core contract. starknetCore.sendMessageToL2(l2ContractAddress, REPUP_SELECTOR, payload); } } ``` The contract implementation is the following: * The contract has the interface to communicate with the [L1 Starknet core contract](https://docs.starknet.io/documentation/tools/important_addresses/). * The constructor of the contract takes the L1 Starknet core contract address. * The `withdrawToIdle` external function withdraws the NFT reputation points sent to L1 using the L2 `rep_down` function. Mechanically, once you hit the L2 `rep_down` function, the L2 state is pushed as a message into the L1 Starknet core contract. The `rep_down` message then sits in the L1 Starknet core contract until it gets consumed by triggering the `withdrawToIdle` function in your L1 contract. * The contract has the `repUp` external function of the L2 contract computed for `REPUP_SELECTOR`. To compute: 1. Create a Python script with the function name (`repUp`): ```py py theme={"system"} from starkware.starknet.compiler.compile import \ get_selector_from_name print(get_selector_from_name('repUp')) ``` 2. Run the script. The reputation points withdrawn to an account through withdrawToIdle can then be used in the L1 `repUp` function to send a message to the L2 NFT contract to increase the NFT reputation. If this seems a little complicated at this point, do not worry—just keep following the tutorial and you will have a full hands-on walkthrough, which will help understand each of the steps better. In Remix, compile the `L1L2ERC721Rep.sol` contract. In Remix, on the deployment tab: * In **Environment**, select **Injected Provider - Metamask**. * In **Contract**, select `L1L2ERC721Rep`. * In **Deploy**, provide `0xde29d060D45901Fb19ED6C6e959EB22d8626708e`. This is the address of the L1 Starknet core contract on the Ethereum Sepolia testnet. Clicking **Deploy** will engage your MetaMask instance connected to the Ethereum Sepolia testnet and deploy the contract. Verify the deployed contract on [Etherscan](https://goerli.etherscan.io/): 1. Find the contract by the address and click **Contract** > **Verify and Publish**. 2. For **Compiler Type**, select **Solidity (Single file)**. 3. For **Compiler Version**, select **v0.8.7**. 4. For **Open Source License Type**, select **No License (None)**. 5. For **Optimization**, select **No**. 6. In the **Solidity Contract Code** field, paste the entirety of your `L1L2ERC721Rep.sol` contract. 7. Click **Verify and Publish**. This will verify the contract and turn it into a web app that you can interact with through MetaMask. ### Deploy the L2 NFT contract Now that you have the address of your L1 contract, swap `L1_MESSAGING_CONTRACT` in the L2 contract code that you have prepared with the actual L1 contract address. In your Nile project directory, compile the contract: ```bash Shell theme={"system"} nile compile ``` Deploy the contract: ```bash Shell theme={"system"} nile deploy CONTRACT_NAME NFT_NAME_INT NFT_TICKER_INT OWNER_ADDRESS --network sepolia ``` where: * CONTRACT\_NAME — the name of the contract, which is `NFT_with_reputation` for this tutorial. * NFT\_NAME\_INT — Nile accepts only integers for the deployment constructor. Provide any NFT collection name in integer format. See below for the conversion script. * NFT\_TICKER\_INT — Any NFT collection ticker in integer format. To convert a string to an integer, run the following Python script: ```py py theme={"system"} # string_to_integer.py import sys for arg in sys.argv[1:]: print(int.from_bytes(arg.encode(), byteorder="big")) ``` Example: ```bash Shell theme={"system"} $ python3 string_to_integer.py NFTwithRep 369641944198362097149296 ``` Example of deploying the contract with the name `NFTwithRep`, ticker `NFTRP`, and owner `0x02a152cb1a753a858c950bb710d957e7f3f78d87435831e7fae394f9233e60fa`: ```bash Shell theme={"system"} nile deploy NFT_with_reputation 369641944198362097149296 336187380304 0x02a152cb1a753a858c950bb710d957e7f3f78d87435831e7fae394f9233e60fa --network goerli ``` You can check the deployment status with the `starknet tx_status --hash HASH --network=alpha-sepolia` command. Example: ```bash Shell theme={"system"} starknet tx_status --hash 0x635132bdc45cfe7f7f3b458bba7e739043f133ee1315ae1e67da70f7ed53782 --network=alpha-goerli ``` When you get the `ACCEPTED_ON_L2` status, the contract is running on the Starknet network. > [!NOTE] > > If you get the `REJECTED` status, reattempt the transaction and up the suggested network fee when prompted by ArgentX. ### Interact with the L2 contract Mint an NFT: 1. Use [Voyager](https://goerli.voyager.online/) to open your deployed contract. 2. Click **Write Contract**. 3. Click **Connect ArgentX Wallet** and make sure you connect with the account address that you provided as the owner when deploying the contract. 4. In `mint`, provide a deployed account to mint an NFT to in the `to` field and any ID in `tokenId`. This will be the ID of the minted NFT that you will assign the reputation points to. 5. Click **Transact**. Your NFT is now minted on L2. ### L2 -> L1 messaging Sending a message from L2 to L1 takes much longer than sending a message from L1 to L2. When sending a message from L2 to L1, Starknet puts messages in a batch and pushes the state onto L1 at different time intervals. It can take 30 minutes for your message to arrive on L1 on testnet. You can watch state updates live as transactions to the L1 Starknet core contracts: * Starknet core mainnet: [0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4](https://etherscan.io/address/0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4) * Starknet core testnet: [0xde29d060D45901Fb19ED6C6e959EB22d8626708e](https://goerli.etherscan.io/address/0xde29d060D45901Fb19ED6C6e959EB22d8626708e) Now that you have an NFT minted on L2, assign reputation points to it: 1. Use [Voyager](https://goerli.voyager.online/) to open your deployed contract. 2. Click **Write Contract**. 3. In `rep_up`, provide the ID of your minted NFT in `nftId` and any integer for reputation points in `points`. For example, assign `1500`. 4. Click **Transact**. Once the transaction is accepted on L2, you can check the assigned reputation points on **Read Contract** > `get_rep`. Now that you have the points assigned, you will decrease the reputation partially and send the reputation points to L1. 1. Use [Voyager](https://goerli.voyager.online/) to open your deployed contract. 2. Click **Write Contract**. 3. In `rep_down`, provide the ID of your minted NFT in `nftId` and any integer for reputation points in `points`. For example, remove `700` points. 4. Click **Transact**. And now you wait. Your transaction will go through the following states in the specified order: * `RECEIVED` — the transaction is received by the Starknet node. * `PENDING` – the transaction is valid and is in a pending block. * `ACCEPTED_ON_L2` – the transaction is in an accepted block on L2. * `ACCEPTED_ON_L1` — the transaction is pushed onto L2 as a message and ready to be consumed on L1. To go from `RECEIVED` to `ACCEPTED_ON_L1` will take about 30 minutes on average on the testnet and 2 hours on the mainnet. You can check the deployment status with the `starknet tx_status --hash HASH --network=alpha-sepolia` command. Example of an L2 transaction sending a message to L1 with `nftId`: `1` and `rep_down`: `700`: [0x6f753d8249a1f94d6ede18e34717cddf35936c1bcce329be599ad6c1ca7afaf](https://goerli.voyager.online/tx/0x6f753d8249a1f94d6ede18e34717cddf35936c1bcce329be599ad6c1ca7afaf#messages). Example of the message arriving on L1 (#89): [0x56a8e663d0e607fc6eb74b6b7bb713c5acdc262130e7790a8b252c192b41fcc9](https://goerli.etherscan.io/tx/0x56a8e663d0e607fc6eb74b6b7bb713c5acdc262130e7790a8b252c192b41fcc9#eventlog). You now have the message in the Starknet core contract on L1 waiting to be consumed by triggering your L1 contract. ### L1 -> L2 messaging Sending a message from L1 to L2 is much faster than sending a message from L2 to L1. The Starknet network will receive the L1 -> L2 message after a few block confirmations on the L1 network. First, consume the message on L1: 1. Use [Etherscan](https://goerli.etherscan.io/) to open your contract. 2. Click **Write Contract** > **Connect to Web3**. 3. In `withdrawToIdle`, provide: * `l2ContractAddress` — the address of your NFT contract deployed on L2 * `nftId` — the ID of the minted NFT. For this tutorial, it is `1`. * `points` — the number of reputation points you removed from the NFT contract on L2. For this tutorial, it is `700`. 4. Click **Write**. This will withdraw the reputation points to the idle state—not assigned to the NFT on L2. This constitutes consuming the L2 message on L1. Now send the L1 -> L2 message—for this tutorial, it is putting the idle reputation points back on L2 for the NFT reputation by calling `repUp` on L1. First, check the current reputation points of the NFT on L2 by calling `get_rep` on the L2 contract. If you are following the example numbers in this tutorial, you should get the reputation of `800` for `nftId`: `1` on L2. Now up the reputation by `300` points by sending a message from L1 to L2: 1. Use [Etherscan](https://goerli.etherscan.io/) to open your contract. 2. Click **Write Contract** > **Connect to Web3**. 3. In `repUp`, provide: * `l2ContractAddress` — the address of your NFT contract deployed on L2 * `nftId` — the ID of the minted NFT. For this tutorial, it is `1`. * `points` — the number of reputation points you want to put back in the NFT reputation on the L2 contract. For this tutorial, it is `300`. 4. Click **Write**. After a few blocks on L1, call `get_rep` on the L2 contract. You should get the reputation of `1100` for `nftId`: `1` on L2. Congratulations! You have completed the full L1 \<-> L2 messaging exercise on Starknet. ## Conclusion This tutorial guided you through the basics of operating on Starknet, using tooling like Nile and OpenZeppelin, implementing your own L1 \<-> L2 messaging protocol on the contract level, and actually sending the L1 \<-> L2 messages. As part of the tutorial, you also created an NFT contract that implements a basic reputation system for each of the minted tokens. This tutorial uses testnet, however, the exact same instructions and sequence work on the mainnet. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Stellar tooling Source: https://docs.chainstack.com/docs/stellar-tooling Connect to your Chainstack Stellar node over the Stellar RPC JSON-RPC API with curl, the JavaScript SDK, and the Python SDK, and check the ledger history your node holds. Your Chainstack Stellar node serves the [Stellar RPC API](https://developers.stellar.org/docs/data/apis/rpc) — the JSON-RPC interface that stellar-rpc exposes for reading ledger data, simulating contract calls, and submitting transactions. Horizon is a separate service with its own REST interface and is not served on this endpoint. ## JSON-RPC over HTTPS Every call is an HTTP POST with a `method` and, for methods that take arguments, a `params` object. The endpoint is HTTPS only — Stellar nodes on Chainstack do not expose a WebSocket endpoint. ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"getHealth"}' ``` `getHealth` is the fastest way to confirm the endpoint works and to see the ledger range the node holds: ```json Response theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": { "status": "healthy", "latestLedger": 64065198, "latestLedgerCloseTime": "1787363249", "oldestLedger": 63028399, "oldestLedgerCloseTime": "1781453141", "ledgerRetentionWindow": 1036800 } } ``` Replace `YOUR_CHAINSTACK_ENDPOINT` with your node's HTTPS endpoint. For the credential in that endpoint and the other ways to authenticate, see [Authentication methods for different scenarios](/docs/authentication-methods-for-different-scenarios). ## Historical data availability Stellar Global Nodes are [Extended](/docs/extended-nodes): they keep a rolling window of recent ledgers that is longer than the stellar-rpc default of about 7 days. Transaction and event queries are limited to that window on both networks. On Mainnet, `getLedgers` also serves ledgers older than the window, back to ledger 2. On Testnet, `getLedgers` is limited to the window. Billing is independent of the window — every Stellar request is billed as full, at 1 RU; see [Request units](/docs/request-units). Your node reports its own window in `getHealth`: * `ledgerRetentionWindow` — the configured size of the window, in ledgers * `oldestLedger` — the oldest ledger in the retention window right now * `latestLedger` — the current tip Work from `oldestLedger`, not from `ledgerRetentionWindow`. The configured size is the ceiling, and a node that has not been running long enough to fill its window holds less than that. A `getTransactions` or `getEvents` request that starts before `oldestLedger` returns `-32600` with a message naming the range the node can serve. The same applies to `getLedgers` on Testnet. On Mainnet, `getLedgers` accepts any `startLedger` from 2 to the tip. ## JavaScript Use the official [`@stellar/stellar-sdk`](https://github.com/stellar/js-stellar-sdk) and point `rpc.Server` at your endpoint. ```javascript JavaScript theme={"system"} // npm install @stellar/stellar-sdk import { rpc } from "@stellar/stellar-sdk"; const server = new rpc.Server("YOUR_CHAINSTACK_ENDPOINT"); const health = await server.getHealth(); console.log("getHealth:", health.status, "| latest", health.latestLedger, "| oldest", health.oldestLedger); const network = await server.getNetwork(); console.log("getNetwork:", network.passphrase); const ledger = await server.getLatestLedger(); console.log("getLatestLedger: sequence", ledger.sequence, "protocol", ledger.protocolVersion); ``` ```text Output theme={"system"} getHealth: healthy | latest 64065193 | oldest 63028394 getNetwork: Public Global Stellar Network ; September 2015 getLatestLedger: sequence 64065193 protocol 27 ``` ### Read recent ledgers `getLedgers` takes a single request object with `startLedger` and a `pagination` block, rather than positional arguments: ```javascript JavaScript theme={"system"} const { latestLedger } = await server.getHealth(); const { ledgers } = await server.getLedgers({ startLedger: latestLedger - 2, pagination: { limit: 3 }, }); for (const l of ledgers) { console.log(`ledger ${l.sequence} closed at ${new Date(l.ledgerCloseTime * 1000).toISOString()}`); } ``` ```text Output theme={"system"} ledger 64065203 closed at 2026-08-22T01:47:57.000Z ledger 64065204 closed at 2026-08-22T01:48:03.000Z ledger 64065205 closed at 2026-08-22T01:48:08.000Z ``` ## Python Use the official [`stellar-sdk`](https://github.com/StellarCN/py-stellar-base) and its `SorobanServer`, which speaks the Stellar RPC API. ```python Python theme={"system"} # pip install stellar-sdk from stellar_sdk import SorobanServer server = SorobanServer("YOUR_CHAINSTACK_ENDPOINT") health = server.get_health() print("get_health:", health.status, "| latest", health.latest_ledger, "| oldest", health.oldest_ledger) network = server.get_network() print("get_network:", network.passphrase, "| protocol", network.protocol_version) ledger = server.get_latest_ledger() print("get_latest_ledger: sequence", ledger.sequence) ``` ```text Output theme={"system"} get_health: healthy | latest 64065195 | oldest 63028396 get_network: Public Global Stellar Network ; September 2015 | protocol 27 get_latest_ledger: sequence 64065195 ``` # Sui GraphQL endpoint Source: https://docs.chainstack.com/docs/sui-graphql-endpoint Query Sui Mainnet over GraphQL RPC on Chainstack Global Nodes: endpoint and authentication, filtered transaction and event queries, history windows per query type, limits, and billing. Sui Mainnet Global Nodes on Chainstack serve Sui's [GraphQL RPC](https://docs.sui.io/develop/accessing-data/graphql/graphql-rpc) at `/graphql` on the same endpoint and auth token as gRPC. Use GraphQL for filtered, paginated reads — transactions by address, events by type, objects by owner and type — that have no gRPC equivalent. Each GraphQL request is 1 request unit (RU). ## Endpoint and authentication The GraphQL endpoint is your Sui Mainnet HTTPS endpoint with `/graphql` appended — `YOUR_CHAINSTACK_ENDPOINT/graphql`. To find your endpoint on Chainstack, see [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Send each query as an HTTP `POST` with a JSON body of `query` and, optionally, `variables`. `GET` requests return `404`, and the endpoint does not serve a GraphiQL IDE. The endpoint accepts the same credentials as the node's other interfaces: | Method | URL | Credential | | - | - | - | | Auth token in the URL | `YOUR_CHAINSTACK_ENDPOINT/graphql` | built into the URL | | Basic auth | `https://sui-mainnet.core.chainstack.com/graphql` | `YOUR_USER_NAME` and `YOUR_PASSWORD` | | x-token header | `https://sui-mainnet.core.chainstack.com/graphql` | `x-token: YOUR_X_TOKEN` header | A request without valid credentials returns `401`. Responses carry `Access-Control-Allow-Origin: *`, so browser apps can call the endpoint directly. For every authentication option, see [Authentication methods available on Chainstack](/docs/authentication-methods-for-different-scenarios). GraphQL is available on Sui Mainnet only. Sui Testnet nodes do not serve `/graphql`. ## Run a query The official Sui TypeScript SDK (`@mysten/sui`) includes a GraphQL client, `SuiGraphQLClient`, that takes the endpoint as its `url`. The client runs raw queries and also exposes the same `client.core` methods as the SDK's gRPC client. ```bash cURL theme={"system"} curl --request POST \ --url YOUR_CHAINSTACK_ENDPOINT/graphql \ --header 'Content-Type: application/json' \ --data '{"query": "{ chainIdentifier checkpoint { sequenceNumber digest timestamp } }"}' ``` ```typescript TypeScript theme={"system"} // npm install @mysten/sui import { SuiGraphQLClient } from '@mysten/sui/graphql'; import { graphql } from '@mysten/sui/graphql/schema'; const client = new SuiGraphQLClient({ url: 'YOUR_CHAINSTACK_ENDPOINT/graphql', network: 'mainnet', }); const result = await client.query({ query: graphql(` query { chainIdentifier checkpoint { sequenceNumber digest timestamp } } `), }); console.log(result.data); // The same client also serves the SDK's core API const { balance } = await client.core.getBalance({ owner: '0xb52b53331b8b7ce1592e9e2447911fb9af43aee835877bc0e6e501b6659408c3', }); console.log(balance.balance); ``` ```python Python theme={"system"} import requests query = """ query { chainIdentifier checkpoint { sequenceNumber digest timestamp } } """ response = requests.post( "YOUR_CHAINSTACK_ENDPOINT/graphql", json={"query": query}, ) print(response.json()["data"]) ``` Response: ```json theme={"system"} { "data": { "chainIdentifier": "4btiuiMPvEENsttpZC7CZ53DruC3MAgfznDbASZ7DR6S", "checkpoint": { "sequenceNumber": 328096486, "digest": "9GV9KiaQ2J3dtPdy11id46Cnz9ft7RJYX8PNufvAeNzy", "timestamp": "2026-09-29T01:10:15.471Z" } } } ``` ## Filter transactions and events The `transactions` and `events` queries take a `filter` argument, which is where GraphQL replaces JSON-RPC's `suix_queryTransactionBlocks` and `suix_queryEvents`: * `transactions` — filter by `affectedAddress`, `sentAddress`, `affectedObject`, `function`, or `kind`, and bound the range with `afterCheckpoint`, `atCheckpoint`, or `beforeCheckpoint` * `events` — filter by `type`, `module`, or `sender`, with the same checkpoint bounds This query returns the latest `OrderPlaced` event from DeepBook v3: ```graphql theme={"system"} query ($type: String!) { events(last: 1, filter: { type: $type }) { nodes { timestamp transaction { digest } contents { json } } } } ``` Variables: ```json theme={"system"} { "type": "0x2c8d603bc51326b8c13cef9dd07031a408a48dddb541963357661df5d3204809::order_info::OrderPlaced" } ``` Response: ```json theme={"system"} { "data": { "events": { "nodes": [ { "timestamp": "2026-09-29T01:10:14.789Z", "transaction": { "digest": "8jVMVLt73kCukre2bX7o5Ev8eFdjps23AdgokknCaXYQ" }, "contents": { "json": { "balance_manager_id": "0x08aa68cf0d865924df66611635885686f420bde78d8eb8a9741bc97ff0c715ff", "pool_id": "0xe05dafb5133bcffb8d59f4e12465dc0e9faeaa05e3e342a08fe135800e3e4407", "order_id": "21082248846264508727448301", "client_order_id": "15030299036365903758", "trader": "0x1a66b986f6e938c9f6d4cf7b98c97c331165cad5759e13fbbb1dee01728841dd", "price": "1142870", "is_bid": true, "placed_quantity": "1500000000", "expire_timestamp": "1790644514680", "timestamp": "1790644214716" } } } ] } } } ``` Lists are cursor-paginated. Page forward with `first` and `after`, or backward with `last` and `before`, and read the cursors from `pageInfo`. A page holds up to 50 items; requesting more fails with `Page size is too large`. ## How far back GraphQL history goes GraphQL history depends on the query type. Each kind of query reads from its own window, and the node reports every window through `serviceConfig.availableRange`: | Query type | Window | | - | - | | Point lookups and unfiltered lists — `checkpoint`, `transaction`, `object`, `objectVersions`, and `checkpoints` or `transactions` without a filter | Starts at the node's retention floor and grows as the chain grows | | Filtered transactions — any `filter` field on `transactions`, and `transactions` on an address — and all `events` | Rolling window of up to the most recent 90 days | | Live `balances` and owned `objects` | Current state, plus a short window of recent checkpoints for `atCheckpoint` reads | | Move `packages` | Every package since genesis | Query `availableRange` for the current window of a specific query and filter. `first` is the oldest checkpoint the query can reach: ```graphql theme={"system"} { serviceConfig { events: availableRange(type: "Query", field: "events") { first { sequenceNumber timestamp } } transactions: availableRange(type: "Query", field: "transactions") { first { sequenceNumber timestamp } } transactionsByAddress: availableRange(type: "Query", field: "transactions", filters: ["affectedAddress"]) { first { sequenceNumber timestamp } } } } ``` ```json theme={"system"} { "data": { "serviceConfig": { "events": { "first": { "sequenceNumber": 294775703, "timestamp": "2026-07-05T02:21:30.438Z" } }, "transactions": { "first": { "sequenceNumber": 237661232, "timestamp": "2026-01-23T22:30:07.386Z" } }, "transactionsByAddress": { "first": { "sequenceNumber": 294775698, "timestamp": "2026-07-05T02:21:29.341Z" } } } } } ``` A query outside its window returns an empty result, not an error. A filtered list returns no `nodes`, a `transaction` lookup returns `null`, and a `checkpoint` below the floor returns its `sequenceNumber` with every other field `null`. Check `availableRange` before you read an empty result as "no data". The windows move — the filtered window rolls forward with every checkpoint — so read `availableRange` at runtime instead of hard-coding checkpoint numbers. The gRPC interface on the same node reports the matching floor as `lowestAvailableCheckpoint` — see [Checkpoint retention](/docs/sui-grpc-endpoint#checkpoint-retention). For data older than these windows, query an archival service — see Sui's [Archival Store and Service](https://docs.sui.io/develop/accessing-data/archival-store). ## Query limits The node reports its query limits through `serviceConfig`. A query over a limit fails with a `GRAPHQL_VALIDATION_FAILED` error that names the limit. ```graphql theme={"system"} { serviceConfig { maxPageSize(type: "Query", field: "transactions") maxQueryPayloadSize maxQueryDepth maxQueryNodes maxMultiGetSize queryTimeoutMs } } ``` ```json theme={"system"} { "data": { "serviceConfig": { "maxPageSize": 50, "maxQueryPayloadSize": 5000, "maxQueryDepth": 20, "maxQueryNodes": 300, "maxMultiGetSize": 200, "queryTimeoutMs": 40000 } } } ``` To see how close a query comes to these limits, send it with the `x-sui-rpc-show-usage: true` header. The response then carries an `extensions.usage` object with the query's node count, depth, and payload size in bytes. ## Transactions and simulation GraphQL also covers writes. The `executeTransaction` mutation submits a signed transaction, and the `simulateTransaction` query dry-runs one, replacing JSON-RPC's `sui_dryRunTransactionBlock` and `sui_devInspectTransactionBlock`. With the TypeScript SDK, call `client.simulateTransaction()` and `client.signAndExecuteTransaction()` on a `SuiGraphQLClient` the same way as on the gRPC client. ## Streaming The GraphQL endpoint does not accept WebSocket connections, so GraphQL subscriptions are not available. To stream new checkpoints, use gRPC `SubscriptionService/SubscribeCheckpoints` on the same node — see [Sui tooling](/docs/sui-tooling#grpc-api). ## Billing Each GraphQL request is 1 RU, the same as a gRPC call on Sui. Sui has no archive billing split, so a query that reaches back to the window floor costs the same as one at the tip. See [Request units](/docs/request-units). ## Next steps * [Migrate Sui from JSON-RPC to gRPC and GraphQL](/docs/sui-migrate-json-rpc-to-grpc) — which JSON-RPC methods move to GraphQL and which to gRPC. * [Sui gRPC endpoint](/docs/sui-grpc-endpoint) — gRPC on the same node, and checkpoint retention. * [Sui tooling](/docs/sui-tooling) — SDKs and per-language connection code. * [GraphQL for Sui RPC](https://docs.sui.io/develop/accessing-data/graphql/graphql-rpc) — Sui's own GraphQL guide and schema reference. # Sui gRPC endpoint Source: https://docs.chainstack.com/docs/sui-grpc-endpoint The Chainstack Sui endpoint serves gRPC, the interface that replaces Sui JSON-RPC, with deep checkpoint retention and x-token authentication. Sui Mainnet Global Nodes also serve GraphQL on the same endpoint. The Chainstack Sui endpoint serves gRPC, the interface that replaces JSON-RPC, which Sui removes from the full node in v1.82 — see [Migrate Sui from JSON-RPC to gRPC and GraphQL](/docs/sui-migrate-json-rpc-to-grpc). Sui Mainnet Global Nodes also serve GraphQL for filtered queries on the same endpoint — see [Sui GraphQL endpoint](/docs/sui-graphql-endpoint). ## What you get * One endpoint — native gRPC over HTTP/2 and gRPC-Web over HTTPS, plus GraphQL on Mainnet. * Deep checkpoint retention — tens of millions of checkpoints of history for point lookups and backfills. * Server reflection enabled on gRPC — explore services and call methods with grpcurl, no proto compilation needed. * `x-token` authentication on gRPC; auth token, basic auth, or `x-token` on GraphQL. ## Endpoints Find your endpoint and credentials on Chainstack — see [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Copy the full HTTPS endpoint (shown below as `YOUR_CHAINSTACK_ENDPOINT`) — it already carries your auth token. | Interface | Endpoint | Authentication | | - | - | - | | gRPC | `sui-mainnet.core.chainstack.com:443` | `x-token` metadata header | | GraphQL (Mainnet) | `YOUR_CHAINSTACK_ENDPOINT/graphql` | auth token, basic auth, or `x-token` header | Testnet gRPC is `sui-testnet.core.chainstack.com:443`. The auth token and x-token are the node's access credentials, not your Chainstack platform API key. For every authentication option, see [Authentication methods available on Chainstack](/docs/authentication-methods-for-different-scenarios). ## Checkpoint retention The Chainstack Sui node holds a window of checkpoints from `lowestAvailableCheckpoint` to the tip, not the full ledger from genesis. `GetServiceInfo` reports both ends of the window — the tip (`checkpointHeight`) and the oldest checkpoint the node holds (`lowestAvailableCheckpoint`): ```bash theme={"system"} grpcurl -H "x-token: YOUR_X_TOKEN" \ sui-mainnet.core.chainstack.com:443 \ sui.rpc.v2.LedgerService/GetServiceInfo ``` ```json theme={"system"} { "chainId": "4btiuiMPvEENsttpZC7CZ53DruC3MAgfznDbASZ7DR6S", "chain": "mainnet", "epoch": "1263", "checkpointHeight": "327352270", "timestamp": "2026-09-27T02:41:27.573Z", "lowestAvailableCheckpoint": "237661232", "lowestAvailableCheckpointObjects": "237661232", "server": "sui-node/1.80.1-782577ca40c9" } ``` In this response from Sep 27, 2026, the node held checkpoints 237,661,232 through 327,352,270 — about 89.7M checkpoints. `lowestAvailableCheckpointObjects` is the matching floor for historical object versions. The floor can change, so read `lowestAvailableCheckpoint` at runtime instead of hard-coding it. How the window applies to each interface: * gRPC — checkpoints, transactions, and object versions older than the window return `NotFound`. * GraphQL — point lookups share this floor, and filtered transaction and event queries keep a shorter window. `serviceConfig.availableRange` reports each window — see [How far back GraphQL history goes](/docs/sui-graphql-endpoint#how-far-back-graphql-history-goes). For data older than the window, query an archival service — see Sui's [Archival Store and Service](https://docs.sui.io/develop/accessing-data/archival-store). ## Verify your endpoint ```bash theme={"system"} grpcurl -H "x-token: YOUR_X_TOKEN" \ sui-mainnet.core.chainstack.com:443 \ list ``` The `list` output shows the node's `sui.rpc.v2` services — [Sui tooling](/docs/sui-tooling#grpc-api) describes what each one is for. > [!NOTE] > > The official Sui TypeScript SDK (`@mysten/sui`) works here over both its default gRPC-Web transport and a native gRPC transport — see [the setup in Sui tooling](/docs/sui-tooling#grpc-from-code). gRPC-Web is served over HTTPS on Mainnet and Testnet, so browser apps can use the SDK directly, no proxy required. ## Next steps * [Migrate Sui from JSON-RPC to gRPC](/docs/sui-migrate-json-rpc-to-grpc) — call-by-call method mapping and quickstart. * [Sui tooling](/docs/sui-tooling) — SDKs and per-language connection code. # Migrate Sui from JSON-RPC to gRPC and GraphQL Source: https://docs.chainstack.com/docs/sui-migrate-json-rpc-to-grpc Sui removes JSON-RPC from the full node in v1.82. Migrate call by call to gRPC and GraphQL on Chainstack, on the same endpoint and auth token. Sui removes JSON-RPC from the full node in v1.82, so no Sui node serves JSON-RPC after the upgrade. On Chainstack, your Sui node serves gRPC, and Sui Mainnet Global Nodes also serve GraphQL, on the **same endpoint** and auth token. Port each JSON-RPC call to its gRPC or GraphQL equivalent with the [method mapping](#method-mapping), with no provider switch and no re-sync. > [!WARNING] > > Sui Testnet nodes on Chainstack no longer serve JSON-RPC. Sui Mainnet nodes stop serving it when they upgrade to v1.82, after Sui releases v1.82 for Mainnet. Sui retires JSON-RPC in stages, per [Sui's migration guide](https://docs.sui.io/develop/accessing-data/json-rpc-migration): | Milestone | Date | | - | - | | JSON-RPC disabled on Sui Foundation's Mainnet full nodes | Week of Jul 27, 2026 | | Sui stops publishing JSON-RPC snapshots | End of Aug 2026 | | Implicit fallback to the archival service disconnected | End of Sep 2026 | | JSON-RPC removed from the full node code in v1.82 | Testnet: Oct 15, 2026. Mainnet: with the v1.82 Mainnet release | Sui's guide gives Mainnet dates only. The Testnet date is when the Testnet network activated the v1.82 protocol. ## What is being removed Sui removes JSON-RPC in two steps. Sui Foundation turned off JSON-RPC on its **public** endpoints in July 2026. Sui v1.82 then removes the JSON-RPC service from the full node code itself, so a node on v1.82 does not serve JSON-RPC at all. Node operators cannot keep JSON-RPC enabled, because a node on an older version stops when the network activates the v1.82 protocol. gRPC and GraphQL are the interfaces Sui supports going forward. gRPC uses HTTP/2 and Protocol Buffers for binary serialization, which makes payloads smaller and adds server-side streaming — you subscribe to new checkpoints instead of polling for them. On Chainstack, the migration is **JSON-RPC → gRPC and GraphQL**, and both run on the same node: * Move performance-sensitive and streaming workloads to **gRPC** — backends, indexers, typed clients, low-latency lookups, transaction submission, and checkpoint streaming. * Move filtered and paginated queries to **GraphQL** on Mainnet — transactions by address, events by type, and objects by owner and type. These are the methods with no gRPC equivalent. See [Sui GraphQL endpoint](/docs/sui-graphql-endpoint). * On Testnet, which has no GraphQL endpoint, use **gRPC**. ## Chainstack Sui endpoints Both interfaces are on the same node. Copy your HTTPS endpoint (shown below as `YOUR_CHAINSTACK_ENDPOINT`, with its auth token built in) from Chainstack — see [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). The gRPC endpoint is the host and port below, and the GraphQL endpoint is the HTTPS endpoint with `/graphql` appended. | Interface | Endpoint | Authentication | | - | - | - | | gRPC | `sui-mainnet.core.chainstack.com:443` | `x-token` metadata header | | GraphQL (Mainnet) | `YOUR_CHAINSTACK_ENDPOINT/graphql` | auth token, basic auth, or `x-token` header | Testnet gRPC is `sui-testnet.core.chainstack.com:443`; Testnet has no GraphQL endpoint. For every authentication option, see [Authentication methods available on Chainstack](/docs/authentication-methods-for-different-scenarios). See [Sui tooling](/docs/sui-tooling) for SDK setup and per-language connection code. ## Method mapping Sui's gRPC API is organized into services under `sui.rpc.v2`. Most read and write JSON-RPC methods map directly to a gRPC call. The query methods with no gRPC equivalent map to GraphQL on Mainnet. A few analytics methods come from Sui's indexer, and neither interface serves them. The tables below give the path for each common JSON-RPC method. ### Maps to gRPC | JSON-RPC method | gRPC service and method | | - | - | | `sui_getChainIdentifier` | `LedgerService/GetServiceInfo` (read `chainId`) | | `sui_getLatestCheckpointSequenceNumber` | `LedgerService/GetServiceInfo` (read `checkpointHeight`) | | `sui_getCheckpoint` | `LedgerService/GetCheckpoint` | | `sui_getObject` | `LedgerService/GetObject` | | `sui_multiGetObjects` | `LedgerService/BatchGetObjects` | | `sui_getTransactionBlock` | `LedgerService/GetTransaction` | | `sui_multiGetTransactionBlocks` | `LedgerService/BatchGetTransactions` | | `suix_getLatestSuiSystemState` | `LedgerService/GetEpoch` (system state in read mask) | | `suix_getCommitteeInfo` | `LedgerService/GetEpoch` (committee in read mask) | | `sui_getProtocolConfig` | `LedgerService/GetEpoch` (protocol config in read mask) | | `suix_getBalance` | `StateService/GetBalance` | | `suix_getAllBalances` | `StateService/ListBalances` | | `suix_getCoins` | `StateService/ListOwnedObjects` (filter `type: 0x2::coin::Coin`) | | `suix_getCoinMetadata` | `StateService/GetCoinInfo` | | `suix_getTotalSupply` | `StateService/GetCoinInfo` | | `suix_getOwnedObjects` | `StateService/ListOwnedObjects` | | `suix_getDynamicFields` | `StateService/ListDynamicFields` | | `suix_getStakesByIds` | `LedgerService/BatchGetObjects` (the stake object IDs, with `json` in the read mask) | | `sui_dryRunTransactionBlock` | `TransactionExecutionService/SimulateTransaction` | | `sui_devInspectTransactionBlock` | `TransactionExecutionService/SimulateTransaction` (`checksEnabled: false`) | | `sui_executeTransactionBlock` | `TransactionExecutionService/ExecuteTransaction` | | `sui_getNormalizedMoveModule` | `MovePackageService/GetPackage` | | `sui_getNormalizedMoveFunction` | `MovePackageService/GetFunction` | | `sui_getNormalizedMoveStruct` | `MovePackageService/GetDatatype` | | `suix_resolveNameServiceAddress` | `NameService/LookupName` | | `suix_resolveNameServiceNames` | `NameService/ReverseLookupName` | ### No gRPC equivalent — use GraphQL These methods have no gRPC equivalent. On Mainnet, use the GraphQL query in the table. Testnet has no GraphQL endpoint, so these queries are available on Mainnet only. | JSON-RPC method | GraphQL query | | - | - | | `suix_queryTransactionBlocks` | `transactions` with a `filter`, or `transactions` on an `address` | | `suix_queryEvents` | `events` with a `filter` | | `sui_getCheckpoints` | `checkpoints` | | `suix_getStakes` | `objects` on an `address`, filtered by type `0x3::staking_pool::StakedSui` | | `suix_getValidatorsApy` | No GraphQL field. Derive APY from each validator's staking pool exchange rates across epochs, or use a staking analytics provider | | `suix_subscribeEvent`, `suix_subscribeTransaction` | No GraphQL subscriptions. Stream checkpoints over gRPC with `SubscriptionService/SubscribeCheckpoints` and filter transactions and events client-side | Filtered `transactions` and all `events` queries cover about the most recent 90 days, a shorter window than point lookups. See [How far back GraphQL history goes](/docs/sui-graphql-endpoint#how-far-back-graphql-history-goes). ### Indexer and analytics methods A few JSON-RPC methods return aggregated analytics that come from Sui's indexer layer, not from a full node. A full node — Sui's or Chainstack's — does not serve them over gRPC. Get the underlying data these ways: | JSON-RPC method | What to use instead | | - | - | | `suix_getEpochs` | On Mainnet, list epochs with the GraphQL `epochs` query; epochs that started before the GraphQL history window return `null` fields. For the current epoch's validators, committee, and gas price, call `LedgerService/GetEpoch` (gRPC). | | `suix_getNetworkMetrics`, `suix_getAddressMetrics`, `suix_getMoveCallMetrics` | Network analytics (TPS, active addresses, popular calls). Derive them by indexing your Chainstack node's checkpoint stream (gRPC `SubscriptionService/SubscribeCheckpoints`) into your own store, or use a Sui analytics provider. Most applications do not need these. | > [!NOTE] > > gRPC streaming exposes only `SubscribeCheckpoints`, not per-event subscriptions. For event streams, subscribe to checkpoints over gRPC and filter events client-side. ## gRPC quickstart The fastest way to confirm your endpoint and explore the API is [grpcurl](https://github.com/fullstorydev/grpcurl). Sui gRPC has server reflection enabled, so you can list services and call methods without compiling proto files. ```bash List services theme={"system"} grpcurl -H "x-token: YOUR_X_TOKEN" \ sui-mainnet.core.chainstack.com:443 \ list ``` ```bash Node and chain info theme={"system"} grpcurl -H "x-token: YOUR_X_TOKEN" \ sui-mainnet.core.chainstack.com:443 \ sui.rpc.v2.LedgerService/GetServiceInfo ``` ```bash Get a checkpoint theme={"system"} grpcurl -H "x-token: YOUR_X_TOKEN" \ -d '{"sequence_number": 296000000}' \ sui-mainnet.core.chainstack.com:443 \ sui.rpc.v2.LedgerService/GetCheckpoint ``` `GetServiceInfo` returns the chain, current epoch, and the node's checkpoint range: ```json theme={"system"} { "chainId": "4btiuiMPvEENsttpZC7CZ53DruC3MAgfznDbASZ7DR6S", "chain": "mainnet", "epoch": "1184", "checkpointHeight": "296723264", "lowestAvailableCheckpoint": "253124671", "server": "sui-node/1.74.1-8fc60f1fa966" } ``` To inspect any service's methods and message shapes, describe it: ```bash theme={"system"} grpcurl -H "x-token: YOUR_X_TOKEN" \ sui-mainnet.core.chainstack.com:443 \ describe sui.rpc.v2.LedgerService ``` ## gRPC from code In TypeScript, use the official Sui SDK (`@mysten/sui`). Its **default gRPC-Web transport** works against Chainstack over HTTPS — in the browser and in Node.js — so you only need your node URL as `baseUrl`. For server-side apps that prefer native gRPC over HTTP/2, pass a native transport instead (see [Sui tooling](/docs/sui-tooling#grpc-from-code) for the native setup). From other languages, generate stubs from [Sui's proto definitions](https://github.com/MystenLabs/sui/tree/main/crates/sui-rpc-api/proto) and attach the `x-token` metadata. > [!NOTE] > > Use recent client versions — `@mysten/sui` 2.16 or later (and `@grpc/grpc-js` 1.14 or later for the native transport). Older releases mishandle gRPC streams and produce intermittent `RST` and internal errors under load; current versions are reliable. ```typescript TypeScript theme={"system"} // npm install @mysten/sui // Default gRPC-Web transport over HTTPS — works in the browser and in Node.js. import { SuiGrpcClient } from '@mysten/sui/grpc'; const client = new SuiGrpcClient({ network: 'mainnet', baseUrl: 'YOUR_CHAINSTACK_SUI_HTTPS_ENDPOINT', }); // High-level helpers on client.core; raw services on client.ledgerService, etc. const gasPrice = await client.core.getReferenceGasPrice(); console.log(gasPrice.referenceGasPrice); ``` ```python Python theme={"system"} import grpc # Native gRPC channel over TLS; x-token metadata on every call channel = grpc.secure_channel( "sui-mainnet.core.chainstack.com:443", grpc.ssl_channel_credentials(), ) metadata = (("x-token", "YOUR_X_TOKEN"),) # Using stubs generated from Sui's protos, e.g. LedgerService: # from sui.rpc.v2 import ledger_service_pb2, ledger_service_pb2_grpc # stub = ledger_service_pb2_grpc.LedgerServiceStub(channel) # resp = stub.GetServiceInfo( # ledger_service_pb2.GetServiceInfoRequest(), metadata=metadata # ) # print(resp.checkpoint_height) ``` > [!NOTE] > > The default gRPC-Web transport shown above works in the browser and in Node.js. Native gRPC over HTTP/2 is server-side only; use it when you specifically need native gRPC. ## Next steps * [Sui tooling](/docs/sui-tooling) — SDKs, gRPC, and GraphQL connection setup. * [Sui gRPC endpoint](/docs/sui-grpc-endpoint) — checkpoint retention and authentication. * [Sui GraphQL endpoint](/docs/sui-graphql-endpoint) — GraphQL authentication, filters, history windows, and limits. * [Sui gRPC overview](https://docs.sui.io/concepts/grpc-overview) — Sui's own gRPC reference. * [Sui JSON-RPC migration guide](https://sdk.mystenlabs.com/sui/migrations/sui-2.0/json-rpc-migration) — the upstream mapping and SDK migration notes. # Sui: On-chain validator analytics with pysui Source: https://docs.chainstack.com/docs/sui-on-chain-validator-analytics-with-pysui Analyze Sui network validators, commission rates, and stake distribution using Python and the pysui library connected to a Chainstack Sui node over gRPC. **TLDR:** * Builds a comprehensive Sui blockchain validator analytics tool using Python and the pysui library over the gRPC API of a Chainstack Sui node. * Analyzes validator commission rates, stake distribution, and network decentralization metrics directly from on-chain data. * Provides interactive search functionality to explore individual validators and their detailed metadata including names, descriptions, and URLs. * Demonstrates how all validator data (financial metrics, operational data, and branding materials) is stored and accessible on-chain without external APIs. ## Overview In this tutorial, we'll build a simple analytics tool for the Sui blockchain network using Python and the `pysui` library. Our script will analyze validator performance metrics, commission rates, stake distribution, and provide interactive search capabilities for exploring the validator ecosystem. The Sui network relies on a proof-of-stake consensus mechanism where validators secure the network and process transactions. Our analytics tool will provide insights into: * Validator commission rates and distribution. * Stake concentration across validators. * Network epoch information. * Individual validator details and search functionality. ## Prerequisites * [pysui](https://pypi.org/project/pysui/) 1.x — Python SDK for Sui blockchain * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Sui RPC endpoint](https://chainstack.com/build-better-with-sui/) ## Implementation Our script connects to the Sui blockchain through the gRPC API of a Chainstack Sui node and retrieves comprehensive validator data directly from the chain. The Sui network stores all validator information on-chain, including not just technical metrics like stake amounts and commission rates, but also metadata such as validator names, descriptions, website URLs, and even profile images. Using the `pysui` gRPC client, we fetch the current system state which contains the complete list of active validators and their properties. pysui's gRPC client is async, so the script fetches the system state once and runs the analysis on the result. This includes financial data (stake balances, commission rates), operational data (voting power, gas prices), and presentation data (names, descriptions, project URLs, image URLs). The script processes this on-chain data to generate analytics on commission rate distributions, stake concentration, and provides search functionality to explore individual validators. The beauty of this approach is that all the data comes directly from the blockchain—there's no need for external APIs or databases. Everything from validator performance metrics to their branding materials is stored on-chain and accessible through our Chainstack node endpoint. The script: ```python theme={"system"} import asyncio import sys from pathlib import Path from typing import Any, List from pysui import PysuiConfiguration from pysui.sui.sui_common.config.confgroup import NetworkType from pysui.sui.sui_grpc.pgrpc_clients import GrpcProtocolClient import pysui.sui.sui_grpc.pgrpc_requests as rn CHAINSTACK_GRPC_URL = "https://sui-mainnet.core.chainstack.com:443" CHAINSTACK_X_TOKEN = "YOUR_X_TOKEN" CONFIG_DIR = Path("~/.pysui-chainstack").expanduser() def load_config() -> PysuiConfiguration: """Load the pysui configuration, creating a Chainstack profile on first run.""" try: return PysuiConfiguration(from_cfg_path=str(CONFIG_DIR), group_name="chainstack") except ValueError: cfg = PysuiConfiguration.initialize_config( in_folder=CONFIG_DIR, init_groups=[{"name": "chainstack", "make_active": True}], ) cfg.new_profile( profile_name="mainnet", url=CHAINSTACK_GRPC_URL, network_type=NetworkType.PRODUCTION, make_active=True, in_group="chainstack", ) return cfg class SuiAnalytics: def __init__(self, cfg: PysuiConfiguration, x_token: str): self.cfg = cfg self.x_token = x_token self._system_state = None async def load(self) -> None: """Fetch the current system state, which holds the active validator set.""" client = GrpcProtocolClient(pysui_config=self.cfg, default_header={"x-token": self.x_token}) result = await client.execute_grpc_request(request=rn.GetLatestSuiSystemState()) await client.close() if not result.is_ok(): raise Exception(f"Failed to fetch system state: {result.result_string}") self._system_state = result.result_data def _get_validators(self) -> List[Any]: return self._system_state.validators.active_validators def analyze_commission_rates(self) -> None: """Analyze and display validator commission rates.""" validators = self._get_validators() # Commission rates are in basis points commission_rates = sorted(int(v.commission_rate) / 100 for v in validators) min_commission = min(commission_rates) max_commission = max(commission_rates) avg_commission = sum(commission_rates) / len(commission_rates) # Median for both even and odd length lists mid = len(commission_rates) // 2 median_commission = commission_rates[mid] if len(commission_rates) % 2 == 1 else (commission_rates[mid-1] + commission_rates[mid]) / 2 print(f"\n📊 Validator Commission Rate Analysis") print("=" * 50) print(f"Total Validators: {len(validators)}") print(f"Minimum Commission: {min_commission:.2f}%") print(f"Maximum Commission: {max_commission:.2f}%") print(f"Average Commission: {avg_commission:.2f}%") print(f"Median Commission: {median_commission:.2f}%") ranges = [(0, 1), (1, 2), (2, 3), (3, 5), (5, 10), (10, 100)] print(f"\n📈 Commission Rate Distribution:") for min_rate, max_rate in ranges: count = sum(1 for rate in commission_rates if min_rate <= rate < max_rate) percentage = (count / len(commission_rates)) * 100 print(f" {min_rate:.0f}%-{max_rate:.0f}%: {count:>3} validators ({percentage:5.1f}%)") def show_lowest_commission_validators(self, top_n: int = 10) -> None: """Show validators with the lowest commission rates.""" validators = self._get_validators() sorted_by_commission = sorted(validators, key=lambda v: int(v.commission_rate)) print(f"\n💰 Top {top_n} Validators by Lowest Commission Rate") print("=" * 65) print(f"{'Rank':<4} {'Validator Name':<25} {'Commission':<10} {'Stake (SUI)':<15}") print("=" * 65) for rank, validator in enumerate(sorted_by_commission[:top_n], 1): name = (validator.name or "Unknown")[:24] commission = int(validator.commission_rate) / 100 stake = int(validator.staking_pool.sui_balance) / 1e9 print(f"{rank:<4} {name:<25} {commission:>8.1f}% {stake:>13,.0f}") def analyze_stake_distribution(self) -> None: """Analyze how stake is distributed across validators.""" validators = self._get_validators() stakes = sorted((int(v.staking_pool.sui_balance) / 1e9 for v in validators), reverse=True) total_stake = sum(stakes) print(f"\n📊 Stake Distribution Analysis") print("=" * 50) print(f"Total Network Stake: {total_stake:,.0f} SUI") top_10_stake = sum(stakes[:10]) top_25_stake = sum(stakes[:25]) top_50_stake = sum(stakes[:50]) print(f"\nStake Concentration:") print(f" Top 10 validators: {top_10_stake:,.0f} SUI ({top_10_stake/total_stake*100:.1f}%)") print(f" Top 25 validators: {top_25_stake:,.0f} SUI ({top_25_stake/total_stake*100:.1f}%)") print(f" Top 50 validators: {top_50_stake:,.0f} SUI ({top_50_stake/total_stake*100:.1f}%)") ranges = [ (0, 1_000_000), (1_000_000, 5_000_000), (5_000_000, 10_000_000), (10_000_000, 25_000_000), (25_000_000, 50_000_000), (50_000_000, float('inf')) ] print(f"\n📈 Validator Stake Ranges:") for min_stake, max_stake in ranges: if max_stake == float('inf'): count = sum(1 for stake in stakes if stake >= min_stake) range_label = f">{min_stake/1_000_000:.0f}M SUI" else: count = sum(1 for stake in stakes if min_stake <= stake < max_stake) range_label = f"{min_stake/1_000_000:.0f}M-{max_stake/1_000_000:.0f}M SUI" percentage = (count / len(stakes)) * 100 print(f" {range_label:<15}: {count:>3} validators ({percentage:5.1f}%)") def show_epoch_info(self) -> None: """Display current epoch information.""" state = self._system_state validators = self._get_validators() total_stake = sum(int(v.staking_pool.sui_balance) for v in validators) / 1e9 print(f"\n🕐 Current Epoch Information") print("=" * 40) print(f"Current Epoch: {state.epoch}") print(f"Protocol Version: {state.protocol_version}") print(f"Active Validators: {len(validators)}") print(f"Total Staked: {total_stake:,.0f} SUI") def find_validators_by_name(self, search_term: str) -> List[Any]: """Search for validators by name.""" search_lower = search_term.lower() matches = [v for v in self._get_validators() if search_lower in (v.name or "").lower()] if not matches: print(f"\n❌ No validators found containing '{search_term}'") return matches print(f"\n🔍 Validators matching '{search_term}' ({len(matches)} found)") print("=" * 70) print(f"{'Name':<25} {'Stake (SUI)':<15} {'Commission':<10} {'Address'}") print("=" * 70) for validator in matches: name = (validator.name or "Unknown")[:24] stake = int(validator.staking_pool.sui_balance) / 1e9 commission = int(validator.commission_rate) / 100 print(f"{name:<25} {stake:>13,.0f} {commission:>8.1f}% {validator.address}") return matches def show_validator_details(self, validator: Any) -> None: """Show detailed information about a validator.""" print(f"\n🔍 Detailed Information for '{validator.name}'") print("=" * 60) print(f"Name: {validator.name}") print(f"Address: {validator.address}") print(f"Staking Pool ID: {validator.staking_pool.id}") print(f"Total Stake: {int(validator.staking_pool.sui_balance) / 1e9:,.0f} SUI") print(f"Commission Rate: {int(validator.commission_rate) / 100:.2f}%") print(f"Gas Price: {validator.gas_price}") print(f"Voting Power: {validator.voting_power}") if validator.description: print(f"Description: {validator.description}") if validator.image_url: print(f"Image URL: {validator.image_url}") if validator.project_url: print(f"Project URL: {validator.project_url}") print("=" * 60) def main(): """Main function to run Sui analytics.""" print("🔬 Sui analytics") print("=" * 50) try: analytics = SuiAnalytics(load_config(), CHAINSTACK_X_TOKEN) asyncio.run(analytics.load()) analytics.show_epoch_info() analytics.analyze_commission_rates() analytics.show_lowest_commission_validators(10) analytics.analyze_stake_distribution() # Interactive search print(f"\n{'='*50}") search_term = input("Enter a validator name to search for (or press Enter to skip): ").strip() if search_term: matches = analytics.find_validators_by_name(search_term) # Offer details if there is exactly one match if len(matches) == 1: print(f"\n{'='*50}") show_details = input("Show detailed information for this validator? (y/n): ").lower().strip() if show_details in ['y', 'yes']: analytics.show_validator_details(matches[0]) except KeyboardInterrupt: print("\n👋 Analysis interrupted by user") except Exception as e: print(f"❌ An error occurred: {e}") sys.exit(1) if __name__ == "__main__": main() ``` ## Usage examples ### Running the analytics tool 1. **Install dependencies**: ```bash theme={"system"} pip install pysui ``` 2. **Configure your Chainstack endpoint**: Replace `YOUR_X_TOKEN` in the script with your node's x-token. To find it on Chainstack, see [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). On the first run, the script creates a pysui configuration in `~/.pysui-chainstack` with your Chainstack gRPC endpoint. 3. **Run the script**: ```bash theme={"system"} python sui_analytics.py ``` ### Sample output When you run the script, you'll see output similar to: ``` 🔬 Sui analytics ================================================== 🕐 Current Epoch Information ======================================== Current Epoch: 1286 Protocol Version: 138 Active Validators: 125 Total Staked: 7,066,702,476 SUI 📊 Validator Commission Rate Analysis ================================================== Total Validators: 125 Minimum Commission: 0.00% Maximum Commission: 20.00% Average Commission: 7.36% Median Commission: 8.00% 📈 Commission Rate Distribution: 0%-1%: 3 validators ( 2.4%) 1%-2%: 0 validators ( 0.0%) 2%-3%: 5 validators ( 4.0%) 3%-5%: 12 validators ( 9.6%) 5%-10%: 75 validators ( 60.0%) 10%-100%: 30 validators ( 24.0%) 💰 Top 10 Validators by Lowest Commission Rate ================================================================= Rank Validator Name Commission Stake (SUI) ================================================================= 1 OKXEarn 0.0% 83,161,491 2 Haedal Protocol 0.0% 38,454,572 3 YOLOTRUMP 0.0% 2,552,369 4 Mysten-2 2.0% 209,763,927 5 Mysten-1 2.0% 218,822,643 6 ORBR 2.0% 53,944,397 7 Ankr 2.0% 20,025,770 8 0xb5ba8c5d646594e8811afa 2.5% 24,740,522 9 mrgn 3.0% 104,404,753 10 0x195454d23060552575a68c 3.0% 39,880,704 📊 Stake Distribution Analysis ================================================== Total Network Stake: 7,066,702,476 SUI Stake Concentration: Top 10 validators: 1,521,284,183 SUI (21.5%) Top 25 validators: 3,092,547,395 SUI (43.8%) Top 50 validators: 4,881,964,722 SUI (69.1%) 📈 Validator Stake Ranges: 0M-1M SUI : 0 validators ( 0.0%) 1M-5M SUI : 2 validators ( 1.6%) 5M-10M SUI : 4 validators ( 3.2%) 10M-25M SUI : 17 validators ( 13.6%) 25M-50M SUI : 50 validators ( 40.0%) >50M SUI : 52 validators ( 41.6%) ``` ## Conclusion This tutorial demonstrates how to build a comprehensive validator analytics tool for the Sui blockchain using Python and pysui. The script provides insights into validator economics, network decentralization, and helps users make informed decisions about staking and network participation. The entirety of the data is retrieved directly from the chain. The analytics tool serves multiple stakeholders in the Sui ecosystem, from individual delegators to network researchers, providing the data needed to maintain a healthy and decentralized network. By leveraging Chainstack's reliable Sui node infrastructure and the powerful pysui library, developers can build sophisticated blockchain analytics tools that provide real value to the Web3 community. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Sui tooling Source: https://docs.chainstack.com/docs/sui-tooling Sui blockchain development tools guide. Use gRPC, GraphQL, the TypeScript SDK, Rust SDK, Python pysui, and Go SDK to build Sui applications with Chainstack. Get started with a [reliable Sui RPC endpoint](https://chainstack.com/build-better-with-sui/) to use the tools below. Your Chainstack Sui node serves gRPC, and Sui Mainnet Global Nodes also serve GraphQL on the same endpoint. > [!WARNING] > > Sui removes JSON-RPC from the full node in v1.82. Sui Testnet nodes no longer serve JSON-RPC, and Sui Mainnet nodes stop serving it with the v1.82 upgrade — see [Migrate Sui from JSON-RPC to gRPC and GraphQL](/docs/sui-migrate-json-rpc-to-grpc). ## Extended history Sui Global Nodes are [Extended](/docs/extended-nodes). The node holds a window of checkpoints from `lowestAvailableCheckpoint` to the tip, not the full ledger from genesis. `LedgerService/GetServiceInfo` reports the current window — see [Checkpoint retention](/docs/sui-grpc-endpoint#checkpoint-retention) for how the window applies to gRPC and GraphQL. ## gRPC API Sui nodes on Chainstack support gRPC for high-performance access. gRPC uses HTTP/2 and Protocol Buffers for binary serialization, which makes payloads smaller and adds server-side streaming — ideal for indexers, backends, and low-latency lookups. gRPC replaces JSON-RPC, which Sui removes from the full node in v1.82. ### Endpoint and authentication Your Sui gRPC endpoint is `sui-mainnet.core.chainstack.com:443` (testnet: `sui-testnet.core.chainstack.com:443`), with TLS. Authenticate by passing your token in the `x-token` metadata header on every call. To find your endpoint and token on Chainstack, see [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Explore with grpcurl Sui gRPC has server reflection enabled, so [grpcurl](https://github.com/fullstorydev/grpcurl) can list services and call methods without compiling proto files: ```bash List services theme={"system"} grpcurl -H "x-token: YOUR_X_TOKEN" \ sui-mainnet.core.chainstack.com:443 \ list ``` ```bash Node and chain info theme={"system"} grpcurl -H "x-token: YOUR_X_TOKEN" \ sui-mainnet.core.chainstack.com:443 \ sui.rpc.v2.LedgerService/GetServiceInfo ``` The node exposes six `sui.rpc.v2` services: | Service | Use it for | | - | - | | `LedgerService` | Checkpoints, objects, transactions, epochs, and node info | | `StateService` | Balances, coins, owned objects, and dynamic fields | | `TransactionExecutionService` | Executing and simulating transactions | | `SubscriptionService` | Streaming checkpoints | | `MovePackageService` | Move packages, modules, functions, and datatypes | | `NameService` | SuiNS name resolution | ### gRPC from code In TypeScript, use the official `@mysten/sui` SDK. Its default gRPC-Web transport works against Chainstack over HTTPS — including from the browser — so the simplest setup needs only your node URL as `baseUrl`. For server-side apps that prefer native gRPC over HTTP/2, pass a native transport instead. From other languages, generate typed stubs from [Sui's proto definitions](https://github.com/MystenLabs/sui/tree/main/crates/sui-rpc-api/proto) and connect over native gRPC. Authenticate with your node's `x-token` — carried in the endpoint URL, or as `x-token` metadata on native-gRPC calls — and use recent client versions — `@mysten/sui` 2.16+ and `@grpc/grpc-js` 1.14+ — since older releases produce intermittent gRPC errors under load. ```typescript TypeScript (gRPC-Web) theme={"system"} // npm install @mysten/sui // Default gRPC-Web transport over HTTPS — works in the browser and in Node.js. import { SuiGrpcClient } from '@mysten/sui/grpc'; const client = new SuiGrpcClient({ network: 'mainnet', baseUrl: 'YOUR_CHAINSTACK_SUI_HTTPS_ENDPOINT', }); const gasPrice = await client.core.getReferenceGasPrice(); console.log(gasPrice.referenceGasPrice); ``` ```typescript TypeScript (native gRPC) theme={"system"} // npm install @mysten/sui @protobuf-ts/grpc-transport @grpc/grpc-js // Native gRPC over HTTP/2 — server runtimes only. import { SuiGrpcClient } from '@mysten/sui/grpc'; import { GrpcTransport } from '@protobuf-ts/grpc-transport'; import { ChannelCredentials } from '@grpc/grpc-js'; const client = new SuiGrpcClient({ network: 'mainnet', transport: new GrpcTransport({ host: 'sui-mainnet.core.chainstack.com:443', channelCredentials: ChannelCredentials.createSsl(), meta: { 'x-token': 'YOUR_X_TOKEN' }, }), }); const gasPrice = await client.core.getReferenceGasPrice(); console.log(gasPrice.referenceGasPrice); ``` ```python Python theme={"system"} import grpc channel = grpc.secure_channel( "sui-mainnet.core.chainstack.com:443", grpc.ssl_channel_credentials(), ) metadata = (("x-token", "YOUR_X_TOKEN"),) # With stubs generated from Sui's protos: # stub = ledger_service_pb2_grpc.LedgerServiceStub(channel) # resp = stub.GetServiceInfo( # ledger_service_pb2.GetServiceInfoRequest(), metadata=metadata # ) # print(resp.checkpoint_height) ``` ```javascript Node.js theme={"system"} const grpc = require('@grpc/grpc-js'); const credentials = grpc.credentials.createSsl(); const metadata = new grpc.Metadata(); metadata.add('x-token', 'YOUR_X_TOKEN'); // With stubs generated from Sui's protos: // const client = new LedgerServiceClient( // 'sui-mainnet.core.chainstack.com:443', credentials); // client.getServiceInfo({}, metadata, (err, resp) => { // console.log(resp.checkpointHeight); // }); ``` ```go Go theme={"system"} package main import ( "context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" ) func main() { creds := credentials.NewTLS(nil) conn, err := grpc.Dial( "sui-mainnet.core.chainstack.com:443", grpc.WithTransportCredentials(creds), ) if err != nil { panic(err) } defer conn.Close() ctx := metadata.AppendToOutgoingContext( context.Background(), "x-token", "YOUR_X_TOKEN", ) _ = ctx // pass ctx to your generated gRPC client calls } ``` > [!NOTE] > > The default gRPC-Web transport works in both the browser and server runtimes, so it is the simplest choice for most apps. The native gRPC transport (HTTP/2) is server-side only — use it when you specifically need native gRPC. ## GraphQL API Sui Mainnet Global Nodes serve Sui's GraphQL RPC at `YOUR_CHAINSTACK_ENDPOINT/graphql`, with the same auth token as your HTTPS endpoint. Use GraphQL for filtered, paginated reads — transactions by address, events by type, objects by owner and type — that have no gRPC equivalent. GraphQL is not available on Sui Testnet. In TypeScript, use `SuiGraphQLClient` from the official `@mysten/sui` SDK: ```typescript TypeScript theme={"system"} // npm install @mysten/sui import { SuiGraphQLClient } from '@mysten/sui/graphql'; import { graphql } from '@mysten/sui/graphql/schema'; const client = new SuiGraphQLClient({ url: 'YOUR_CHAINSTACK_ENDPOINT/graphql', network: 'mainnet', }); const result = await client.query({ query: graphql(` query { checkpoint { sequenceNumber timestamp } } `), }); console.log(result.data); ``` Filtered queries and events keep a shorter history window than point lookups. For authentication options, filters, history windows, and query limits, see [Sui GraphQL endpoint](/docs/sui-graphql-endpoint). ## Sui TypeScript SDK The [Sui TypeScript SDK](https://github.com/MystenLabs/ts-sdks/tree/main/packages/sui) is the official SDK for building Sui applications. Use its gRPC client, `SuiGrpcClient`, for reads and transactions, and `SuiGraphQLClient` for filtered queries — see [GraphQL API](#graphql-api). ### Installation ```bash theme={"system"} npm install @mysten/sui ``` > [!NOTE] > > The SDK's JSON-RPC client, `SuiJsonRpcClient` from `@mysten/sui/jsonRpc`, stops working when JSON-RPC is removed. `SuiGrpcClient` covers the same reads and transactions through methods such as `getObject`, `listOwnedObjects`, `getBalance`, and `signAndExecuteTransaction`. See the [SDK 2.0 migration guide](https://sdk.mystenlabs.com/sui/migrations/sui-2.0). ### Basic usage ```typescript Connection theme={"system"} import { SuiGrpcClient } from '@mysten/sui/grpc'; // Connect to your Chainstack node const client = new SuiGrpcClient({ network: 'mainnet', baseUrl: 'YOUR_CHAINSTACK_SUI_HTTPS_ENDPOINT', }); // Get the reference gas price for the current epoch const { referenceGasPrice } = await client.getReferenceGasPrice(); console.log('Reference gas price:', referenceGasPrice); ``` ```typescript Query Objects theme={"system"} import { SuiGrpcClient } from '@mysten/sui/grpc'; const client = new SuiGrpcClient({ network: 'mainnet', baseUrl: 'YOUR_CHAINSTACK_SUI_HTTPS_ENDPOINT', }); // Get objects owned by an address const { objects } = await client.listOwnedObjects({ owner: 'SUI_ADDRESS', limit: 10, }); for (const object of objects) { console.log(object.objectId, object.type); } ``` ```typescript Send Transaction theme={"system"} import { SuiGrpcClient } from '@mysten/sui/grpc'; import { Transaction } from '@mysten/sui/transactions'; import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'; const client = new SuiGrpcClient({ network: 'mainnet', baseUrl: 'YOUR_CHAINSTACK_SUI_HTTPS_ENDPOINT', }); const keypair = Ed25519Keypair.generate(); const tx = new Transaction(); // Replace with your object ID and recipient address const objectId = 'YOUR_OBJECT_ID'; const recipient = 'RECIPIENT_ADDRESS'; // Add transaction commands tx.transferObjects([objectId], recipient); // Sign and execute const result = await client.signAndExecuteTransaction({ signer: keypair, transaction: tx, }); ``` ## Sui Rust SDK The [`sui-rpc`](https://github.com/MystenLabs/sui-rust-sdk/tree/master/crates/sui-rpc) crate from Mysten Labs' Sui Rust SDK is a gRPC client for Sui, published on crates.io. ### Installation Add to your `Cargo.toml`: ```toml theme={"system"} sui-rpc = "0.4" tokio = { version = "1", features = ["full"] } anyhow = "1" ``` ### Basic usage ```rust theme={"system"} use sui_rpc::client::{Client, HeadersInterceptor}; use sui_rpc::proto::sui::rpc::v2::GetServiceInfoRequest; #[tokio::main] async fn main() -> anyhow::Result<()> { let mut headers = HeadersInterceptor::new(); headers.headers_mut().insert("x-token", "YOUR_X_TOKEN".parse()?); let mut client = Client::new("https://sui-mainnet.core.chainstack.com")?.with_headers(headers); let info = client .ledger_client() .get_service_info(GetServiceInfoRequest::default()) .await? .into_inner(); println!("Chain: {:?}, checkpoint: {:?}", info.chain, info.checkpoint_height); Ok(()) } ``` ## Python SDK (pysui) [pysui](https://github.com/FrankC01/pysui) is a community-maintained Python SDK for Sui. pysui 1.x connects over gRPC and GraphQL, and its gRPC client is async. pysui reads its connection settings from a configuration folder, so the example creates one with a Chainstack profile on the first run and passes your x-token as a header on every call. ### Installation ```bash theme={"system"} pip install pysui ``` ### Basic usage ```python theme={"system"} import asyncio from pathlib import Path from pysui import PysuiConfiguration from pysui.sui.sui_common.config.confgroup import NetworkType from pysui.sui.sui_grpc.pgrpc_clients import GrpcProtocolClient import pysui.sui.sui_grpc.pgrpc_requests as rn CONFIG_DIR = Path("~/.pysui-chainstack").expanduser() # Create a pysui configuration with a Chainstack profile on first run try: cfg = PysuiConfiguration(from_cfg_path=str(CONFIG_DIR), group_name="chainstack") except ValueError: cfg = PysuiConfiguration.initialize_config( in_folder=CONFIG_DIR, init_groups=[{"name": "chainstack", "make_active": True}], ) cfg.new_profile( profile_name="mainnet", url="https://sui-mainnet.core.chainstack.com:443", network_type=NetworkType.PRODUCTION, make_active=True, in_group="chainstack", ) async def main(): client = GrpcProtocolClient(pysui_config=cfg, default_header={"x-token": "YOUR_X_TOKEN"}) result = await client.execute_grpc_request(request=rn.GetServiceInfo()) if result.is_ok(): print(f"Latest checkpoint: {result.result_data.checkpoint_height}") else: print(result.result_string) await client.close() asyncio.run(main()) ``` For a complete pysui program, see [Sui: On-chain validator analytics with pysui](/docs/sui-on-chain-validator-analytics-with-pysui). ## Go SDK The [Sui Go SDK](https://github.com/block-vision/sui-go-sdk) from BlockVision provides Go language bindings for Sui, including a gRPC client. The client dials without TLS by default, so pass TLS credentials for your Chainstack endpoint. ### Installation ```bash theme={"system"} go get github.com/block-vision/sui-go-sdk ``` ### Basic usage ```go theme={"system"} package main import ( "context" "fmt" "log" "github.com/block-vision/sui-go-sdk/common/grpcconn" v2 "github.com/block-vision/sui-go-sdk/pb/sui/rpc/v2" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func main() { client := grpcconn.NewSuiGrpcClientWithAuth( "sui-mainnet.core.chainstack.com:443", "YOUR_X_TOKEN", grpcconn.WithDialOptions(grpc.WithTransportCredentials(credentials.NewTLS(nil))), ) defer client.Close() ctx := context.Background() ledger, err := client.LedgerService(ctx) if err != nil { log.Fatal(err) } info, err := ledger.GetServiceInfo(ctx, &v2.GetServiceInfoRequest{}) if err != nil { log.Fatal(err) } fmt.Printf("Chain: %s, checkpoint: %d\n", info.GetChain(), info.GetCheckpointHeight()) } ``` # Tempo methods Source: https://docs.chainstack.com/docs/tempo-methods Complete reference of supported Tempo JSON-RPC methods on Chainstack nodes, including eth, debug, and trace namespace method availability details. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive Tempo API call examples](/reference/tempo-getting-started). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blobBaseFee | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_createAccessList | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_protocolVersion | | | | eth\_sendRawTransaction | | | | eth\_sendRawTransactionSync | | Tempo-specific | | eth\_syncing | | | | eth\_uninstallFilter | | | | debug\_getBadBlocks | | | | debug\_getRawBlock | | | | debug\_getRawHeader | | | | debug\_getRawReceipts | | | | debug\_getRawTransaction | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceCallMany | | | | debug\_traceTransaction | | | | trace\_block | | | | trace\_call | | | | trace\_callMany | | | | trace\_filter | | | | trace\_get | | | | trace\_rawTransaction | | | | trace\_replayBlockTransactions | | | | trace\_replayTransaction | | | | trace\_transaction | | | | txpool\_content | | Dedicated only | | txpool\_status | | Dedicated only | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | tempo\_fundAddress | | Testnet public RPC only | # Tempo tooling Source: https://docs.chainstack.com/docs/tempo-tooling Set up MetaMask, Hardhat, Foundry, and the tempo-foundry CLI to build and deploy smart contracts on Tempo through your Chainstack RPC node endpoints. > [!NOTE] > > **Stablecoin gas fees**: Tempo has no native gas token. Transaction fees are paid in TIP-20 stablecoins (like pathUSD). When developing on testnet, get stablecoins from the [faucet](/reference/tempo-tempo-fundaddress). On mainnet, acquire stablecoins from issuers or ecosystem partners. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet** to inject your Chainstack endpoint automatically. If you need to add the network manually, use: **Mainnet:** * Network name: `Tempo Mainnet` * RPC URL: your Chainstack HTTPS endpoint * Chain ID: `4217` * Currency symbol: `USD` * Block explorer URL: `https://explore.mainnet.tempo.xyz` **Testnet:** * Network name: `Tempo Testnet (Moderato)` * RPC URL: your Chainstack HTTPS endpoint * Chain ID: `42431` * Currency symbol: `USD` * Block explorer URL: `https://explore.tempo.xyz` Chainstack provides both HTTPS and WSS endpoints for Tempo. Use HTTPS for MetaMask and general RPC calls. Use WSS for real-time subscriptions in your applications. Refer to [node access details](/docs/manage-your-node#view-node-access-and-credentials) for your endpoint URL format and credentials. > [!TIP] > > MetaMask may show an unusually high balance for the native token. This is expected—Tempo has no native gas token, and wallets see a placeholder value. Fees are paid in TIP-20 stablecoins denominated in USD. Check your stablecoin balances by adding the token addresses to MetaMask. ### Stablecoin addresses These addresses are the same on both mainnet and testnet: | Token | Address | | - | - | | pathUSD | `0x20c0000000000000000000000000000000000000` | | AlphaUSD | `0x20c0000000000000000000000000000000000001` | | BetaUSD | `0x20c0000000000000000000000000000000000002` | | ThetaUSD | `0x20c0000000000000000000000000000000000003` | ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Tempo nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s' % ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` ### WebSocket Use the `WebSocketProvider` to connect over WebSocket. WebSocket connections enable subscriptions for real-time events like new blocks and pending transactions. ```python Key protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebSocketProvider('YOUR_CHAINSTACK_WSS_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebSocketProvider('wss://%s:%s@%s' % ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * YOUR\_CHAINSTACK\_WSS\_ENDPOINT — your node WSS endpoint protected either with the key or password * USERNAME — the username to your node if you use a password-protected endpoint * PASSWORD — the password to your node if you use a password-protected endpoint * HOSTNAME — the host name of your node Tempo network IDs: * Mainnet: `4217` * Moderato Testnet: `42431` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). > [!NOTE] > > web3.py works for basic JSON-RPC operations but does not support Tempo's native `feeToken` parameter. For full Tempo Transaction support in Python, use [pytempo](#pytempo) instead. For TypeScript, use [Viem](#viem). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Tempo nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP or WebSocket. ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Mainnet theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT", { chainId: 4217, name: "tempo" }); provider.getBlockNumber().then(console.log); ``` ```javascript Testnet theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT", { chainId: 42431, name: "tempo-testnet" }); provider.getBlockNumber().then(console.log); ``` ### WebSocket Use the `WebSocketProvider` to connect over WebSocket. WebSocket connections enable subscriptions for real-time events like new blocks and pending transactions. ```javascript Mainnet theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.WebSocketProvider("YOUR_CHAINSTACK_WSS_ENDPOINT", { chainId: 4217, name: "tempo" }); // Get block number provider.getBlockNumber().then(console.log); // Subscribe to new blocks provider.on("block", (blockNumber) => { console.log("New block:", blockNumber); }); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * YOUR\_CHAINSTACK\_WSS\_ENDPOINT — your node WSS endpoint protected either with the key or password See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). > [!NOTE] > > ethers.js works for basic JSON-RPC operations but does not support Tempo's native `feeToken` parameter. For transactions, Tempo uses a cascading fee algorithm that defaults to pathUSD for non-TIP20 contract interactions. For full Tempo Transaction support (fee token selection, fee sponsorship, batch transactions), use [Viem](#viem) instead. ## Viem [Viem](https://viem.sh) is the recommended TypeScript library for Tempo development. Starting from `viem@2.43.0`, Viem has native Tempo support with full access to Tempo Transaction features. 1. Install Viem: ```bash theme={"system"} npm install viem ``` 2. Create a client configured for Tempo: ```typescript Mainnet theme={"system"} import { createPublicClient, createWalletClient, http } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { tempo as tempoChain } from 'viem/chains' import { tempo } from 'viem/tempo' // Public client for read operations const publicClient = createPublicClient({ chain: tempoChain, transport: http('YOUR_CHAINSTACK_ENDPOINT'), }).extend(tempo()) // Wallet client for transactions const walletClient = createWalletClient({ account: privateKeyToAccount('YOUR_PRIVATE_KEY'), chain: tempoChain, transport: http('YOUR_CHAINSTACK_ENDPOINT'), }).extend(tempo()) // Get block number const blockNumber = await publicClient.getBlockNumber() console.log(blockNumber) ``` ```typescript Testnet theme={"system"} import { createPublicClient, createWalletClient, http } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import { tempoModerato } from 'viem/chains' import { tempo } from 'viem/tempo' // Public client for read operations const publicClient = createPublicClient({ chain: tempoModerato, transport: http('YOUR_CHAINSTACK_ENDPOINT'), }).extend(tempo()) // Wallet client for transactions const walletClient = createWalletClient({ account: privateKeyToAccount('YOUR_PRIVATE_KEY'), chain: tempoModerato, transport: http('YOUR_CHAINSTACK_ENDPOINT'), }).extend(tempo()) // Get block number const blockNumber = await publicClient.getBlockNumber() console.log(blockNumber) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * YOUR\_PRIVATE\_KEY — the private key of your account (with `0x` prefix) > [!NOTE] > > Use `tempoModerato` for the active testnet (chain ID 42431). The `tempoTestnet` export points to the deprecated Andantino testnet (chain ID 42429). ### WebSocket Use the `webSocket` transport for real-time subscriptions: ```typescript Mainnet theme={"system"} import { createPublicClient, webSocket } from 'viem' import { tempo as tempoChain } from 'viem/chains' import { tempo } from 'viem/tempo' const publicClient = createPublicClient({ chain: tempoChain, transport: webSocket('YOUR_CHAINSTACK_WSS_ENDPOINT'), }).extend(tempo()) // Subscribe to new blocks const unwatch = publicClient.watchBlocks({ onBlock: (block) => { console.log('New block:', block.number) }, }) ``` ```typescript Testnet theme={"system"} import { createPublicClient, webSocket } from 'viem' import { tempoModerato } from 'viem/chains' import { tempo } from 'viem/tempo' const publicClient = createPublicClient({ chain: tempoModerato, transport: webSocket('YOUR_CHAINSTACK_WSS_ENDPOINT'), }).extend(tempo()) // Subscribe to new blocks const unwatch = publicClient.watchBlocks({ onBlock: (block) => { console.log('New block:', block.number) }, }) ``` where YOUR\_CHAINSTACK\_WSS\_ENDPOINT is your node WSS endpoint protected either with the key or password. ### Send transactions with fee token Viem's Tempo extension supports the `feeToken` parameter to specify which stablecoin pays the transaction fee: ```typescript Transfer with fee token theme={"system"} import { parseUnits } from 'viem' const alphaUsd = '0x20c0000000000000000000000000000000000001' const betaUsd = '0x20c0000000000000000000000000000000000002' // Transfer AlphaUSD, pay fees in BetaUSD const receipt = await walletClient.token.transferSync({ amount: parseUnits('100', 6), to: '0xRecipientAddress', token: alphaUsd, feeToken: betaUsd, }) console.log('Transaction hash:', receipt.transactionHash) ``` ### Get token balance ```typescript Get balance theme={"system"} const balance = await publicClient.token.getBalance({ account: '0xYourAddress', token: '0x20c0000000000000000000000000000000000001', // AlphaUSD }) console.log('Balance:', balance) ``` See also [Viem Tempo documentation](https://viem.sh/tempo). ## Wagmi [Wagmi](https://wagmi.sh) provides React Hooks for Tempo development. Starting from `wagmi@3.2.0`, Wagmi has native Tempo support. 1. Install Wagmi: ```bash theme={"system"} npm install wagmi viem @tanstack/react-query ``` 2. Configure Wagmi with Tempo: ```typescript Mainnet theme={"system"} import { http, createConfig } from 'wagmi' import { tempo } from 'wagmi/chains' export const config = createConfig({ chains: [tempo], transports: { [tempo.id]: http('YOUR_CHAINSTACK_ENDPOINT'), }, }) ``` ```typescript Testnet theme={"system"} import { http, createConfig } from 'wagmi' import { tempoModerato } from 'wagmi/chains' export const config = createConfig({ chains: [tempoModerato], transports: { [tempoModerato.id]: http('YOUR_CHAINSTACK_ENDPOINT'), }, }) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * YOUR\_CHAINSTACK\_WSS\_ENDPOINT — your node WSS endpoint for real-time subscriptions 3. Use Tempo hooks in your React components: ```tsx Transfer with fee token theme={"system"} import { Hooks } from 'wagmi/tempo' import { parseUnits } from 'viem' const alphaUsd = '0x20c0000000000000000000000000000000000001' const betaUsd = '0x20c0000000000000000000000000000000000002' function SendPayment() { const sendPayment = Hooks.token.useTransferSync() const handleSend = () => { sendPayment.mutate({ amount: parseUnits('100', 6), to: '0xRecipientAddress', token: alphaUsd, feeToken: betaUsd, }) } return ( ) } ``` See also [Wagmi Tempo documentation](https://wagmi.sh/tempo). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Tempo nodes. 1. Install Hardhat and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomicfoundation/hardhat-ethers"); ... module.exports = { solidity: "0.8.26", networks: { tempo: { url: "YOUR_CHAINSTACK_ENDPOINT", chainId: 4217, accounts: ["YOUR_PRIVATE_KEY"] }, tempoTestnet: { url: "YOUR_CHAINSTACK_ENDPOINT", chainId: 42431, accounts: ["YOUR_PRIVATE_KEY"] } } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network tempo` (or `--network tempoTestnet` for testnet) and Hardhat will deploy using Chainstack. > [!WARNING] > > Hardhat's default gas estimation may not work correctly with Tempo's stablecoin fee model. For contract deployments, consider using tempo-foundry instead, which has native support for stablecoin gas payments. ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy & run transactions** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. > [!NOTE] > > Remix IDE uses standard EVM transactions and does not support Tempo's `feeToken` parameter. Transactions default to pathUSD for gas fees when interacting with non-TIP20 contracts. Ensure your wallet has pathUSD balance. For smart contract deployment with fee token control, use [tempo-foundry](#tempo-foundry) instead. ## tempo-foundry Tempo has a [custom Foundry fork](https://github.com/tempoxyz/foundry) with native support for Tempo Transactions, including stablecoin gas fees via the `--tempo.fee-token` flag. ### Installation Install tempo-foundry using the standard `foundryup` installer with the `-n tempo` flag: ```bash theme={"system"} foundryup -n tempo ``` This installs the latest nightly release of all precompiled binaries: `forge`, `cast`, `anvil`, and `chisel`. Verify the installation: ```bash theme={"system"} forge -V ``` You should see version information including `-tempo`, indicating you are using the Tempo fork: ```bash theme={"system"} # forge -tempo ( ) ``` ### Create a new project Initialize a new project with Tempo support: ```bash theme={"system"} forge init -n tempo my-project && cd my-project ``` Each new project is configured for Tempo out of the box with `tempo-std`, the Tempo standard library, which contains helpers for Tempo's protocol-level features. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create src/MyContract.sol:MyContract \ --rpc-url YOUR_CHAINSTACK_ENDPOINT \ --interactive \ --broadcast \ --verify ``` To deploy with a specific fee token: ```shell Shell theme={"system"} forge create src/MyContract.sol:MyContract \ --tempo.fee-token 0x20c0000000000000000000000000000000000001 \ --rpc-url YOUR_CHAINSTACK_ENDPOINT \ --interactive \ --broadcast \ --verify ``` where * `src/MyContract.sol:MyContract` — path to your contract file and the contract name * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * `--tempo.fee-token` — the TIP-20 token address to pay gas fees (AlphaUSD in this example) * `--interactive` — prompts for your private key instead of passing it on the command line * `--broadcast` — broadcasts the transaction to the network * `--verify` — verifies the contract on [contracts.tempo.xyz](https://contracts.tempo.xyz) (Sourcify-compatible) ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` To send a transaction with a fee token: ```shell Shell theme={"system"} cast send CONTRACT_ADDRESS \ "functionName(args)" \ --private-key YOUR_PRIVATE_KEY \ --rpc-url YOUR_CHAINSTACK_ENDPOINT \ --tempo.fee-token 0x20c0000000000000000000000000000000000001 ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password. ### Solidity integration with tempo-std Import protocol interfaces from `tempo-std` in your Solidity contracts: ```bash theme={"system"} forge install tempoxyz/tempo-std ``` ```solidity theme={"system"} import {IStablecoinExchange} from "tempo-std/interfaces/IStablecoinExchange.sol"; IStablecoinExchange constant DEX = IStablecoinExchange(0xDEc0000000000000000000000000000000000000); // Approve first, then: uint128 amountOut = DEX.swapExactAmountIn(tokenIn, tokenOut, amountIn, minAmountOut); ``` See also [Foundry for Tempo documentation](https://docs.tempo.xyz/sdk/foundry). ## pytempo [pytempo](https://pypi.org/project/pytempo/) is Tempo's native Python SDK, built as a web3.py extension. It adds support for Tempo Transactions, including call batching, fee token selection, fee sponsorship, and access key management. ### Installation ```bash theme={"system"} pip install pytempo ``` Requires Python 3.9 or higher and web3.py 7.0+. ### Connect to Tempo ```python theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) print(f"Connected to Tempo at block {w3.eth.block_number}") ``` ### Send a Tempo Transaction Build and send a transaction using the `TempoTransaction` class: ```python theme={"system"} import os from web3 import Web3 from pytempo import Call, TempoTransaction w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) private_key = os.environ["PRIVATE_KEY"] account = w3.eth.account.from_key(private_key) tx = TempoTransaction.create( chain_id=w3.eth.chain_id, gas_limit=100_000, max_fee_per_gas=w3.eth.gas_price * 2, max_priority_fee_per_gas=w3.eth.gas_price, nonce=w3.eth.get_transaction_count(account.address), calls=( Call.create(to="0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), ), ) signed_tx = tx.sign(private_key) tx_hash = w3.eth.send_raw_transaction(signed_tx.encode()) receipt = w3.eth.wait_for_transaction_receipt(tx_hash) print(f"Transaction hash: {tx_hash.hex()}") ``` ### Pay fees in a stablecoin Specify a fee token so users pay gas in any supported stablecoin: ```python theme={"system"} tx = TempoTransaction.create( chain_id=w3.eth.chain_id, gas_limit=100_000, max_fee_per_gas=w3.eth.gas_price * 2, max_priority_fee_per_gas=w3.eth.gas_price, nonce=w3.eth.get_transaction_count(account.address), fee_token="0x20c0000000000000000000000000000000000001", # AlphaUSD calls=( Call.create(to="0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), ), ) ``` ### Batch multiple calls Execute multiple operations atomically in a single transaction: ```python theme={"system"} tx = TempoTransaction.create( chain_id=w3.eth.chain_id, gas_limit=200_000, max_fee_per_gas=w3.eth.gas_price * 2, max_priority_fee_per_gas=w3.eth.gas_price, nonce=w3.eth.get_transaction_count(account.address), calls=( Call.create(to=token_a.address, data=transfer_data_a), Call.create(to=token_b.address, data=transfer_data_b), ), ) ``` See also [pytempo documentation](https://docs.tempo.xyz/sdk/python). ## tempo-go [tempo-go](https://github.com/tempoxyz/tempo-go) is Tempo's Go SDK for building application clients. It provides packages for RPC communication, transaction signing, and key management. ### Installation ```bash theme={"system"} go get github.com/tempoxyz/tempo-go@latest ``` Requires Go 1.21 or higher. ### Connect to Tempo ```go theme={"system"} package main import ( "context" "fmt" "github.com/tempoxyz/tempo-go/pkg/client" ) func main() { c := client.New("YOUR_CHAINSTACK_ENDPOINT") ctx := context.Background() blockNum, _ := c.GetBlockNumber(ctx) fmt.Printf("Connected to Tempo at block %d\n", blockNum) } ``` ### Send a Tempo Transaction Build and send a transaction using the builder pattern: ```go theme={"system"} package main import ( "context" "log" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/tempoxyz/tempo-go/pkg/client" "github.com/tempoxyz/tempo-go/pkg/signer" "github.com/tempoxyz/tempo-go/pkg/transaction" ) func main() { c := client.New("YOUR_CHAINSTACK_ENDPOINT") s, _ := signer.NewSigner("YOUR_PRIVATE_KEY") ctx := context.Background() nonce, _ := c.GetTransactionCount(ctx, s.Address().Hex()) recipient := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8") tx := transaction.NewBuilder(big.NewInt(4217)). // Tempo mainnet SetNonce(nonce). SetGas(100000). SetMaxFeePerGas(big.NewInt(20000000000)). SetMaxPriorityFeePerGas(big.NewInt(1000000000)). AddCall(recipient, big.NewInt(0), []byte{}). Build() transaction.SignTransaction(tx, s) serialized, _ := transaction.Serialize(tx, nil) hash, _ := c.SendRawTransaction(ctx, serialized) log.Printf("Transaction hash: %s", hash) } ``` See also [tempo-go documentation](https://docs.tempo.xyz/sdk/go). ## tempo-alloy (Rust) [tempo-alloy](https://github.com/tempoxyz/tempo) is Tempo's Rust SDK in the form of an Alloy crate. Alloy is a popular Rust crate for interacting with EVM-compatible blockchains. ### Installation ```bash theme={"system"} cargo add alloy --features providers,transport-http,network tokio --features full cargo add tempo-alloy --git https://github.com/tempoxyz/tempo --tag v1.4.2 ``` Requires Rust 1.93.0 or higher. ### Connect to Tempo Create a provider using the `TempoNetwork` type to enable Tempo-specific features: ```rust theme={"system"} use alloy::providers::ProviderBuilder; use tempo_alloy::TempoNetwork; #[tokio::main] async fn main() -> Result<(), Box> { let provider = ProviderBuilder::new_with_network::() .connect(&std::env::var("RPC_URL").expect("No RPC URL set")) .await?; println!("Provider connected successfully"); Ok(()) } ``` ### Read chain data ```rust theme={"system"} use alloy::providers::{Provider, ProviderBuilder}; use tempo_alloy::TempoNetwork; #[tokio::main] async fn main() -> Result<(), Box> { let provider = ProviderBuilder::new_with_network::() .connect(&std::env::var("RPC_URL").expect("No RPC URL set")) .await?; println!("{}", provider.get_block_number().await?); Ok(()) } ``` See also [Alloy documentation](https://docs.rs/alloy) and [tempo-alloy documentation](https://docs.tempo.xyz/sdk/rust). ## Get testnet tokens > [!NOTE] > > The faucet is only available on Tempo testnet. On mainnet, acquire stablecoins from issuers or ecosystem partners. Fund your wallet using the `tempo_fundAddress` RPC method on the public Tempo RPC: ```bash theme={"system"} cast rpc tempo_fundAddress YOUR_ADDRESS --rpc-url https://rpc.moderato.tempo.xyz ``` The faucet sends 1M of each testnet stablecoin (pathUSD, AlphaUSD, BetaUSD, ThetaUSD) to the specified address. After receiving tokens, use your Chainstack endpoint for all other operations. See also [tempo\_fundAddress API reference](/reference/tempo-tempo-fundaddress). ## See also * [Tempo: Building your first payment app](/docs/tempo-tutorial-first-payment-app) * [Tempo: Basic DEX swap with Foundry](/docs/tempo-tutorial-dex-swap-foundry) * [Tempo API reference](/reference/tempo-getting-started) * [Tempo methods](/docs/tempo-methods) * [Viem Tempo documentation](https://viem.sh/tempo) * [Wagmi Tempo documentation](https://wagmi.sh/tempo) * [pytempo on PyPI](https://pypi.org/project/pytempo/) * [tempo-go on GitHub](https://github.com/tempoxyz/tempo-go) * [Official Tempo documentation](https://docs.tempo.xyz) # Tempo: Basic DEX swap with Foundry Source: https://docs.chainstack.com/docs/tempo-tutorial-dex-swap-foundry Swap stablecoins on Tempo's native DEX using Foundry's tempo-foundry CLI tools and a Chainstack node endpoint for on-chain transaction broadcasting. This tutorial teaches you how to perform a basic token swap on Tempo's native stablecoin DEX. You'll use tempo-foundry CLI tools to interact directly with the protocol-level exchange. **TLDR:** * Swap stablecoins directly on Tempo's enshrined DEX precompile * Use tempo-foundry's `cast` with the `--fee-token` flag for gas payment * Pay fees in the same stablecoin you're swapping—no native gas token required * Execute swaps in seconds with sub-second finality ## Prerequisites * A Tempo node deployed on [Chainstack](https://console.chainstack.com/) (mainnet or testnet) * Basic command-line knowledge * A wallet with a private key ## Deploy a Tempo node 1. [Sign up with Chainstack](https://console.chainstack.com/). 2. [Deploy a node](/docs/manage-your-networks). See also [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Tempo stablecoin exchange overview Tempo features an enshrined DEX—a precompiled contract built into the protocol at a fixed address. You interact with it directly using the `cast` command from tempo-foundry. ### Key addresses | Contract | Address | | - | - | | Stablecoin Exchange | `0xDEc0000000000000000000000000000000000000` | | pathUSD (quote token) | `0x20C0000000000000000000000000000000000000` | | AlphaUSD | `0x20C0000000000000000000000000000000000001` | | BetaUSD | `0x20C0000000000000000000000000000000000002` | | ThetaUSD | `0x20C0000000000000000000000000000000000003` | ### Network configuration > [tab] Mainnet > > | Parameter | Value | > | - | - | > | Network name | Tempo Mainnet | > | Chain ID | `4217` | > | RPC URL | `YOUR_CHAINSTACK_ENDPOINT` | > | Explorer | [explore.mainnet.tempo.xyz](https://explore.mainnet.tempo.xyz) | > [tab] Testnet > > | Parameter | Value | > | - | - | > | Network name | Tempo Testnet (Moderato) | > | Chain ID | `42431` | > | RPC URL | `YOUR_CHAINSTACK_ENDPOINT` | > | Explorer | [explore.tempo.xyz](https://explore.tempo.xyz) | ## Step 1. Install tempo-foundry Tempo-foundry is a Foundry fork with Tempo-specific features, including the `--fee-token` flag for paying gas in stablecoins. ```bash theme={"system"} foundryup -n tempo ``` Verify the installation: ```bash theme={"system"} forge -V ``` You should see version information including `-tempo`, indicating you are using the Tempo fork. ## Step 2. Fund your wallet > [!NOTE] > > The faucet is only available on Tempo testnet. On mainnet, acquire stablecoins from issuers or ecosystem partners. Get testnet stablecoins using the faucet RPC method on the public Tempo RPC: ```bash theme={"system"} cast rpc tempo_fundAddress YOUR_ADDRESS --rpc-url https://rpc.moderato.tempo.xyz ``` After receiving tokens, use your Chainstack endpoint for all other operations. Check your balance using your Chainstack endpoint: ```bash theme={"system"} cast call 0x20C0000000000000000000000000000000000001 \ "balanceOf(address)(uint256)" \ YOUR_ADDRESS \ --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` > [!NOTE] > > The faucet provides AlphaUSD tokens. All testnet stablecoins use 6 decimals, so `100000000` equals \$100. ## Step 3. Get a swap quote Before swapping, check the expected output. The DEX uses `uint128` for amounts: ```bash theme={"system"} cast call 0xDEc0000000000000000000000000000000000000 \ "quoteSwapExactAmountIn(address,address,uint128)(uint128)" \ 0x20C0000000000000000000000000000000000001 \ 0x20C0000000000000000000000000000000000002 \ 100000000 \ --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` This quotes swapping 100 AlphaUSD for BetaUSD. Expected output: `100000000` (100 BetaUSD at 1:1 rate). ## Step 4. Approve the DEX Set your private key as an environment variable: ```bash theme={"system"} export PRIVATE_KEY=your_private_key_here ``` Approve the DEX to spend your tokens: ```bash theme={"system"} cast send 0x20C0000000000000000000000000000000000001 \ "approve(address,uint256)" \ 0xDEc0000000000000000000000000000000000000 \ 100000000 \ --rpc-url YOUR_CHAINSTACK_ENDPOINT \ --private-key $PRIVATE_KEY \ --fee-token 0x20C0000000000000000000000000000000000001 ``` > [!TIP] > > The `--fee-token` flag specifies which TIP-20 token to use for gas payment. Here we're paying gas in AlphaUSD—the same token we're approving. ## Step 5. Execute the swap Swap 100 AlphaUSD for BetaUSD with 1% slippage tolerance: ```bash theme={"system"} cast send 0xDEc0000000000000000000000000000000000000 \ "swapExactAmountIn(address,address,uint128,uint128)(uint128)" \ 0x20C0000000000000000000000000000000000001 \ 0x20C0000000000000000000000000000000000002 \ 100000000 \ 99000000 \ --rpc-url YOUR_CHAINSTACK_ENDPOINT \ --private-key $PRIVATE_KEY \ --fee-token 0x20C0000000000000000000000000000000000001 ``` The parameters are: * `tokenIn` — AlphaUSD address * `tokenOut` — BetaUSD address * `amountIn` — 100 tokens (100000000 with 6 decimals) * `minAmountOut` — minimum 99 tokens (1% slippage protection) ## Step 6. Verify the swap Check your new BetaUSD balance: ```bash theme={"system"} cast call 0x20C0000000000000000000000000000000000002 \ "balanceOf(address)(uint256)" \ YOUR_ADDRESS \ --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` ## DEX interface reference The stablecoin exchange provides these key functions: ```solidity theme={"system"} // Swap exact input for variable output function swapExactAmountIn( address tokenIn, address tokenOut, uint128 amountIn, uint128 minAmountOut ) external returns (uint128 amountOut); // Swap variable input for exact output function swapExactAmountOut( address tokenIn, address tokenOut, uint128 amountOut, uint128 maxAmountIn ) external returns (uint128 amountIn); // Quote functions (view, no gas) function quoteSwapExactAmountIn(address tokenIn, address tokenOut, uint128 amountIn) external view returns (uint128 amountOut); function quoteSwapExactAmountOut(address tokenIn, address tokenOut, uint128 amountOut) external view returns (uint128 amountIn); ``` > [!NOTE] > > The interface uses `uint128` for amounts, not `uint256`. This is important when building Solidity contracts that interact with the DEX. ## How routing works Each stablecoin has pathUSD as its quote token. When swapping AlphaUSD → BetaUSD, the DEX routes automatically: 1. AlphaUSD → pathUSD 2. pathUSD → BetaUSD Both orderbooks need liquidity for the swap to succeed. The quote functions account for this routing when calculating output amounts. ## Slippage protection Always set slippage bounds to protect against price movement: * `minAmountOut` — minimum tokens to receive (for exact input swaps) * `maxAmountIn` — maximum tokens to spend (for exact output swaps) For stablecoin pairs, 0.5-1% is typical since prices are pegged. ## Fee payment Tempo has no native gas token. Use `--fee-token` with any TIP-20 stablecoin to pay transaction fees. The Fee AMM converts your payment to the validator's preferred token automatically. ## Integrating from Solidity If you're building a smart contract that needs to swap, import the interface from tempo-std: ```bash theme={"system"} forge install tempoxyz/tempo-std ``` ```solidity theme={"system"} import {IStablecoinExchange} from "tempo-std/interfaces/IStablecoinExchange.sol"; IStablecoinExchange constant DEX = IStablecoinExchange(0xDEc0000000000000000000000000000000000000); // Approve first, then: uint128 amountOut = DEX.swapExactAmountIn(tokenIn, tokenOut, amountIn, minAmountOut); ``` ## Troubleshooting ### Quote returns zero or fails The orderbook may lack liquidity for the requested pair or amount. Try: * A smaller swap amount * Checking if orders exist for this token pair on the explorer ### Swap reverts Common causes: * Insufficient token approval for the DEX * Slippage tolerance too tight for current liquidity * No liquidity in one leg of the route (tokenIn → pathUSD or pathUSD → tokenOut) ### Transaction fails with gas error Ensure you're using the `--fee-token` flag with a valid TIP-20 token address. Tempo requires stablecoin gas payment. ## Next steps Now that you can execute basic swaps: * Build a swap aggregator that finds optimal routes * Monitor orderbook state using the stablecoin exchange contract * Create limit orders by providing liquidity to the orderbook ## See also * [Tempo: Building your first payment app](/docs/tempo-tutorial-first-payment-app) * [Tempo API reference](/reference/tempo-getting-started) * [Tempo methods](/docs/tempo-methods) — available RPC methods # Tempo: Building your first payment app Source: https://docs.chainstack.com/docs/tempo-tutorial-first-payment-app Send TIP-20 stablecoin transfers with payment memos on the Tempo blockchain using Foundry and a Chainstack RPC endpoint for real-time settlement. This tutorial teaches you how to build a payment application on Tempo using TIP-20 tokens. You'll learn to send transfers, attach payment memos for reconciliation, and monitor incoming payments. ## What you'll build A simple payment app that can: * Check TIP-20 token balances * Send stablecoin transfers * Attach invoice IDs as payment memos * Monitor incoming payments ## Prerequisites * Node.js v18 or higher * A Tempo node deployed on [Chainstack](https://console.chainstack.com/) (mainnet or testnet) * Basic JavaScript/TypeScript knowledge ## Deploy a Tempo node 1. [Sign up with Chainstack](https://console.chainstack.com/). 2. [Deploy a node](/docs/manage-your-networks). See also [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Tempo network details > [tab] Mainnet > > | Property | Value | > | - | - | > | Network | Tempo Mainnet | > | Chain ID | 4217 | > | RPC endpoint | `YOUR_CHAINSTACK_ENDPOINT` | > | Explorer | [explore.mainnet.tempo.xyz](https://explore.mainnet.tempo.xyz) | > | Finality | \~0.5 seconds | > [tab] Testnet > > | Property | Value | > | - | - | > | Network | Tempo Testnet (Moderato) | > | Chain ID | 42431 | > | RPC endpoint | `YOUR_CHAINSTACK_ENDPOINT` | > | Explorer | [explore.tempo.xyz](https://explore.tempo.xyz) | > | Finality | \~0.5 seconds | > [!NOTE] > > **No native gas token**: Tempo uses USD stablecoins for fees. When you transfer a TIP-20 token, fees are paid in that same token automatically. ## Stablecoin tokens Tempo provides these TIP-20 stablecoins (same addresses on mainnet and testnet): | Token | Address | | - | - | | pathUSD | `0x20c0000000000000000000000000000000000000` | | AlphaUSD | `0x20c0000000000000000000000000000000000001` | | BetaUSD | `0x20c0000000000000000000000000000000000002` | | ThetaUSD | `0x20c0000000000000000000000000000000000003` | ## Project setup Create a new project and install dependencies: ```bash theme={"system"} mkdir tempo-payment-app && cd tempo-payment-app npm init -y npm install ethers ``` ## Get testnet tokens > [!NOTE] > > The faucet is only available on Tempo testnet. On mainnet, acquire stablecoins from issuers or ecosystem partners. Fund your testnet wallet using the [`tempo_fundAddress`](/reference/tempo-tempo-fundaddress) RPC method on the public Tempo RPC: ```bash theme={"system"} curl -X POST "https://rpc.moderato.tempo.xyz" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "tempo_fundAddress", "params": ["YOUR_WALLET_ADDRESS"], "id": 1 }' ``` This provides 1M of each testnet stablecoin. After receiving tokens, use your Chainstack endpoint for all other operations. ## TIP-20 token interface TIP-20 extends ERC-20 with payment-optimized features. The key functions are: ```solidity theme={"system"} interface ITIP20 { // Standard ERC-20 transfer function transfer(address to, uint256 amount) external returns (bool); // Transfer with 32-byte memo for payment references function transferWithMemo(address to, uint256 amount, bytes32 memo) external; // Standard ERC-20 functions function balanceOf(address account) external view returns (uint256); function decimals() external view returns (uint8); function symbol() external view returns (string memory); } ``` > [!TIP] > > **6 decimals**: TIP-20 tokens use 6 decimals (like USDC), not 18 like ETH. So \$100.00 = `100000000` (100 \* 10^6). ## Check token balance Query the balance of any address: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; // TIP-20 ABI (subset) const TIP20_ABI = [ "function balanceOf(address account) view returns (uint256)", "function decimals() view returns (uint8)", "function symbol() view returns (string)" ]; async function getBalance(address) { const token = new ethers.Contract(PATHUSD, TIP20_ABI, provider); const balance = await token.balanceOf(address); const decimals = await token.decimals(); const symbol = await token.symbol(); const formatted = ethers.formatUnits(balance, decimals); console.log(`Balance: ${formatted} ${symbol}`); return balance; } getBalance("0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"); ``` ## Send a basic transfer Transfer TIP-20 tokens to another address: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); const PATHUSD = "0x20c0000000000000000000000000000000000000"; const TIP20_ABI = [ "function transfer(address to, uint256 amount) returns (bool)", "function decimals() view returns (uint8)" ]; async function sendPayment(recipient, amount) { const token = new ethers.Contract(PATHUSD, TIP20_ABI, wallet); const decimals = await token.decimals(); // Convert dollar amount to token units (6 decimals) const amountInUnits = ethers.parseUnits(amount.toString(), decimals); console.log(`Sending $${amount} pathUSD to ${recipient}...`); const tx = await token.transfer(recipient, amountInUnits); console.log(`Transaction hash: ${tx.hash}`); const receipt = await tx.wait(); console.log(`Confirmed in block ${receipt.blockNumber}`); return receipt; } sendPayment("0x9729187D9E8Bbefa8295F39f5634cA454dd9d294", 10); ``` ## Send a transfer with memo Attach a payment reference (invoice ID, order number) to your transfer: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); const PATHUSD = "0x20c0000000000000000000000000000000000000"; const TIP20_ABI = [ "function transferWithMemo(address to, uint256 amount, bytes32 memo)", "function decimals() view returns (uint8)" ]; async function sendPaymentWithMemo(recipient, amount, invoiceId) { const token = new ethers.Contract(PATHUSD, TIP20_ABI, wallet); const decimals = await token.decimals(); const amountInUnits = ethers.parseUnits(amount.toString(), decimals); // Convert invoice ID to bytes32 (padded to 32 bytes) const memoBytes = ethers.encodeBytes32String(invoiceId.slice(0, 31)); console.log(`Sending $${amount} pathUSD with memo "${invoiceId}"...`); const tx = await token.transferWithMemo(recipient, amountInUnits, memoBytes); console.log(`Transaction hash: ${tx.hash}`); const receipt = await tx.wait(); console.log(`Confirmed in block ${receipt.blockNumber}`); return receipt; } sendPaymentWithMemo( "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294", 25, "INV-2025-001234" ); ``` > [!NOTE] > > **Memo limit**: The memo field is 32 bytes. For longer references, store the full data off-chain and use a hash or short ID in the memo. ## Monitor incoming payments Watch for transfers to your address using event filters: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const PATHUSD = "0x20c0000000000000000000000000000000000000"; const MY_ADDRESS = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"; // TransferWithMemo event signature const TIP20_ABI = [ "event Transfer(address indexed from, address indexed to, uint256 value)", "event TransferWithMemo(address indexed from, address indexed to, uint256 value, bytes32 indexed memo)", "function decimals() view returns (uint8)" ]; async function monitorPayments() { const token = new ethers.Contract(PATHUSD, TIP20_ABI, provider); const decimals = await token.decimals(); console.log(`Monitoring payments to ${MY_ADDRESS}...`); // Filter for transfers TO our address const filter = token.filters.Transfer(null, MY_ADDRESS); token.on(filter, (from, to, value, event) => { const amount = ethers.formatUnits(value, decimals); console.log(`\nPayment received!`); console.log(` From: ${from}`); console.log(` Amount: $${amount}`); console.log(` Tx: ${event.log.transactionHash}`); }); // Also monitor transfers with memos const memoFilter = token.filters.TransferWithMemo(null, MY_ADDRESS); token.on(memoFilter, (from, to, value, memo, event) => { const amount = ethers.formatUnits(value, decimals); const memoText = ethers.decodeBytes32String(memo); console.log(`\nPayment with memo received!`); console.log(` From: ${from}`); console.log(` Amount: $${amount}`); console.log(` Memo: ${memoText}`); console.log(` Tx: ${event.log.transactionHash}`); }); } monitorPayments(); ``` ## Query historical payments Fetch past transfers using `eth_getLogs`: ```javascript theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const PATHUSD = "0x20c0000000000000000000000000000000000000"; const MY_ADDRESS = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"; const TIP20_ABI = [ "event TransferWithMemo(address indexed from, address indexed to, uint256 value, bytes32 indexed memo)", "function decimals() view returns (uint8)" ]; async function getPaymentHistory(fromBlock, toBlock) { const token = new ethers.Contract(PATHUSD, TIP20_ABI, provider); const decimals = await token.decimals(); // Query TransferWithMemo events where we're the recipient const filter = token.filters.TransferWithMemo(null, MY_ADDRESS); const events = await token.queryFilter(filter, fromBlock, toBlock); console.log(`Found ${events.length} payments with memos:\n`); for (const event of events) { const amount = ethers.formatUnits(event.args.value, decimals); const memo = ethers.decodeBytes32String(event.args.memo); console.log(`Block ${event.blockNumber}:`); console.log(` From: ${event.args.from}`); console.log(` Amount: $${amount}`); console.log(` Memo: ${memo}`); console.log(` Tx: ${event.transactionHash}\n`); } return events; } // Query last 10000 blocks getPaymentHistory(-10000, "latest"); ``` ## Complete payment app Here's a full example combining all the functionality. Replace `YOUR_CHAINSTACK_ENDPOINT` with your actual Chainstack Tempo node endpoint: ```javascript theme={"system"} const { ethers } = require("ethers"); // Configuration - use your Chainstack endpoint const RPC_URL = "YOUR_CHAINSTACK_ENDPOINT"; const PATHUSD = "0x20c0000000000000000000000000000000000000"; const TIP20_ABI = [ "function balanceOf(address account) view returns (uint256)", "function decimals() view returns (uint8)", "function symbol() view returns (string)", "function transfer(address to, uint256 amount) returns (bool)", "function transferWithMemo(address to, uint256 amount, bytes32 memo)", "event Transfer(address indexed from, address indexed to, uint256 value)", "event TransferWithMemo(address indexed from, address indexed to, uint256 value, bytes32 indexed memo)" ]; class TempoPaymentApp { constructor(privateKey) { this.provider = new ethers.JsonRpcProvider(RPC_URL); this.wallet = new ethers.Wallet(privateKey, this.provider); this.token = new ethers.Contract(PATHUSD, TIP20_ABI, this.wallet); } async getBalance(address) { const balance = await this.token.balanceOf(address || this.wallet.address); const decimals = await this.token.decimals(); return ethers.formatUnits(balance, decimals); } async send(recipient, amount, invoiceId = null) { const decimals = await this.token.decimals(); const amountInUnits = ethers.parseUnits(amount.toString(), decimals); let tx; if (invoiceId) { const memo = ethers.encodeBytes32String(invoiceId.slice(0, 31)); tx = await this.token.transferWithMemo(recipient, amountInUnits, memo); } else { tx = await this.token.transfer(recipient, amountInUnits); } return tx.wait(); } async watchPayments(callback) { const decimals = await this.token.decimals(); const filter = this.token.filters.Transfer(null, this.wallet.address); this.token.on(filter, async (from, to, value, event) => { callback({ from, amount: ethers.formatUnits(value, decimals), txHash: event.log.transactionHash }); }); } } // Usage example async function main() { const app = new TempoPaymentApp("YOUR_PRIVATE_KEY"); // Check balance const balance = await app.getBalance(); console.log(`Balance: $${balance} pathUSD`); // Send payment with invoice ID const receipt = await app.send( "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294", 50, "INV-2025-001234" ); console.log(`Payment sent: ${receipt.hash}`); // Watch for incoming payments app.watchPayments((payment) => { console.log(`Received $${payment.amount} from ${payment.from}`); }); } main().catch(console.error); ``` ## Tempo-specific notes > [!WARNING] > > **Fee behavior**: When you call `transfer` or `transferWithMemo`, the full amount goes to the recipient. Fees are deducted separately from your balance. This differs from some chains where fees reduce the transfer amount. Key differences from other EVM chains: * **Sub-second finality**: Transactions finalize in \~0.5 seconds with no reorganizations * **USD-denominated fees**: No volatile gas token; fees are paid in the token you're transferring * **6 decimals**: All TIP-20 tokens use 6 decimals (standard for stablecoins) * **32-byte memos**: Native support for payment references aligned with ISO 20022 ## Next steps Now that you can send and receive payments: * Add invoice tracking with a database * Implement webhook notifications for incoming payments * Build a payment page for your application * Explore batch payments with TempoTransactions ## See also * [Tempo API reference](/reference/tempo-getting-started) * [eth\_getLogs](/reference/tempo-eth-getlogs) — query historical events * [eth\_call](/reference/tempo-eth-call) — read contract state # TON: Choosing v2, v3, or v4 Source: https://docs.chainstack.com/docs/ton-choosing-v2-or-v3 Compare TON API v2, v3, and v4 on Chainstack: real-time node requests, indexed data, and block-pinned reads with WebSocket block streams. Includes the v4 endpoint, routes, and examples. > [!NOTE] > > ### TON billing: full vs archive > > v2, v3, and v4 are priced the same, and all data is available. Billing follows the request: most TON requests are **full** (1 request unit), while requests that read historical data are **archive** (2 request units). [`getTransactions`](/reference/ton-gettransactions-v2), `getTransactionsStd`, and the v4 account transaction routes are always archive; block/seqno methods are archive when the requested block is 128 or more seqno behind the tip. See [Request units — TON method scope](/docs/request-units#ton-method-scope) for the full method list. Check out our [TON API reference](/reference/getting-started-ton), which has both v2 and v3 methods. For v4 routes, see [TON API v4](#ton-api-v4). The question is—which one should you choose for your project? As a reminder: * TON API v2 endpoints serve real-time requests. TON API v2 requests serve off a node. * TON API v3 endpoints serve indexed requests. TON API v3 requests serve off an indexer. * TON API v4 endpoints serve block-pinned requests: each account-state read and get-method call names a masterchain block seqno in the URL. TON API v4 requests serve off a node. ## Which one to choose If speed and real-time freshness are crucial (for example, you need near-instant visibility of new transactions), go with v2. If you need highly stable, processed, and precise data, and can tolerate a slight indexing delay—v3 is likely the better option. If your code uses `TonClient4` from `@ton/ton`, or you need account state and get-method results as of a specific block, use v4. v4 also streams new blocks over WebSocket. Tip: If the data provided by v2 is sufficient for your use case, it should remain your best choice for simplicity and quick data access. ## Key differences detailed ### Indexing latency (data freshness) **v2**: Delivers raw data more quickly, effectively providing the "freshest" data at any given moment. Feels more "native" because it uses the [ADNL protocol](https://docs.ton.org/v3/documentation/network/protocols/adnl/overview) directly, which often lets it react faster to blockchain changes. This native ADNL access is available on Chainstack Dedicated Nodes, enabling direct low-level network communication. **v3**: Performs additional data processing and indexing. As a result, it may have a slight lag compared to raw data sources. Offers well-structured and cleaned data, which can be advantageous for complex queries. If ultra-fresh data is a priority (for instance, if you need to see changes at near real-time), use v2 tends to have the edge. **v4**: Reads from the node over lite-server connections, with no indexing step. ### Request latency Our TON RPC node network is global and we strive to always have the shortest travel time for your requests based on our highly tuned infrastructure. See also [Available clouds, regions, and locations](/docs/nodes-clouds-regions-and-locations). ## TON API v4 TON API v4 is an HTTP and WebSocket API in which block, account-state, and get-method routes take a masterchain block seqno in the URL, so each result is pinned to that block. Chainstack serves the open-source [ton-api-v4](https://github.com/ton-community/ton-api-v4) server maintained by the TON community (originally built by Whales Corp), which reads from the TON node over lite-server connections. TON API v4 is available on TON Global Nodes on Mainnet and Testnet. The `TonClient4` class in [`@ton/ton`](https://github.com/ton-org/ton) is a client for TON API v4. ### TON API v4 endpoint The TON API v4 base URL is `YOUR_CHAINSTACK_ENDPOINT/api/v4` — your node endpoint, which ends with your auth token, followed by `/api/v4`: ```text theme={"system"} https://ton-mainnet.core.chainstack.com/YOUR_AUTH_TOKEN/api/v4 https://ton-testnet.core.chainstack.com/YOUR_AUTH_TOKEN/api/v4 ``` For the WebSocket routes, use the same URL with the `wss://` scheme. The full URLs for your node are in [node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### TON API v4 routes Each route is relative to the TON API v4 base URL. Routes that take `{seqno}` return data as of that masterchain block. | Method | Route | Returns | | - | - | - | | GET | `/block/latest` | Latest masterchain block, with its Unix time in `now` | | GET | `/block/{seqno}` | Masterchain and shard blocks at the seqno, with their transaction lists | | GET | `/block/utime/{utime}` | Block at a Unix timestamp | | GET | `/block/{seqno}/{address}` | Account state at the block — balance, code, data, and last transaction | | GET | `/block/{seqno}/{address}/lite` | Account state at the block, with code and data hashes instead of code and data | | GET | `/block/{seqno}/{address}/run/{method}` | Result of a get-method run at the block; arguments go in an extra path segment | | GET | `/block/{seqno}/config/{ids}` | Blockchain config parameters at the block, for comma-separated parameter IDs | | GET | `/account/{address}/tx/{lt}/{hash}` | Up to 20 account transactions as a bag of cells (BOC), starting at the given transaction and going back | | GET | `/account/{address}/tx/parsed/{lt}/{hash}` | Parsed account transactions; the `count` query parameter sets how many | | WebSocket | `/block/watch` | A message for each new masterchain block with its `seqno`, block time, and server time | | WebSocket | `/block/watch/changed` | A message for each new masterchain block with the accounts changed in it | Consecutive WebSocket messages can skip seqnos; fetch a skipped block with `/block/{seqno}`. The [ton-api-v4 README](https://github.com/ton-community/ton-api-v4#methods) documents the routes with example responses. Responses from block-pinned routes do not change and are returned with `Cache-Control: public, max-age=31536000`. `/block/latest` is returned with a `max-age` of 5 seconds or less. ### Call TON API v4 with curl Get the latest masterchain block: ```bash theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT/api/v4/block/latest ``` ```json theme={"system"} { "last": { "seqno": 95317858, "shard": "-9223372036854775808", "workchain": -1, "fileHash": "M/EVkJdLlaRAJecavcQgM0B7WlLmOk4JNaf1Gzgm8MU=", "rootHash": "zFZKplNSAUG0p4ABJXgyW2bCRGYIfMRcyIcOiE0g/z4=" }, "init": { "fileHash": "XplPz01CXAps5qeSWUtxcyBfdAo5zVb1N979KLSKD24=", "rootHash": "F6OpKZKqvqeFp6CQmFomXNMfMj2EnaUSOXN+Mh+wVWk=" }, "stateRootHash": "IF7c/VCNXnyScSMHPrCHvtIR+DIPlNMOhiZq1/NxK1k=", "now": 1790477242 } ``` Get an account's state at masterchain block 95318361: ```bash theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT/api/v4/block/95318361/EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N/lite ``` ```json theme={"system"} { "account": { "state": { "type": "active", "codeHash": "hNr6RJ+Ypph3ibojI1gHK8D3bcRSQAKl0JGLmnXS1Zk=", "dataHash": "4O4a0XnqF0b5MpZIpFTlll4r2Gn+MRiukcnVhtSdFG8=" }, "balance": { "coins": "1592540632745552", "currencies": {} }, "last": { "lt": "105876895000014", "hash": "cfO6LqgwjPbf+9DL+tmRGp6pFf6raoCl41L6lgQLlLI=" }, "storageStat": { "lastPaid": 1790368385, "duePayment": null, "used": { "bits": 1339, "cells": 3 } } } } ``` ### Call TON API v4 with TonClient4 Install the packages: ```bash theme={"system"} npm install @ton/ton @ton/core @ton/crypto ``` Save the script as `index.mjs`. It reads the latest block, then the wallet's balance and `seqno` get-method result at that block: ```javascript index.mjs theme={"system"} import { TonClient4, Address } from '@ton/ton'; const client = new TonClient4({ endpoint: 'YOUR_CHAINSTACK_ENDPOINT/api/v4', }); const address = Address.parse('EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N'); const { last } = await client.getLastBlock(); const { account } = await client.getAccountLite(last.seqno, address); console.log(`Block ${last.seqno}: balance ${account.balance.coins} nanotons`); const { reader } = await client.runMethod(last.seqno, address, 'seqno'); console.log(`Wallet seqno: ${reader.readNumber()}`); ``` Run it with `node index.mjs`: ```text theme={"system"} Block 95318879: balance 1592540632745552 nanotons Wallet seqno: 343 ``` ### TON API v4 billing TON API v4 requests follow the TON full and archive rules. Block-pinned `/block/{seqno}/...` routes are archive when the seqno is 128 or more behind the masterchain tip, the `/account/{address}/tx/...` routes are always archive, and all other routes are full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # TON: How to customize fungible tokens (Jettons) Source: https://docs.chainstack.com/docs/ton-how-to-customize-fungible-tokens-jettons Customize TON Jettons with capped supply and mint price. Extend Jetton minter contracts using Blueprint, FunC, and Sandbox for advanced token features. **TLDR:** * We extend the Jetton minter to enforce a capped supply and a mint price for each token. * The updated contract logic checks the available supply and calculates the token amount based on the TON sent. * We added new getters and fields to handle capped supply, price, and test scenarios for correct minting. * Lastly, we illustrated how to deploy the updated Jetton minter using Blueprint and Sandbox. ### Main article In this tutorial, we will explore how to customize the standard implementation of Jetton tokens on TON. We will guide you through the update process using the [Blueprint](https://github.com/ton-org/blueprint) environment and [Sandbox](https://github.com/ton-org/sandbox). Specifically, we will introduce a capped supply and a mint price per token to our minter contract. Users will be able to mint tokens at the preset price until the capped supply is reached. > [!NOTE] > > ### Run nodes on Chainstack > > [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required. You can sign up with your GitHub, X, Google, or Microsoft account. ## Introduction Let's briefly recap what we know about fungible tokens on TON. TON fungible tokens are called Jettons. The Jetton standard is detailed in [TEP74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md), while the specification for token metadata is outlined in [TEP64](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md). The Jetton standard (TEP74) covers: 1. The method for Jetton transfers. 2. How to retrieve common information (name, circulating supply, etc.) about a given Jetton asset. [An example of the Jetton minter and wallet contracts](https://github.com/ton-blockchain/token-contract/tree/main/ft) has been prepared by the TON core team. We will take these contracts as a starting point for our development. ![Parent-children approach](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/3eae1cc1b0164d6a445316bb4efaa58be9b4f2d4995a1091e6e444897ac9385d-image.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=71ca946a8035e57eb4870b7d906de13e) Source: [How to shard your TON smart contract and why - studying the anatomy of TON's Jettons](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons). > [!NOTE] > > ### Tutorial code > > The GitHub repository with all code written in this tutorial is published [here](https://github.com/chainstacklabs/ton-jetton-tutorial-2). ### Setting up project Clone the project from the [previous tutorial](/docs/ton-how-to-develop-fungible-tokens-jettons) into your current folder: ```bash Bash theme={"system"} git clone https://github.com/chainstacklabs/ton-jetton-tutorial-1.git . ``` Next, update the `blueprint.config.ts` file with your Chainstack endpoint. For this tutorial, we will use the testnet: ```typescript TypeScript theme={"system"} import { Config } from '@ton/blueprint'; export const config: Config = { network: { endpoint: 'https://ton-testnet.core.chainstack.com/.../api/v2/jsonRPC', type: 'testnet', version: 'v2', // key: 'YOUR_API_KEY', }, }; ``` To ensure the contracts are set up properly, compile and run the tests using the following commands: ```Text Bash theme={"system"} npx blueprint build npx blueprint test ``` ## Development ### Contracts In this section, we will update the minter contract to include a capped supply and token price functionality. ### Adding capped supply and price fields In the contract storage, we will add two new fields, `capped_supply` and `price`. Here is the updated `load_data` and `save_data` functions: ```c FunC theme={"system"} (int, int, int, slice, cell, cell) load_data() inline { slice ds = get_data().begin_parse(); return ( ds~load_coins(), ;; total_supply ds~load_coins(), ;; capped_supply ds~load_uint(32), ;; price ds~load_msg_addr(), ;; admin_address ds~load_ref(), ;; content ds~load_ref() ;; jetton_wallet_code ); } () save_data(int total_supply, int capped_supply, int price, slice admin_address, cell content, cell jetton_wallet_code) impure inline { set_data(begin_cell() .store_coins(total_supply) .store_coins(capped_supply) .store_uint(price, 32) .store_slice(admin_address) .store_ref(content) .store_ref(jetton_wallet_code) .end_cell() ); } ``` ### Updating function calls Since we have updated the mentioned functions, we now need to update their calls with the respective parameters. Here is an example of the updated getters: ```c FunC theme={"system"} (int, int, slice, cell, cell) get_jetton_data() method_id { (int total_supply, _, _, slice admin_address, cell content, cell jetton_wallet_code) = load_data(); return (total_supply, -1, admin_address, content, jetton_wallet_code); } slice get_wallet_address(slice owner_address) method_id { (_, _, _, _, _, cell jetton_wallet_code) = load_data(); return calculate_user_jetton_wallet_address(owner_address, my_address(), jetton_wallet_code); } (int, int) get_supply_price() method_id { (_, int capped_supply, int price, _, _, _) = load_data(); return (capped_supply, price); } ``` To prevent minting more than the capped supply and ensure that the correct price is applied, we need to modify the `recv_internal` function. We will: 1. Calculate the number of jettons based on the amount of TON sent. 2. Ensure the total minted supply does not exceed the capped supply. Please note that we need to calculate the buy amount to reserve some TON for storage in the Jetton wallet contract. ```c FunC theme={"system"} if (op == op::mint()) { slice to_address = in_msg_body~load_msg_addr(); int buy_amount = msg_value - min_tons_for_storage; int jetton_amount = muldiv(buy_amount, 1, price); ;; Check if minting exceeds the capped supply throw_unless(256, total_supply + jetton_amount <= capped_supply); var mint_request = begin_cell() .store_uint(op::internal_transfer(), 32) .store_uint(0, 64) .store_coins(jetton_amount) ;; max 124 bit .store_uint(0, 2) ;; from_address, addr_none$00 .store_slice(my_address()) ;; response_address, 3 + 8 + 256 = 267 bit .store_coins(0) ;; forward_amount, 4 bit if zero .store_uint(0, 1) ;; no forward_payload, 1 bit .end_cell(); mint_tokens(to_address, jetton_wallet_code, min_tons_for_storage, mint_request); save_data(total_supply + jetton_amount, capped_supply, price, admin_address, content, jetton_wallet_code); return (); } ``` ### Wrappers > [!NOTE] > > ### Minter wrapper > > Note that in this tutorial, we will only work with the Jetton Minter wrapper. Next, we will update the wrapper to include the new capped supply and token price functionality. The wrapper will provide functions to retrieve and manipulate these fields. ### Updating minter config We need to update the configuration type in `JettonMinter.ts` to include the new fields `capped_supply` and `price`. ```typescript TypeScript theme={"system"} export type JettonMinterConfig = { admin: Address; jetton_content: Cell | JettonMinterContent; wallet_code: Cell; capped_supply: bigint; price: bigint; }; export function jettonMinterConfigToCell(config: JettonMinterConfig): Cell { const content = config.jetton_content instanceof Cell ? config.jetton_content : jettonContentToCell(config.jetton_content); return beginCell() .storeCoins(0) .storeCoins(config.capped_supply) .storeUint(config.price, 32) .storeAddress(config.admin) .storeRef(content) .storeRef(config.wallet_code) .endCell(); } ``` ### Updating minting message In this version, we are packing the jetton minting message within the minter contract itself. This means that our interface now composes a simple message containing the TON amount and the recipient's address. ```typescript TypeScript theme={"system"} static mintMessage(from: Address, to: Address, query_id: number | bigint = 0) { return beginCell().storeUint(Op.mint, 32).storeUint(query_id, 64) // op, queryId .storeAddress(to) .endCell(); } async sendMint(provider: ContractProvider, via: Sender, to: Address, forward_ton_amount: bigint, total_ton_amount: bigint) { if(total_ton_amount < forward_ton_amount) { throw new Error("Total ton amount should be > forward amount"); } await provider.internal(via, { sendMode: SendMode.PAY_GAS_SEPARATELY, body: JettonMinter.mintMessage(this.address, to), value: total_ton_amount }); } ``` ### Updating getters As we added new properties, the getters also must be updated. Note that we modified `getJettonData` and added `getTokenPrice`. ```typescript TypeScript theme={"system"} async getJettonData(provider: ContractProvider) { const res = await provider.get('get_jetton_data', []); const totalSupply = res.stack.readBigNumber(); const mintable = res.stack.readBoolean(); const adminAddress = res.stack.readAddress(); const content = res.stack.readCell(); const walletCode = res.stack.readCell(); return { totalSupply, mintable, adminAddress, content, walletCode }; } async getWalletAddress(provider: ContractProvider, owner: Address): Promise
{ const res = await provider.get('get_wallet_address', [{ type: 'slice', cell: beginCell().storeAddress(owner).endCell() }]) return res.stack.readAddress() } async getSupplyPrice(provider: ContractProvider) { const res = await provider.get('get_supply_price', []); const cappedSupply = res.stack.readBigNumber(); const price = res.stack.readBigNumber(); return { cappedSupply, price } } ``` ### Tests We will add test cases to verify that the capped supply and token price functionality work as expected. ### Testing minting within capped supply This test verifies jetton minting based on the TON sent, ensuring the correct jetton amount is minted and updates both user balance and total supply accordingly. ```typescript TypeScript theme={"system"} it('should mint correct amount of jettons based on the sent TON amount', async () => { // Calculate costs of minting const jettonsToPurchase = (await jettonMinter.getSupplyPrice()).cappedSupply; const jettonsCost = jettonsToPurchase * price; const amountToSend = jettonsCost + toNano('1'); // Assuming 1 TON for storage fees const forwardFee = toNano('0.01'); const expectedMintedJettons = jettonsCost / price; // Retrieve initial balance and supply const userJettonWallet = await userWallet(user.address); const initUserJettonBalance = await userJettonWallet.getJettonBalance(); const initJettonSupply = (await jettonMinter.getJettonData()).totalSupply; // Send the minting message const res = await jettonMinter.sendMint( user.getSender(), user.address, forwardFee, amountToSend ); // Verify the transaction expect(res.transactions).toHaveTransaction({ on: userJettonWallet.address, op: Op.internal_transfer, success: true, deploy: true }); // Verify that the user's minted jettons match the expected amount const currentUserJettonBalance = await userJettonWallet.getJettonBalance(); const mintedUserJettons = currentUserJettonBalance - initUserJettonBalance; expect(mintedUserJettons).toEqual(expectedMintedJettons); // Verify that the total supply matches the expected amount of minted jettons const updatedTotalSupply = (await jettonMinter.getJettonData()).totalSupply; const mintedTotalSupply = updatedTotalSupply - initJettonSupply; expect(mintedTotalSupply).toEqual(expectedMintedJettons); printTransactionFees(res.transactions); }); ``` This test checks that minting beyond the capped supply fails, verifying that the transaction is aborted with the correct exit code. ```typescript TypeScript theme={"system"} it('should not mint more than capped supply', async () => { // Calculate costs of minting const jettonsToPurchase = (await jettonMinter.getSupplyPrice()).cappedSupply + 1n; const jettonsCost = jettonsToPurchase * price; const amountToSend = jettonsCost + toNano('1'); // Assuming 1 TON for storage fees const forwardFee = toNano('0.01'); // Send the minting message const res = await jettonMinter.sendMint( user.getSender(), user.address, forwardFee, amountToSend ); // Verify the transaction expect(res.transactions).toHaveTransaction({ from: user.address, to: jettonMinter.address, aborted: true, // High exit codes are considered to be fatal exitCode: 256, }); }); ``` To test the contracts using Sandbox, run the command: ```Text Bash theme={"system"} npx blueprint test ``` ### Deployment In the `scripts` folder, update`deployJettonMinter.ts` with the `capped_supply` and `price` properties: ```typescript TypeScript theme={"system"} import {toNano} from '@ton/core'; import {JettonMinter} from '../wrappers/JettonMinter'; import {compile, NetworkProvider} from '@ton/blueprint'; import {jettonWalletCodeFromLibrary, promptUrl, promptUserFriendlyAddress} from "../wrappers/ui-utils"; export async function run(provider: NetworkProvider) { const isTestnet = provider.network() !== 'mainnet'; const ui = provider.ui(); const jettonWalletCodeRaw = await compile('JettonWallet'); const adminAddress = await promptUserFriendlyAddress("Enter the address of the jetton owner (admin):", ui, isTestnet); const jettonMetadataUri = await promptUrl("Enter jetton metadata uri (https://jettonowner.com/jetton.json)", ui) const jettonWalletCode = jettonWalletCodeFromLibrary(jettonWalletCodeRaw); const minter = provider.open(JettonMinter.createFromConfig({ admin: adminAddress.address, wallet_code: jettonWalletCode, jetton_content: {type: 1, uri: jettonMetadataUri}, capped_supply: 1000n, price: toNano('0.01') }, await compile('JettonMinter')) ); await minter.sendDeploy(provider.sender(), toNano("1.5")); // send 1.5 TON } ``` The metadata JSON must have the following format with the image data having base64-encoded value: ```Json JSON theme={"system"} { "name": "Example Token", "description": "Official token", "symbol": "EXTO", "decimals": 9, "image_data": "4bWxuczPHN2ZyB0..." } ``` To deploy the contracts to the testnet, run the command: ```Text Bash theme={"system"} npx blueprint run ``` ## Conclusion We walked through the customization of the Jetton token standard on TON, focusing on key update steps using Blueprint and Sandbox. The process involved updating the minter contract, its wrapper, and the associated tests. ### About the author > [card] Anton Sauchyk > > ![Anton Sauchyk](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1817926677730664448/3nNn0T2p_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1d13d3584862f1be3a84fa3f8e38fea7) > > Developer Advocate @ Chainstack > \ > Multiple years of software development and Web3 expertise > > [https://github.com/smypmsa](https://github.com/smypmsa) > > [https://x.com/sensuniama](https://x.com/sensuniama) > > [https://www.linkedin.com/in/anton-sauchyk/](https://www.linkedin.com/in/anton-sauchyk/) # TON: How to develop fungible tokens (Jettons) Source: https://docs.chainstack.com/docs/ton-how-to-develop-fungible-tokens-jettons Develop TON Jettons using TEP-74 standard with Blueprint and Sandbox. Create minter and wallet contracts in FunC for fungible tokens on TON blockchain. **TLDR:** * You’ll create a fungible token (Jetton) on TON using a master “minter” contract plus a wallet contract for each user. * You’ll rely on Blueprint and Sandbox to compile, test, and deploy standard TON FunC contracts. * You’ll apply the Jetton standard (TEP74) for token transfers and metadata (TEP64) for properties like symbol and decimals. * By the end, you’ll have a baseline fungible token on the TON testnet ready for any custom logic. ## Main article In this tutorial, we will discuss the standard implementation of fungible tokens on the TON blockchain and walk through the development steps using the [Blueprint](https://github.com/ton-org/blueprint) environment and [Sandbox](https://github.com/ton-org/sandbox). > [!NOTE] > > ### Run nodes on Chainstack > > [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required. You can sign up with your GitHub, X, Google, or Microsoft account. ## Introduction The suggested approach for fungible tokens on TON involves having a minter contract (also called a master contract) and a specific wallet contract for each user. This allows taking advantage of the TON blockchain architecture and its sharding feature. The minter contract holds key token metrics and parameters, while the wallet contract maintains a balance of a specific user and allows transferring and burning tokens. This is described in more detail in the TON standards ([TEPs](https://github.com/ton-blockchain/TEPs/)). Let’s examine those related to fungible tokens. ### Jetton standard TON fungible tokens are called Jettons. The Jetton standard is described in [TEP74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md). The specification for metadata of all tokens on TON is in [TEP64](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md). The Jetton standard (TEP74) describes: 1. The method for Jetton transfers. 2. How to retrieve common information (name, circulating supply, etc.) about a given Jetton asset. Jettons are organized as follows: each Jetton has a master smart contract used to mint new Jettons, account for circulating supply, and provide common information. At the same time, information about the amount of jettons owned by each user is stored in a decentralized manner in individual smart contracts (referred to as “Jetton-wallets”) for each owner. [An example of the Jetton minter and wallet contracts](https://github.com/ton-blockchain/token-contract/tree/main/ft) has been prepared by the TON core team. We will take these contracts as a starting point for our development. ![Parent-children approach](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/3eae1cc1b0164d6a445316bb4efaa58be9b4f2d4995a1091e6e444897ac9385d-image.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=71ca946a8035e57eb4870b7d906de13e) Source: [How to shard your TON smart contract and why - studying the anatomy of TON's Jettons](https://blog.ton.org/how-to-shard-your-ton-smart-contract-and-why-studying-the-anatomy-of-tons-jettons). > [!NOTE] > > ### Tutorial code > > The GitHub repository with all code written in this tutorial is published [here](https://github.com/chainstacklabs/ton-jetton-tutorial-1). ## Development ### Contracts Let’s create an empty Blueprint project with FunC in the current folder: ```bash Bash theme={"system"} npm create ton@latest . ``` After that, create the `blueprint.config.ts` file and paste your Chainstack endpoint. In this tutorial, we work with the testnet: ```typescript TypeScript theme={"system"} import { Config } from '@ton/blueprint'; export const config: Config = { network: { endpoint: 'https://ton-testnet.core.chainstack.com/.../api/v2/jsonRPC', type: 'testnet', version: 'v2', // key: 'YOUR_API_KEY', }, }; ``` Then, let’s copy the contracts from [the standard implementation](https://github.com/ton-blockchain/token-contract/tree/main/ft) to our project’s `contracts` folder. We need `jetton-minter-discoverable.fc`, `jetton-wallet.fc` and `jetton-utils.fc`, `discovery-params.fc`, `op-codes.fc`, `params.fc`. Let’s briefly walk through them. The minter contract allows the admin (the contract owner) to manage the minting process and total tupply. Additionally, it implements the [TEP89](https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md) feature for Jetton wallet discovery, which simplifies obtaining a Jetton wallet address by other smart contracts. Previously, this was challenging because contracts couldn't use get methods on other contracts directly. The wallet contract manages Jetton token transfers, storage, and burning for a specific user. It tracks the wallet balance, owner’s address, and Jetton master details, updating the data after transfers or burns. The `jetton-utils.fc` file contains functions that help in creating and managing a Jetton wallet, making sure that all the necessary data is packed and that the correct address for the wallet is calculated. The `discovery-params.fc`, `op-code.fc` files contain the standard operational codes for the minter and wallet contracts. Additionally, the function `is_resolvable` checks if a given address is within the same workchain as the contract. It extracts the workchain ID from the address and compares it with the current contract's workchain ID. The `params.fc` also contains the same check that is used by the wallet contract. Before proceeding, please make sure your contracts include required imports. `jetton-minter-discoverable.fc` ```c C theme={"system"} #include "imports/stdlib.fc"; #include "jetton-utils.fc"; #include "discovery-params.fc"; #include "op-codes.fc"; ``` `jetton-wallet.fc` ```c C theme={"system"} #include "jetton-utils.fc"; #include "op-codes.fc"; ``` `jetton-utils.fc` ```c C theme={"system"} #include "params.fc"; ``` `params.fc` ```c C theme={"system"} #include "imports/stdlib.fc"; ``` To check that everything is alright, please try to compile the contracts: 1. In the folder `wrappers` create or update the compile files: `JettonMinter.compile.ts` ```typescript TypeScript theme={"system"} import { CompilerConfig } from '@ton/blueprint'; export const compile: CompilerConfig = { lang: 'func', targets: [ 'contracts/jetton-minter-discoverable.fc' ], }; ``` `JettonWallet.compile.ts` ```typescript TypeScript theme={"system"} import { CompilerConfig } from '@ton/blueprint'; export const compile: CompilerConfig = { lang: 'func', targets: [ 'contracts/jetton-wallet.fc' ], }; ``` 2. Run the command `build`: ```bash Bash theme={"system"} npx blueprint build ``` ## Wrappers To test and run scripts for our smart contracts, we need to have TypeScript interfaces for them. The `wrappers` folder contains such interface classes for all contracts (implementing `Contract` from `@ton/core`), including serialization primitives, getter wrappers and compilation functions. Let’s copy the wrappers, `JettonMinter.ts`, `JettonWallet.ts`, `JettonConstants.ts` and `ui-utils.ts`, from [the existing implementation](https://github.com/ton-blockchain/token-contract/tree/main/wrappers) to our project’s folder `wrappers`. You can also copy the shortened versions of the same files from [our repository](https://github.com/chainstacklabs/ton-jetton-tutorial-1). Additionally, `ui-utils.ts` has a few adjustments there. ## Tests and deployment The Blueprint framework incorporates [Sandbox](https://github.com/ton-org/sandbox). The package allows developers to emulate TON smart contracts behaviour as if they were deployed on a real network. Please copy the tests from [our repository](https://github.com/chainstacklabs/ton-jetton-tutorial-1). As in the case with the wrappers, it’s a shortened version with a few adjustments based on examples prepared by the TON core team ([here](https://github.com/ton-blockchain/token-contract/tree/main/sandbox_tests) and [here](https://github.com/ton-blockchain/stablecoin-contract/tree/main/sandbox_tests)). To test the contracts using Sandbox, run the command: ```Text Bash theme={"system"} npx blueprint test ``` In the `scripts` folder, create `deployJettonMinter.ts` with the following code: ```typescript TypeScript theme={"system"} import {toNano} from '@ton/core'; import {JettonMinter} from '../wrappers/JettonMinter'; import {compile, NetworkProvider} from '@ton/blueprint'; import {jettonWalletCodeFromLibrary, promptUrl, promptUserFriendlyAddress} from "../wrappers/ui-utils"; export async function run(provider: NetworkProvider) { const isTestnet = provider.network() !== 'mainnet'; const ui = provider.ui(); const jettonWalletCodeRaw = await compile('JettonWallet'); const adminAddress = await promptUserFriendlyAddress("Enter the address of the jetton owner (admin):", ui, isTestnet); const jettonMetadataUri = await promptUrl("Enter jetton metadata uri (https://jettonowner.com/jetton.json)", ui) const jettonWalletCode = jettonWalletCodeFromLibrary(jettonWalletCodeRaw); const minter = provider.open(JettonMinter.createFromConfig({ admin: adminAddress.address, wallet_code: jettonWalletCode, jetton_content: {type: 1, uri: jettonMetadataUri} }, await compile('JettonMinter')) ); await minter.sendDeploy(provider.sender(), toNano("1.5")); // send 1.5 TON } ``` The metadata JSON must have the following format with the image data having base64-encoded value: ```Json JSON theme={"system"} { "name": "Example Token", "description": "Official token", "symbol": "EXTO", "decimals": 9, "image_data": "4bWxuczPHN2ZyB0..." } ``` To deploy the contracts to the testnet, run the command: ```Text Bash theme={"system"} npx blueprint run ``` ## Conclusion We walked through the fungible token standard on the TON blockchain and its main development steps using Blueprint and Sandbox. In future tutorials, we will add custom behaviour for our token. Stay tuned! ### About the author > [card] Anton Sauchyk > > ![Anton Sauchyk](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1817926677730664448/3nNn0T2p_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1d13d3584862f1be3a84fa3f8e38fea7) > > Developer Advocate @ Chainstack > \ > Multiple years of software development and Web3 expertise > > [https://github.com/smypmsa](https://github.com/smypmsa) > > [https://x.com/sensuniama](https://x.com/sensuniama) > > [https://www.linkedin.com/in/anton-sauchyk/](https://www.linkedin.com/in/anton-sauchyk/) # TON: How to develop non-fungible tokens (NFT) Source: https://docs.chainstack.com/docs/ton-how-to-develop-non-fungible-tokens Develop TON NFTs using TEP-62 and TEP-64 standards. Build NFT item and collection contracts with Blueprint, FunC, and Sandbox for blockchain deployment. **TLDR:** * Explains the NFT standard on TON (TEP-62, TEP-64) and key features for ownership and metadata. * Demonstrates how to set up, compile, and deploy both NFT item and collection contracts. * Shows how to interact with these contracts via Blueprint wrappers and test them using Sandbox. * Provides a starting reference to extend NFT functionality on the TON blockchain. ## Main article In this tutorial, we will discuss the standard implementation of non-fungible tokens (NFTs) on the TON blockchain and walk through the development steps using the [Blueprint](https://github.com/ton-org/blueprint) environment and [Sandbox](https://github.com/ton-org/sandbox). > [!NOTE] > > ### Run nodes on Chainstack > > [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required. You can sign up with your GitHub, X, Google, or Microsoft account. ## Introduction To develop NFTs on the TON blockchain, it's essential to understand the standards that define their structure and behavior. The two key standards are: 1. **[TEP-62: NFT standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md)** describes the interface and functionality of NFT smart contracts on TON. It specifies how NFTs are created, transferred, and managed. 2. **[TEP-64: Token data standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md)** defines how metadata associated with tokens (both fungible and non-fungible) is stored and retrieved. It ensures a consistent format for token metadata, whether stored on-chain or off-chain. Each NFT is represented by its own smart contract, known as an NFT item contract. This contract manages ownership, metadata, and interactions specific to that NFT. An NFT collection contract manages a group of NFTs. It can deploy new NFT item contracts, assign unique indices, and associate them with the collection. > [!NOTE] > > ## Tutorial code > > The GitHub repository with all code written in this tutorial is published [here](https://github.com/chainstacklabs/ton-nft-tutorial-1). ## Development ### Setting up project First, ensure that you have Node.js and npm installed on your system. You can verify the installation by running: ```Text Bash theme={"system"} node -v npm -v ``` Create a new directory for your project and navigate into it: ```Text Bash theme={"system"} mkdir ton-nft-project cd ton-nft-project ``` Initialize a new Blueprint project by running: ```Text Bash theme={"system"} npm create ton@latest . ``` This command sets up a new TON project with the necessary files and dependencies. Follow the prompts to select an empty FunC project. Next, we'll configure the project to connect to the testnet. Create a file named `blueprint.config.ts` in the root of your project directory and add the following content: ```typescript TypeScript theme={"system"} import { Config } from '@ton/blueprint'; export const config: Config = { network: { endpoint: 'https://ton-testnet.core.chainstack.com/.../api/v2/jsonRPC', type: 'testnet', version: 'v2', // key: 'YOUR_API_KEY', }, }; ``` Replace the endpoint URL with your Chainstack RPC endpoint. ### Contracts We'll use [the reference implementations](https://github.com/ton-blockchain/token-contract/tree/main/nft) of the NFT standard provided by the TON core team. To your project's contracts directory, copy the following files: * `nft-item.fc`is the smart contract for individual NFT items. * `nft-collection.fc`is the smart contract for the NFT collection. * `op-codes.fc`, `params.fc` are utility files that contain common functions and definitions used by the contracts. Ensure that your contracts include the required imports. For example, at the top of `nft-item.fc` and `nft-collection.fc`, include: ```c FunC theme={"system"} #include "imports/stdlib.fc"; #include "op-codes.fc"; #include "params.fc"; ``` Add new operational codes to `op-codes.fc`: ```c FunC theme={"system"} ;; Minting int op::mint() asm "0x249cbfa1 PUSHINT"; int op::batch_mint() asm "0x7362d09c PUSHINT"; int op::change_admin() asm "0x26aa0f46 PUSHINT"; ``` Update the collection contract: ``` if (op == op::mint()) { ;; deploy new nft // Mint handling } if (op == op::batch_mint()) { ;; batch deploy of new nfts // Batch mint handling } if (op == op::change_admin()) { ;; change owner // Change admin handling } ``` Before testing the contracts, we need to compile them. In the `wrappers` directory, create compile configuration files for each contract. For the NFT item contract, create `NFTItem.compile.ts`: ```typescript TypeScript theme={"system"} import { CompilerConfig } from '@ton/blueprint'; export const compile: CompilerConfig = { lang: 'func', targets: ['contracts/nft-item.fc'], }; ``` For the NFT collection contract, create `NFTCollection.compile.ts`: ```typescript TypeScript theme={"system"} import { CompilerConfig } from '@ton/blueprint'; export const compile: CompilerConfig = { lang: 'func', targets: ['contracts/nft-collection.fc'], }; ``` To compile the contracts, run: ```Text Bash theme={"system"} npx blueprint build ``` If you encounter errors, try replacing `stdlib.fc` in the `imports` folder with the one from the reference implementation. ### Wrappers To interact with our smart contracts in tests, we'll create TypeScript wrappers that implement the contract interfaces. In the wrappers directory, create `NFTItem.ts` and `NFTCollection.ts`. `NFTConstants.ts` ```typescript TypeScript theme={"system"} export enum NftOp { Transfer = 0x5fcc3d14, OwnershipAssigned = 0x05138d91, Excesses = 0xd53276db, GetStaticData = 0x2fcb26a2, ReportStaticData = 0x8b771735, GetRoyaltyParams = 0x693d3950, ReportRoyaltyParams = 0xa8cb00ad, // NFTEditable EditContent = 0x1a0b9d51, TransferEditorship = 0x1c04412a, EditorshipAssigned = 0x511a4463, // Minting Mint = 0x249cbfa1, BatchMint = 0x7362d09c, ChangeAdmin = 0x26aa0f46 } ``` `NFTCollection.ts` ```typescript TypeScript theme={"system"} import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode } from '@ton/core'; import { NftOp } from './NFTConstants'; export type NftCollectionContent = { uri: string; }; export type NftCollectionConfig = { ownerAddress: Address; nextItemIndex: bigint; nftItemCode: Cell; collectionContent: Cell | NftCollectionContent; royaltyParams: Cell; }; export function nftContentToCell(content: NftCollectionContent): Cell { return beginCell() .storeRef( beginCell() .storeUint(0x01, 8) // Content type (off-chain) .storeStringTail(content.uri) .endCell() ) .endCell(); } export function nftCollectionConfigToCell(config: NftCollectionConfig): Cell { const content = config.collectionContent instanceof Cell ? config.collectionContent : nftContentToCell(config.collectionContent); return beginCell() .storeAddress(config.ownerAddress) .storeUint(config.nextItemIndex, 64) .storeRef(content) .storeRef(config.nftItemCode) .storeRef(config.royaltyParams) .endCell(); } export class NFTCollection implements Contract { constructor(readonly address: Address, readonly init?: { code: Cell; data: Cell }) {} static createFromAddress(address: Address) { return new NFTCollection(address); } static createFromConfig(config: NftCollectionConfig, code: Cell, workchain = 0) { const data = nftCollectionConfigToCell(config); const init = { code, data }; return new NFTCollection(contractAddress(workchain, init), init); } async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) { await provider.internal(via, { value, sendMode: SendMode.PAY_GAS_SEPARATELY, body: new Cell(), }); } async sendMint(provider: ContractProvider, via: Sender, params: { value: bigint; queryId: number | bigint; nftItemContent: Cell | NftCollectionContent; itemIndex: number | bigint; amount: bigint; } ) { const content = params.nftItemContent instanceof Cell ? params.nftItemContent : nftContentToCell(params.nftItemContent); const mintBody = beginCell() .storeUint(NftOp.Mint, 32) .storeUint(params.queryId, 64) .storeUint(params.itemIndex, 64) .storeCoins(params.amount) .storeRef(content) .endCell(); await provider.internal(via, { value: params.value, body: mintBody, }); } async getCollectionData(provider: ContractProvider) { const res = await provider.get('get_collection_data', []); const nextItemIndex = res.stack.readBigNumber(); const collectionContent = res.stack.readCell(); const ownerAddress = res.stack.readAddress(); return { nextItemIndex, collectionContent, ownerAddress, }; } async getNftAddressByIndex(provider: ContractProvider, index: bigint): Promise
{ const res = await provider.get('get_nft_address_by_index', [ { type: 'int', value: index }, ]); return res.stack.readAddress(); } } ``` `NFTItem.ts` ```typescript TypeScript theme={"system"} import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode, } from '@ton/core'; import { NftOp } from './NFTConstants'; export type NftItemConfig = { index: number | bigint; collectionAddress: Address; ownerAddress: Address | null; content: Cell | null; }; export function nftItemConfigToCell(config: NftItemConfig): Cell { const dataCell = beginCell() .storeUint(config.index, 64) .storeAddress(config.collectionAddress); if (config.ownerAddress && config.content) { dataCell .storeAddress(config.ownerAddress) .storeRef(config.content); } return dataCell.endCell(); } export class NFTItem implements Contract { constructor(readonly address: Address, readonly init?: { code: Cell; data: Cell }) {} static createFromAddress(address: Address) { return new NFTItem(address); } static createFromConfig(config: NftItemConfig, code: Cell, workchain = 0) { const data = nftItemConfigToCell(config); const init = { code, data }; const address = contractAddress(workchain, init); return new NFTItem(address, init); } async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) { await provider.internal(via, { value, sendMode: SendMode.PAY_GAS_SEPARATELY, body: new Cell(), }); } async sendTransfer( provider: ContractProvider, via: Sender, params: { value: bigint; queryId: number | bigint; newOwner: Address; responseAddress: Address; forwardAmount: bigint; } ) { const transferBody = beginCell() .storeUint(NftOp.Transfer, 32) // Operation Code (32 bits) .storeUint(params.queryId, 64) // Query ID (64 bits) .storeAddress(params.newOwner) // New Owner Address .storeAddress(params.responseAddress) // Response Destination .storeBit(false) // Custom Payload Flag (1 bit, false) .storeCoins(params.forwardAmount) // Forward Amount (Coins) .storeBit(false) // Additional bit (satisfies slice_bits >= 1) .endCell(); await provider.internal(via, { value: params.value, body: transferBody, }); } async getNftData(provider: ContractProvider) { const res = await provider.get('get_nft_data', []); const isInitialized = res.stack.readNumber(); let index = null; let collectionAddress = null; let ownerAddress = null; let individualContent = null; if (isInitialized === -1) { index = res.stack.readBigNumber(); collectionAddress = res.stack.readAddress(); ownerAddress = res.stack.readAddress(); individualContent = res.stack.readCell(); } return { isInitialized, index, collectionAddress, ownerAddress, individualContent, }; } } ``` ### Tests The Blueprint framework incorporates [Sandbox](https://github.com/ton-org/sandbox). The package allows developers to emulate TON smart contracts behaviour as if they were deployed on a real network. Please copy the tests from [our repository](https://github.com/chainstacklabs/ton-nft-tutorial-1). `NFTCollection.spec.ts` ```typescript TypeScript theme={"system"} import { Blockchain, internal, printTransactionFees, SandboxContract, TreasuryContract } from '@ton/sandbox'; import { Cell, toNano, Address, beginCell } from '@ton/core'; import { compile } from '@ton/blueprint'; import '@ton/test-utils'; import { NFTCollection, nftContentToCell, NftCollectionConfig } from '../wrappers/NFTCollection'; import { NFTItem } from '../wrappers/NFTItem'; import { NftOp } from '../wrappers/NFTConstants'; import { inherits } from 'util'; let blockchain: Blockchain; let deployer: SandboxContract; let owner: SandboxContract; let user: SandboxContract; let collection: SandboxContract; let nftCollectionCode: Cell; let nftItemCode: Cell; describe('NFTCollection Contract', () => { beforeAll(async () => { blockchain = await Blockchain.create(); deployer = await blockchain.treasury('deployer'); owner = await blockchain.treasury('owner'); user = await blockchain.treasury('user'); // Compile the contracts nftCollectionCode = await compile('NFTCollection'); nftItemCode = await compile('NFTItem'); // Prepare the collection content const collectionContentCell = nftContentToCell({ uri: 'https://example.com/collection.json', }); const royaltyParams = beginCell() .storeUint(500, 16) // Example: 5% royalty fee .storeAddress(owner.address) // Royalty recipient address .endCell(); // Create the collection config const collectionConfig: NftCollectionConfig = { ownerAddress: owner.address, nextItemIndex: 0n, nftItemCode: nftItemCode, collectionContent: collectionContentCell, royaltyParams: royaltyParams }; // Create the collection contract instance const collectionContract = NFTCollection.createFromConfig( collectionConfig, nftCollectionCode ); collection = blockchain.openContract(collectionContract); }); it('should deploy NFTCollection contract', async () => { const deployResult = await collection.sendDeploy(deployer.getSender(), toNano('1')); expect(deployResult.transactions).toHaveTransaction({ from: deployer.address, to: collection.address, deploy: true, success: true, }); }); it('should get collection data', async () => { const collectionData = await collection.getCollectionData(); expect(collectionData.nextItemIndex).toBe(0n); expect(collectionData.ownerAddress.equals(owner.address)).toBe(true); }); it('should mint a new NFT item', async () => { // Prepare the NFT item content const nftItemContentCell = nftContentToCell({ uri: 'https://example.com/nft1.json', }); const initialCollectionData = await collection.getCollectionData(); // Mint the NFT const mintResult = await collection.sendMint(owner.getSender(), { value: toNano('0.5'), queryId: 0, nftItemContent: nftItemContentCell, itemIndex: initialCollectionData.nextItemIndex, amount: toNano('0.1') }); expect(mintResult.transactions).toHaveTransaction({ from: owner.address, to: collection.address, success: true }) // Check that nextItemIndex has incremented const updCollectionData = await collection.getCollectionData(); expect(updCollectionData.nextItemIndex).toBe(initialCollectionData.nextItemIndex + 1n); }); it('should verify not initialized NFT item', async () => { const nftItemAddress = await collection.getNftAddressByIndex(0n); const nftItemContract = NFTItem.createFromAddress(nftItemAddress); const nftItem = blockchain.openContract(nftItemContract); const nftData = await nftItem.getNftData(); expect(nftData.isInitialized).toBe(0); expect(nftData.ownerAddress).toBe(null); }); }); ``` To test the contracts using Sandbox, run the command: ```Text Bash theme={"system"} npx blueprint test ``` ## Conclusion We covered the the non-fungible token standard on the TON blockchain and its main development steps using Blueprint and Sandbox. In future tutorials, we will explore other aspects of NFT. Stay tuned! ### About the author > [card] Anton Sauchyk > > ![Anton Sauchyk](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1817926677730664448/3nNn0T2p_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1d13d3584862f1be3a84fa3f8e38fea7) > > Developer Advocate @ Chainstack > \ > Multiple years of software development and Web3 expertise > > [https://github.com/smypmsa](https://github.com/smypmsa) > > [https://x.com/sensuniama](https://x.com/sensuniama) > > [https://www.linkedin.com/in/anton-sauchyk/](https://www.linkedin.com/in/anton-sauchyk/) # TON: How to interact with Jettons Source: https://docs.chainstack.com/docs/ton-how-to-interact-with-jettons Interact with TON Jettons using TonWeb SDK. Fetch metadata, user balances, and transaction histories with JavaScript and Chainstack endpoints. **TLDR:** * Demonstrates how to use TonWeb (JavaScript SDK) with a Chainstack endpoint to fetch Jetton metadata, user balances, and transaction histories on the TON blockchain. * Shows step-by-step examples of requesting token data via TonWeb or HTTP, highlighting the modular approach for Jetton data, balance, and transfer queries. * Emphasizes the difference between the minter contract (master) and wallet contracts, and how to verify or fetch wallet addresses and check balances. * Provides working code snippets to integrate these concepts easily into your application. ## Introduction Jettons are the standard token implementation on the TON blockchain, analogous to ERC-20 tokens on Ethereum. They enable developers to create and manage custom tokens that can represent a variety of assets or utilities within decentralized applications. The standard for Jettons on TON is described in [TEP64](https://github.com/ton-blockchain/TEPs). ### TON APIs To interact with the TON blockchain, developers can use either HTTP APIs or the [ADNL protocol](https://docs.ton.org/v3/documentation/network/protocols/adnl/overview). HTTP APIs are simpler and suitable for most applications, while ADNL offers advanced low-level network communication. Chainstack Dedicated Nodes provide ADNL support for advanced use cases requiring direct network layer interaction. The TON HTTP API has two versions: V2, which provides real-time data, and V3, which offers indexed blockchain data. In this tutorial, we will use TonWeb, which operates on the V2 JSON-RPC endpoint. ### Jettons on TON Jettons are smart contracts on TON that follow a standard interface, allowing wallets and applications to interact with them uniformly. Each Jetton consists of: 1. Master (or minter) contract: the contract that manages the Jetton metadata and logic. 2. Wallet contract: a contract for each user holding the Jetton, managing the user's balance and transfers. ### Prerequisites 1. Node.js and npm installed. 2. [TonWeb](https://github.com/toncenter/tonweb) installed. 3. RPC endpoint from Chainstack. TonWeb is a JavaScript SDK that allows developers to interact with the TON blockchain via HTTP APIs. Install it using npm: ```Text Bash theme={"system"} npm install tonweb ``` ## Interactions ### Fetching Jetton metadata Fetching Jetton metadata allows you to retrieve essential information about a specific Jetton token. To achieve the same using HTTP requests, you can use the `/getTokenData` endpoint of TON API v2. ```javascript Javascript (TonWeb) theme={"system"} const TonWeb = require('tonweb'); // Initialize TonWeb with a provider const tonweb = new TonWeb( new TonWeb.HttpProvider('https://ton-mainnet.core.chainstack.com/.../api/v2/jsonRPC') ); // Replace with the actual Jetton master contract address const jettonMasterAddress = 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'; // e.g., mainnet USDT async function fetchJettonMetadata(jettonMasterAddress) { try { const jettonMinter = new TonWeb.token.jetton.JettonMinter(tonweb.provider, {address: jettonMasterAddress}); const data = await jettonMinter.getJettonData(); console.log('Jetton Metadata:'); console.log('Total supply:', data.totalSupply.toString()); console.log('URI to off-chain metadata:', data.jettonContentUri); } catch (error) { console.error('Error fetching jetton metadata:', error); } } fetchJettonMetadata(jettonMasterAddress); ``` ```javascript Javascript (HTTP) theme={"system"} const fetch = require('node-fetch'); const apiUrl = 'https://ton-mainnet.core.chainstack.com/.../api/v2/getTokenData'; const jettonMasterAddress = 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'; async function fetchJettonMetadata(jettonMasterAddress) { try { const params = new URLSearchParams({ address: jettonMasterAddress }); const url = `${apiUrl}?${params.toString()}`; const response = await fetch(url); const data = (await response.json()).result; console.log('Jetton Metadata:'); console.log('Total supply:', data.total_supply); console.log('Mintable:', data.mintable); console.log('Admin address:', data.admin_address); console.log('URI:', data.jetton_content.data.uri); console.log('Decimals:', data.jetton_content.data.decimals); } catch (error) { console.error('Error fetching jetton metadata:', error); } } fetchJettonMetadata(jettonMasterAddress); ``` ### Fetching user Jetton balance To display a user's Jetton balance, you need to query the blockchain for the amount of a specific Jetton that the user holds. The first example shows how to obtain a Jetton wallet for a specific user. ```javascript Javascript theme={"system"} const TonWeb = require('tonweb'); const tonweb = new TonWeb( new TonWeb.HttpProvider('https://ton-mainnet.core.chainstack.com/.../api/v2/jsonRPC') ); // Replace with the actual Jetton master contract address and owner wallet address const jettonMasterAddress = 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'; // e.g., mainnet USDT const ownerWalletAddress = 'UQ...'; async function fetchJettonWalletAddress(jettonMasterAddress, ownerWalletAddress) { try { const jettonMinter = new TonWeb.token.jetton.JettonMinter(tonweb.provider, { address: jettonMasterAddress }); const jettonWalletAddress = await jettonMinter.getJettonWalletAddress( new TonWeb.utils.Address(ownerWalletAddress) ); const jettonWallet = new TonWeb.token.jetton.JettonWallet(tonweb.provider, { address: jettonWalletAddress }); const jettonData = await jettonWallet.getData(); // Verify that the Jetton Minter address matches if (jettonData.jettonMinterAddress.toString(false) !== jettonMinter.address.toString(false)) { throw new Error('Jetton minter address from jetton wallet does not match the expected minter address'); } console.log('Jetton wallet address:', jettonWalletAddress.toString(true, true, true)); } catch (error) { console.error('Error fetching jetton wallet address:', error); } } fetchJettonWalletAddress(jettonMasterAddress, ownerWalletAddress); ``` When the Jetton wallet is known, we can fetch its balance: ```javascript Javascript theme={"system"} const TonWeb = require('tonweb'); const tonweb = new TonWeb( new TonWeb.HttpProvider('https://ton-mainnet.core.chainstack.com/.../api/v2/jsonRPC') ); // Replace with the actual Jetton wallet address const walletAddress = 'EQ...'; async function fetchJettonBalance(walletAddress) { try { const jettonWallet = new TonWeb.token.jetton.JettonWallet(tonweb.provider, { address: walletAddress }); const data = await jettonWallet.getData(); console.log('Jetton balance:', data.balance.toString()); console.log('Jetton owner address:', data.ownerAddress.toString(true, true, true)); } catch (error) { console.error('Error fetching jetton balance:', error); } } fetchJettonBalance(walletAddress); ``` ### Fetching user Jetton transfers Fetching a user's Jetton transfer history allows you to display past transactions involving the Jetton token for that user. ```javascript Javascript theme={"system"} const TonWeb = require('tonweb'); // Initialize TonWeb with a provider const tonweb = new TonWeb( new TonWeb.HttpProvider('https://ton-mainnet.core.chainstack.com/.../api/v2/jsonRPC') ); // Replace with the actual Jetton wallet address (user's Jetton wallet address) const jettonWalletAddress = 'EQ...'; async function fetchJettonTransferHistory(jettonWalletAddress) { try { const limit = 10; const transactions = await tonweb.provider.getTransactions(jettonWalletAddress, limit); console.log(`Last ${limit} transactions for Jetton wallet ${jettonWalletAddress}:`); for (const tx of transactions) { // Basic transaction info console.log('Transaction ID:', tx.transaction_id.hash); console.log('LT:', tx.transaction_id.lt); console.log('Timestamp:', new Date(tx.utime * 1000).toISOString()); // Check if the transaction is an inbound or outbound message if (tx.in_msg && tx.in_msg.msg_data) { console.log('Incoming Message Data:', tx.in_msg.msg_data); } if (tx.out_msgs && tx.out_msgs.length > 0) { tx.out_msgs.forEach((outMsg, index) => { console.log(`Outgoing Message ${index + 1} Data:`, outMsg.msg_data); }); } console.log('----------------------------------------'); } } catch (error) { console.error('Error fetching jetton transfer history:', error); } } fetchJettonTransferHistory(jettonWalletAddress); ``` ## Conlcusion TonWeb is a JavaScript SDK that simplifies the process of interacting with Jetton contracts on the TON blockchain. In this tutorial, we explored how to peform basic fetching operations with Jettons. ### About the author > [card] Anton Sauchyk > > ![Anton Sauchyk](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1817926677730664448/3nNn0T2p_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=1d13d3584862f1be3a84fa3f8e38fea7) > > Developer Advocate @ Chainstack > \ > Multiple years of software development and Web3 expertise > > [https://github.com/smypmsa](https://github.com/smypmsa) > > [https://x.com/sensuniama](https://x.com/sensuniama) > > [https://www.linkedin.com/in/anton-sauchyk/](https://www.linkedin.com/in/anton-sauchyk/) # TON methods Source: https://docs.chainstack.com/docs/ton-methods Complete TON blockchain API methods reference. Includes v2, v3, and v4 endpoints for addresses, transactions, blocks, NFTs, Jettons, and smart contracts. See also [interactive TON API call examples](/reference/getting-started-ton). Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which TON methods are full vs archive, see [Request units — TON method scope](/docs/request-units#ton-method-scope). The v4 routes are served on Global Nodes under the `/api/v4` path. For the v4 endpoint format and examples, see [TON API v4](/docs/ton-choosing-v2-or-v3#ton-api-v4). | Method | Availability | Comment | | - | - | - | | /v2/getAddressInformation | | | | /v2/getExtendedAddressInformation | | | | /v2/getWalletInformation | | | | /v2/getTransactions | | | | /v2/getAddressBalance | | | | /v2/getAddressState | | | | /v2/packAddress | | | | /v2/unpackAddress | | | | /v2/getTokenData | | | | /v2/detectAddress | | | | /v2/getMasterchainInfo | | | | /v2/getMasterchainBlockSignatures | | | | /v2/getShardBlockProof | | | | /v2/getConsensusBlock | | | | /v2/lookupBlock | | | | /v2/shards | | | | /v2/getBlockTransactions | | | | /v2/getBlockTransactionsExt | | | | /v2/getBlockHeader | | | | /v2/tryLocateTx | | | | /v2/tryLocateResultTx | | | | /v2/tryLocateSourceTx | | | | /v2/getConfigParam | | | | /v2/runGetMethod | | | | /v2/sendBoc | | | | /v2/sendBocReturnHash | | | | /v2/sendQuery | | | | /v2/estimateFee | | | | /v3/masterchainInfo | | | | /v3/blocks | | | | /v3/masterchainBlockShardState | | | | /v3/addressBook | | | | /v3/masterchainBlockShards | | | | /v3/transactions | | | | /v3/transactionsByMasterchainBlock | | | | /v3/transactionsByMessage | | | | /v3/adjacentTransactions | | | | /v3/messages | | | | /v3/nft/collections | | | | /v3/nft/items | | | | /v3/nft/transfers | | | | /v3/jetton/masters | | | | /v3/jetton/wallets | | | | /v3/jetton/transfers | | | | /v3/jetton/burns | | | | /v3/runGetMethod | | | | /v3/estimateFee | | | | /v3/account | | | | /v3/wallet | | | | /v3/addressInformation | | | | /v3/walletStates | | | | /v3/accountStates | | | | /v3/metadata | | | | /v3/traces | | | | /v3/actions | | | | /v3/events | | | | /v3/pendingTransactions | | | | /v3/pendingTraces | | | | /v3/multisig/wallets | | | | /v3/multisig/orders | | | | /v3/message | | | | /v3/topAccountsByBalance | | | | `/v4/block/latest` | | | | `/v4/block/{seqno}` | | | | `/v4/block/utime/{utime}` | | | | `/v4/block/{seqno}/{address}` | | | | `/v4/block/{seqno}/{address}/lite` | | | | `/v4/block/{seqno}/{address}/run/{method}` | | | | `/v4/block/{seqno}/config/{ids}` | | | | `/v4/account/{address}/tx/{lt}/{hash}` | | | | `/v4/account/{address}/tx/parsed/{lt}/{hash}` | | | | `/v4/block/watch` | | WebSocket | | `/v4/block/watch/changed` | | WebSocket | # TON tooling Source: https://docs.chainstack.com/docs/ton-tooling TON blockchain development tools guide. Use TonWeb, Ton.js, Blueprint, wallets, and SDKs for Python, Go, Rust, and .NET with Chainstack endpoints. ## Wallets Wallets allow the addition of custom RPC endpoints either by directly modifying the settings of an existing network or by adding a custom network and specifying a custom RPC endpoint. To obtain the address of your RPC endpoint, navigate to the node details on the Chainstack console and locate the Execution client HTTPS endpoint in the Access and credentials section. Note that there are [different versions of TON wallets](https://docs.ton.org/participate/wallets/contracts). This is because, essentially, any wallet is a smart contract running on the TON Blockchain. These smart contracts can be configured in different ways and may have different features. ### [Tonkeeper](https://github.com/tonkeeper/tonkeeper-web/) This wallet does not offer an option to set custom RPC endpoints through the user interface. However, since the wallet is open-source, you can modify the RPC endpoint directly in the source code. Additionally, the wallet supports switching between different versions of wallet contracts. To switch to Testnet, go to Settings, click on the Tonkeeper Web icon 5 times, and then change the active network. ### [MyTonWallet](https://github.com/mytonwalletorg/mytonwallet) This wallet does not offer an option to set custom RPC endpoints through the user interface. However, since the wallet is open-source, you can modify the RPC endpoint directly in the source code. Additionally, the wallet supports switching between different versions of wallet contracts. To switch to Testnet, go to Settings, click on the MyTonWallet label 5 times, and then change the active network. ### [OpenMask](https://github.com/OpenProduct/openmask-extension) This wallet offers an option to set custom RPC endpoints through the user interface. It is an open-source MetaMask extension equivalent for TON. The wallet supports switching between different versions of wallet contracts. To set a custom RPC endpoint, navigate to **Wallet Settings**. To switch to Testnet, change the active network in the top menu of the wallet window. ### [TON Wallet](https://github.com/toncenter/ton-wallet) This wallet does not offer an option to set custom RPC endpoints through the user interface. However, since the wallet is open-source, you can modify the RPC endpoint directly in the source code. The wallet doesn’t support switching between different versions of wallet contracts. ## IDEs Cloud-based IDEs offer the flexibility to use injected providers. By adding a Chainstack RPC node to a wallet and connecting that wallet in your IDE, you can seamlessly interact with the network through the Chainstack node. There are also multiple TON-related plugins for various IDEs. You can find them on [the Awesome List page](https://github.com/ton-community/awesome-ton) or [the TON Research page](https://tonresear.ch/t/ton-ecosystem/505). ### [Nujan IDE](https://ide.nujan.io/) To enable Nujan IDE to interact with the network via a Chainstack node, you can follow these steps: 1. Install and set up OpenMask to use a Chainstack node for interaction. Refer to the guide on interacting through OpenMask for detailed instructions. 2. Open the IDE and navigate to the **Build & Deploy** tab. Click on **Connect wallet** and select a wallet with your configured custom RPC endpoint. ## Blockchain interaction libraries ### TON API types Note that there are [two types of APIs](https://docs.ton.org/v3/guidelines/dapps/apis-sdks/api-types) available for interacting with the TON network. The TON HTTP API enables access to indexed blockchain information, providing a way to work with data efficiently. On the other hand, the TON ADNL API offers a secure communication method based on the [ADNL protocol](https://docs.ton.org/v3/documentation/network/protocols/adnl/overview), ensuring reliable and protected interactions with the TON network. > [!TIP] > > ### ADNL support on Dedicated Nodes > > Chainstack TON Dedicated Nodes support both HTTP API and ADNL protocol, enabling advanced low-level network communication for specialized use cases. ### TonWeb Build DApps on the TON Blockchain using [TonWeb](https://github.com/toncenter/tonweb) and custom RPC nodes. **Prerequisites** 1. Node.js installed on your machine. 2. A package manager like npm or yarn. **Initialize a project** ```bash Bash theme={"system"} mkdir tonweb-project cd tonweb-project npm init -y ``` **Install TonWeb** ```bash Bash theme={"system"} npm install tonweb ``` **Initialize connection with a Chainstack RPC Node** ```javascript Javascript theme={"system"} // For ES Module // import TonWeb from "tonweb"; // For CommonJS const TonWeb = require('tonweb'); const rpcEndpoint = "YOUR_CHAINSTACK_RPC_ENDPOINT"; const tonweb = new TonWeb(new TonWeb.HttpProvider(rpcEndpoint)); tonweb.getBalance('EQ...').then(info => { console.log(info); }); ``` ### Ton.js Build DApps on the TON Blockchain using [Ton.js](https://github.com/ton-org/ton) and custom RPC nodes. **Prerequisites** 1. Node.js installed on your machine. 2. A package manager like npm or yarn. **Initialize a project** ```bash Bash theme={"system"} mkdir ton-project cd ton-project npm init -y ``` **Install Ton.js** ```bash Bash theme={"system"} npm install ton ``` **Initialize connection with a Chainstack RPC Node** ```javascript Javascript theme={"system"} // For ES Module // import { TonClient } from 'ton'; // For CommonJS const { TonClient } = require('ton'); const rpcEndpoint = "YOUR_CHAINSTACK_RPC_ENDPOINT"; const tonClient = new TonClient({ endpoint: rpcEndpoint }); tonClient.getBalance('EQ...').then(info => { console.log(info); }); ``` For TON API v4, use the `TonClient4` class from `@ton/ton` with `YOUR_CHAINSTACK_ENDPOINT/api/v4` — see [Call TON API v4 with TonClient4](/docs/ton-choosing-v2-or-v3#call-ton-api-v4-with-tonclient4). ### TonUtils Build DApps on the TON Blockchain using [TonUtils](https://github.com/xssnick/tonutils-go) and custom RPC nodes. **Prerequisites** 1. Go installed on your machine (version 1.16 or higher). 2. A Go package manager like go mod. **Initialize a project** ```bash Bash theme={"system"} go mod init go-tonproject ``` Please make sure that `go.mod` contains all required dependencies. **Install TonUtils** ```bash Bash theme={"system"} go get github.com/xssnick/tonutils-go@latest ``` **Initialize connection with a Chainstack RPC Node** ```go Go theme={"system"} package main import ( "context" "fmt" "log" "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/liteclient" "github.com/xssnick/tonutils-go/ton" ) func main() { client := liteclient.NewConnectionPool() // Add a connection to a custom Lite Server using the global config URL err := client.AddConnectionsFromConfigUrl(context.Background(), "https://ton.org/global.config.json") if err != nil { log.Fatalf("Connection error: %v", err) } // Initialize the API client with proof check policy api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry() // Create a context bound to a single TON node ctx := client.StickyContext(context.Background()) // Get the current masterchain block info block, err := api.CurrentMasterchainInfo(ctx) if err != nil { log.Fatalf("Error getting block info: %v", err) } // Specify the wallet address to check the balance walletAddr := address.MustParseAddr("EQ...") // Use WaitForBlock to ensure the block is ready account, err := api.WaitForBlock(block.SeqNo).GetAccount(ctx, block, walletAddr) if err != nil { log.Fatalf("Error getting account: %v", err) } // Display account information fmt.Printf("Is active: %v\n", account.IsActive) if account.IsActive { fmt.Printf("Status: %s\n", account.State.Status) fmt.Printf("Balance: %s TON\n", account.State.Balance.String()) } } ``` ### TonGo Build DApps on the TON Blockchain using [tongo](https://github.com/tonkeeper/tongo) and custom RPC nodes. **Prerequisites** 1. Go installed on your machine (version 1.16 or higher). 2. A Go package manager like go mod. **Initialize a project** ```bash Bash theme={"system"} go mod init go-tonproject ``` **Install TonGo** ```bash Bash theme={"system"} go get github.com/tonkeeper/tongo@latest ``` **Initialize connection with a Chainstack RPC Node** ```go Go theme={"system"} package main import ( "context" "fmt" "github.com/tonkeeper/tongo" "github.com/tonkeeper/tongo/liteapi" ) func main() { // options, err := config.ParseConfigFile("path/to/config.json") tongoClient, err := liteapi.NewClientWithDefaultMainnet() if err != nil { fmt.Printf("Unable to create tongo client: %v", err) } tonAddress := tongo.MustParseAddress("EQ...") state, err := tongoClient.GetAccountState(context.Background(), tonAddress.ID) if err != nil { fmt.Printf("Get account state error: %v", err) } fmt.Printf("Account status: %v\nBalance: %v\n", state.Account.Status(), state.Account.Account.Storage.Balance.Grams) } ``` ### Tonlib-rs Build DApps on the TON Blockchain using [Tonlib-rs](https://github.com/ston-fi/tonlib-rs) and custom RPC nodes. **Prerequisites** 1. Rust installed on your machine (version 1.56.0 or higher). 2. Cargo (the Rust package manager) should be installed as part of the Rust installation. **Install required packages** For Linux, ensure the following packages are installed: ```bash Bash theme={"system"} sudo apt install build-essential cmake libsodium-dev libsecp256k1-dev lz4 liblz4-dev ``` For macOS, ensure the following packages are installed: ```bash Bash theme={"system"} brew install readline secp256k1 ccache pkgconfig cmake libsodium ``` For Windows, ensure the following are installed: 1. CMake: Install via [cmake.org](http://cmake.org/) 2. Install `libsodium` and other dependencies via `vcpkg`: ```bash theme={"system"} vcpkg install libsodium libsecp256k1 lz4 ``` **Initialize a project** ```bash Bash theme={"system"} cargo new tonlib-rs-project cd tonlib-rs-project ``` **Install tonlib-rs** 1. Open the `Cargo.toml` file in the root of your project. 2. Add the following dependencies under `[dependencies]`: ```toml TOML theme={"system"} [dependencies] tokio = { version = "1", features = ["full"] } tonlib = "0.15" anyhow = "1.0" ``` **Initialize connection with a Chainstack RPC Node** Create a new file named `main.rs` in the `src` directory and add the following code: ```Rust Rust theme={"system"} use tonlib::address::TonAddress; use tonlib::client::TonClient; use tonlib::client::TonClientInterface; use anyhow::Result; async fn get_state() -> Result<()> { let client = TonClient::builder().build().await?; let address = TonAddress::from_base64_url( "EQ...", )?; let r = client.get_account_state(&address).await?; println!("Account balance: {:?}", r.balance); Ok(()) } #[tokio::main] async fn main() { if let Err(e) = get_state().await { eprintln!("Error: {:?}", e); } } ``` ### TONsdk Build DApps on the TON Blockchain using [TONsdk](https://github.com/tonfactory/tonsdk) and custom RPC nodes. **Prerequisites** 1. Python installed on your machine (version 3.6.0 or higher). 2. pip (Python package manager) should be installed as part of the Python installation. **Initialize a project** ```bash Bash theme={"system"} mkdir tonsdk-project cd tonsdk-project python3 -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate ``` **Install TONsdk** ```bash Bash theme={"system"} pip install tonsdk tvm_valuetypes aiohttp ``` **Initialize connection with a Chainstack RPC Node** Create a new file named `main.py` in the project directory and add the following code: ```python Python theme={"system"} import asyncio import aiohttp from tonsdk.provider import ToncenterClient, prepare_address, address_state from tonsdk.utils import TonCurrencyEnum, from_nano async def get_wallet_balance(address: str): client = ToncenterClient(base_url="YOUR_CHAINSTACK_RPC_ENDPOINT", api_key="") address = prepare_address(address) async with aiohttp.ClientSession() as session: # Get the account state function and its arguments account_state_func = client.raw_get_account_state(address) # Invoke the function with the session account_state = await account_state_func['func'](session, *account_state_func['args'], **account_state_func['kwargs']) # Process the account state to get the balance account_state["state"] = address_state(account_state) if "balance" in account_state: if int(account_state["balance"]) < 0: account_state["balance"] = 0 else: account_state["balance"] = from_nano(int(account_state["balance"]), TonCurrencyEnum.ton) return account_state["balance"] address = "EQ..." balance = asyncio.run(get_wallet_balance(address)) print(f"Balance: {balance} TON") ``` ### PyTONLib Build DApps on the TON Blockchain using [PyTONLib](https://github.com/toncenter/pytonlib) and custom RPC nodes. **Prerequisites** 1. Python installed on your machine (version 3.6.0 or higher). 2. pip (Python package manager) should be installed as part of the Python installation. **Initialize a project** ```bash Bash theme={"system"} mkdir new-project cd new-project python3 -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate ``` **Install PyTONLib** ```bash Bash theme={"system"} pip install pytonlib ``` **Initialize connection with a Chainstack RPC Node** Create a new file named `main.py` in the project directory and add the following code: ```python Python theme={"system"} import requests import asyncio from pathlib import Path from pytonlib import TonlibClient async def get_address_balance(address): ton_config = requests.get('https://ton.org/testnet-global.config.json').json() keystore_dir = '/tmp/ton_keystore' Path(keystore_dir).mkdir(parents=True, exist_ok=True) client = TonlibClient(ls_index=4, config=ton_config, keystore=keystore_dir) await client.init() account_state = await client.raw_get_account_state(address) balance = int(account_state['balance']) / 1e9 print(f"Balance: {balance} TON") await client.close() if __name__ == "__main__": asyncio.run(get_address_balance("EQ...")) ``` ### Pytoniq Build DApps on the TON Blockchain using [Pytoniq](https://github.com/yungwine/pytoniq) and custom RPC nodes. **Prerequisites** 1. Python installed on your machine (version 3.6.0 or higher). 2. pip (Python package manager) should be installed as part of the Python installation. **Initialize a project** ```bash Bash theme={"system"} mkdir new-project cd new-project python3 -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate ``` **Install Pytoniq** ```bash Bash theme={"system"} pip install pytoniq ``` **Initialize connection with a Chainstack RPC Node** Create a new file named `main.py` in the project directory and add the following code: ```python Python theme={"system"} import asyncio from pytoniq import LiteClient async def main(): client = LiteClient.from_mainnet_config() await client.connect() account_state = await client.get_account_state("EQ...") print(f"Balance: {account_state.balance}") if __name__ == "__main__": asyncio.run(main()) ``` ### TonSdk.NET Build DApps on the TON Blockchain using [TonSdk.NET](https://github.com/continuation-team/TonSdk.NET) and custom RPC nodes. **Prerequisites** 1. .NET SDK installed on your machine (version 5.0 or higher). **Initialize a project** ```bash Bash theme={"system"} dotnet new console -n TonSdkNetProject cd TonSdkNetProject ``` **Install TonSDK.NET** ```bash Bash theme={"system"} dotnet add package TonSdk.Client --version 0.3.10 ``` **Initialize connection with a Chainstack RPC Node** Replace the contents of the `Program.cs` file with the following code: ```csharp C# theme={"system"} using TonSdk.Client; using TonSdk.Core; class Program { static async Task Main(string[] args) { var clientParams = new HttpParameters() { Endpoint = "YOUR_CHAINSTACK_ENDPOINT", ApiKey = "" }; var client = new TonClient(TonClientType.HTTP_TONCENTERAPIV2, clientParams); var address = new Address("EQ..."); var accBalance = await client.GetBalance(address); var balance = accBalance.ToDecimal() / 1_000_000_000; Console.WriteLine($"Balance: {balance} TON"); client.Dispose(); } } ``` ## Development frameworks and toolkits ### Blueprint Build DApps on the TON Blockchain using [Blueprint](https://github.com/ton-org/blueprint) and custom RPC nodes. **Prerequisites** 1. Node.js installed on your machine (version 18 or higher). 2. A package manager like npm or yarn. **Initialize a Blueprint project** ```csharp Bash theme={"system"} npm create ton@latest ``` **Create or update the configuration file** Add to your the project configuration file named `blueprint.config.ts` your Chainstack endpoint: ```typescript TypeScript theme={"system"} import { Config } from '@ton/blueprint'; export const config: Config = { network: { endpoint: 'YOUR_CHAINSTACK_ENDPOINT', type: 'testnet', version: 'v2', key: 'YOUR_API_KEY', }, }; ``` **Compile and deploy contracts** Update contracts wrappers and deployment scripts if needed. Compile the contracts: ```tsx Bash theme={"system"} npx blueprint build ``` Pick the appropriate network after running the followind command to deploy the contracts: ```tsx Bash theme={"system"} npx blueprint run ``` # TON: Wallet initialization with Tonweb Source: https://docs.chainstack.com/docs/ton-wallet-initialization-with-tonweb Initialize TON wallets with TonWeb JavaScript SDK. Deploy v4R2 wallet contracts, generate key pairs, and automate wallet deployment with Chainstack nodes. **TLDR:** * Explains the NFT standard on TON (TEP-62, TEP-64) and key features for ownership and metadata. * Demonstrates how to set up, compile, and deploy both NFT item and collection contracts. * Shows how to interact with these contracts via Blueprint wrappers and test them using Sandbox. * Provides a starting reference to extend NFT functionality on the TON blockchain. ## Main article To be able to use your account on TON, you need to have an initialized wallet. Wallet initialization means deploying a TON wallet contract for your account. While this is a great feature of the TON blockchain, you might find it cumbersome dealing with wallet deployments during your development stage. So here's a couple of scripts in JavaScript using [tonweb](https://github.com/toncenter/tonweb) that can help you: * GenerateAndInitialize.js — a script that generates a key pair for an account and then keeps running until you top it up with TON; once the account has enough TON, the script deploys a wallet contract. * CheckAndDepoy.js — a script that checks whether an account is initialized; if it's not initialized, the script deploys a wallet contract. ## Prerequisites Make sure [tonweb](https://www.npmjs.com/package/tonweb) is installed. ## GenerateAndInitialize This script does the following: * Generates a key pair and saves it to `wallet_keys.json` * Keeps running and checking the balance of the generated account until it's topped up * Once the account is topped up, the script deploys a `v4R2` wallet contract ```javascript Javascript theme={"system"} const TonWeb = require('tonweb'); const fs = require('fs'); const httpProvider = new TonWeb.HttpProvider('CHAINSTACK_NODE_WITH_jsonRPC'); const tonweb = new TonWeb(httpProvider); async function generateAndInitializeWallet() { // Generate a new key pair const keyPair = TonWeb.utils.nacl.sign.keyPair(); // Create a v4R2 wallet instance const WalletClass = tonweb.wallet.all['v4R2']; const wallet = new WalletClass(tonweb.provider, { publicKey: keyPair.publicKey, wc: 0 }); // Get the wallet address const walletAddress = await wallet.getAddress(); const addressString = walletAddress.toString(true, true, true); console.log('Generated wallet address:', addressString); console.log('Please send some TON to this address to initialize the wallet.'); // Save the key pair to a file const keyPairData = { publicKey: TonWeb.utils.bytesToHex(keyPair.publicKey), secretKey: TonWeb.utils.bytesToHex(keyPair.secretKey) }; fs.writeFileSync('wallet_keys.json', JSON.stringify(keyPairData, null, 2)); console.log('Key pair saved to wallet_keys.json'); // Wait for the wallet to be topped up await waitForBalance(walletAddress); // Initialize the wallet console.log('Initializing wallet...'); const deployResult = await wallet.deploy(keyPair.secretKey).send(); console.log('Deployment transaction sent:', deployResult); console.log('Waiting for deployment to complete...'); await new Promise(resolve => setTimeout(resolve, 10000)); // Wait for 10 seconds const seqno = await wallet.methods.seqno().call(); if (seqno !== null) { console.log('Wallet successfully initialized!'); } else { console.log('Wallet initialization might have failed. Please check the address on a block explorer.'); } } async function waitForBalance(address) { while (true) { const balance = await tonweb.getBalance(address); if (balance && balance !== '0') { console.log('Balance detected:', TonWeb.utils.fromNano(balance), 'TON'); break; } await new Promise(resolve => setTimeout(resolve, 5000)); // Check every 5 seconds } } generateAndInitializeWallet().catch(console.error); ``` Make sure you put your Chainstack TON node endpoint with a `/jsonRPC` at the end instead of `CHAINSTACK_NODE_WITH_jsonRPC`. ## CheckAndDepoy This script does the following: * Converts the private key to an account address * Checks the account if it's initialized or not * Checks the balance * If the account is not initialized and has enough TON coins for wallet contract deployment, proceeds with the deployment ```javascript Javascript theme={"system"} const TonWeb = require('tonweb'); const privateKey = 'PRIVATE_KEY'; const httpProvider = new TonWeb.HttpProvider('CHAINSTACK_NODE_WITH_jsonRPC'); const tonweb = new TonWeb(httpProvider); async function deployWallet() { try { const keyPair = TonWeb.utils.nacl.sign.keyPair.fromSecretKey(TonWeb.utils.hexToBytes(privateKey)); const WalletClass = tonweb.wallet.all['v4R2']; const wallet = new WalletClass(tonweb.provider, { publicKey: keyPair.publicKey, wc: 0 }); const walletAddress = await wallet.getAddress(); console.log('Wallet address:', walletAddress.toString(true, true, true)); const balance = await tonweb.provider.getBalance(walletAddress.toString()); console.log('Wallet balance:', balance); if (balance === '0') { console.error('Wallet has no balance. Please add funds before deploying.'); return; } const seqno = await wallet.methods.seqno().call(); console.log('Seqno:', seqno); if (seqno === null) { console.log('Wallet not deployed. Deploying...'); const deployResult = await wallet.deploy(keyPair.secretKey).send(); console.log('Deploy result:', deployResult); } else { console.log('Wallet already deployed. Seqno:', seqno); } } catch (error) { console.error('Unexpected error:', error.message); } } deployWallet(); ``` Make sure you put your Chainstack TON node endpoint with a `/jsonRPC` at the end instead of `CHAINSTACK_NODE_WITH_jsonRPC`; and put your private key in`PRIVATE_KEY`. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Tracking some Bored Apes: The Ethereum event logs tutorial Source: https://docs.chainstack.com/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial Learn Ethereum event logs with BAYC NFT transfers. Fetch smart contract events using eth_getLogs, web3.js subscriptions, and filter by topics with Chainstack. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR:** * Shows how Ethereum events in Solidity serve as logs, capturing contract activity (e.g. NFT transfers). * Explains the event signature and parameter indexing, plus how to find them on Etherscan or through hashing with keccak256. * Demonstrates fetching logs via both curl and web3.js – either by specifying a block range and contract address, or by filtering on event signatures, addresses, and token IDs. * Emphasizes the importance of logs for analyzing, debugging, and auditing smart contract transactions in production. ## Main article Imagine that you are holding a meeting to discuss the progress of a project. During the meeting, you touch upon various topics, make decisions, assign tasks, and, maybe even partake in some friendly banter. Once the session is adjourned, a minute of the meeting will be prepared and it will serve as a record of all the important things that were discussed and decided during the meeting. This will include information such as the date and time of the meeting, the attendees, the agenda, and the minutes of each discussion point (no, you don't include the banter). In programming, logs have a similar purpose. They are used to record important events and activities that occur within the program. For example, when a customer makes a purchase from an online store, the program can record the event by writing a log entry to a file or database. This log entry would contain information such as the date and time of the purchase, the customer's information, the item purchased, and the price. These entries will act as valuable sources of information for debugging, troubleshooting, and understanding how the program works. And just like meeting minutes, logs in programming can also be used to look back at the history of events and activities that have occurred within the program. In Web3, people who have dabbled in the art of writing Solidity programs know that Solidity doesn't support any explicit form of logging. You can’t `print` or `display` the data from within a Solidity program. So how do you keep track of the happenings within a Solidity smart contract? Well, the workaround for this comes in the form of events. You see, Ethereum (or the Ethereum Virtual Machine (EVM), to be exact) supports the storage of specifically indexed data structures and this feature is conveniently called *logs*. The EVM logs allow for the recording and storage of events and activities on the Ethereum network. These logs can be accessed and analyzed by developers, allowing them to gain a deeper understanding of the activities taking place on the Ethereum network. This *logging* feature is leveraged by Solidity in order to implement events. Just like the `print` statements in other languages, `events` in Solidity are a way for smart contracts to communicate with the outside world and to record important information on the Ethereum network. Events are useful for a variety of purposes, such as tracking the execution of transactions, monitoring the state of a contract, and providing notifications to other contracts or external applications. Understanding how to fetch and utilize these events will enable developers to better audit and monitor the behavior of smart contracts on the network and this article is essentially a guide that will help you do just that. So, LFG!!! ## Understanding event logs In Solidity, an event is a way to log information or data that can be read by external clients. Events are declared using the `event` keyword, and they allow smart contracts to send asynchronous notifications to clients. Events can be used to notify users of changes in the contract state, and they can be filtered, searched, and archived for future reference. An event can have parameters, which are typed variables that provide additional information about the event. To emit an event in a Solidity function, use the `emit` keyword followed by the event name and its parameters. Event logs are stored in the blockchain, and they can be accessed through APIs or [block explorers](https://etherscan.io/). The following code demonstrates the declaration and emission of events in Solidity: ```solidity solidity theme={"system"} pragma solidity ^0.8.0; contract Example { event LogSomethingHappened(string message); function doSomething() public { emit LogSomethingHappened("Something happened!"); } } ``` Event logs are a type of log entry in the Ethereum blockchain that contains information about events that have been triggered by smart contracts. Event logs are created when a contract emits an event in Solidity. The following are the major components of an event log: * Event signature — a unique identifier for the event, which is generated by hashing the event name and its parameter types using the Keccak-256 hash function. * Contract address — address of the smart contract that emitted the event. * Topics — an array of indexed parameters that provide additional information about the event. Each event can have up to 4 topics in the array, which can be used to filter and search for specific events. The first topic is always the event signature. * Data — the field where every non-indexed parameter in an event is added to. It can contain any type of information, including strings, numbers, or arrays. The data is encoded as hexadecimal values. * Block number — the number of blocks in the blockchain where the event was emitted. * Transaction hash — the hash of the transaction that triggered the event. To understand these components better, we can refer to an actual contract event. Now, for this, we might as well go for one of the most eventful contracts out there, [The Bored Ape Yacht Club (BAYC) contract](https://etherscan.io/address/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d). ## Prerequisites Before you start writing the code, make sure you have the following components installed on your system: * Node (Version ≥ 16) and the corresponding npm * curl ([https://curl.se/download.html](https://curl.se/download.html)) * A code editor (VS Code, preferably) Once you have everything, the next step is to set up a node project and install the web3.js library. 1. Create a new directory. 2. Open a terminal in the directory. 3. Run the following command to initialize a Node project: ```bash theme={"system"} npm init ``` 4. Provide the details for the project, as per the prompt. 5. Once the project is initialized, use the following command to install the web3.js package: ```bash theme={"system"} npm install web3 ``` ## Decoding the logs using Bored Apes The [Bored Ape Yacht Club](https://boredapeyachtclub.com/) (BAYC) is a non-fungible token (NFT) collectible based on the Ethereum blockchain. The BAYC contract is the smart contract that manages the creation, transfer, and ownership of the Bored Ape Yacht Club NFTs. Each Bored Ape Yacht Club NFT is unique and can be bought, sold, and traded on various NFT marketplaces. The BAYC contract enforces the rules of ownership and transfer of the NFTs, ensuring that each NFT can only be owned by one person at a time. The BAYC contract also includes events that are emitted whenever a transfer of an NFT occurs. These events can be monitored by external clients to track the state of the BAYC contract and keep track of transfers that have taken place. To understand the event logs better, let’s take a look at the BAYC contract’s `Transfer` event : ```solidity solidity theme={"system"} /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); ``` The event consists of three parameters—`from`, `to`, and `tokenId`—and all of them are preceded by the `indexed` keyword. Upon emitting this event, an event log will be created and within that log, the event signature will be generated by hashing (using Keccak-256) the name (`Transfer`) and parameter types (`address`, `address`,`uint256`) of the event. Below is the web3.js code for generating the event signature for the `Transfer` event: ```js JavaScript theme={"system"} const Web3 = require('web3'); keccakHash = Web3.utils.keccak256("Transfer(address,address,uint256)") //output : 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef ``` A much simpler way to get the event signature is to use a block explorer, for example, Etherscan. When you [view the contract details](https://etherscan.io/address/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d#events) using the Etherscan explorer, there is a specific section dedicated to events and this section contains the latest event logs. You can use these logs to get the event signature—check `topic[0]` of every log. The `topics` array represents all the event parameters that are marked using the `indexed` keyword: `from`, `to`, and `tokenId`, in our case. These *topics* help query and identify particular event logs. The `topics` array can include a maximum of 4 elements (topics) and the first element in the array will always be the event signature. Each topic (parameter data) in the array can be represented using a maximum of 32 bytes of data and while representing the data, if the data falls short of 32 bytes, you can add filler data to cover the size specification. Any topic carrying data exceeding the prescribed size limit will be hashed before storing. Due to the size limit, arrays or string type data are not given as indexed parameters in order to avoid them from being part of the `topics` array. To explain the `topics` array better, let’s look at an example. Suppose a Bored Ape NFT with token ID: `8009` was transferred from address [`0xdafce4acc2703a24f29d1321adaadf5768f54642`](https://etherscan.io/address/0xdafce4acc2703a24f29d1321adaadf5768f54642) to the address [`0xdbfd76af2157dc15ee4e57f3f942bb45ba84af24`](https://etherscan.io/address/0xdbfd76af2157dc15ee4e57f3f942bb45ba84af24). The `topics` array of the generated `Transfer` event log then will look like this: ```javascript Javascript theme={"system"} topics: [ //event signature '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // from address '0x000000000000000000000000dafce4acc2703a24f29d1321adaadf5768f54642', // to address '0x000000000000000000000000dbfd76af2157dc15ee4e57f3f942bb45ba84af24', // hex representation of the token id '0x0000000000000000000000000000000000000000000000000000000000001f49' ] ``` > [!NOTE] > > To get the token ID of a particular Bored Ape NFT, you can use NFT marketplaces like [OpenSea](https://opensea.io/assets/ethereum/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d/8009). Here, you can see that some of the topics have a lot of zeros in them. These zeros are the fillers used to fill up the 32-byte size specification of the topic fields. I.e. an address is represented using 20 bytes of data, so while we add any address type data to the topics array, zeros amounting to 12 bytes (20 + 12 = 32 bytes) will be added in between to cover the rest of the size specification. The number of zeros in between will defer based on the type of data that is being added. > [!NOTE] > > ### Why you see more than 32 characters > > If you are confused as to why the elements in the `topics` array have 64 characters (omit `0x`) instead of 32, know that they are in hex format and 2 hex digits make up one byte. All the non-indexed parameters (ones without the `indexed` keyword) are added to the `data` section of the log. Unlike `topics`, the `data` section cannot be queried. In the case of our `Transfer` event, since all the parameters are indexed, the `data` section will be empty and it will simply contain `0x` as a placeholder. All the other fields, like the contract address, block number, and transaction hash contain the respective information. As a whole, the log for the `Transfer` event will look something like this: ```js JavaScript theme={"system"} { address: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', //contract address topics: [ //topics array '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', '0x000000000000000000000000dafce4acc2703a24f29d1321adaadf5768f54642', '0x000000000000000000000000dbfd76af2157dc15ee4e57f3f942bb45ba84af24', '0x0000000000000000000000000000000000000000000000000000000000001f49' ], data: '0x', //data blockNumber: 16547382, transactionHash: '0x4bb12bc228be6a9cbe40a7f45d803a15be2cc8b48cc7dd07848e40fed9406950', transactionIndex: 116, blockHash: '0x1a5d5d08da3ade3e35bff2bebaf0216cb096f4179e34f984e92383d63ab87f1a', logIndex: 214, removed: false, //true, if the log was removed id: 'log_cd90ebee' //log id } ``` Alright, now all this looks nice, but how do we actually capture these logs? Well, let’s find out. ## Ways to capture the logs > [!NOTE] > > ### Get an Ethereum node > > One of the most crucial components that we need in order to capture the event logs is access to an Ethereum node. To set up your own node: > > 1. [Head over to Chainstack and set up an account](https://console.chainstack.com/user/account/create). > 2. Once you have your account, [deploy an Ethereum mainnet node in Chainstack](/docs/manage-your-networks). As with many other functionalities of Ethereum, the method for capturing the event logs includes a [curl](https://curl.se/) way and a code way. ### Using curl to capture event logs To [get the event logs using curl](https://support.chainstack.com/hc/en-us/articles/4408014291993-Getting-logs-and-events-of-EVM-smart-contracts), we need to invoke the `eth_getLogs` RPC method. This method retrieves the logs that match certain filter criteria defined by the user. The filter criteria can include things like the address of the contract emitting the logs, the topics of the logs, and the range of blocks to search. Here is the sample curl request for fetching the `Transfer` event logs from the BAYC contract: ```js JavaScript theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ --data '{"method":"eth_getLogs","params":[{"fromBlock": "0xFC7FA4","toBlock":"latest","address": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}],"id":1,"jsonrpc":"2.0"}' ``` As you can see, within the `params` of the request, we have provided the `fromBlock` and `toBlock` fields. They are used to represent the range of blocks to search. While providing the range, keep in mind that the larger the range, the higher the number of logs and thus the higher the response size. > [!NOTE] > > ### Recommended block range cap > > Even though there are no limitations set in stone, in order to avoid node resource wastage and bulky response payloads, it is recommended to cap the block range at 8,000 for Chainstack Ethereum nodes. While providing block numbers for the range, you can either give the hexadecimal representation or use words like `latest` to refer to the latest block. You can also use `pending` and `earliest` for referring to transactions that are not yet mined. The `address` field in the request parameter helps filter the events based on the contract (represented using the address) from which it was emitted. Since a contract can emit multiple events, we can use the `topics` field to provide additional filtering. Here, within the `topics` array, we have provided the signature of the `Transfer` event—`topics[0]`. This will get us the logs of only the `Transfer` event from the BAYC contract. ### Using JavaScript to fetch the logs The web3.js library provides a far more convenient way to fetch the event logs. To do this, we make use of the `subscribe()` function. The function helps us subscribe to incoming logs, which we can filter and process using different options. The following script help subscribe to the `Transfer` event logs from the BAYC contract: ```js JavaScript theme={"system"} const Web3 = require('web3'); //node url const web3 = new Web3("wss://chainstack-node-endpoint"); //contract address const BAYC_CONTRACT_ADDRESS = '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' //event signature const BAYC_TRANSFER_SIGN = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' var subscription = web3.eth.subscribe('logs', { fromBlock: '', toBlock: 'latest', address: BAYC_CONTRACT_ADDRESS, topics: [BAYC_TRANSFER_SIGN] }, function(error, result){ if (!error) console.log(result); }) .on("connected", function(subscriptionId){ console.log(subscriptionId); }) .on("data", function(log){ console.log(log); }) .on("changed", function(log){ }); // unsubscribes the subscription subscription.unsubscribe(function(error, success){ if(success) console.log('Successfully unsubscribed!'); }); ``` Using the above script, we are setting up a subscription channel that will capture all the logs that were generated within the given block range (`fromBlock`,`toBlock`). In the script, while providing the block number, you can directly use the decimal representation of the number. Here, since we are expecting a stream of data, we are using the WSS endpoint of our Chainstack node. Just like our curl request, we have provided the contract address and the event signature within the `topics` field as a means to filter the events. To provide more filtering, you can specify more topics in the `topics` array. To demonstrate this, here is a script that subscribes to the `Transfer` event log of a particular BAYC NFT based on its token ID: ```js JavaScript theme={"system"} const Web3 = require('web3'); //node URL const web3 = new Web3("wss://chainstack-node-endpoint"); //contract address const BAYC_CONTRACT_ADDRESS = '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D' //event signature const BAYC_TRANSFER_SIGN = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' //token id var token_id = //get the hex representation of the token id var hex = web3.utils.toHex(token_id) // add zeros to cover the 32 byte size limit var hex_32 = web3.utils.padLeft(hex,64) var subscription = web3.eth.subscribe('logs', { fromBlock: '', address: BAYC_CONTRACT_ADDRESS, topics: [BAYC_TRANSFER_SIGN,null,null,hex_32] }, function(error, result){ if (!error) console.log(result); }) .on("connected", function(subscriptionId){ console.log(subscriptionId); }) .on("data", function(log){ console.log(log); }) .on("changed", function(log){ }); // unsubscribes the subscription subscription.unsubscribe(function(error, success){ if(success) console.log('Successfully unsubscribed!'); }); ``` In the above-given script, we have provided the hex value of the token ID in our `topics` array. This script will only fetch the `Transfer` event logs that were generated while transferring that particular NFT. When providing topics to the `topics` array, we can use the `null` value to represent all the topics whose values we didn't specify (the `from` and `to` address in this case). Also, before adding the token ID to the array, we used the `padLeft()` function to add zeros to the hex value, in order to cover the 32-byte size specification. And with that, we have covered all the major ways of fetching Ethereum event logs. ## Conclusion Ethereum event logs play a crucial role in providing transparency and accountability in DApps. They serve as a record of all activities taking place on the blockchain, enabling developers and users to retrieve and analyze important information. The process of fetching these logs is relatively straightforward and can be done using various tools and libraries, such as curl or web3.js. By understanding the importance of event logs and learning how to access them, we can gain deeper insights into the inner workings of Ethereum-based DApps and make more informed decisions. > [!NOTE] > > ### See also > > > [card] [Uncovering the power of eth\\\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) > > > [card] [Expanding your blockchain horizons: The eth\\\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) > > > [card] [Deep dive into Merkle proofs and eth\\\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) > > > [card] [Geth vs Erigon: Deep dive into RPC methods on Ethereum clients](/docs/geth-vs-erigon-deep-dive-into-rpc-methods-on-ethereum-clients) > > > [card] [Ethereum logs tutorial series: Logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters) ### About the author > [card] Sethu Raman Omanakuttan > > ![Sethu Raman Omanakuttan](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/u/44131280.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=a94536fa650a30812be2fef8e02ed093) > > Developer Advocate @ Chainstack > \ > BUIDLs on Ethereum, NEAR, Graph Protocol and Oasis > \ > Majored in computer science and technology > > [https://github.com/SethuRamanOmanakuttan](https://github.com/SethuRamanOmanakuttan) > > [https://twitter.com/Sethu\_Raman\_O](https://twitter.com/Sethu_Raman_O) > > [https://www.linkedin.com/in/sethuraman-omanakuttan](https://www.linkedin.com/in/sethuraman-omanakuttan) # Trader Node Source: https://docs.chainstack.com/docs/trader-node Deploy Trader Nodes on Chainstack for regional, low-latency RPC endpoints with Warp transaction support for faster block inclusion on supported networks. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). > [!TIP] > > ### Warp transactions > > Trader Nodes are extremely useful when used with [Warp transactions](/docs/warp-transactions) on Solana, Ethereum, BNB Smart Chain. > [!NOTE] > > Trader Nodes are available starting from the [paid plans](https://chainstack.com/pricing/). ## Trader Node deprecation Chainstack plans to deprecate Trader Nodes on December 1, 2026, and recommends moving your workloads to [Global Nodes](/docs/global-elastic-node) before then. Global Nodes cover what Trader Nodes were built for, and add more. They match Trader Node latency, add built-in failover for higher reliability, route each request from the nearest location, and provision instantly, so an endpoint is ready in under a second. To switch, deploy a Global Node from your [Chainstack account](https://console.chainstack.com/), then point your application to the Global Node endpoint in place of your Trader Node endpoint. Your latency stays the same. If you need help, [contact support](https://chainstack.com/contact/). ## Modes For Trader Nodes deployed on the most of public chains, Chainstack supports the following modes: * Full — a node that stores full blockchain data. However, it has limitations to how many blocks are available for querying. * Archive — a node that stores full blockchain data and an archive of historical states, which makes it possible to query any block since the chain genesis. The mode also sets the billing class for every request on the node. A full-mode Trader Node bills every request at 1 RU. An archive-mode Trader Node bills every request at 2 RUs, regardless of the method or the block requested, including each WebSocket subscription notification. See [How your node type affects this](/docs/request-units#how-your-node-type-affects-this). ## Mode and mempool availability by region Trader Nodes are bound to a single region, and the modes on offer differ from region to region. Check [Available clouds, regions, & locations](/docs/nodes-clouds-regions-and-locations) for the modes in your region. Subscribing to pending transactions on a Trader Node works in every region and mode, and Trader Nodes do not serve `txpool_content`, `txpool_contentFrom`, or `txpool_inspect` in any region or mode. If you need the pool contents, deploy a [Dedicated Node](/docs/dedicated-node) and ask for the `txpool_*` namespace. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ## Warp transactions [Warp transactions](/docs/warp-transactions) propagate and land your transactions at the fastest speed possible. They are now a node [add-on](/docs/add-ons) you install on Trader and Global Nodes from the node's **Add-ons** tab, rather than a deployment-time option. ## Dedicated gateways A dedicated gateway is one of the Trader Node customization features that is available on the [Enterprise plan](https://chainstack.com/pricing/). Dedicated gateways process connections and traffic exclusively for a specific customer, avoiding potential issues from handling multiple customers' workload. This ensures a stable and reliable connection while reducing the likelihood of connection errors. # Solana: Transferring SPL tokens in TypeScript Source: https://docs.chainstack.com/docs/transferring-spl-tokens-on-solana-typescript Transfer SPL tokens on Solana using TypeScript with the solana-web3.js and spl-token libraries through a Chainstack node for reliable token delivery. **TLDR:** * This guide shows how to send SPL tokens on Solana in TypeScript using @solana/web3.js and @solana/spl-token. * You’ll set up a Node.js/TypeScript project, load your private key and RPC node URL via environment variables, then compile and send a transaction that creates/fetches associated token accounts and transfers tokens. * Priority fees can help avoid transaction expiration errors, and best practices include better retry logic and possibly using durable nonces when immediate finality isn’t critical. * Once the script is run, you’ll see logs and a transaction link to confirm the token transfer on Solscan. ## Main article Solana's blockchain technology offers a platform for fast and secure transactions. Central to its appeal is the ability to transfer SPL tokens, representing anything from digital currencies to assets. This guide cuts through the complexity of these transactions, using TypeScript to show how it's done. Perfect for developers or blockchain enthusiasts, we'll break down a script that handles token transfers from start to finish. Get ready to dive into Solana's ecosystem, understand the nuts and bolts of SPL token transfers, and equip yourself with the know-how to explore more advanced topics in blockchain development. ## What are SPL tokens on Solana The Solana blockchain's standard for creating digital assets is SPL tokens or Solana Program Library tokens. They are akin to Ethereum's ERC-20 tokens but are designed to leverage Solana's high throughput and low transaction costs. These tokens can represent various assets, ranging from cryptocurrencies and utility tokens to digital representations of physical assets. The flexibility and efficiency of SPL tokens make them a popular choice for developers looking to build fast, scalable, and cost-effective applications on the blockchain. With SPL tokens, creators can quickly launch new currencies, reward systems, or any form of digital assets, providing a robust foundation for the innovative development of decentralized applications on the Solana platform. ## Prerequisites Before diving into the process of transferring SPL tokens on the Solana blockchain using TypeScript, there are several vital prerequisites you need to ensure are in place. This foundation will set you up for success, enabling a smooth development experience. Let's go through what you need: ### Deploy a Chainstack Solana node > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Deploy a node](/docs/manage-your-networks) > [card] [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials) ### Environment setup * **Node.js**: Ensure you have [Node.js](https://nodejs.org/en/download/current) (version 18 or above) installed on your machine. Node.js is essential for running the scripts and managing the dependencies of the Raydium SDK. * **npm**: Node Package Manager is the default package manager for Node.js projects. It is used to install the necessary dependencies for this project. npm comes pre-installed with Node.js, so you don't need to install it separately. * **Solana wallet**: A Solana wallet with SOL and SPL tokens. > [!NOTE] > > If you prefer, you can also use Yarn as an alternative package manager. Yarn is a fast, reliable, and secure dependency management tool. After installing Node.js, you can install Yarn by running `npm install --global yarn` in your terminal. ## Setting up a TypeScript project Before working on the code, we need to set up a TypeScript project. This will provide us with a structured environment for our code and allow us to take advantage of TypeScript's type-checking and other features. Follow these steps to create a new TypeScript project: **Create a new directory for your project**: Open your terminal and navigate to the location where you want to create your project directory. Run the following command to create a new directory: ```bash Bash theme={"system"} mkdir solana-spl-token-transfer ``` **Initialize a new Node.js project**: Navigate into the newly created directory and initialize a new Node.js project by running: ```bash Bash theme={"system"} cd solana-spl-token-transfer npm init -y ``` This will create a `package.json` file, which is used to manage project dependencies. **Install TypeScript and type definitions**: Next, we must install TypeScript and the type definitions for the Solana Web3.js library. Run the following command: ```bash Bash theme={"system"} npm install --save-dev typescript @types/node ``` **Create a TypeScript configuration file**: To configure TypeScript for our project, we must create a `tsconfig.json` file. Run the following command to generate a basic configuration file: ```bash Bash theme={"system"} tsc --init ``` This will create a `tsconfig.json` file with default settings. You can customize these settings as needed for your project. **Create a source file**: Let's create our first TypeScript file where we'll write our code. ```bash Bash theme={"system"} touch main.ts ``` ### Installing required packages We must install several packages to interact with the Solana blockchain and work with SPL tokens. These packages will provide the necessary functionality to connect to a Solana node, manage wallets, and perform token transfers. Follow these steps to install the required packages: **Install the Solana Web3.js library and the SPL Token library**: ```bash Bash theme={"system"} npm install @solana/web3.js @solana/spl-token ``` * The `@solana/web3.js` package is the official Solana Web3 library, which provides a JavaScript API for interacting with the Solana blockchain. * The `@solana/spl-token` package is a library simplifying working with SPL tokens on the Solana blockchain. **Install additional dependencies**: ```bash Bash theme={"system"} npm install bs58 dotenv ``` * The `bs58` package is a base58 encoding/decoding library for Solana addresses and keypairs. * The `dotenv` package allows us to load environment variables from a `.env` file, which helps store sensitive information like private keys. After running these commands, your `package.json` file should have the following dependencies: ```Json JSON theme={"system"} "dependencies": { "@solana/spl-token": "^0.3.11", "@solana/web3.js": "^1.90.1", "bs58": "^5.0.0", "dotenv": "^16.4.5" } ``` With these packages installed, you'll have the tools to connect to a Solana node, manage wallets, and perform SPL token transfers using TypeScript. In the next section, we'll start coding and explore how to use these libraries to transfer SPL tokens from one wallet to another. ## Setting up environment variables This tutorial will use sensitive information such as private keys and RPC node URLs. It's crucial to keep this information secure and avoid committing it to version control systems like Git. We'll use the `dotenv` package to load environment variables from a `.env` file to achieve this. Follow these steps to set up your environment variables: 1. **Create a `.env` file**: In the root directory of your project, create a new file called `.env`. This file will store your environment variables. 2. **Add your environment variables**: Open the `.env` file and add the following variables, replacing the placeholders with your actual values: ``` SOLANA_RPC="YOUR_HTTPS_CHAINSTACK_URL" SOLANA_WSS="YOUR_WEBSOCKET_CHAINSTACK_URL" PRIVATE_KEY="YOUR_PRIVATE_KEY" ``` * `SOLANA_RPC`: This variable should contain your Solana node's HTTP RPC URL. If you're using a Chainstack node, the RPC URL is in the node's credentials. * `SOLANA_WSS`: This variable should contain the WebSocket URL of your Solana node. If you're using a Chainstack node, you can find the WebSocket URL in the node's credentials. * `PRIVATE_KEY`: This variable should contain the private key of the Solana wallet you want to use for token transfers. ### How to use environment variables Once the `.env` file is set up, you can load environment variables in your TypeScript code (e.g., `main.ts`), import the `dotenv` package, and load the environment variables at the beginning of your script: ```typescript TypeScript theme={"system"} import "dotenv/config"; ``` This will load the environment variables from the `.env` file into the `process.env` object, allowing you to access them using `process.env.VARIABLE_NAME`. **Access environment variables**: You can now access the environment variables in your code like this: ```typescript TypeScript theme={"system"} const rpcUrl = process.env.SOLANA_RPC; const wsUrl = process.env.SOLANA_WSS; const privateKey = process.env.PRIVATE_KEY; ``` By following this approach, you can keep sensitive information like private keys and node URLs out of your codebase, making it more secure and easier to manage different environments (e.g., development, staging, production). > [!NOTE] > > Remember to add the `.env` file to your `.gitignore` file to prevent it from being committed to version control systems, as it contains sensitive information. ## Transferring SPL Tokens code walkthrough Now that we have set up the project, installed the required packages, and configured the environment variables, it's time to put everything together and implement the token transfer functionality. ### Adding the code 1. Open the `main.ts` file you created earlier in your preferred code editor. 2. Paste the following code into the `main.ts` file: ```typescript TypeScript theme={"system"} import { getOrCreateAssociatedTokenAccount, createTransferInstruction, } from "@solana/spl-token"; import { Connection, PublicKey, TransactionMessage, VersionedTransaction, Keypair, ParsedAccountData, ComputeBudgetProgram } from "@solana/web3.js"; import bs58 from "bs58"; import "dotenv/config"; // Fetches the number of decimals for a given token to accurately handle token amounts. async function getNumberDecimals( mintAddress: PublicKey, connection: Connection ): Promise { const info = await connection.getParsedAccountInfo(mintAddress); const decimals = (info.value?.data as ParsedAccountData).parsed.info .decimals as number; console.log(`Token Decimals: ${decimals}`); return decimals; } // Initializes a Keypair from the secret key stored in environment variables. Essential for signing transactions. function initializeKeypair(): Keypair { const privateKey = new Uint8Array(bs58.decode(process.env.PRIVATE_KEY!)); const keypair = Keypair.fromSecretKey(privateKey); console.log( `Initialized Keypair: Public Key - ${keypair.publicKey.toString()}` ); return keypair; } // Sets up the connection to the Solana cluster, utilizing environment variables for configuration. function initializeConnection(): Connection { const rpcUrl = process.env.SOLANA_RPC!; const connection = new Connection(rpcUrl, { commitment: "confirmed", wsEndpoint: process.env.SOLANA_WSS, }); // Redacting part of the RPC URL for security/log clarity console.log(`Initialized Connection to Solana RPC: ${rpcUrl.slice(0, -32)}`); return connection; } // Main function orchestrates sending tokens by calling the defined functions in order. async function main() { console.log("Starting Token Transfer Process"); const connection = initializeConnection(); const fromKeypair = initializeKeypair(); // Address receiving the tokens const destinationWallet = new PublicKey( "CzNGm14nMopjGYyycMbWqEF2e1aEHcJLKk2CHw9BiZwC" ); // The SPL token being transferred, this is the address for USDC const mintAddress = new PublicKey( "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" ); // Config priority fee and amount to transfer const PRIORITY_RATE = 12345; // MICRO_LAMPORTS const transferAmount = 0.01; // Instruction to set the compute unit price for priority fee const PRIORITY_FEE_INSTRUCTIONS = ComputeBudgetProgram.setComputeUnitPrice({microLamports: PRIORITY_RATE}); console.log("----------------------------------------"); const decimals = await getNumberDecimals(mintAddress, connection); // Creates or fetches the associated token accounts for the sender and receiver. let sourceAccount = await getOrCreateAssociatedTokenAccount( connection, fromKeypair, mintAddress, fromKeypair.publicKey ); console.log(`Source Account: ${sourceAccount.address.toString()}`); let destinationAccount = await getOrCreateAssociatedTokenAccount( connection, fromKeypair, mintAddress, destinationWallet ); console.log(`Destination Account: ${destinationAccount.address.toString()}`); console.log("----------------------------------------"); // Adjusts the transfer amount according to the token's decimals to ensure accurate transfers. const transferAmountInDecimals = transferAmount * Math.pow(10, decimals); // Prepares the transfer instructions with all necessary information. const transferInstruction = createTransferInstruction( // Those addresses are the Associated Token Accounts belonging to the sender and receiver sourceAccount.address, destinationAccount.address, fromKeypair.publicKey, transferAmountInDecimals ); console.log( `Transaction instructions: ${JSON.stringify(transferInstruction)}` ); let latestBlockhash = await connection.getLatestBlockhash("confirmed"); // Compiles and signs the transaction message with the sender's Keypair. const messageV0 = new TransactionMessage({ payerKey: fromKeypair.publicKey, recentBlockhash: latestBlockhash.blockhash, instructions: [PRIORITY_FEE_INSTRUCTIONS, transferInstruction], }).compileToV0Message(); const versionedTransaction = new VersionedTransaction(messageV0); versionedTransaction.sign([fromKeypair]); console.log("Transaction Signed. Preparing to send..."); // Attempts to send the transaction to the network, handling success or failure. try { const txid = await connection.sendTransaction(versionedTransaction, { maxRetries: 20, }); console.log(`Transaction Submitted: ${txid}`); const confirmation = await connection.confirmTransaction( { signature: txid, blockhash: latestBlockhash.blockhash, lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, }, "confirmed" ); if (confirmation.value.err) { throw new Error("🚨Transaction not confirmed."); } console.log( `Transaction Successfully Confirmed! 🎉 View on SolScan: https://solscan.io/tx/${txid}` ); } catch (error) { console.error("Transaction failed", error); } } main(); ``` This code contains all the necessary functions and logic to initiate a transfer of SPL tokens from one wallet to another on the Solana blockchain. ### Code explanation Let's break down the code and understand how it works: 1. **Importing dependencies**: The code starts by importing the necessary functions and types from the `@solana/spl-token` and `@solana/web3.js` packages, as well as the `bs58` package for base58 encoding/decoding and the `dotenv` package for loading environment variables. 2. **Helper functions**: * `getNumberDecimals`: This function retrieves the number of decimals for a given token mint address from the Solana blockchain. This information is crucial for accurately handling token amounts during transfers. * `initializeKeypair`: This function initializes a Keypair object from the private key stored in the environment variables. The Keypair is used for signing transactions. * `initializeConnection`: This function sets up the connection to the Solana cluster using the RPC URL and WebSocket URL stored in the environment variables. 3. **Main function**: * The primary function orchestrates the entire token transfer process. * It initializes the connection to the Solana cluster and the sender's Keypair. * It defines the transfer's destination wallet address and the token mint address. * It retrieves the number of decimals for the token using the `getNumberDecimals` function. * It creates or fetches the [Associated Token Accounts](/docs/solana-glossary#associated-token-account) for the sender and receiver using the `getOrCreateAssociatedTokenAccount` function from the `@solana/spl-token` package. * It adjusts the transfer amount according to the token's decimals to ensure accurate transfers. * It prepares the transfer instruction using the `createTransferInstruction` function from the `@solana/spl-token` package. * It compiles the transaction message, signs it with the sender's Keypair, and sends the transaction to the Solana network using the [`sendTransaction`](/reference/solana-sendtransaction) method from the `@solana/web3.js` package. * It handles the transaction's success or failure by logging the appropriate messages and providing a link to view the transaction on the Solana Explorer (in case of success). ### Running the script Now that we have the code and understand how it works, it’s time to send some SPL tokens. The script, by default, transfers 0.01 USDC to the destination wallet. You can edit the destination, token address, and amount from this portion of the code in the `main` function: ```typescript TypeScript theme={"system"} // Address receiving the tokens const destinationWallet = new PublicKey( "H2zoMMQhNUY4ADhBBZ6Av7yfNR31cuCc7TeqVwnRZdW9" ); // The SPL token being transferred, this is the address for USDC const mintAddress = new PublicKey( "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" ); // Config priority fee and amount to transfer in SOL, the lamport conversion is handled automatically based on the token's decimals const PRIORITY_RATE = 12345; // MICRO_LAMPORTS const transferAmount = 0.01; ``` To run the code after you configured the parameter, run the following command in the terminal: ```shell Shell theme={"system"} ts-node main.ts ``` This will perform the transfer and log the process in the console; it will look similar to this: ```shell Shell theme={"system"} Starting Token Transfer Process Initialized Connection to Solana RPC: https://solana-mainnet.core.chainstack.com/ Initialized Keypair: Public Key - CzNGm14nMopjGYyycMbWqEF2e1aEHcJLKk2CHw9BiZwC ---------------------------------------- Token Decimals: 6 Source Account: 46cFTq7dJ858wHpLHUtoSgiixdpUf1TBabbriS2DTCMG Destination Account: 5sQerrN2zH6udDZya96gMEnMmC7hUqP1THNidxeuV2UX ---------------------------------------- Transaction instructions: {"keys":[{"pubkey":"46cFTq7dJ858wHpLHUtoSgiixdpUf1TBabbriS2DTCMG","isSigner":false,"isWritable":true},{"pubkey":"5sQerrN2zH6udDZya96gMEnMmC7hUqP1THNidxeuV2UX","isSigner":false,"isWritable":true},{"pubkey":"CzNGm14nMopjGYyycMbWqEF2e1aEHcJLKk2CHw9BiZwC","isSigner":true,"isWritable":false}],"programId":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","data":[3,16,39,0,0,0,0,0,0]} Transaction Signed. Preparing to send... Transaction Submitted: fuJnmA4ChNbg3HJwCXVZtku7ggWydN4Aj5TbcxEYoYMjqS7Yxh1dxbAMErBN7RAJGHPwXXpWk54joyoszp7v6PE Transaction Successfully Confirmed! 🎉 View on SolScan: https://solscan.io/tx/fuJnmA4ChNbg3HJwCXVZtku7ggWydN4Aj5TbcxEYoYMjqS7Yxh1dxbAMErBN7RAJGHPwXXpWk54joyoszp7v6PE ``` > [!NOTE] > > You can find an example of this kind of transaction on the [Solscan explorer](https://solscan.io/tx/fuJnmA4ChNbg3HJwCXVZtku7ggWydN4Aj5TbcxEYoYMjqS7Yxh1dxbAMErBN7RAJGHPwXXpWk54joyoszp7v6PE). ## Possible error `TransactionExpiredBlockheightExceededError` When attempting an SPL transfer using this code, you might get the following error: `TransactionExpiredBlockheightExceededError`. This section aims to shed light on what causes this error and propose strategies to mitigate its occurrence. ### Understanding `TransactionExpiredBlockheightExceededError` This error occurs when a transaction is repeatedly forwarded to subsequent block leaders without being included in any block until the associated blockhash, or recent blockhash, expires. The blockhash is a critical component of a transaction on Solana, acting as a reference to a recent block to ensure the transaction is processed promptly and to prevent double-spending. > [!NOTE] > > It's important to recognize that this is not primarily an issue with the RPC setup unless the global coverage of the RPC nodes is significantly inadequate, resulting in consistently high latency due to geographical distance from the block leader. ### Mitigation strategies #### 1. Understanding the limitations First, it's important to acknowledge that it is currently *impossible* to completely eliminate this issue until the Solana mainnet scheduler becomes fully deterministic, which is anticipated in release 1.18. This limitation stems from the inherent unpredictability in transaction processing times due to network congestion, variations in transaction fees, and the non-deterministic nature of the current scheduler. #### 2. Adjusting fees Adding higher fees to your transactions can increase their priority, making them more likely to be included in a block. This approach leverages the network's fee-based prioritization to reduce the chances of your transaction being sidelined. > [!NOTE] > > This code includes priority fees, which should help with this issue, you can edit it in this line. > > `const PRIORITY_RATE = 12345; // MICRO_LAMPORTS` > > Learn more about implementing priority fees reading [Solana: How to use Priority Fees to unlock faster transactions](/docs/solana-how-to-priority-fees-faster-transactions). #### 3. Optimizing geographical proximity Minimizing the physical distance between your servers (or clients), the block leader, and your chosen RPC nodes can significantly affect transaction latency. Since transaction inclusion can sometimes resemble a race, being geographically closer to these nodes can provide a critical edge. #### 4. Implementing better retries Enhancing your retry logic can also help. This involves reattempting transactions more intelligently when they fail and dynamically adjusting the strategy based on network conditions, such as increasing fees or choosing optimal times for submission. > [!NOTE] > > Note that the code in this tutorial implements retries from the `@solana/web3.js` library, but it might not be enough, and you might need to add more logic. Check [Solana: Enhancing SPL Token transfers with retry logic](/docs/enhancing-solana-spl-token-transfers-with-retry-logic) to learn how to implement better retries. #### 5. Utilizing durable nonces Employing [durable nonces](/docs/solana-glossary#durable-transaction-nonces) can be a strategic choice for applications that can tolerate some delay in transaction finality. Durable nonces allow transactions to remain valid for longer, thus mitigating the risk of expiration. This approach is particularly useful for transactions where immediate finality is not critical. ## Conclusion This tutorial explored transferring SPL tokens on the Solana blockchain using TypeScript. We covered the following key points: * Understanding SPL tokens and their role in the Solana ecosystem * Setting up the necessary prerequisites, including a Chainstack Solana node, Node.js environment, and a Solana wallet * Creating a TypeScript project and installing required packages like `@solana/web3.js`, `@solana/spl-token`, `bs58`, and `dotenv` * Securely storing sensitive information, such as private keys and node URLs, using environment variables and the `dotenv` package * Implementing the token transfer functionality by connecting to the Solana cluster, initializing the sender's keypair, fetching/creating associated token accounts, preparing the transfer instruction, and sending the transaction to the network * Running the script and examining the console logs to verify the successful transfer of SPL tokens Following this tutorial, you have gained hands-on experience working with the Solana blockchain and SPL tokens using TypeScript. You now possess the knowledge and skills to build applications that leverage the speed and efficiency of the Solana ecosystem, enabling you to explore more advanced topics in blockchain development. Remember, this tutorial serves as a starting point, and numerous possibilities exist to expand upon this foundation. You can enhance the script with additional features, integrate it into larger applications, or explore other aspects of Solana development. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # TRON: Energy and Bandwidth with Python & Chainstack Source: https://docs.chainstack.com/docs/tron-mastering-energy-bandwidth-with-python-and-chainstack Learn how to manage TRON's unique fee model using Python and Chainstack nodes to reduce or eliminate transaction costs through resource management. **TLDR:** * Explains TRON's unique fee model with Energy and Bandwidth resources that can reduce transaction costs to zero. * Demonstrates how to freeze (stake) TRX to gain daily allocations of Energy for smart contracts and Bandwidth for basic transactions. * Shows how to use Python and Chainstack TRON nodes to manage resources, check balances, and execute transactions. * Provides a complete script to freeze/unfreeze TRX, monitor resource usage, and optimize transaction fees. ## Overview The short version is if you stake your TRX (called Freezing), you will get your daily allowance of gas free EOA to EOA transactions (Bandwidth allocation) and EOA to contract transactions (Energy allocation). EOA to EOA transactions are called Bandwidth-consuming transactions. EOA to contract transactions are called Energy-consuming transactions. TRON's unique fee model revolves around two core resources—Energy and Bandwidth. Mastering how these resources function allows developers and users to significantly reduce transaction costs, potentially to zero. This article will guide you through understanding and managing TRON resources using Python and a Chainstack TRON nodes, offering practical examples and scripts to streamline your interaction with the TRON blockchain. > [!NOTE] > > For the detailed TRON Resource Model overview, see TRON docs: [Resource Model](https://developers.tron.network/docs/resource-model). ### Bandwidth Bandwidth on TRON is consumed by basic operations such as sending TRX or TRC-10 tokens (TRC-10s are like colored coins—they are similar to TRC-20 but don't involve an actual smart contract processing). Accounts receive daily free bandwidth based on their state, but heavy usage can quickly exhaust these allowances. ### Energy Energy powers smart contract executions, including TRC-20 token interactions. Without sufficient Energy, transactions consume TRX as fees. ### Freezing TRX Freezing is pretty much staking. Freezing (staking) TRX grants daily Energy or Bandwidth, enabling fee-free transactions. You can reclaim your frozen TRX after a 3-day staking period. ## Implementation Let's do a full cycle script that retrieves the account's Energy and Bandwidth data from the chain, stakes (freezes) and unstakes (unfreezes) the TRXs. ### Prerequisites 1. Python 3.7+ 2. [Chainstack account](https://console.chainstack.com/) to deploy a [reliable TRON RPC endpoint](https://chainstack.com/build-better-with-tron/). Make sure you pick the HTTP API endpoint that looks like this `https://tron-nile.core.chainstack.com/11112222333444555666677778888/wallet` 3. A TRON account with a private key and some TRX balance that you can get from the [Nile faucet](https://nileex.io/join/getJoinPage) ### Install dependencies ```bash theme={"system"} pip install tronpy ``` ### Connect to Chainstack Node In your Python script, connect to your Chainstack node: ```python theme={"system"} from tronpy import Tron from tronpy.providers import HTTPProvider CHAINSTACK_TRON_ENDPOINT = "CHAINSTACK_NODE_ENDPOINT" client = Tron( provider=HTTPProvider(CHAINSTACK_TRON_ENDPOINT), network='mainnet' ) ``` ### Generate or load a TRON account Create a new account: ```python theme={"system"} new_account = client.generate_address() print("Address:", new_account['base58check_address']) print("Private Key:", new_account['private_key']) ``` Or load your existing key: ```python theme={"system"} PRIVATE_KEY = "YOUR_PRIVATE_KEY" MY_ADDRESS = "YOUR_TRON_ADDRESS" ``` ### Check resources by retrieving the data from the chain Retrieve your current Bandwidth and Energy: ```python theme={"system"} def get_account_resources(address): resources = client.get_account_resource(address) bandwidth = resources.get('freeNetLimit', 0) energy = resources.get('TotalEnergyLimit', 0) print(f"Bandwidth: {bandwidth}, Energy: {energy}") get_account_resources(MY_ADDRESS) ``` ### Freeze TRX for resources Freeze TRX to gain Energy: ```python theme={"system"} def freeze_for_energy(private_key, address, amount_trx): freeze_amount_sun = int(amount_trx * 1_000_000) txn = client.trx.freeze_balance( address, amount=freeze_amount_sun, resource="ENERGY" ).build() signed_txn = txn.sign(private_key) result = signed_txn.broadcast() print("Freeze Transaction ID:", result['txid']) freeze_for_energy(PRIVATE_KEY, MY_ADDRESS, 10) ``` ### Send TRX with minimal fees Execute a simple TRX transaction: ```python theme={"system"} def send_trx(private_key, owner, recipient, amount_trx): amount_sun = int(amount_trx * 1_000_000) txn = client.trx.transfer(owner, recipient, amount_sun).build() signed_txn = txn.sign(private_key) result = signed_txn.broadcast() print("Transaction ID:", result['txid']) recipient = "RECIPIENT_ADDRESS" send_trx(PRIVATE_KEY, MY_ADDRESS, recipient, 1) ``` ### Verify transaction fees Check the fees for your transaction: ```python theme={"system"} import time def get_tx_info(tx_id): time.sleep(3) tx_info = client.get_transaction_info(tx_id) print("Net Fee:", tx_info.get('net_fee')) print("Energy Fee:", tx_info.get('energy_fee')) tx_id = "YOUR_TX_ID" get_tx_info(tx_id) ``` ### Unfreeze TRX > [!NOTE] > > Note that on the mainnet it'll take 3 days to unstake. Reclaim your staked TRX: ```python theme={"system"} def unfreeze_energy(private_key, address): txn = client.trx.unfreeze_balance( address, resource='ENERGY' ).build() signed_txn = txn.sign(private_key) result = signed_txn.broadcast() print("Unfreeze Transaction ID:", result['txid']) unfreeze_energy(PRIVATE_KEY, MY_ADDRESS) ``` ### Full script ```python theme={"system"} from tronpy import Tron from tronpy.providers import HTTPProvider from tronpy.keys import PrivateKey import time # Replace this with your Chainstack TRON HTTP API endpoint URL # Example (note the /wallet append) https://tron-nile.core.chainstack.com/11112222333444555666677778888/wallet CHAINSTACK_TRON_ENDPOINT = "CHAINSTACK_NODE_ENDPOINT" # Replace with your actual private key and address PRIVATE_KEY = "YOUR_PRIVATE_KEY" MY_ADDRESS = "YOUR_ADDRESS" # base58 or hex # Initialize the client client = Tron( provider=HTTPProvider(CHAINSTACK_TRON_ENDPOINT), network='nile' # Change this to 'mainnet' if you want mainnet ) # Generate a new Tron account def generate_new_account(): new_account = client.generate_address() print("Generated address:", new_account['base58check_address']) print("Private key:", new_account['private_key']) return new_account # Retrieve account resource info def get_account_resources(address): resources = client.get_account_resource(address) bandwidth = resources.get('freeNetLimit', 0) energy = resources.get('TotalEnergyLimit', 0) print(f"Bandwidth (Free Net Limit): {bandwidth}") print(f"Total Energy Limit: {energy}") # For more detailed information: print(f"Total Net Limit: {resources.get('TotalNetLimit', 0)}") print(f"Total Net Weight: {resources.get('TotalNetWeight', 0)}") print(f"Total Energy Weight: {resources.get('TotalEnergyWeight', 0)}") return bandwidth, energy # Freeze TRX for energy def freeze_for_energy(private_key, owner_address, amount_trx): freeze_amount_sun = int(amount_trx * 1_000_000) # 1 TRX = 1,000,000 SUN txn = client.trx.freeze_balance( owner_address, amount=freeze_amount_sun, resource="ENERGY" ).build() # Sign and broadcast priv_key = PrivateKey(bytes.fromhex(private_key)) signed_txn = txn.sign(priv_key) result = signed_txn.broadcast() print("Freeze transaction ID:", result['txid']) return result # Freeze TRX for bandwidth def freeze_for_bandwidth(private_key, owner_address, amount_trx): freeze_amount_sun = int(amount_trx * 1_000_000) # 1 TRX = 1,000,000 SUN txn = client.trx.freeze_balance( owner_address, amount=freeze_amount_sun, resource="BANDWIDTH" ).build() # Sign and broadcast priv_key = PrivateKey(bytes.fromhex(private_key)) signed_txn = txn.sign(priv_key) result = signed_txn.broadcast() print("Freeze transaction ID:", result['txid']) return result # Send TRX transaction def send_trx(private_key, owner_address, to_address, amount_trx): amount_sun = int(amount_trx * 1_000_000) # 1 TRX = 1,000,000 SUN try: txn = ( client.trx.transfer( owner_address, to_address, amount_sun ) .build() ) priv_key = PrivateKey(bytes.fromhex(private_key)) signed_txn = txn.sign(priv_key) txn_result = signed_txn.broadcast() print("Send TRX transaction ID:", txn_result) return txn_result except Exception as e: print(f"Error sending transaction: {str(e)}") print("Using your own address as recipient for testing purposes...") # Fall back to using sender as recipient for testing try: txn = ( client.trx.transfer( owner_address, owner_address, # Send to self amount_sun ) .build() ) priv_key = PrivateKey(bytes.fromhex(private_key)) signed_txn = txn.sign(priv_key) txn_result = signed_txn.broadcast() print("Send TRX transaction ID (to self):", txn_result) return txn_result except Exception as e2: print(f"Error in fallback transaction: {str(e2)}") return {"txid": None, "error": str(e2)} # Get transaction info def get_tx_info(tx_id): if not tx_id: print("No transaction ID provided.") return {} try: # Give the network a moment to confirm time.sleep(3) tx_info = client.get_transaction_info(tx_id) return tx_info except Exception as e: print(f"Error getting transaction info: {str(e)}") return {} # Unfreeze TRX from energy allocation def unfreeze_energy(private_key, owner_address): try: txn = ( client.trx.unfreeze_balance( owner_address, resource='ENERGY', unfreeze_balance=10_000_000 # 10 TRX in SUN ) .build() ) priv_key = PrivateKey(bytes.fromhex(private_key)) signed_txn = txn.sign(priv_key) txn_result = signed_txn.broadcast() print("Unfreeze transaction ID:", txn_result) return txn_result except Exception as e: print(f"Error unfreezing energy: {str(e)}") try: txn = ( client.trx.unfreeze_balance( owner_address=owner_address, resource='ENERGY', unfreeze_balance=10_000_000 # 10 TRX in SUN ) .build() ) priv_key = PrivateKey(bytes.fromhex(private_key)) signed_txn = txn.sign(priv_key) txn_result = signed_txn.broadcast() print("Unfreeze transaction ID (using alternate format):", txn_result) return txn_result except Exception as e2: print(f"Error with alternate unfreeze format: {str(e2)}") return {"error": str(e2)} # Unfreeze TRX from bandwidth allocation def unfreeze_bandwidth(private_key, owner_address): try: txn = ( client.trx.unfreeze_balance( owner_address, resource='BANDWIDTH', unfreeze_balance=10_000_000 # 10 TRX in SUN ) .build() ) priv_key = PrivateKey(bytes.fromhex(private_key)) signed_txn = txn.sign(priv_key) txn_result = signed_txn.broadcast() print("Unfreeze transaction ID:", txn_result) return txn_result except Exception as e: print(f"Error unfreezing bandwidth: {str(e)}") try: txn = ( client.trx.unfreeze_balance( owner_address=owner_address, resource='BANDWIDTH', unfreeze_balance=10_000_000 # 10 TRX in SUN ) .build() ) priv_key = PrivateKey(bytes.fromhex(private_key)) signed_txn = txn.sign(priv_key) txn_result = signed_txn.broadcast() print("Unfreeze transaction ID (using alternate format):", txn_result) return txn_result except Exception as e2: print(f"Error with alternate unfreeze format: {str(e2)}") return {"error": str(e2)} # Get account TRX balance def get_account_balance(address): account = client.get_account(address) balance_sun = account.get('balance', 0) # Balance in SUN balance_trx = balance_sun / 1_000_000 # Convert to TRX (1 TRX = 1,000,000 SUN) print(f"Account Balance: {balance_trx} TRX ({balance_sun} SUN)") return balance_trx, balance_sun # Get detailed account resource information def get_detailed_resource_info(address): # Get account info account = client.get_account(address) resources = client.get_account_resource(address) # Get balance balance_sun = account.get('balance', 0) balance_trx = balance_sun / 1_000_000 # Extract bandwidth information free_net_used = account.get('free_net_used', 0) free_net_limit = resources.get('freeNetLimit', 0) free_net_remaining = free_net_limit - free_net_used net_used = account.get('net_used', 0) net_limit = account.get('net_limit', {}).get('limit', 0) net_remaining = net_limit - net_used if net_limit > 0 else 0 # Extract energy information energy_used = account.get('energy_used', 0) energy_limit = account.get('energy_limit', 0) energy_remaining = energy_limit - energy_used if energy_limit > 0 else 0 print("\n=== Detailed Resource Information ===") print(f"Address: {address}") print(f"Balance: {balance_trx} TRX") print("\n--- Bandwidth (NET) ---") print(f"Free Bandwidth Used: {free_net_used} / {free_net_limit} ({free_net_remaining} remaining)") print(f"Frozen Bandwidth Used: {net_used} / {net_limit} ({net_remaining} remaining)") print("\n--- Energy ---") print(f"Energy Used: {energy_used} / {energy_limit} ({energy_remaining} remaining)") # Return the data for comparison return { 'balance': balance_trx, 'balance_sun': balance_sun, 'free_net_used': free_net_used, 'free_net_limit': free_net_limit, 'net_used': net_used, 'net_limit': net_limit, 'energy_used': energy_used, 'energy_limit': energy_limit } # Compare resource usage before and after an operation def compare_resources(before, after): print("\n=== Resource Usage Comparison ===") # Balance comparison balance_change = after['balance'] - before['balance'] print(f"Balance: {before['balance']} TRX → {after['balance']} TRX (Change: {balance_change} TRX)") # Bandwidth comparison free_net_change = after['free_net_used'] - before['free_net_used'] net_change = after['net_used'] - before['net_used'] print("Bandwidth Changes:") print(f" Free Bandwidth Used: {before['free_net_used']} → {after['free_net_used']} (Change: {free_net_change})") print(f" Frozen Bandwidth Used: {before['net_used']} → {after['net_used']} (Change: {net_change})") # Energy comparison energy_change = after['energy_used'] - before['energy_used'] print("Energy Changes:") print(f" Energy Used: {before['energy_used']} → {after['energy_used']} (Change: {energy_change})") # Limit changes (in case of freezing/unfreezing) if before['net_limit'] != after['net_limit']: print(f" Bandwidth Limit: {before['net_limit']} → {after['net_limit']} (Change: {after['net_limit'] - before['net_limit']})") if before['energy_limit'] != after['energy_limit']: print(f" Energy Limit: {before['energy_limit']} → {after['energy_limit']} (Change: {after['energy_limit'] - before['energy_limit']})") if __name__ == "__main__": print("TRON Resources Management Tool") print("------------------------------") # Check initial resources print("\n1. Checking initial account resources...") bandwidth, energy = get_account_resources(MY_ADDRESS) initial_resources = get_detailed_resource_info(MY_ADDRESS) # Create a test recipient address - generating a new one to ensure it's valid print("\nGenerating a test recipient address...") try: TEST_RECIPIENT = generate_new_account()['base58check_address'] print(f"Test recipient address: {TEST_RECIPIENT}") except Exception as e: print(f"Error generating test address: {str(e)}") print("Using your own address as recipient for testing purposes...") TEST_RECIPIENT = MY_ADDRESS # Fall back to sending to self # Ask user what operation to perform print("\nSelect an operation to perform:") print("1. Freeze TRX for Energy") print("2. Freeze TRX for Bandwidth") print("3. Send a test transaction") print("4. Unfreeze Energy") print("5. Unfreeze Bandwidth") print("6. Run all operations in sequence") print("7. Check account balance") choice = input("\nEnter your choice (1-7): ") if choice == "7": # Just check account balance get_account_balance(MY_ADDRESS) if choice == "1" or choice == "6": # Freeze for energy print("\n2. Freezing 10 TRX for Energy...") freeze_energy_tx = freeze_for_energy(PRIVATE_KEY, MY_ADDRESS, 10) print("Waiting 15 seconds for transaction confirmation...") time.sleep(15) # Increased wait time # Check resources after freezing for energy print("\n3. Checking resources after freezing for Energy...") post_energy_freeze_resources = get_detailed_resource_info(MY_ADDRESS) compare_resources(initial_resources, post_energy_freeze_resources) if choice == "2" or choice == "6": # Freeze for bandwidth print("\n4. Freezing 10 TRX for Bandwidth...") pre_bandwidth_freeze = get_detailed_resource_info(MY_ADDRESS) freeze_bandwidth_tx = freeze_for_bandwidth(PRIVATE_KEY, MY_ADDRESS, 10) print("Waiting 15 seconds for transaction confirmation...") time.sleep(15) # Increased wait time # Check resources after freezing for bandwidth print("\n5. Checking resources after freezing for Bandwidth...") post_bandwidth_freeze_resources = get_detailed_resource_info(MY_ADDRESS) compare_resources(pre_bandwidth_freeze, post_bandwidth_freeze_resources) if choice == "3" or choice == "6": # Send a test transaction print("\n6. Sending a test transaction (1 TRX)...") pre_tx_resources = get_detailed_resource_info(MY_ADDRESS) tx_result = send_trx(PRIVATE_KEY, MY_ADDRESS, TEST_RECIPIENT, 1) print("Waiting 15 seconds for transaction confirmation...") time.sleep(15) # Increased wait time # Get transaction details tx_id = None if isinstance(tx_result, dict): tx_id = tx_result.get('txid') elif isinstance(tx_result, str): tx_id = tx_result if tx_id: tx_info = get_tx_info(tx_id) print(f"\nTransaction Info:") print(f" Net Fee: {tx_info.get('net_fee', 'N/A')}") print(f" Energy Fee: {tx_info.get('energy_fee', 'N/A')}") print(f" Net Usage: {tx_info.get('net_usage', 'N/A')}") print(f" Energy Usage: {tx_info.get('energy_usage', 'N/A')}") else: print("\nNo transaction ID available to fetch details.") # Check resources after transaction print("\n7. Checking resources after transaction...") post_tx_resources = get_detailed_resource_info(MY_ADDRESS) compare_resources(pre_tx_resources, post_tx_resources) if choice == "4" or choice == "6": # Unfreeze Energy print("\n8. Unfreezing Energy...") print("Note: On mainnet, there's a 3-day waiting period before unfreezing.") pre_unfreeze_resources = get_detailed_resource_info(MY_ADDRESS) try: unfreeze_tx = unfreeze_energy(PRIVATE_KEY, MY_ADDRESS) print("Waiting 9 seconds for transaction confirmation...") time.sleep(9) # 3 blocks worth of time # Check resources after unfreezing energy print("\n9. Checking resources after unfreezing Energy...") post_unfreeze_resources = get_detailed_resource_info(MY_ADDRESS) compare_resources(pre_unfreeze_resources, post_unfreeze_resources) except Exception as e: print(f"Error unfreezing energy: {e}") print("If on mainnet, remember there's a 3-day waiting period.") if choice == "5" or choice == "6": # Unfreeze Bandwidth print("\n10. Unfreezing Bandwidth...") print("Note: On mainnet, there's a 3-day waiting period before unfreezing.") pre_unfreeze_bw_resources = get_detailed_resource_info(MY_ADDRESS) try: unfreeze_bw_tx = unfreeze_bandwidth(PRIVATE_KEY, MY_ADDRESS) print("Waiting 9 seconds for transaction confirmation...") time.sleep(9) # 3 blocks worth of time # Check resources after unfreezing bandwidth print("\n11. Checking resources after unfreezing Bandwidth...") post_unfreeze_bw_resources = get_detailed_resource_info(MY_ADDRESS) compare_resources(pre_unfreeze_bw_resources, post_unfreeze_bw_resources) except Exception as e: print(f"Error unfreezing bandwidth: {e}") print("If on mainnet, remember there's a 3-day waiting period.") print("\nOperations completed!") print("Final resource status:") get_detailed_resource_info(MY_ADDRESS) ### Important parameters - CHAINSTACK_NODE_ENDPOINT — Make sure you pick the HTTP API endpoint that looks like this `https://tron-nile.core.chainstack.com/11112222333444555666677778888/wallet` - PRIVATE_KEY — your private key - MY_ADDRESS — your address derived from the private key. ## Summary Now that you've run the full script, did you notice one curious thing? There is no smart contract involved. Staking is implemented in TRON on the protocol level. Mastering TRON's resource model using Python and Chainstack allows developers to leverage the full potential of the blockchain efficiently. This tutorial provides you with the tools and knowledge to minimize or even eliminate transaction fees—significantly improving the cost-effectiveness of your TRON-based projects. ### About the author Ake Director of Developer Experience @ Chainstack
Talk to me all things Web3
20 years in technology | 8+ years in Web3 full time years experience
``` # TRON methods Source: https://docs.chainstack.com/docs/tron-methods Complete TRON blockchain API methods reference. Includes JSON-RPC, wallet, walletsolidity, and gRPC endpoints for smart contracts, transactions, and accounts. Our pricing is simple: 1 RU per request. TRON has no archive split — see [Request units](/docs/request-units#full-vs-archive-by-protocol). > [!NOTE] > > TRON also supports **gRPC** for high-performance access. For gRPC setup, authentication, and available methods, see [TRON tooling: gRPC API](/docs/tron-tooling#grpc-api). | Method | Endpoint | Availability | | - | - | - | | buildTransaction | /jsonrpc | | | eth\_accounts | /jsonrpc | | | eth\_blockNumber | /jsonrpc | | | eth\_call | /jsonrpc | | | eth\_chainId | /jsonrpc | | | eth\_coinbase | /jsonrpc | | | eth\_estimateGas | /jsonrpc | | | eth\_gasPrice | /jsonrpc | | | eth\_getBalance | /jsonrpc | | | eth\_getBlockByHash | /jsonrpc | | | eth\_getBlockByNumber | /jsonrpc | | | eth\_getBlockTransactionCountByHash | /jsonrpc | | | eth\_getBlockTransactionCountByNumber | /jsonrpc | | | eth\_getCode | /jsonrpc | | | eth\_getFilterChanges | /jsonrpc | | | eth\_getFilterLogs | /jsonrpc | | | eth\_getLogs | /jsonrpc | | | eth\_getStorageAt | /jsonrpc | | | eth\_getTransactionByBlockHashAndIndex | /jsonrpc | | | eth\_getTransactionByBlockNumberAndIndex | /jsonrpc | | | eth\_getTransactionByHash | /jsonrpc | | | eth\_getTransactionReceipt | /jsonrpc | | | eth\_getWork | /jsonrpc | | | eth\_newBlockFilter | /jsonrpc | | | eth\_newFilter | /jsonrpc | | | eth\_protocolVersion | /jsonrpc | | | eth\_syncing | /jsonrpc | | | eth\_uninstallFilter | /jsonrpc | | | net\_listening | /jsonrpc | | | net\_peerCount | /jsonrpc | | | net\_version | /jsonrpc | | | web3\_clientVersion | /jsonrpc | | | web3\_sha3 | /jsonrpc | | | accountpermissionupdate | /wallet | | | broadcasthex | /wallet | | | broadcasttransaction | /wallet | | | cancelallunfreezev2 | /wallet | | | clearabi | /wallet | | | createaccount | /wallet | | | createassetissue | /wallet | | | createshieldedcontractparameters | /wallet | | | createshieldedcontractparameterswithoutask | /wallet | | | createspendauthsig | /wallet | | | createtransaction | /wallet | | | createwitness | /wallet | | | delegateresource | /wallet | | | deploycontract | /wallet | | | estimateenergy | /wallet | | | exchangecreate | /wallet | | | exchangeinject | /wallet | | | exchangetransaction | /wallet | | | exchangewithdraw | /wallet | | | freezebalance | /wallet | | | freezebalancev2 | /wallet | | | getaccount | /wallet | | | getaccountbalance | /wallet | | | getaccountnet | /wallet | | | getaccountresource | /wallet | | | getakfromask | /wallet | | | getapprovedlist | /wallet | | | getassetissuebyaccount | /wallet | | | getassetissuebyid | /wallet | | | getassetissuebyname | /wallet | | | getassetissuelist | /wallet | | | getassetissuelistbyname | /wallet | | | getavailableunfreezecount | /wallet | | | getbandwidthprices | /wallet | | | getblock | /wallet | | | getblockbalance | /wallet | | | getblockbyid | /wallet | | | getblockbylatestnum | /wallet | | | getblockbylimitnext | /wallet | | | getblockbynum | /wallet | | | getBrokerage | /wallet | | | getburntrx | /wallet | | | getcandelegatedmaxsize | /wallet | | | getcanwithdrawunfreezeamount | /wallet | | | getchainparameters | /wallet | | | getcontract | /wallet | | | getcontractinfo | /wallet | | | getdelegatedresource | /wallet | | | getdelegatedresourceaccountindex | /wallet | | | getdelegatedresourceaccountindexv2 | /wallet | | | getdelegatedresourcev2 | /wallet | | | getdiversifier | /wallet | | | getenergyprices | /wallet | | | getexchangebyid | /wallet | | | getexpandedspendingkey | /wallet | | | getincomingviewingkey | /wallet | | | getnewshieldedaddress | /wallet | | | getnextmaintenancetime | /wallet | | | getnodeinfo | /wallet | | | getnowblock | /wallet | | | getpaginatedassetissuelist | /wallet | | | getpendingsize | /wallet | | | getproposalbyid | /wallet | | | getReward | /wallet | | | getspendingkey | /wallet | | | gettransactionbyid | /wallet | | | gettransactionfrompending | /wallet | | | gettransactioninfobyblocknum | /wallet | | | gettransactioninfobyid | /wallet | | | gettransactionlistfrompending | /wallet | | | gettriggerinputforshieldedtrc20contract | /wallet | | | getzenpaymentaddress | /wallet | | | isshieldedtrc20contractnotespent | /wallet | | | listexchanges | /wallet | | | listnodes | /wallet | | | listproposals | /wallet | | | listwitnesses | /wallet | | | totaltransaction | /wallet | | | participateassetissue | /wallet | | | proposalapprove | /wallet | | | proposalcreate | /wallet | | | proposaldelete | /wallet | | | scanshieldedtrc20notesbyivk | /wallet | | | scanshieldedtrc20notesbyovk | /wallet | | | transferasset | /wallet | | | triggerconstantcontract | /wallet | | | triggersmartcontract | /wallet | | | undelegateresource | /wallet | | | unfreezeasset | /wallet | | | unfreezebalance | /wallet | | | unfreezebalancev2 | /wallet | | | updateaccount | /wallet | | | updateasset | /wallet | | | updateBrokerage | /wallet | | | updateenergylimit | /wallet | | | updatesetting | /wallet | | | updatewitness | /wallet | | | validateaddress | /wallet | | | votewitnessaccount | /wallet | | | withdrawbalance | /wallet | | | withdrawexpireunfreeze | /wallet | | | estimateenergy | /walletsolidity | | | getaccount | /walletsolidity | | | getassetissuebyid | /walletsolidity | | | getassetissuebyname | /walletsolidity | | | getassetissuelist | /walletsolidity | | | getassetissuelistbyname | /walletsolidity | | | getavailableunfreezecount | /walletsolidity | | | getblock | /walletsolidity | | | getblockbyid | /walletsolidity | | | getblockbylatestnum | /walletsolidity | | | getblockbylimitnext | /walletsolidity | | | getblockbynum | /walletsolidity | | | getBrokerage | /walletsolidity | | | getburntrx | /walletsolidity | | | getcandelegatedmaxsize | /walletsolidity | | | getcanwithdrawunfreezeamount | /walletsolidity | | | getdelegatedresource | /walletsolidity | | | getdelegatedresourceaccountindex | /walletsolidity | | | getdelegatedresourceaccountindexv2 | /walletsolidity | | | getexchangebyid | /walletsolidity | | | getnodeinfo | /walletsolidity | | | getnowblock | /walletsolidity | | | getpaginatedassetissuelist | /walletsolidity | | | getReward | /walletsolidity | | | gettransactionbyid | /walletsolidity | | | gettransactioncountbyblocknum | /walletsolidity | | | gettransactioninfobyblocknum | /walletsolidity | | | gettransactioninfobyid | /walletsolidity | | | isshieldedtrc20contractnotespent | /walletsolidity | | | listexchanges | /walletsolidity | | | listwitnesses | /walletsolidity | | | scanshieldedtrc20notesbyivk | /walletsolidity | | | scanshieldedtrc20notesbyovk | /walletsolidity | | | triggerconstantcontract | /walletsolidity | | # TRON: Polling for TRC20 transfers in Node.js Source: https://docs.chainstack.com/docs/tron-polling-for-trc20-transfers Monitor and poll for TRC20 token transfers on the TRON network in real time using Node.js, TronWeb, and a Chainstack TRON node for reliable data access. **TLDR:** * Demonstrates how to monitor TRC20 transfers on TRON using a polling approach with Node.js and Chainstack nodes. * Provides a robust alternative to event-based monitoring while the TRON event plugin is unavailable. * Includes production-ready features like state persistence, error handling with exponential backoff, and sequential block processing. * Shows how to parse TRC20 transfer transactions, detect large transfers, and ensure no transfers are missed during restarts or network issues. ## Overview To listen to events on the TRON blockchain as they come, you need the TRON event API from the TRON event plugin. As the event plugin is not available as of now, you can vote on the feature request to bump it up on the roadmap: [Implement TRON event plugin](https://ideas.chainstack.com/p/implement-tron-event-plugin). Until then, you can use the polling method to listen to events on the TRON blockchain. This article provides a Node.js script that polls for TRC20 transfers with persistence. ## Prerequisites * [Axios](https://axios-http.com/) library for making HTTP requests * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable TRON RPC endpoint](https://chainstack.com/build-better-with-tron/). Make sure you pick the base HTTP API endpoint without the postfixes that looks like this `https://tron-mainnet.core.chainstack.com/11112222333444555666677778888` ## Implementation We are going to implement simple yet robust logic to be receiving the TRC20 transfer events by constantly polling the latest blocks and retrieving the TRC20 transfers from the blocks. Key parameters in our example: * CHAINSTACK\_NODE\_ENDPOINT — Make sure you pick the base HTTP API endpoint without the postfixes that looks like this `https://tron-mainnet.core.chainstack.com/11112222333444555666677778888` * USDT\_CONTRACT — The script uses the USDT TRC20 contract [TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t](https://tronscan.org/#/token20/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t) as an example. If you are going to track a different contract, just make sure you change the default USDT 6 decimals to your TRC20 token decimals. * `a9059cbb` — The transfer function signature [0xa9059cbb](https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb). This should work for the majority of TRC20 tokens, including USDT. The script: ```javascript theme={"system"} const axios = require('axios'); const fs = require('fs').promises; const path = require('path'); // Configuration const USDT_CONTRACT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'; // USDT TRC20 contract const CHAINSTACK_ENDPOINT = 'CHAINSTACK_NODE_ENDPOINT'; const POLLING_INTERVAL = 3000; // 3 seconds which is the TRON block time const STATE_FILE = path.join(__dirname, 'usdt-listener-state.json'); // Persistence file const MAX_RETRY_ATTEMPTS = 5; const BASE_RETRY_DELAY = 1000; // 1 second base delay for exponential backoff class PureUSDTListener { constructor() { this.isRunning = false; this.lastProcessedBlock = 0; this.retryAttempts = 0; console.log('🚀 Pure USDT Listener Started (Zero Dependencies!)'); console.log(`📍 Contract: ${USDT_CONTRACT}`); console.log(`🔗 Endpoint: ${CHAINSTACK_ENDPOINT}`); console.log(`💾 State file: ${STATE_FILE}`); console.log('─'.repeat(80)); } // Save state to file for persistence async saveState() { try { const state = { lastProcessedBlock: this.lastProcessedBlock, timestamp: new Date().toISOString() }; await fs.writeFile(STATE_FILE, JSON.stringify(state, null, 2)); } catch (error) { console.error('❌ Failed to save state:', error.message); } } // Load state from file async loadState() { try { const data = await fs.readFile(STATE_FILE, 'utf8'); const state = JSON.parse(data); this.lastProcessedBlock = state.lastProcessedBlock || 0; console.log(`📁 Loaded state: last processed block ${this.lastProcessedBlock}`); return true; } catch (error) { console.log('📁 No previous state found, starting fresh'); return false; } } // Calculate exponential backoff delay getBackoffDelay() { return Math.min(BASE_RETRY_DELAY * Math.pow(2, this.retryAttempts), 30000); // Max 30 seconds } // Reset retry counter on success resetRetries() { if (this.retryAttempts > 0) { console.log(`✅ Connection recovered after ${this.retryAttempts} attempts`); this.retryAttempts = 0; } } // Convert hex to decimal (BigInt for large numbers) hexToDecimal(hex) { try { if (!hex) return '0'; const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex; return BigInt('0x' + cleanHex).toString(); } catch (error) { return '0'; } } // Format USDT amount (6 decimals) formatUSDTAmount(amount) { try { const divisor = BigInt(10 ** 6); // USDT has 6 decimals const amountBig = BigInt(amount); const wholePart = amountBig / divisor; const fractionalPart = amountBig % divisor; const formatted = wholePart.toString() + '.' + fractionalPart.toString().padStart(6, '0'); return parseFloat(formatted).toString(); } catch (error) { return amount.toString(); } } // Get current block number via HTTP async getCurrentBlock() { try { const response = await axios.post(`${CHAINSTACK_ENDPOINT}/wallet/getnowblock`, {}, { timeout: 10000, headers: { 'Content-Type': 'application/json' } }); this.resetRetries(); return response.data.block_header.raw_data.number; } catch (error) { console.error('❌ Failed to get current block:', error.message); throw error; } } // Get block data via HTTP async getBlockData(blockNumber) { try { const response = await axios.post(`${CHAINSTACK_ENDPOINT}/wallet/getblockbynum`, { num: blockNumber, visible: true, }, { timeout: 10000, headers: { 'Content-Type': 'application/json' } }); return response.data; } catch (error) { console.error(`❌ Failed to get block ${blockNumber}:`, error.message); return null; } } // Check if transaction is USDT transfer isUSDTTransfer(transaction) { try { if (!transaction.raw_data?.contract || !transaction.ret?.[0]) { return false; } if (transaction.ret[0].contractRet !== 'SUCCESS') { return false; } const contract = transaction.raw_data.contract[0]; if (contract.type !== 'TriggerSmartContract') { return false; } const contractAddress = contract.parameter?.value?.contract_address; const data = contract.parameter?.value?.data; // Check if it's calling USDT contract with transfer function const isUSDTContract = contractAddress === USDT_CONTRACT; const isTransferFunction = data && data.startsWith('a9059cbb'); // transfer(address,uint256) return isUSDTContract && isTransferFunction; } catch (error) { return false; } } // Parse USDT transfer transaction parseUSDTTransfer(transaction, blockNumber) { try { const contract = transaction.raw_data.contract[0]; const data = contract.parameter.value.data; const fromAddress = contract.parameter.value.owner_address; const valueHex = data.slice(72, 136); const toHex = data.slice(32, 72); let toAddress = 'T' + toHex; const amount = this.hexToDecimal(valueHex); const formattedAmount = this.formatUSDTAmount(amount); return { txHash: transaction.txID, blockNumber: blockNumber, timestamp: new Date(transaction.raw_data.timestamp).toISOString(), from: fromAddress, to: toAddress, amount: amount, formattedAmount: formattedAmount, }; } catch (error) { console.error('❌ Failed to parse USDT transfer:', error.message); return null; } } // Process a single block async processBlock(blockNumber) { const blockData = await this.getBlockData(blockNumber); if (!blockData?.transactions) { return []; } const transfers = []; for (const transaction of blockData.transactions) { if (this.isUSDTTransfer(transaction)) { const transfer = this.parseUSDTTransfer(transaction, blockNumber); if (transfer) { transfers.push(transfer); } } } return transfers; } // Log transfer event logTransfer(transfer) { const amount = parseFloat(transfer.formattedAmount); const emoji = amount >= 100000 ? '🚨' : amount >= 10000 ? '💰' : amount >= 1000 ? '💵' : '💳'; console.log(`${emoji} USDT Transfer Detected:`); console.log(` 📊 Amount: ${transfer.formattedAmount} USDT`); console.log(` 📤 From: ${transfer.from}`); console.log(` 📥 To: ${transfer.to}`); console.log(` 🔗 TX: ${transfer.txHash}`); console.log(` 📦 Block: ${transfer.blockNumber}`); console.log(` ⏰ Time: ${transfer.timestamp}`); if (amount >= 100000) { console.log(` 🚨 LARGE TRANSFER ALERT: $${amount.toLocaleString()}`); } console.log('─'.repeat(80)); } // Main listening loop async start() { this.isRunning = true; try { // Load previous state or get initial block const hasState = await this.loadState(); if (!hasState) { this.lastProcessedBlock = await this.getCurrentBlock() - 1; await this.saveState(); } console.log(`📍 Starting from block: ${this.lastProcessedBlock}`); console.log('👂 Listening for USDT transfers (Zero Dependencies!)...\n'); while (this.isRunning) { try { const currentBlock = await this.getCurrentBlock(); // Process all blocks from last processed + 1 to current (NO SKIPPING) const fromBlock = this.lastProcessedBlock + 1; if (fromBlock <= currentBlock) { const blocksToProcess = currentBlock - fromBlock + 1; if (blocksToProcess > 10) { console.log(`⚠️ Processing ${blocksToProcess} blocks (catching up from block ${fromBlock} to ${currentBlock})`); } // Process blocks sequentially to maintain order for (let blockNum = fromBlock; blockNum <= currentBlock; blockNum++) { const transfers = await this.processBlock(blockNum); for (const transfer of transfers) { this.logTransfer(transfer); } if (transfers.length > 0) { console.log(`✅ Processed block ${blockNum}: ${transfers.length} USDT transfers found`); } // Update and save state after each block this.lastProcessedBlock = blockNum; await this.saveState(); } } // Reset retry counter on successful iteration this.resetRetries(); // Wait before next iteration await this.sleep(POLLING_INTERVAL); } catch (error) { this.retryAttempts++; const backoffDelay = this.getBackoffDelay(); console.error(`❌ Error in main loop (attempt ${this.retryAttempts}/${MAX_RETRY_ATTEMPTS}):`, error.message); if (this.retryAttempts >= MAX_RETRY_ATTEMPTS) { console.error('🚨 Max retry attempts reached. Resetting retry counter.'); this.retryAttempts = 0; } console.log(`⏳ Retrying in ${backoffDelay}ms (exponential backoff)...`); await this.sleep(backoffDelay); } } } catch (error) { console.error('❌ Failed to start listener:', error.message); } } // Stop listening stop() { this.isRunning = false; console.log('\n🛑 Stopping Pure USDT listener...'); } // Sleep utility sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } // Create and start the listener const listener = new PureUSDTListener(); // Handle graceful shutdown process.on('SIGINT', () => { console.log('\n📝 Received SIGINT. Shutting down gracefully...'); listener.stop(); process.exit(0); }); process.on('SIGTERM', () => { console.log('\n📝 Received SIGTERM. Shutting down gracefully...'); listener.stop(); process.exit(0); }); // Start listening listener.start().catch(error => { console.error('❌ Failed to start Pure USDT listener:', error); process.exit(1); }); ``` ## Conclusion This tutorial demonstrated how to build a robust TRC20 transfer monitoring system using Node.js and Chainstack's TRON nodes. This polling-based approach provides a reliable alternative for real-time transfer detection. The implementation includes several production-ready features: * Persistence — State management ensures no transfers are missed, even after restarts. * Error handling — Exponential backoff and retry logic handle network issues gracefully. * Block processing — Sequential processing prevents missed transfers during catch-up periods. * Transfer parsing — Accurate detection and parsing of TRC20 transfer transactions. This pattern can be easily adapted for other TRC20 tokens by simply changing the contract address and decimal precision. Remember to vote for the [TRON event plugin feature request](https://ideas.chainstack.com/p/implement-tron-event-plugin) to help prioritize native event support. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # TRON tooling Source: https://docs.chainstack.com/docs/tron-tooling TRON blockchain development tools guide. Use TronWeb.js, gRPC, web3.py, Hardhat, and Foundry with Chainstack TRON nodes and endpoints. Chainstack TRON nodes expose four API surfaces over a single node endpoint. Get started with a [reliable TRON RPC endpoint](https://chainstack.com/build-better-with-tron/), and see the official [TRON API reference](https://developers.tron.network/reference/) for the full method list. | API | Endpoint | Use it for | | - | - | - | | JSON-RPC | `/jsonrpc` | Ethereum-compatible read operations | | Wallet | `/wallet` | TRON HTTP API — full node operations, including transactions | | Solidity | `/walletsolidity` | TRON HTTP API — confirmed (solidified) data | | gRPC | host on port `443` | High-performance binary protocol — see [available services](#available-services) | TRON nodes run in archive mode, which for TRON means the complete block and transaction history from genesis rather than historical state — see [Historical data availability](#historical-data-availability). ## Tooling compatibility TRON's `/jsonrpc` endpoint provides limited Ethereum compatibility for read operations only — the methods required for transaction submission (`eth_sendRawTransaction`, `eth_getTransactionCount`) are not available. Ethereum-native tools like Foundry, Hardhat, and web3.py can read from a TRON node, but they cannot deploy contracts or send transactions directly. For writes, use [TronWeb.js](#tronwebjs) with the `/wallet` endpoint, or compile and test with Foundry or Hardhat and deploy through the [hybrid workflow](#hybrid-workflow). TRON nodes don't support WebSocket connections for event subscriptions. To track that capability, follow the feature request for [TRON event plugin support](https://ideas.chainstack.com/p/implement-tron-event-plugin). ## Historical data availability TRON nodes on Chainstack run in archive mode. For TRON, archive means the complete block and transaction history — not historical state. Unlike EVM archive nodes, java-tron has no state-at-block queries; this is a protocol limitation ([java-tron#6289](https://github.com/tronprotocol/java-tron/issues/6289)), not a Chainstack one. Billing is independent of the node mode — every TRON request is billed as full (1 RU); see [Request units](/docs/request-units). Available on your node: * Complete block and transaction history from genesis — block and transaction methods accept any historical block, for example `/wallet/getblockbynum` and `eth_getBlockByNumber` * Historical TRX balances — `/wallet/getaccountbalance` and `/wallet/getblockbalance` with a `block_identifier` Not available on any TRON node: * Historical contract or account state, beyond the TRX balance lookups above — `eth_getBalance`, `eth_call`, `eth_getCode`, and `eth_getStorageAt` accept only the `latest` block parameter and return `QUANTITY not supported, just support TAG as latest` for anything else; `triggerconstantcontract` always runs against current state. java-tron tracks archive-node support in [java-tron#6289](https://github.com/tronprotocol/java-tron/issues/6289). ## gRPC API TRON nodes on Chainstack support gRPC for high-performance access. gRPC uses HTTP/2 and Protocol Buffers for efficient binary serialization, making it ideal for high-throughput applications and data indexing. ### Endpoint format Your TRON gRPC endpoints are available at: * **Mainnet**: `tron-mainnet.core.chainstack.com:443` * **Nile Testnet**: `tron-nile.core.chainstack.com:443` ### Authentication gRPC endpoints use x-token authentication. Pass your token in the request metadata: * **x-token** — your authentication token from the Chainstack console Find your gRPC endpoint and x-token in the Chainstack console under your TRON node's **Access and credentials** section. ### Available services The TRON proto files define several gRPC services. Here's what a Chainstack TRON node currently serves: | Service | Status on Chainstack | | - | - | | `protocol.Wallet` | Available — the full node API: accounts, blocks, contracts, and transaction operations | | `protocol.Database` | Available — block metadata helpers | | `protocol.WalletSolidity` | Available — the solidity node API: read-only access to solidified (confirmed) data | | `protocol.WalletExtension` | Not available yet | | `protocol.Monitor` | Not available yet | Both `protocol.Wallet` and `protocol.WalletSolidity` run on the same endpoint and port. Select the one you need by the service (stub) in your generated client, not by a different URL: * `Wallet` — the latest state and all write operations * `WalletSolidity` — solidified (confirmed), read-only data This differs from self-hosted java-tron, which splits the two across separate gRPC ports (`50051` for the full node and `50061` for the solidity node); Chainstack routes both through the single `:443` endpoint. Calling a service that isn't served — `protocol.WalletExtension` or `protocol.Monitor` — returns the gRPC `UNIMPLEMENTED` status (`Method not found`). The same solidified data is also available over the HTTP `/walletsolidity` API — see [TRON methods](/docs/tron-methods). > [!NOTE] > > The endpoint supports gRPC server reflection, but the reflection list includes every service in the TRON protos — including ones that aren't served, such as `protocol.WalletExtension` and `protocol.Monitor`. Don't infer availability from reflection or the proto definitions; the table above reflects actual behavior. ### Proto files The endpoint supports server reflection for discovery, but to generate typed clients you need the protocol buffer definitions. Get them from the official TRON protocol repository: ```shell theme={"system"} git clone https://github.com/tronprotocol/protocol.git ``` The main service definitions are in: * `api/api.proto` — Wallet service with methods like `GetNowBlock`, `GetBlockByNum`, `GetAccount` * `core/Tron.proto` — Core data types (blocks, transactions, accounts) ### Generate client code To use gRPC with TRON, generate client code from the proto files. > [tab] Python > > Install dependencies and generate the Python stubs: > > ```shell theme={"system"} > pip install grpcio grpcio-tools > ``` > > ```shell theme={"system"} > # Clone proto files > git clone https://github.com/tronprotocol/protocol.git > mkdir -p googleapis/google/api > > # Download Google API dependencies > curl -sL https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto \ > -o googleapis/google/api/annotations.proto > curl -sL https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto \ > -o googleapis/google/api/http.proto > > # Generate Python code > python -m grpc_tools.protoc \ > -I./protocol \ > -I./googleapis \ > --python_out=./generated \ > --grpc_python_out=./generated \ > $(find ./protocol -name "*.proto") > ``` > [tab] Node.js > > Install dependencies: > > ```shell theme={"system"} > npm install @grpc/grpc-js @grpc/proto-loader > ``` > > Clone the proto files: > > ```shell theme={"system"} > git clone https://github.com/tronprotocol/protocol.git > ``` > [tab] Go > > Install the protoc compiler and Go plugins: > > ```shell theme={"system"} > go install google.golang.org/protobuf/cmd/protoc-gen-go@latest > go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest > ``` > > Clone and generate: > > ```shell theme={"system"} > git clone https://github.com/tronprotocol/protocol.git > > protoc --go_out=. --go-grpc_out=. \ > -I./protocol \ > protocol/api/api.proto protocol/core/Tron.proto > ``` ### Usage examples ```python Python theme={"system"} import grpc import sys sys.path.insert(0, './generated') from api import api_pb2, api_pb2_grpc ENDPOINT = "tron-mainnet.core.chainstack.com:443" X_TOKEN = "YOUR_X_TOKEN" def get_now_block(): # Create secure channel with TLS credentials = grpc.ssl_channel_credentials() channel = grpc.secure_channel(ENDPOINT, credentials) # Create stub stub = api_pb2_grpc.WalletStub(channel) # Add x-token to metadata metadata = [("x-token", X_TOKEN)] # Call GetNowBlock response = stub.GetNowBlock( api_pb2.EmptyMessage(), metadata=metadata, timeout=15 ) print(f"Block number: {response.block_header.raw_data.number}") print(f"Transactions: {len(response.transactions)}") channel.close() get_now_block() ``` ```javascript Node.js theme={"system"} const grpc = require('@grpc/grpc-js'); const protoLoader = require('@grpc/proto-loader'); const path = require('path'); const ENDPOINT = 'tron-mainnet.core.chainstack.com:443'; const X_TOKEN = 'YOUR_X_TOKEN'; // Load proto files const packageDefinition = protoLoader.loadSync( path.join(__dirname, 'protocol/api/api.proto'), { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, includeDirs: [ path.join(__dirname, 'protocol'), path.join(__dirname, 'googleapis') ] } ); const proto = grpc.loadPackageDefinition(packageDefinition); // Create credentials with TLS const credentials = grpc.credentials.createSsl(); // Create client const client = new proto.protocol.Wallet(ENDPOINT, credentials); // Add x-token to metadata const metadata = new grpc.Metadata(); metadata.add('x-token', X_TOKEN); // Call GetNowBlock client.GetNowBlock({}, metadata, (error, response) => { if (error) { console.error('Error:', error); return; } console.log('Block number:', response.block_header.raw_data.number); console.log('Transactions:', response.transactions.length); }); ``` ```go Go theme={"system"} package main import ( "context" "fmt" "log" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" // Import your generated proto packages pb "your-module/api" ) const ( endpoint = "tron-mainnet.core.chainstack.com:443" xToken = "YOUR_X_TOKEN" ) func main() { // Create TLS credentials creds := credentials.NewTLS(nil) // Connect to gRPC endpoint conn, err := grpc.Dial(endpoint, grpc.WithTransportCredentials(creds)) if err != nil { log.Fatalf("Failed to connect: %v", err) } defer conn.Close() // Create client client := pb.NewWalletClient(conn) // Add x-token to context ctx := metadata.AppendToOutgoingContext( context.Background(), "x-token", xToken, ) // Call GetNowBlock response, err := client.GetNowBlock(ctx, &pb.EmptyMessage{}) if err != nil { log.Fatalf("GetNowBlock failed: %v", err) } fmt.Printf("Block number: %d\n", response.BlockHeader.RawData.Number) fmt.Printf("Transactions: %d\n", len(response.Transactions)) } ``` where YOUR\_X\_TOKEN is your authentication token from the Chainstack console. For the full list of available gRPC methods, see the official [TRON gRPC documentation](https://developers.tron.network/docs/trons-grpc-calls). ## TronWeb.js [TronWeb](https://tronweb.network/) is the official JavaScript library for TRON. It supports all TRON operations including contract deployment and transactions. ### Get balance ```js theme={"system"} const { TronWeb } = require('tronweb'); const tronWeb = new TronWeb({ fullHost: 'YOUR_CHAINSTACK_ENDPOINT' }); const address = 'TWiEv2wfqQ8FkbAJ6bXt1uA2Uav9ZWvXip'; async function getBalance() { try { const balanceInSun = await tronWeb.trx.getBalance(address); const balanceInTRX = tronWeb.fromSun(balanceInSun); console.log(`Address: ${address}`); console.log(`Balance in SUN: ${balanceInSun}`); console.log(`Balance in TRX: ${balanceInTRX}`); } catch (error) { console.error('Error getting balance:', error); } } getBalance(); ``` where YOUR\_CHAINSTACK\_ENDPOINT is your TRON node **base** endpoint without the `/jsonrpc`, `/wallet`, or `/walletsolidity` postfixes. ### Deploy a contract ```js theme={"system"} const { TronWeb } = require('tronweb'); const tronWeb = new TronWeb({ fullHost: 'YOUR_CHAINSTACK_ENDPOINT', privateKey: 'YOUR_PRIVATE_KEY' }); // Contract ABI and bytecode const abi = [/* your contract ABI */]; const bytecode = 'your_contract_bytecode'; async function deployContract() { try { console.log('Deploying contract...'); console.log('From address:', tronWeb.defaultAddress.base58); const transaction = await tronWeb.transactionBuilder.createSmartContract({ abi: abi, bytecode: bytecode, feeLimit: 100000000, callValue: 0, userFeePercentage: 100, originEnergyLimit: 10000000 }, tronWeb.defaultAddress.base58); const signedTx = await tronWeb.trx.sign(transaction); const result = await tronWeb.trx.sendRawTransaction(signedTx); if (result.result) { console.log('Contract deployed!'); console.log('Transaction ID:', result.txid); console.log('Contract address:', tronWeb.address.fromHex(result.transaction.contract_address)); } } catch (error) { console.error('Deployment error:', error.message || error); } } deployContract(); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your TRON node **base** endpoint without the `/jsonrpc`, `/wallet`, or `/walletsolidity` postfixes * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## web3.py web3.py performs read operations on TRON through the `/jsonrpc` endpoint. For contract deployment and transactions, use [TronWeb.js](#tronwebjs). Build DApps using [web3.py](https://github.com/ethereum/web3.py) and TRON nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint with the `/jsonrpc` postfix, protected either with the key or password. See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Hardhat Hardhat performs read operations on TRON through the `/jsonrpc` endpoint. For contract deployment, use the [hybrid workflow](#hybrid-workflow) or [TronWeb.js](#tronwebjs) directly. Configure [Hardhat](https://hardhat.org/) to compile contracts and perform read operations through your TRON nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.8.19", networks: { tron: { url: "YOUR_CHAINSTACK_ENDPOINT", }, } }; ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint with the `/jsonrpc` postfix, protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). 3. Compile your contracts with `npx hardhat compile`, then deploy using TronWeb.js. ## Foundry Foundry performs read operations on TRON through the `/jsonrpc` endpoint. For contract deployment, use the [hybrid workflow](#hybrid-workflow) below. 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run read operations through your Chainstack node. ### Cast Use `cast` to query the network. To get the latest block number: ```shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` To get an account balance: ```shell theme={"system"} cast balance 0xYOUR_HEX_ADDRESS --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint with the `/jsonrpc` postfix, protected either with the key or password. ## Hybrid workflow Use Foundry for fast compilation and testing, then deploy to TRON with TronWeb.js. ### Set up the project 1. Initialize a Foundry project: ```shell theme={"system"} forge init my-tron-project cd my-tron-project ``` 2. Write your contract in `src/`: ```solidity theme={"system"} // src/MyContract.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; contract MyContract { string public message; constructor(string memory _message) { message = _message; } function setMessage(string memory _message) public { message = _message; } } ``` 3. Compile with Foundry: ```shell theme={"system"} forge build ``` The compiled artifacts are in `out/MyContract.sol/MyContract.json`. ### Deploy with TronWeb.js 1. Install TronWeb: ```shell theme={"system"} npm init -y npm install tronweb ``` 2. Create a deployment script `deploy.js`: ```js theme={"system"} const { TronWeb } = require('tronweb'); const fs = require('fs'); const tronWeb = new TronWeb({ fullHost: 'YOUR_CHAINSTACK_ENDPOINT', privateKey: 'YOUR_PRIVATE_KEY' }); // Load Foundry artifacts const artifact = JSON.parse( fs.readFileSync('./out/MyContract.sol/MyContract.json', 'utf8') ); async function deploy() { try { console.log('Deploying from:', tronWeb.defaultAddress.base58); const transaction = await tronWeb.transactionBuilder.createSmartContract({ abi: artifact.abi, bytecode: artifact.bytecode.object, feeLimit: 150000000, callValue: 0, userFeePercentage: 100, originEnergyLimit: 10000000, parameters: ['Hello TRON!'] // Constructor arguments }, tronWeb.defaultAddress.base58); const signedTx = await tronWeb.trx.sign(transaction); const result = await tronWeb.trx.sendRawTransaction(signedTx); if (result.result) { console.log('Contract deployed!'); console.log('Transaction ID:', result.txid); console.log('Contract address:', tronWeb.address.fromHex(result.transaction.contract_address)); } } catch (error) { console.error('Error:', error.message || error); } } deploy(); ``` 3. Run the deployment: ```shell theme={"system"} node deploy.js ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your TRON node **base** endpoint without the `/jsonrpc`, `/wallet`, or `/walletsolidity` postfixes * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). # JWT: Implement secure user authentication Source: https://docs.chainstack.com/docs/tutorial-mastering-jwt-how-to-implement-secure-user-authentication Implement JWT authentication in a Node.js application with token generation, validation middleware, refresh tokens, and secure session management. **TLDR:** * Explains the structure of JWTs (header, payload, signature) and how they protect user data. * Shows how to generate and validate JWTs in JavaScript (jose), Python (PyJWT), and Go (jwt-go). * Demonstrates setting custom claims (e.g., issuer, audience, expiration) for authorization. * Highlights the importance of secure key management and up-to-date libraries for token integrity. ## Introduction JSON Web Tokens (JWTs) are a crucial component in modern web applications, providing a secure and standardized method for transmitting and validating information between parties. JWTs are compact, URL-safe tokens carrying user claims in a JSON object, which are digitally signed to ensure data integrity and prevent tampering. They simplify the authentication process, enable stateless server design, and facilitate easy transmission through various channels. In this tutorial, we're going to dive into the nitty-gritty of JWT tokens. We'll cover what JWTs are and how to create and validate them. By the time we're done, you'll understand JWTs and how they help keep the web safe and speedy. ## What are JSON Web Tokens? **JWTs** are a way to share important information securely between two parties, like a user and a website. They're often used to check if a user is allowed to access certain parts of a website or to identify who the user is. JWTs are made up of a series of characters that contain data in JSON format. The data inside a JWT is protected from tampering thanks to a process called signing. Signing uses a secret key to ensure the information in the JWT is legit. JWTs have gained popularity due to their ability to simplify user tracking on websites without the need to store extensive user information. This is particularly beneficial for modern websites that rely on multiple services and technologies that require communication. With JWTs, websites can easily verify user identity and permissions while improving website efficiency and manageability. ### Structure of a JWT A JWT consists of three distinct parts: the **header**, the **payload**, and the **signature**. These parts are Base64Url-encoded and concatenated using periods (`.`) to form the complete JWT token. #### Header The header is a JSON object that typically contains two properties: the token type (usually `JWT`) and the signing algorithm used, such as `HS256` for HMAC SHA-256 or `ES256` for ECDSA using P-256 and SHA-256. The header provides information about how the JWT is encoded and secured. > [!NOTE] > > Find a complete list of available algorithms in the [RFC 7518](https://tools.ietf.org/html/rfc7518#section-3) specifications. Example of a header: ```Json JSON theme={"system"} { "alg": "HS256", "typ": "JWT" } ``` > [!NOTE] > > Note that this guide shows the `HS256` algorithms as an example, but Chainstack uses the `EdDSA`algorithms as it is more secure. #### Payload The payload is a JSON object that contains the claims (pieces of information) about the user and other relevant data. Claims are key-value pairs that can be registered, public or private claims. Registered claims are predefined by the JWT specification, such as `iss` for issuer and `exp` for expiration time, while public and private claims can be custom-defined by the developer. Some of the most common JWT claims are: * `iss` (Issuer) — identifies the provider or application that issued the JWT. The issuer claim is usually a string or a URI representing the entity that created the JWT, for example, `chainstack`. * `sub` (Subject) — identifies who the JWT is for. This is used to keep track of which user the token is meant for by storing the unique identifier of the user. * `aud` (Audience) — identifies the recipients of the JWT. It essentially specifies the applications, services, or servers that are allowed to process the token. * `exp` (Expiration Time) — specifies the expiration date and time, in seconds since the Unix epoch, after which the JWT must not be accepted for processing. This claim is used to define the token's validity period, after which it should be considered expired and rejected. * `nbf` (Not Before) — specifies the date and time, in seconds since the Unix epoch, before which the JWT must not be accepted for processing. This claim is used to define the earliest possible time when the token can be considered valid. * `iat` (Issued At) — specifies the date and time, in seconds since the Unix epoch, when the JWT was issued. This claim can be used to determine the token's age and potentially reject tokens issued too far in the past. > [!NOTE] > > ### Learn more > > Find a complete list of JWT claims in the [JSON Web Token Claims](https://www.iana.org/assignments/jwt/jwt.xhtml#claims) page from the Internet Assigned Numbers Authority organization. Example of a payload: ```Json JSON theme={"system"} { "iss": "chainstack", "sub": "user-number-5", "aud": "www.mydapp.io", "iat": 1516239022 } ``` #### Signature The signature is a cryptographic hash generated using the encoded header, encoded payload, and a secret key. The signature ensures that the token has not been tampered with and verifies the authenticity of the JWT. To create the signature, the encoded header, and payload are combined with a period (`.`) separator and then hashed with the secret key using the algorithm specified in the header. Example of signature creation using HMAC SHA-256: ```javascript Javascript theme={"system"} HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret_key) ``` The final JWT token is a combination of the encoded header, payload, and signature, separated by periods (`.`), like the following: ```text text theme={"system"} .. ``` Example of a real JWT: ```shell JWT theme={"system"} eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJSRy04MzctMzgwIiwiaXNzIjoiY2hhaW5zdGFjayIsImV4cCI6MTY3OTk0NjY0NSwibmJmIjoxNjc5OTM5NDQ1LCJpYXQiOjE2Nzk5NDMwNDUsImF1ZCI6InRlc3QtbmV3LXNsdWcifQ.6cJTsqUA3ocZFaiBcrviZ15LzVT4T1J7-zM4X-4C8Ck ``` ## Use cases for JWTs JWTs are versatile and can be used in various scenarios within web applications. Here, we will discuss two common use cases: authentication and authorization. ### Authentication Authentication is the process of verifying the identity of a user, device, or system. In web applications, it's essential to make sure that users are who they claim to be before allowing them access to protected resources or services. JWTs are commonly used for authentication in web applications. When a user logs in with their credentials, for example, username and password, the server verifies the provided information. If the credentials are valid, the server generates a JWT containing the user's information and sends it back to the user. The user's browser or application then stores the token and sends it along with each subsequent request to the server. The server checks the token's signature and verifies the user's identity based on the information stored in the token. Since the token is self-contained and carries the necessary user data, the server doesn't need to perform additional database lookups or maintain session information, making the authentication process more efficient. ### Authorization Authorization is the process of determining which actions a user can perform or which resources they can access within an application. It's crucial to ensure that users have appropriate permissions and can only access the data and services to which they're entitled. When JWTs are used for authorization purposes. In addition to the user's identity, the token's payload may contain other claims related to the user's roles, permissions, or access rights, usually using an `Authorization` header using the `Bearer` schema, which looks like the following: ```shell Shell theme={"system"} Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ ``` When a user requests access to a protected resource, the server can extract the required information from the JWT and determine if the user is authorized to perform the requested action or access the resource. This is done by extracting the JWT from the header and performing the following steps: 1. Validate the token's signature to ensure it hasn't been tampered with. 2. Check the token's claims, such as `iss`, `exp`, `nbf`, `sub`, and `aud`, to confirm it's a valid token issued for the user and intended for the server. 3. Verify that the user is authorized to access the requested resources or perform the desired actions based on the claims in the token. ## Generating JSON Web Tokens In this section, we will discuss how to generate JWT tokens using popular libraries and frameworks for different programming languages, there are many libraries available, but for this tutorial, we will use the tools listed in the next section in JavaScript, Python, and Golang. These libraries help simplify the process of creating, signing, and managing JWT tokens, allowing developers to focus on building secure and efficient web applications. ### Tools and libraries When generating JWTs, it's essential to choose a well-maintained library or framework that supports the required functionality and provides a secure implementation. Below is a list of popular libraries for different programming languages: #### JavaScript and node.js **jose** is a comprehensive suite of tools for signing, encrypting, and decrypting JSON-based data, making it easier to implement secure communication between parties in web applications. Install with: ```shell Shell theme={"system"} npm install jose ``` > [!NOTE] > > Find the jose library source code on the [jose's GitHub repository](https://github.com/panva/jose). #### Python **PyJWT** is a popular library for encoding, decoding, and verifying JWT tokens in Python applications. It supports various signing algorithms and provides a clean, easy-to-use API. Install with: ```shell Shell theme={"system"} pip install pyjwt ``` > [!NOTE] > > Find more information on [PyJWT’s GitHub repository](https://github.com/jpadilla/pyjwt/), or the [PyJWT docs](https://pyjwt.readthedocs.io/). #### Golang **jwt-go** is a widely adopted library for creating, parsing, and validating JWT tokens in Golang applications. It supports multiple signing algorithms and offers a straightforward API. > [!NOTE] > > Note that starting from Go 1.16, Go modules are the recommended way to manage dependencies in your Go projects. Run this command to generate a Go module before running the install command: ```shell Shell theme={"system"} go mod init ``` Install with: ```shell Shell theme={"system"} go get -u github.com/golang-jwt/jwt/v5 ``` > [!NOTE] > > Find more information on [jwt-go’s GithHub repository](https://github.com/golang-jwt/jwt). These libraries provide a convenient way to generate JWTs for different programming languages. Once you've chosen a library that suits your application's needs, you can use its provided functions to create, sign, and manage JWTs securely and efficiently. > [!WARNING] > > ### Check your versions > > It is extremely important to note that these tools might not be up to date when you read this tutorial, so make sure to check their repositories and documentation to learn about possible updates and security issues. > > You can find a comprehensive list of JWT libraries on the [JWT tools collection page Auth0](https://jwt.io/libraries). ### Generate and validate JSON web tokens This section will demonstrate how you can you the libraries and languages mentioned above to generate and validate JWTs. For this tutorial, we will: * Generate and encode JWTs with claims. * Decode and validate JWTs. ### JavaScript jose library To use the jose library in JavaScript, firstly, make sure your coding environment is set up correctly by having [node.js installed](https://nodejs.org/en/download). Then, you can install jose using npm. #### Generate JWTs Here you will find two JavaScript programs using the jose library: the first one to generate a JWT and the second one to validate it. > [!NOTE] > > This project uses the [dotenv library](https://github.com/motdotla/dotenv) to handle environment variables. Create a new JavaScript file named `generate-jwt.js` in your project’s directory and paste the following code: ```js generate-jwt.js theme={"system"} // Importing required modules and dependencies const { SignJWT } = require('jose'); const encoder = new TextEncoder(); const dotenv = require('dotenv'); dotenv.config(); // Extracting and encoding the secret key from the environment variables const secretKey = process.env.SECRET_KEY; const secret = encoder.encode(secretKey); // Defining the algorithm used for signing the token const algorithm = 'HS256'; // Defining the custom claims to be used in the JWT const customIssuer = 'chainstack'; const customAudience = 'www.cooldapp.io'; const userId = 'web3-dev'; // Defining a function which generates tokens with options specified const generateToken = async () => { try { // Setting the token's "not before" value to 60 seconds before the current time const nowInSeconds = Math.floor(Date.now() / 1000); const nbf = nowInSeconds - 60; // Creating a new JWT token and setting its values const jwt = await new SignJWT({ user_id: userId }) .setProtectedHeader({ alg: algorithm }) .setIssuedAt(nowInSeconds) .setIssuer(customIssuer) .setAudience(customAudience) .setExpirationTime('1h') .setNotBefore(nbf) .sign(secret); console.log(`New JWT:\n${jwt}`); } catch (error) { console.error(`Error generating token: ${error}`); } }; generateToken(); ``` The JSON Web Token is generated by encoding a secret key from the environment variables and setting custom claims like the issuer, audience, and user ID. A function is defined to generate the token with options specified using the `HS256` algorithm. Finally, the generated token is printed to the console. > [!NOTE] > > Find the code reference in [jose’s repository](https://github.com/panva/jose/blob/main/docs/classes/jwt_sign.SignJWT.md#readme). Create a `.env` file where you can store your secret key, then run this code and generate your own JWT. Create the secret key variable in this format: ```shell Shell theme={"system"} SECRET_KEY=YOUR_KEY ``` The response will look like the following: ```shell Shell theme={"system"} $ node generate-jwt New JWT: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2ViMy1kZXYiLCJpYXQiOjE2ODAwMTg1MjMsImlzcyI6ImNoYWluc3RhY2siLCJhdWQiOiJ3d3cuY29vbGRhcHAuaW8iLCJleHAiOjE2ODAwMjIxMjMsIm5iZiI6MTY4MDAxODQ2M30.LGZebttbACMKuHt-7w7wN7T4Qgbgnr6cen5hcr_TWys ``` #### Validate JWTs Now that we've successfully generated a JWT, it's time to explore how to decode and validate it using the jose library. To do this, we'll first create a new file in the same directory and name it `validate-jwt.js`. Once you've created the file, simply copy and paste the following code snippet: ```js validate-jwt.js theme={"system"} // Importing required modules and dependencies const { jwtVerify } = require('jose'); const encoder = new TextEncoder(); const dotenv = require('dotenv'); dotenv.config(); // Async function to verify JWT async function verifyJwt (secret, jwt, issuer, audience) { try { const { payload, protectedHeader } = await jwtVerify(jwt, secret, { issuer: issuer, audience: audience }); return { protectedHeader, payload }; } catch (error) { console.error(`Something went wrong: ${error}`); } } // Main function async function main () { // Extracting and encoding the secret key from the environment variables const secretKey = process.env.SECRET_KEY; if (!secretKey) { console.log('Error: Secret key not found in environment variables.'); return; } const secret = encoder.encode(secretKey); const jwt = 'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2ViMy1kZXYiLCJpYXQiOjE2ODAwMTg1MjMsImlzcyI6ImNoYWluc3RhY2siLCJhdWQiOiJ3d3cuY29vbGRhcHAuaW8iLCJleHAiOjE2ODAwMjIxMjMsIm5iZiI6MTY4MDAxODQ2M30.LGZebttbACMKuHt-7w7wN7T4Qgbgnr6cen5hcr_TWys'; // Defining the custom claims to be used in the JWT const customIssuer = 'chainstack'; const customAudience = 'www.cooldapp.io'; const { protectedHeader, payload } = await verifyJwt(secret, jwt, customIssuer, customAudience); console.log('protectedHeader:', protectedHeader); console.log('payload:', payload); } main(); ``` This code is responsible for verifying the authenticity and integrity of a JWT using a secret key and specific parameters. To accomplish this task, the code defines an `async` function named `verifyJwt`, which accepts four parameters: `secret`, `jwt`, `issuer`, and `audience`. This function attempts to verify the provided JWT using the secret key, the issuer, and the audience parameters specified. If the verification process is successful, it returns the protected header and payload of the JWT. In case of any errors, an error message is logged. To use this code, simply replace the `jwt` constant with the JWT that you generated using the previous script and run it. If the token is valid, you'll receive a response that looks something like this: ```shell Shell theme={"system"} $ node validate-jwt protectedHeader: { alg: 'HS256' } payload: { user_id: 'web3-dev', iat: 1680018523, iss: 'chainstack', aud: 'www.cooldapp.io', exp: 1680022123, nbf: 1680018463 } ``` At this stage, you can implement JWT-based authentication to your app. This will make sure that your users have the right permissions for a safe and smooth experience. ### Python PyJWT library Now, let’s see how to generate and validate JWTs using Python. The principle is the same as in JavaScript. Ensure your Python environment is set up and the PyJWT library is installed. > [!NOTE] > > This project uses the [python-dotenv library](https://github.com/motdotla/dotenv) to handle environment variables. #### Generate JWTs Create a new file named `generate_jwt.py` and paste the following code: ```python generate_jwt.py theme={"system"} import jwt # for generating a JSON Web Token import time # for accessing the current time import os # for accessing environment variables from dotenv import load_dotenv # for loading environment variables from a file # Loading environment variables load_dotenv() # Defining function to generate a JWT token def generate_jwt_token(claims: dict, secret_key: str, algorithm: str) -> str: # Creating a token with the specified claims and secret key token = jwt.encode( claims, secret_key, algorithm=algorithm, headers={"alg": "HS256", "typ": "JWT"} ) return token # Main function to execute if __name__ == "__main__": # Setting the secret key to sign the token using an environment variable. secret_key = os.environ.get("SECRET_KEY") # Defining the algorithm to use for signing the token. algorithm = "HS256" # Setting the payload or claims for the token. claims = { "sub": "web3_dev", # Subject - who the token is for "iss": "chainstack", # Issuer - who issued the token "exp": int(time.time()) + 3600, # Expiration time - token is only valid for an hour "nbf": int(time.time()) - 3600, # Time before which token is not valid "iat": int(time.time()), # Issued at - when the token was issued "aud": "www.cooldapp.io", # Audience - intended recipient of the token } # Generating the JWT token with the given claims token = generate_jwt_token(claims, secret_key, algorithm) # Printing the generated JWT token print("JWT token:") print(token) ``` This code generates a JSON Web Token (JWT) using the pyjwt library, with a specific set of claims, a secret key loaded from an environment variable, and the `HS256` signing algorithm. The claims include subject, issuer, expiration time, not before time, issued at time, and audience. The dotenv library is used to load the secret key from a `.env` file. The generated JWT token is printed as output. To run it, create a `.env` file where you can store your secret key, then run this code and generate your own JWT. Create the secret key variable in this format: ```text text theme={"system"} SECRET_KEY=YOUR_KEY ``` The response will look like the following: ```shell Shell theme={"system"} $ python generate_jwt.py JWT token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ3ZWIzX2RldiIsImlzcyI6ImNoYWluc3RhY2siLCJleHAiOjE2ODAwMjU3MTEsIm5iZiI6MTY4MDAxODUxMSwiaWF0IjoxNjgwMDIyMTExLCJhdWQiOiJ3d3cuY29vbGRhcHAuaW8ifQ.3kFOThDwqMCm5c71B6kRvR8efzf4edKkDI-iYzb_S1I ``` #### Validate JWTs Create a new file named `validate_jwt.py`, and paste the following code: ```python validate_jwt.py theme={"system"} import jwt import os from dotenv import load_dotenv from typing import List, Union # Load the environment variables from the .env file load_dotenv() SECRET_KEY = os.environ["SECRET_KEY"] ALGORITHM = "HS256" EXPECTED_AUDIENCE = ["www.cooldapp.io"] def verify_token(token: str, secret_key: str, algorithm: str, expected_audience: Union[str, List[str]]) -> Union[dict, None]: """ Verify the provided token and return the claims if the token is valid Args: token (str): The JWT token string to verify secret_key (str): The secret key used to sign the token algorithm (str): The algorithm used to sign the token expected_audience (Union[str, List[str]]): The expected audience value(s) Returns: If the token is valid, a dictionary containing the claims. Otherwise, None. """ try: claims = jwt.decode(token, secret_key, algorithms=[algorithm], audience=expected_audience) return claims except jwt.exceptions.InvalidTokenError as error: print(f"Error verifying token: {error}") return None if __name__ == '__main__': # Set the token to verify token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ3ZWIzX2RldiIsImlzcyI6ImNoYWluc3RhY2siLCJleHAiOjE2ODAwMjU3MTEsIm5iZiI6MTY4MDAxODUxMSwiaWF0IjoxNjgwMDIyMTExLCJhdWQiOiJ3d3cuY29vbGRhcHAuaW8ifQ.3kFOThDwqMCm5c71B6kRvR8efzf4edKkDI-iYzb_S1I" # Verify the token and get the claim claims = verify_token(token, SECRET_KEY, ALGORITHM, EXPECTED_AUDIENCE) if claims: print(claims) ``` The code then defines some variables—`SECRET_KEY`, `ALGORITHM`, and `EXPECTED_AUDIENCE`—which are used by the `verify_token` function. Then, the code defines a function named `verify_token` that takes as input parameters a token string, a secret key, an algorithm, and an expected audience value. The function attempts to decode the token and returns the decoded claims data as a dictionary if the token is valid. If the token is invalid, an error message is printed, and `None` is returned. Replace the `token` variable with the JWT you generated using the previous script, then run it; you will be able to validate the token. The response will look like the following: ```shell Shell theme={"system"} $ python validate_jwt.py {'sub': 'web3_dev', 'iss': 'chainstack', 'exp': 1680025711, 'nbf': 1680018511, 'iat': 1680022111, 'aud': 'www.cooldapp.io'} ``` Now you have the tools to generate and validate JWTs in Python and can implement the logic in your application. ### Golang jwt-go library This section of this guide will show you how to generate and validate JWTs using Golang. First of all, make sure your Go environment is configured; follow the instruction on the [Golang docs](https://go.dev/doc/install) to install Go, then install the jwt-go library as it was shown in the libraries section. > [!NOTE] > > This project uses the [GoDotEnv library](https://github.com/joho/godotenv) to use `.env` files. #### Generate JWTs For this part of the project, create a new directory named `generate`, then create a new file named `generate-jtw.go` and paste the following code: ```go generate-jtw.go theme={"system"} package main import ( "fmt" "log" "os" "time" "github.com/golang-jwt/jwt/v5" "github.com/joho/godotenv" ) // loadEnvVars loads environment variables from .env file func loadEnvVars() error { err := godotenv.Load() if err != nil { return fmt.Errorf("error loading .env file: %w", err) } return nil } // getSecretKey gets the secret key from environment variables func getSecretKey() (string, error) { secretKey := os.Getenv("SECRET_KEY") if secretKey == "" { return "", fmt.Errorf("SECRET_KEY environment variable not set") } return secretKey, nil } // createToken creates a token object with signing method and claims func createToken(secretKey, customIssuer, customAudience, userId string) (string, error) { // Defining custom claims for JWT claims := jwt.MapClaims{ "iss": customIssuer, "aud": customAudience, "nbf": time.Now().Add(-60 * time.Second).Unix(), "iat": time.Now().Unix(), "exp": time.Now().Add(1 * time.Hour).Unix(), "user_id": userId, } // Create a token object with signing method and claims token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) // Sign the token using the secret key signedToken, err := token.SignedString([]byte(secretKey)) if err != nil { return "", fmt.Errorf("Error signing token: %w", err) } return signedToken, nil } // main function func main() { err := loadEnvVars() if err != nil { log.Fatalf("Error occurred: %v", err) } secretKey, err := getSecretKey() if err != nil { log.Fatalf("Error occurred: %v", err) } // Defining custom claims for JWT customIssuer := "chainstack" customAudience := "www.cooldapp.io" userId := "web3-dev" token, err := createToken(secretKey, customIssuer, customAudience, userId) if err != nil { log.Fatalf("Error occurred: %v", err) } fmt.Printf("New JWT:\n%s\n", token) } ``` The code defines three functions: `loadEnvVars()`, `getSecretKey()`, and `createToken()`. The `loadEnvVars()` function loads environment variables from an external `.env` file. The `getSecretKey()` function retrieves the `SECRET_KEY` value from environment variables or returns an error message if the value is not set. The `createToken()` function creates a JWT object with custom claims, which include the issuer, audience, timestamp, and user id. In this case, we are using the `HS266` algorithm with `SigningMethodHS256`. It then signs the JWT using the secret key value. The `main()` function calls these three functions in order to create a new JWT with custom claims and prints the token to the console. > [!NOTE] > > Find more examples in the [Golang docs](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#example-New-Hmac). Before running this code, create a `.env` file in the same directory and set up your secret key using this format: ```text text theme={"system"} SECRET_KEY=YOUR_SECRET_KEY ``` Then build the Go program by running the `go build` command. This will create an executable file you can run: ```shell Shell theme={"system"} $ ./generate New JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ3d3cuY29vbGRhcHAuaW8iLCJleHAiOjE2ODAwMzM5ODAsImlhdCI6MTY4MDAzMDM4MCwiaXNzIjoiY2hhaW5zdGFjayIsIm5iZiI6MTY4MDAzMDMyMCwidXNlcl9pZCI6IndlYjMtZGV2In0.DPzzRv5mvb5u40gx9DiNXBEffIocN3ntN936dulh2F0 ``` > [!NOTE] > > ### For Windows > > Run `generate.exe` on Windows. #### Validate JWTs Let’s now build the program to validate JWTs in Go. Create a new directory named `validate` in the root of your project, create a `.env` file like the previous one, and create a new file named `validate-jwt.go`; then paste the following code in it: ```go validate-jwt.go theme={"system"} package main import ( "fmt" "log" "os" "github.com/golang-jwt/jwt/v5" "github.com/joho/godotenv" ) func main() { if err := loadEnvVars(); err != nil { log.Fatalf("Error loading environment variables: %v", err) } secretKey, err := getSecretKey() if err != nil { log.Fatalf("Error getting secret key: %v", err) } tokenToValidate := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ3d3cuY29vbGRhcHAuaW8iLCJleHAiOjE2ODAwMzM5ODAsImlhdCI6MTY4MDAzMDM4MCwiaXNzIjoiY2hhaW5zdGFjayIsIm5iZiI6MTY4MDAzMDMyMCwidXNlcl9pZCI6IndlYjMtZGV2In0.DPzzRv5mvb5u40gx9DiNXBEffIocN3ntN936dulh2F0" claims, err := validateToken(tokenToValidate, secretKey) if err != nil { log.Fatalf("Error validating token: %v", err) } fmt.Printf("Token is valid. Claims: %v\n", claims) } // loadEnvVars loads environment variables from .env file. func loadEnvVars() error { err := godotenv.Load() if err != nil { return fmt.Errorf("godotenv.Load error: %w", err) } return nil } // getSecretKey gets the secret key from environment variables. func getSecretKey() (string, error) { secretKey := os.Getenv("SECRET_KEY") if secretKey == "" { return "", fmt.Errorf("Secret key not set") } return secretKey, nil } // validateToken validates a JWT token and returns its claims if valid. func validateToken(tokenString string, secretKey string) (jwt.Claims, error) { token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } return []byte(secretKey), nil }) if err != nil { return nil, fmt.Errorf("error parsing token: %w", err) } if !token.Valid { return nil, fmt.Errorf("invalid token") } return token.Claims, nil } ``` In this code, the `main` function loads environment variables, gets a secret key, validates a JWT token using the secret key, and prints the token's claims if it's valid. The `loadEnvVars()` function uses the `godotenv` package to load environment variables from a `.env` file. The `getSecretKey()` function retrieves the secret key from the loaded environment variables. The `validateToken()` function takes a token string and a secret key as input. It uses the **jwt-go** package to parse and validate the token. It checks if the token signature is valid by verifying it with the provided secret key. If the token is valid, it returns the token's claims. Finally, the `main` function calls the `loadEnvVars()` and `getSecretKey()` functions to get the secret key, then calls the `validateToken()` function to validate a token string. If the token is valid, it prints the token's claims. To run this code, replace the `tokenToValidate` variable with your JWT, then build and run the application. Now you are ready to validate users using Golang as well. ## Conclusion Throughout this guide, we've explored the basics of JWTs, including their structure and the different algorithms used to encode and decode them. We've also provided practical examples of how to generate and validate JWTs using popular libraries in JavaScript, Python, and Go. By incorporating JWTs into your projects, users' data is well protected while providing a seamless and user-friendly experience. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Scaling your dApp with Kubernetes: Comprehensive guide Source: https://docs.chainstack.com/docs/tutorial-on-how-to-make-your-dapp-reliable-and-scalable-with-kubernetes Scale DApps with Kubernetes on Linode. Deploy pods, configure load balancers, manage deployments with YAML manifests for reliable blockchain applications. **TLDR:** * Kubernetes is a container orchestration platform that simplifies deploying, scaling, and managing applications through features like self-healing and load balancing. * Pods are the fundamental units of Kubernetes, but deploying a single pod via the CLI isn’t scalable; that’s why Deployments with YAML manifests are crucial for versioning, rollbacks, and scaling. * Services expose Pods to external traffic and can be paired with a LoadBalancer (like Linode’s NodeBalancer) to ensure high availability and efficient traffic distribution. * By using environment variables in manifests, you can dynamically configure containerized applications (e.g., an Express.js DApp) across multiple Pods in a cluster. ## Introduction: Understanding Kubernetes and its importance for reliability and scalability In today's fast-paced digital landscape, deploying and managing applications isn't just about pushing code to a server. It's about deploying, scaling, and managing applications to ensure high availability, fault tolerance, and seamless user experience. This is where Kubernetes comes into play. ### What is Kubernetes? Kubernetes, often abbreviated as k8s, is an open-source container orchestration platform designed to automate containerized applications' deployment, scaling, and management. Originating from Google, Kubernetes has become the de facto standard for container orchestration and is maintained by the Cloud Native Computing Foundation (CNCF). > [!NOTE] > > The name "Kubernetes" has its roots in Greek, where it means **helmsman** or **pilot**. The name is very fitting as just as a skilled helmsman navigates a ship through turbulent seas, Kubernetes steers your applications through the challenges of scaling, fault tolerance, and resource management. #### Why use Kubernetes for reliability? 1. **High availability**. Kubernetes ensures your application is available when your users need it. It does this by distributing application instances across multiple nodes in the cluster, automatically replacing failed instances, and rescheduling them to healthy nodes. 2. **Self-healing**. Kubernetes can detect when a pod or service is unhealthy and automatically replace it without human intervention. This ensures system degradation is handled proactively, maintaining the application's reliability. 3. **Rollback and versioning**. Mistakes happen. Kubernetes allows you to roll back to previous stable versions of your application, ensuring you can quickly revert in case of failure. #### Why use Kubernetes for scalability? 1. **Horizontal scaling**. With just a simple command or based on CPU usage or other metrics, Kubernetes can automatically scale the number of pod replicas up or down. This is crucial for applications that experience varying loads. 2. **Load balancing**. Kubernetes automatically distributes network traffic across multiple pods, ensuring no single pod is overwhelmed with too much load. This is key for applications that require high concurrency. 3. **Resource optimization**. Kubernetes allows you to specify how much CPU and memory each container needs, which helps in utilizing underlying resources more efficiently. This is particularly useful for multi-tenant environments where resource optimization is critical. By leveraging Kubernetes, organizations can build applications that are not only reliable but also scalable. It provides a robust framework that accommodates the complexities and demands of modern-day applications, allowing developers to focus more on building features that add business value rather than worrying about operational challenges. ## Condensed glossary of key Kubernetes terms To help you navigate through this guide, here's a glossary of essential Kubernetes terms that are directly related to the concepts discussed: * Pod — the smallest deployable unit in Kubernetes, usually hosting a single container. * Node — a worker machine in a Kubernetes cluster where pods are scheduled. * Cluster — a set of nodes managed by a master node, forming the computing environment for Kubernetes. * Kubectl — the command-line interface for interacting with a Kubernetes cluster. * Deployment — a configuration that manages the state and scaling of a set of identical pods. * Service — an abstraction that defines a logical set of pods and policies for accessing them, often via a load balancer. * Load balancer — a service that distributes network traffic across multiple pods. * Namespace — a virtual cluster within a Kubernetes cluster used for isolating resources. * ConfigMap — an object used to store non-confidential configuration data in key-value pairs. * kubeconfig — a configuration file for accessing one or more Kubernetes clusters. ## The project This starter guide will walk you through deploying and managing applications in a Kubernetes cluster hosted on [Linode](https://cloud.linode.com/dashboard). We'll focus on deploying a Docker image of a simple Express.js server, which is the backbone of our example application. ### About the example DApp The example [DApp](https://github.com/soos3d/get-started-with-kubernetes/blob/main/index.js) is a straightforward Express.js server that exposes an API endpoint for fetching Ethereum balances using a Chainstack node. It's written in JavaScript and uses the web3.js library to interact with the Ethereum blockchain. The server is configured to listen on a port defined by an environment variable and uses rate-limiting to control the number of API requests. This makes it an excellent candidate for understanding how to manage environment variables and configurations in a Kubernetes deployment. Find the repository with the server app and the k8s scripts on GitHub: * [Kubernetes starter](https://github.com/chainstacklabs/get-started-with-kubernetes) ## Prerequisites * Install [kubectl](https://kubernetes.io/docs/tasks/tools/), the k8s CLI interface. * Deploy a [k8s cluster on Linode](https://www.linode.com/docs/products/compute/kubernetes/get-started/) and get the `kubeconfig` file. > [!NOTE] > > Once you deploy the k8s cluster on Linode, find the `kubeconfig` file and either download it into the projects directory or copy its content and paste it into a new file named `kubeconfig.yaml`. ## Initiating the project This project doesn't require you to write any code, as we'll leverage a pre-built Docker image available on Docker Hub. You have two options for getting started: 1. Clone the repository. Clone the repository into a new directory on your local machine to check the server code and Kubernetes scripts. 2. Create an Empty project. If you focus solely on deployment, create an empty directory. This will serve as the workspace where we'll add necessary Kubernetes scripts. Once you've chosen your path, open a terminal window in the project's directory. From here, we'll primarily be using the `kubectl` command-line interface to interact with our Kubernetes cluster. ## The `kubeconfig` file In Kubernetes, the `kubeconfig.yaml` file is a configuration file that stores the information required for connecting to one or more Kubernetes clusters. This file is essential for using `kubectl`, the CLI tool for interacting with Kubernetes clusters. The file contains various data types, such as cluster details, user credentials, and context information, which enable secure communication between your local machine and the Kubernetes API server. ### Components of a `kubeconfig.yaml` file Here, we'll go over the basic parts of the `kubeconfig` file, which you will find on Linode after you deploy the cluster. #### `apiVersion and kind` These fields specify the API version and the kind of Kubernetes object the file represents. For `kubeconfig` files, the `kind` is usually set to `Config`. ```yaml kubeconfig.yaml theme={"system"} apiVersion: v1 kind: Config ``` #### `clusters` This contains information about one or more clusters. Each entry typically includes the cluster name, API server URL, and the certificate authority data used to validate the server's certificate. ```yaml kubeconfig.yaml theme={"system"} clusters: - name: my-cluster cluster: server: https://api-server-url:port certificate-authority-data: base64-encoded-ca-cert ``` #### `users` This section contains the credentials for authenticating to the cluster. It can include a username and password, client certificates, or even tokens. ```yaml kubeconfig.yaml theme={"system"} users: - name: my-user user: client-certificate: /path/to/cert.crt client-key: /path/to/cert.key ``` #### `contexts` `Contexts` are combinations of clusters and users, along with an optional namespace. They define the environment in which `kubectl` commands are run. ```yaml kubeconfig.yaml theme={"system"} contexts: - name: my-context context: cluster: my-cluster user: my-user namespace: my-namespace ``` #### `current-context` This field specifies which context should be used by default when running `kubectl` commands. It should match one of the context names defined in the `contexts` section. ```yaml kubeconfig.yaml theme={"system"} current-context: my-context ``` ## How `kubeconfig` works When you run a `kubectl` command, the tool looks for a `kubeconfig` file in a default location, usually `~/.kube/config`, although you can specify a different file using the `--kubeconfig` flag. `kubectl` uses the `current-context` to determine which cluster to connect to and which credentials to use for authentication. ### Example `kubeconfig.yaml` Here's a simplified example: ```yaml kubeconfig.yaml theme={"system"} apiVersion: v1 kind: Config clusters: - name: my-cluster cluster: server: https://api-server-url:port certificate-authority-data: base64-encoded-ca-cert users: - name: my-user user: client-certificate: /path/to/cert.crt client-key: /path/to/cert.key contexts: - name: my-context context: cluster: my-cluster user: my-user namespace: my-namespace current-context: my-context ``` Understanding the `kubeconfig.yaml` file is crucial for effectively managing Kubernetes clusters, especially when dealing with multiple clusters or switching between user credentials. Now, in your project's directory, where the `kubeconfig.yaml` file is, run: ```shell Shell theme={"system"} export KUBECONFIG=kubeconfig.yaml ``` This command sets the `KUBECONFIG` environment variable to point to the `kubeconfig.yaml` file. This environment variable informs the `kubectl` CLI to find the configuration file that contains all the necessary details for connecting to a Kubernetes cluster. You can run the following commands to see some info: ```shell Shell theme={"system"} kubectl get nodes ``` This will give you the nodes details. ```shell Shell theme={"system"} kubectl cluster-info ``` This will give info about the cluster, including the master connection URL. ## Create a pod running a Docker image In Kubernetes, a pod is the smallest deployable unit and is the basic building block for orchestrating containerized applications. A pod encapsulates one or more containers, storage resources, a unique network IP, and options that govern how the containers should run. In this case, the pod is where the Express server will run. ### Key characteristics of pods * **Multi-container support**. A pod can host multiple tightly coupled containers that need to share resources efficiently. These containers are scheduled together and run on the same node. * **Shared network and storage**. Containers in the same pod share the same IP address, port space, and storage, allowing easy communication and data sharing between containers. * **Lifecycle**. Pods have a defined lifecycle, similar to containers. They can be in a pending, running, succeeded, or failed state. * **Immutability**. Once created, you cannot change the specification of a running pod. To make changes, you must delete the existing pod and create a new one with the modified specifications. * **Configuration and secrets**. Pods can be configured using `ConfigMaps` and `Secrets`, which can be mounted as files or exposed as environment variables. * **Resource allocation**. You can specify resource limits and requests for the containers in a pod, which helps the Kubernetes scheduler place the pod on a node with sufficient resources. * **Labels and annotations**. Metadata in the form of labels and annotations can be attached to pods for identification and to add contextual information. ### Typical use cases * Single-container pods. Even if you only have one container, using a pod allows you to conform to Kubernetes' operational model. * Multi-container pods. When you have tightly coupled application components that need to share resources efficiently, you can place them in the same pod. * Init containers. These special containers run before the application container, set up the necessary environment, or perform other pre-run tasks. ### Example pod YAML configuration Here's a simple example of a pod configuration in YAML format: ```yaml yaml theme={"system"} apiVersion: v1 kind: Pod metadata: name: my-pod labels: app: my-app spec: containers: - name: my-container image: my-image ports: - containerPort: 80 ``` ### Deploying a pod using a Docker image In this section, we'll walk through deploying your first Kubernetes pod. A pod is the smallest deployable unit in a Kubernetes cluster and can contain one or more containers. We'll use a pre-built Docker image I uploaded to Docker Hub for this example. * [Server's Docker image](https://hub.docker.com/r/soos3d/addressbalance/tags) #### Why use a pre-built Docker image? Using a pre-built Docker image simplifies the deployment process, as you don't have to build the image yourself. It's especially useful for quick deployments and testing. The image we're using, `soos3d/addressbalance:latest`, contains all the code and dependencies needed to run our application. At this point, let's keep in mind that this app requires an Ethereum RPC node URL and port number as environment variables. Let's use Chainstack to get an RPC node URL: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Get the RPC URL to use as an environment variable using the `--env` flag. Now, in the terminal, run the following command: ```shell Shell theme={"system"} kubectl run addressbalance --image=soos3d/addressbalance:latest --port=80 --env="ETHEREUM_RPC_URL=YOUR_CHAINSTACK_ENDPOINT" --env="PORT=3333" ``` ### Command breakdown The basic syntax for deploying a pod with an environment variable using `kubectl run` is as follows: ```shell Shell theme={"system"} kubectl run [POD_NAME] --image=[IMAGE]:[TAG] --env="[KEY]=[VALUE]" ``` #### `kubectl run addressbalance` This part of the command instructs Kubernetes to create a new pod named `addressbalance`. #### `--image=soos3d/addressbalance:latest` Specifies the Docker image to use for the pod. The `latest` tag indicates that we want to use the most recent version of this image. #### `--port=80` This flag sets the port on which the container will listen for incoming traffic, mapping it to port 80. #### `--env="ETHEREUM_RPC_URL=YOUR_CHAINSTACK_ENDPOINT"` This flag sets an environment variable named `ETHEREUM_RPC_URL` in the container. The value for this variable is set to the Ethereum RPC URL. Environment variables are used for configuration settings that shouldn't be hard-coded into the application. #### `--env="PORT=3333"` Similarly, this flag sets another environment variable named `PORT` with the value `3333`. The application uses this to know which port to run on or listen to internally. The `--env` flags allow you to inject environment variables into your container, which can then be accessed by the application running inside it. This is useful for passing in configuration settings or other dynamic values your application needs to function correctly. #### Verifying the deployment After running the above command, you'll want to ensure that the pod has been successfully created and is running as expected. To do this, use the following command: ```shell Shell theme={"system"} kubectl get pods ``` The output should resemble: ```bash Bash theme={"system"} NAME READY STATUS RESTARTS AGE addressbalance 1/1 Running 0 4m34s ``` #### What does the output mean? * `NAME` — the name of the pod, which in this case is `addressbalance`. * `READY` — indicates the number of containers in the pod that are ready. `1/1` means one out of one container is ready. * `STATUS` — shows the current status of the pod. `Running` means the pod has been successfully deployed and is running. * `RESTARTS` — indicates how many times the container within the pod has been restarted. A value of `0` means no restarts have occurred. * `AGE` — shows how long the pod has been running since its creation. Congratulations, you've just deployed your first pod in your Kubernetes cluster! You can also use the `describe` command to get more info; this will also show the environment where you can see if the environment variables are correctly set: ```shell Shell theme={"system"} kubectl describe pod addressbalance ``` If the pod's status indicates an error or unexpected behavior, you can diagnose the issue using the Kubernetes' logging capabilities. Use the following command to view the logs and gain insights into what might be causing the problem: ```shell Shell theme={"system"} kubectl logs addressbalance ``` ## Use a deployment file to deploy multiple pods While deploying a single pod via the CLI is straightforward and effective for simple use cases, it's not the most scalable or manageable approach for deploying multiple identical pods. To handle such scenarios more efficiently, Kubernetes offers the concept of a deployment, defined through a manifest file. A Kubernetes deployment manifest is a structured YAML or JSON file that outlines the desired state, behavior, and characteristics of a collection of interchangeable pods. This manifest acts as an architectural blueprint, guiding Kubernetes on instantiating, updating, and managing these pods to ensure the cluster's actual state aligns with the desired configuration. By leveraging deployment manifests, you not only gain more control over the lifecycle of your pods but also benefit from additional features like versioning, rollbacks, and scaling. It's a more methodical and robust way to manage pod deployments, mainly when dealing with complex or large-scale applications. ### Why use deployment manifests? 1. **Versioning and rollbacks**. Deployments allow you to update the pod template and roll back to previous versions, making application updates and rollbacks smooth and manageable. 2. **Scaling**. You can quickly scale your application up or down by changing the number of replicas in the deployment manifest and reapplying it. 3. **Self-healing**. If a pod crashes or is terminated, the deployment controller will automatically create a new pod to maintain the desired number of replicas. 4. **Load balancing**. Deployments work well with Kubernetes Services, which can distribute traffic to the pods the deployment manages. 5. **Consistency**. By defining the desired state in a file, you can version-control it and ensure that the same configuration is used in different environments (e.g., dev, staging, production). 6. **Automation**. Deployment manifests can be managed through CI/CD pipelines, allowing for automated testing, deployment, and scaling of applications. 7. **Resource efficiency**. Deployments help optimize resource usage by distributing pods across nodes based on resource requirements and other constraints. Kubernetes deployment manifests offer a declarative way to manage your containerized applications' lifecycle, scalability, and update strategy, making operating complex systems at scale easier. In your project's directory, create a new file named `server_deployment.yaml`; note that you can name it however you want, then paste the following: ```yaml server_deployment.yaml theme={"system"} apiVersion: apps/v1 kind: Deployment metadata: name: addressbalance spec: replicas: 4 selector: matchLabels: app: addressbalance template: metadata: labels: app: addressbalance spec: containers: - name: addressbalance-container image: soos3d/addressbalance:latest ports: - containerPort: 3333 env: - name: ETHEREUM_RPC_URL value: "YOUR_CHAINSTACK_NODE_URL" - name: PORT value: "3333" ``` ### Understanding Kubernetes deployment manifest #### General configuration * `apiVersion: apps/v1` — specifies the API version to use for creating this deployment object. * `kind: Deployment` — indicates that the Kubernetes object being defined is a deployment. #### Metadata * `metadata` — contains metadata like the name (`addressbalance`) and labels (`app: addressbalance`) for the deployment. #### Specification * `spec` — specifies the desired state of the deployment. * `replicas: 4` — indicates that 4 replicas (instances) of the pod should be running. * `selector` — defines how to identify the pods that belong to this deployment using the label `app: addressbalance`. * `template` — describes the pod template for the replicas. * `metadata` — specifies the labels that should be applied to each pod created by this deployment. * `spec` — describes the containers that should be part of each pod. * `containers` — lists the containers to run within the pod. * `name: addressbalance-container` — names the container. * `image: soos3d/chatgpt_server:latest` — specifies the container image to use. * `imagePullPolicy: Always` — ensures that the image is always pulled from the registry, even if it already exists locally. * `ports` — specifies that the container should listen on port 3333, the port set up for the server. * `env` — this section is used to define environment variables for the container. Each environment variable is represented as a key-value pair. In this example, two environment variables are set: `ETHEREUM_RPC_URL` and `PORT`. These variables can be accessed by the application running inside the container and are often used for configuration settings that should not be hard-coded. This deployment manifest will create 4 replicas of a pod, each running a container based on the `soos3d/addressbalance:latest` image and listening on port 3333. The deployment will manage the lifecycle of these pods, ensuring that 4 instances are always running. If any pod fails or is terminated, a new one will be created to maintain the desired state. Run the deployment with this command: ```shell Shell theme={"system"} kubectl apply -f server_deployment.yaml ``` Using `kubectl get pods` will now show the 5 pods running, the 4 we just deployed, plus the first one deployed manually. ```bash Shell theme={"system"} kubectl get pods NAME READY STATUS RESTARTS AGE addressbalance 1/1 Running 0 28m addressbalance-7bfb4f6587-bqr7b 1/1 Running 0 167m addressbalance-7bfb4f6587-chnhk 1/1 Running 0 77m addressbalance-7bfb4f6587-vzdqc 1/1 Running 0 167m addressbalance-7bfb4f6587-wqtwn 1/1 Running 0 77m ``` Use the get deployments command to see the deployments: ```shell Shell theme={"system"} kubectl get deployments ``` It will display: ```bash Shell theme={"system"} NAME READY UP-TO-DATE AVAILABLE AGE addressbalance 4/4 4 4 168m ``` You can edit the deployment manifest if you want to change something, such as the number of replicas: ```shell Shell theme={"system"} kubectl edit deployment addressbalance ``` ## Adding a load balancer for external access To make your application accessible from outside the Kubernetes cluster, you'll need to set up a load balancer. This component will distribute incoming network traffic across multiple pods, ensuring high availability and reliability. Linode offers a built-in load balancer service, so we'll set that one up. ### Creating a service manifest for the load balancer The service manifest will specify the type of service as `LoadBalancer`, the ports to forward, and other configurations specific to Linode's load balancer. Create a YAML file named `server_loadbalance.yaml` and paste the following: ```yaml server_loadbalance.yaml theme={"system"} apiVersion: v1 kind: Service metadata: name: addressbalance-service annotations: service.beta.kubernetes.io/linode-loadbalancer-throttle: "4" labels: app: addressbalance spec: type: LoadBalancer ports: - name: http protocol: TCP port: 80 targetPort: 3333 selector: app: addressbalance sessionAffinity: None ``` Let's go over what's happening here. #### `apiVersion: v1` This specifies the API version to use for creating this service object. The `v1` version is the stable version for service objects in Kubernetes. #### `kind: Service` This indicates that the defined Kubernetes object is a service that exposes applications inside your cluster to the outside world or for internal communication between pods. #### `metadata` This section contains metadata for the service. * `name: addressbalance-service` — the name of the service. This is how you will refer to the service within your cluster. * `annotations` — annotations are arbitrary metadata you can use for your purposes but are not used to identify the object. * [`service.beta.kubernetes.io/linode-loadbalancer-throttle`]()`: "4"` — this is a Linode-specific annotation that controls the throttle settings for the load balancer. This sets the client connection throttle to limit the number of new connections per second from the same client IP to 4 and can go up to 20. This is useful for controlling the rate at which a single client can make new connections to your service, which can be particularly important for mitigating certain types of denial-of-service attacks or for managing resources effectively. * `labels` — key-value pairs that are used to identify the service. * `app: addressbalance` — this label specifies that the service is part of the `addressbalance` application. #### `spec` This section specifies the desired state of the service. * `type: LoadBalancer` — this specifies that the service will be exposed via a load balancer. * `ports` — this array specifies the network ports on which the service will accept traffic. * `name: http` — the name of the port (for reference). * `protocol: TCP` — the network protocol to use (TCP/UDP). * `port: 80` — the port on which the service will listen for incoming traffic. * `targetPort: 3333` — the port on the pod to which traffic will be forwarded. * `selector` — this specifies the labels that must be present on a pod to receive traffic from this service. * `app: addressbalance` — only pods with this label will receive traffic from this service. * `sessionAffinity: None` — this specifies that the service will not try to send all requests from a single client to the same pod. Use the command to deploy it: ```shell Shell theme={"system"} kubectl apply -f server_loadbalance.yaml ``` Run the command to see the service and the IP address: ```shell Shell theme={"system"} kubectl get services ``` You'll find the IP: ```bash Shell theme={"system"} NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE addressbalance-service LoadBalancer 10.128.63.81 IP_HERE 80:32273/TCP 98m kubernetes ClusterIP 10.128.0.1 443/TCP 3h46m ``` You can also go on the Linode platform and check the load balancer page, which is called [NodeBalancer](https://cloud.linode.com/nodebalancers). At this point, you have a fully deployed app in a Kubernetes cluster with load balancing and 5 self-healing pods. It is a bit overkill for this specific application, but it's a good starting point to work with more complex projects. You can try the app with a curl request or in a browser. ```bash cURL theme={"system"} curl --location 'YOUR_LOAD_BALANCER_IP/getBalance/0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13' ``` You can also map the IP to a domain. ### Conclusion Congratulations on successfully navigating the intricacies of deploying an application on a Kubernetes cluster hosted on Linode! This guide walked you through the entire process, from setting up your Kubernetes environment to deploying a multi-pod application with environment variables and a load balancer. Along the way, you gained insights into key Kubernetes concepts like pods, deployments, services, and Linode-specific configurations. By following this guide, you've deployed a functional application and laid a strong foundation for scaling and managing more complex projects in the future. The skills you've acquired are transferable to various Kubernetes-based environments and will serve you well as you continue to explore the vast landscape of container orchestration. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Uncovering the power of eth_getBlockReceipts Source: https://docs.chainstack.com/docs/uncovering-the-power-of-ethgetblockreceipts Master eth_getBlockReceipts RPC method on Erigon and Geth clients. Retrieve all transaction receipts efficiently, detect contract deployments with ethers.js. **TLDR:** * Demonstrates how to use the eth\_getBlockReceipts JSON-RPC method (available on Erigon, now also on Geth ≥1.13) to retrieve receipts for all transactions in a specified block. * Highlights how this improves efficiency and simplifies logic compared to calling eth\_getTransactionReceipt individually. * Shows a practical example using ethers.js, scanning blocks for contract deployments by checking if contractAddress is non-null in the returned receipts. * Chainstack supports Erigon archive nodes on Ethereum, Polygon, and BNB Smart Chain for robust developer workflows. ## Main article `eth_getBlockReceipts` is a powerful JSON-RPC method available on nodes running the [Erigon client](https://github.com/ledgerwatch/erigon). This article will explore how it works and how you can use it to make powerful and more efficient applications. ## The Erigon client Erigon is one of the most popular Ethereum clients and is written in [Go](https://go.dev/). It is very much inspired by [Go Ethereum](https://geth.ethereum.org/). Still, it focuses on efficiency, which allows a user to synchronize a new node much faster and use fewer resources, especially when talking about storage requirements. This is why Erigon is gaining popularity, and you can read our [Erigon vs Geth guide](https://chainstack.com/ethereum-clients-geth-and-erigon/) if you are interested in understanding more about the underlying principles and architecture. Compared to Geth, Erigon comes with some unique JSON-RPC methods, and `eth_getBlockReceipts` is one of them. Let’s dive into it. > [!NOTE] > > ### Update on eth\_getBlockReceipts > > As of September 2023, the `eth_getBlockReceipts` method is also available on the Geth client from V [1.13.0](https://github.com/ethereum/go-ethereum/releases/tag/v1.13.0). ## `eth_getBlockReceipts` explained The `eth_getBlockReceipts` method is a JSON-RPC method that allows you to retrieve the receipts for all transactions included in a block and takes the block identifier as a parameter. You can send a [curl](https://curl.se/) request to call this method using the following syntax: ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_ARCHIVE_NODE_URL" \ -H 'Content-Type: application/json' \ --data '{"method":"eth_getBlockReceipts","params":["latest"], "jsonrpc":"2.0","id":1}' ``` ## Parameters accepted `eth_getBlockReceipts` only takes one parameter to identify the block, and you have a few options, a string tag or a block identifier. For `tag`, you can use one of the following options: * `latest` — the latest validated block. At this stage, it is likely that the chain will be reorganized, so be careful using this one. * `safe` — a safe head of the chain block. Under normal circumstances, a safe block will not be reorganized. * `finalized` — the block that is accepted by two-thirds of the Ethereum validators, and a reorganization at this point is extremely unlikely. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. Alternatively, you can also specify `blocknumber` either as a decimal or encoded as hexadecimal starting with `0x`. For example: * Block number `16341960` will be `0xF95BC8` in hexadecimal. > [!NOTE] > > ### Decimal \<> hexadecimal converter > > To convert a decimal integer to hexadecimal, you can use an [online converter](https://www.rapidtables.com/convert/number/decimal-to-hex.html) or a library like Ethers. As a bonus, here is an example showing how to make a simple function to convert to hexadecimal using one of ethers.js utilities. ```js block number converter theme={"system"} // Import the ethers library const ethers = require('ethers'); // The function takes a decimal integer as a parameter function toHex(decimal) { const hex = ethers.utils.hexlify(decimal) const fixPadding = ethers.utils.hexStripZeros(hex) return fixPadding } // Call the function and display the result const blockInHex = toHex(16341960) console.log(blockInHex) // result -> 0xf95bc8 ``` Using a web3.js library has its benefits since it already adds `0x`. ## eth\_getBlockReceipts response The response returned by this method will be an array of **transaction receipts** for all the transactions included in the block specified in the method call. So, what is a transaction receipt? A transaction receipt is a record of a transaction that has been processed by the network. When you send a transaction on the Ethereum blockchain, for example, the transaction is broadcast to all nodes on the network. Once the transaction has been included in a block and added to the blockchain, a receipt is generated that contains information about the transaction. Here is an example of such a response: ```Json JSON theme={"system"} { "blockHash":"0x72b42a924425d37e4529b9e1bb0082bfad4d6b13761878a2ba641b13cad7d21f", "blockNumber":"0xf959a7", "contractAddress":null, "cumulativeGasUsed":"0x2aa60", "effectiveGasPrice":"0x1033e0d411", "from":"0xeb00c0f8bb1ddf074b464dbcbe32164ad00e1a4b", "gasUsed":"0x2aa60", "logs":[ { "address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "topics":[ "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c", "0x0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d" ], "data":"0x0000000000000000000000000000000000000000000000000058d15e17628000", "blockNumber":"0xf959a7", "transactionHash":"0x0edddb16d2ab807043427090f46a7bc2196908d4a1d4ae2125ce220d4a9c0a4d", "transactionIndex":"0x0", "blockHash":"0x72b42a924425d37e4529b9e1bb0082bfad4d6b13761878a2ba641b13cad7d21f", "logIndex":"0x0", "removed":false }, { "address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "topics":[ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d", "0x0000000000000000000000009ac96265a4e63dacc86c6e3f4ab547fbcf93e899" ], "data":"0x0000000000000000000000000000000000000000000000000058d15e17628000", "blockNumber":"0xf959a7", "transactionHash":"0x0edddb16d2ab807043427090f46a7bc2196908d4a1d4ae2125ce220d4a9c0a4d", "transactionIndex":"0x0", "blockHash":"0x72b42a924425d37e4529b9e1bb0082bfad4d6b13761878a2ba641b13cad7d21f", "logIndex":"0x1", "removed":false }, { "address":"0xa6cd7dbfb83e2d963d6867eb3d65c64013217fc7", "topics":[ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000009ac96265a4e63dacc86c6e3f4ab547fbcf93e899", "0x000000000000000000000000a6cd7dbfb83e2d963d6867eb3d65c64013217fc7" ], "data":"0x0000000000000000000000000000000000000000000000000039d4c88723482c", "blockNumber":"0xf959a7", "transactionHash":"0x0edddb16d2ab807043427090f46a7bc2196908d4a1d4ae2125ce220d4a9c0a4d", "transactionIndex":"0x0", "blockHash":"0x72b42a924425d37e4529b9e1bb0082bfad4d6b13761878a2ba641b13cad7d21f", "logIndex":"0x2", "removed":false }, { "address":"0xa6cd7dbfb83e2d963d6867eb3d65c64013217fc7", "topics":[ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000009ac96265a4e63dacc86c6e3f4ab547fbcf93e899", "0x000000000000000000000000eb00c0f8bb1ddf074b464dbcbe32164ad00e1a4b" ], "data":"0x000000000000000000000000000000000000000000000000044acae2079e7280", "blockNumber":"0xf959a7", "transactionHash":"0x0edddb16d2ab807043427090f46a7bc2196908d4a1d4ae2125ce220d4a9c0a4d", "transactionIndex":"0x0", "blockHash":"0x72b42a924425d37e4529b9e1bb0082bfad4d6b13761878a2ba641b13cad7d21f", "logIndex":"0x3", "removed":false }, { "address":"0x9ac96265a4e63dacc86c6e3f4ab547fbcf93e899", "topics":[ "0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1" ], "data":"0x0000000000000000000000000000000000000000000000016978e1b70b8b28fa0000000000000000000000000000000000000000000000001c0576b9739871d1", "blockNumber":"0xf959a7", "transactionHash":"0x0edddb16d2ab807043427090f46a7bc2196908d4a1d4ae2125ce220d4a9c0a4d", "transactionIndex":"0x0", "blockHash":"0x72b42a924425d37e4529b9e1bb0082bfad4d6b13761878a2ba641b13cad7d21f", "logIndex":"0x4", "removed":false }, { "address":"0x9ac96265a4e63dacc86c6e3f4ab547fbcf93e899", "topics":[ "0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822", "0x0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d", "0x000000000000000000000000eb00c0f8bb1ddf074b464dbcbe32164ad00e1a4b" ], "data":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058d15e1762800000000000000000000000000000000000000000000000000004849faa8ec1baac0000000000000000000000000000000000000000000000000000000000000000", "blockNumber":"0xf959a7", "transactionHash":"0x0edddb16d2ab807043427090f46a7bc2196908d4a1d4ae2125ce220d4a9c0a4d", "transactionIndex":"0x0", "blockHash":"0x72b42a924425d37e4529b9e1bb0082bfad4d6b13761878a2ba641b13cad7d21f", "logIndex":"0x5", "removed":false } ], "logsBloom":"0x00200000000000000000000080000000000000020000000000010000000000000000000000200000000000000000004082000000080000000000000000000000000000000000804000000008000000600000000000000000000000008000000000000000000000000000000000000000000000000000000000000010000000000000000000000000004000000000000000000001000000080000004000000800000000000800000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000001000000000808020000000200000c00000000000800000000000000000000000410000000000000000", "status":"0x1", "to":"0x7a250d5630b4cf539739df2c5dacb4c659f2488d", "transactionHash":"0x0edddb16d2ab807043427090f46a7bc2196908d4a1d4ae2125ce220d4a9c0a4d", "transactionIndex":"0x0", "type":"0x2" }, ``` > [!NOTE] > > Check the details about the `eth_getBlockReceipts` response in the [Chainstack docs](/reference/ethereum-getblockreceipts). A `eth_getBlockReceipts` call will return the following parameters: * `result` — an array of objects with: * `Transaction receipt` — the object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the address of the contract created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects generated by this transaction, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. Transaction receipts contain details such as the transaction hash (a unique identifier for the transaction), the block number and hash in which the transaction was included, the amount of gas used by the transaction, and the status of the transaction (whether it was successful or encountered an error). Receipts also include the contract address of any contract created as a result of the transaction. Transaction receipts are useful because they provide a way to track the success or failure of a transaction, as well as the amount of gas used and any logs emitted by the transaction. ## Difference between `eth_getBlockReceipts` and `eth_getBlockByNumber` At this point, you are probably wondering why I’m talking about this specific method when `eth_getBlockByNumber` and `eth_getBlockByHash` are available in Geth and can also return the detail of each transaction in a block if we call it with the second parameter as `true`. The difference is that `eth_getBlockReceipts` returns the receipt directly, including extra information you might want to extract. More noticeably, the receipt returns the event logs that might have been emitted by a smart contract during the transaction, the status of the transaction (*1 if successful and 0 if failed*), gas used, and more. Of course, you can use `eth_getTransactionReceipt` to get the receipt of a transaction that you might have identified using `eth_getBlockByNumber` instead of `eth_getBlockReceipts`, but why make more requests when you can be more efficient? > [!NOTE] > > ### Erigon code base on GitHub > > Check out the Erigon code base on GitHub to see the source code of this method and how the data is retrieved: > > * [eth\_receipts.go](https://github.com/ledgerwatch/erigon/blob/62d9f3793e193c7ef825ecb70b9deea73606e2a7/turbo/jsonrpc/eth_receipts.go#L612) ## Deploy an Erigon node with Chainstack Chainstack supports the Erigon client on Ethereum, Polygon, and BNB smart chain [archive nodes](https://chainstack.com/evm-nodes-a-dive-into-the-full-vs-archive-mode/). Follow the steps to deploy an archive node running Erigon on Global Nodes: > [card] [Sign up with Chainstack](https://console.chainstack.com/user/account/create) > [card] [Create a project](/docs/manage-your-project#create-a-project) After you created a project: 1. Select the project with the network. 2. Select the network. 3. Click **Add node**. 4. Provide a node name. 5. Under **Type**, select **Global Node**. 6. Under **Mode**, select **Archive**. With an archive node, you can query historical states for the entire chain. 7. Under **Debug and trace APIs**, select **On**. 8. Under **Hosting**, select **Chainstack**. 9. For Chainstack hosting, select a cloud provider and a region. 10. Review your changes and click **Add node**. The node status will change from **Pending** to **Running** once deployed. You will also see the **Debug and trace** tag next to the node name. You can then verify in the Chainstack console that the client running is indeed Erigon. To do that, open your node details and check the **Clients** column. Now you only need to copy your RPC endpoint and go BUIDL cool stuff. ## Let’s BUIDL Now that you know how `eth_getBlockReceipts` works and how to get a node running Erigon for it, I want to show you how to use it with a practical example. For this project, we’ll use [ethers.js](https://docs.ethers.org/v5/) to build a ‘smart contracts watcher’ tool that can examine a block using `eth_getBlockReceipts`, identify which transaction deploys a new smart contract, and give us details about it. ## The projects structure The project is built in node.js and interacts with the blockchain using the Ethers library. > [!NOTE] > > ### Project source code > > Find the full code in the repository: > > * [smart-contract-watcher](https://github.com/soos3d/smart-contract-watcher-chainstack) This is what the structure looks like: ```bash Shell theme={"system"} ├── Root directory ├── index.js │ │── src │   ├── provider.js │   ├── analyzer.js │  ├── latestBlock.js │  ├── parser.js │  ├── ToHex.js │  ├──.env ``` Let’s explain what each file does: | File | Explanation | | - | - | | index.js | The entry point of the project. It runs the main program. | | provider.js | Creates a provider instance to be used from the other scripts. | | latestBlock.js | It holds the function to retrieve the latest block using ethers.js. | | ToHex.js | It has a function to convert a decimal number to hexadecimal using ethers.js utils. | | analyzer.js | This file uses the functions from toHex.js and latestBlock.js; it calls `eth_getBlockReceipts` on the latest block and looks for new smart contract deployments. | | parser.js | It extracts the desired data from the transaction receipt and prints them to the console. | | .env | It holds the node RPC URL. | ## Inside the code The bulk of the process is managed by the function in the `analyzer.js` file, which connects to the node and calls the `eth_getBlockReceipts` method. ```js JavaScript theme={"system"} // Import the support scripts const provider = require('./provider'); const { toHex } = require('./toHex') const { latestBlock } = require('./latestBlock') const { parseTransaction } = require('./parser') const findSmartContract = async () => { // Find the latest block const blockToAnalyze = await latestBlock(); // Convert the latest block to Hex const blockInHex = toHex(blockToAnalyze) // Sometimes Erigon returns an invalid response; try/catch will tell you if something is wrong. try { // Get the transaction receipts const blockReceipt = await provider.send("eth_getBlockReceipts", [blockInHex]); //console.log(blockReceipt); // Iterate over the transactions in the block for (const tx of blockReceipt) { if (tx.contractAddress != null) { // This function parses and prints the result parseTransaction(tx) } } } catch (error) { console.log(`An error occurred, please try again. \n ERROR: ${error}`) } } module.exports = { findSmartContract } ``` This modular design helps keep the code clean and organized. Now onto the meat and potatoes. The `findSmartContract` calls the `latestBlock` function to find the latest block on the network. It converts this block to hexadecimal format using the `toHex` function and then uses the `eth_getBlockReceipts` method of the `provider` instance to get the transaction receipts for the specified block. > [!NOTE] > > The provider instance is created in the `provider.js` file and exported to be used where necessary. The function then iterates over the transaction receipts in the block. For each transaction that has a non-null `contractAddress` field, it calls the `parseTransaction` function and passes the transaction as an argument. The logic here is that if the `contractAddress` field of the receipt is not empty, a new smart contract is deployed, and the field holds its address. The `parseTransaction` then isolates the fields we are interested in and prints them to the console. Sometimes the `latestBlock` and `toHex` functions might return a ‘non-iterable object’ from Erigon; this stops the program. For this, we use a `try`-`catch` block, and the error message will be logged to the console. You can also add some logic that does not stop the program in case there is an error and tries again automatically. Finally, the function exports an object containing the `findSmartContract` function so that it can be imported and used by other parts of the program, the `main` function in `index.js` in this case. This is a very simple example of how you can use `eth_getBlockReceipts` in the real world, and you can, for instance, add a listener to pick up every new block header and automatically scan the block for new contracts if you want to make it more advanced. This is what you will get when the watcher finds a new smart contract: ```shell Shell theme={"system"} $ npm run start > smart-contract-watcher-chainstack@1.0.0 start > node index.js New smart contract detected! New smart contract address: 0x00e9a503b88732ee3e28a0fec3343c405f5e963d Deployed by address: 0x984380fc12cc3bdb894e1f35111dd992fb52a231 Deployed by transaction: 0xad8d3a6647a4ad7e2f8eedef258b4c3233d54e5999a855ca59500f5df70d35de ``` > [!NOTE] > > Note that you might need to run the script multiple times before you find a contract deployment on mainnet. ## Conclusion In summary, the `eth_getBlockReceipts` method is a powerful tool for developers working with the Erigon client. It allows them to retrieve the receipts for all of the transactions in a given block, which can be very useful to avoid calling multiple methods and keep programs easy and efficient. Chainstack supports the Erigon client on Ethereum, BNB Smart Chain, and Polygon. In this article, we also showed you a program that uses the `eth_getBlockReceipts` method to find new smart contracts deployed on the blockchain. It then uses the `eth_getBlockReceipts` method to retrieve the transaction receipts for the block and iterates over these receipts to find transactions deploying new smart contracts. > [!NOTE] > > ### See also > > > [card] [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) > > > [card] [Expanding your blockchain horizons: The eth\\\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) > > > [card] [Deep dive into Merkle proofs and eth\\\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) > > > [card] [Geth vs Erigon: Deep dive into RPC methods on Ethereum clients](/docs/geth-vs-erigon-deep-dive-into-rpc-methods-on-ethereum-clients) > > > [card] [Ethereum logs tutorial series: Logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters) ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Understanding eth_getLogs limitations Source: https://docs.chainstack.com/docs/understanding-eth-getlogs-limitations Master eth_getLogs block range limits and best practices. Paginate queries, filter efficiently, and optimize Ethereum log retrieval for scalable DApps. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR:** * `eth_getLogs` is a powerful method to retrieve Ethereum logs but can strain node resources if not managed properly. * Always adhere to your network’s recommended block range limit and paginate larger queries to avoid timeout errors. * Apply relevant filters (topics, contract addresses) to minimize data retrieval overhead. * For production use, combine best practices like smaller query ranges, pagination, and efficient filtering to ensure reliable and scalable log fetching. ## Main article As decentralized applications (DApps) continue to evolve and scale, the efficient management of Ethereum logs has become crucial in Web3 development. One of the common ways to access Ethereum logs is via the `eth_getLogs` RPC method. This method provides an essential tool for querying past events in Ethereum-based blockchains. However, understanding its limitations and the optimal ways to use it can significantly enhance your DApp performance and reliability. ## About `eth_getLogs` The [`eth_getLogs`](/reference/ethereum-getlogs) method is an Ethereum JSON-RPC endpoint used to query logs based on a `filter` object from the Ethereum blockchain. It can be used directly or indirectly through libraries like web3.js or ethers.js that provide convenient wrappers. This function is crucial for auditing and retrieving past events emitted by smart contracts. > [!NOTE] > > Learn more about `eth_getLogs` and how you can retrieve emitted events from [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial). ## Block range limitations While `eth_getLogs` is a powerful tool, it's crucial to understand its limitations, particularly when working with different EVM-compatible chains, as these networks often have different constraints. The `eth_getLogs` method allows you to select a range of blocks to get events from and is important to exercise proper management. `eth_getLogs` is a resource-intensive method, so the block range you can query in a single request (the difference between `fromBlock` and `toBlock`) is capped by your subscription plan: Developer plans are capped at 100 blocks and paid plans at 10,000 blocks. For the authoritative per-plan figures, see [Throughput guidelines](/docs/limits#evm-range-limits). If you need a larger range, [Dedicated Nodes](/docs/dedicated-node) support custom range limits on request. Some chains also enforce their own hard limits at the client level, regardless of plan, and return an error past that range. These ceilings vary by client, so it's worth checking the node client's docs or source for the chain you're targeting. Even within your plan's cap, a busy chain or a high-traffic contract can return an oversized response or time out. On high-activity ranges, query fewer blocks and paginate (see below). ## Best practices when using `eth_getLogs` While using `eth_getLogs`, consider these best practices to ensure efficient and reliable log data retrieval. ### Limit the block range Stay within the block range limits for the specific network you are working with. This practice reduces the likelihood of receiving an oversized response or a timeout error due to a long query. ### Paginate queries If you need to retrieve logs over a range that exceeds the network's limit, split your request into multiple queries. This method is similar to pagination in traditional APIs. The following is an example using web3.js: ```javascript Javascript theme={"system"} const Web3 = require("web3"); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const web3 = new Web3(NODE_URL); async function getLogs() { const startBlock = 14204533; const endBlock = 15204533; const range = 5000; const address = '0x4d224452801ACEd8B2F0aebE155379bb5D594381'; const topics = ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']; for (let i = startBlock; i <= endBlock; i += range) { const fromBlock = i; const toBlock = Math.min(i + range - 1, endBlock); const filter = { fromBlock, toBlock, address, topics }; const logs = await web3.eth.getPastLogs(filter); console.log(logs); } } getLogs(); ``` This example retrieves the `Transfer` events from the first one million blocks after the deployment of the [ApeCoin](https://etherscan.io/address/0x4d224452801aced8b2f0aebe155379bb5d594381) smart contract in chunks of 5,000 blocks. ### Efficiently filter logs Retrieving logs from a blockchain can result in a large amount of data, especially when dealing with a busy network or a large number of blocks. Applying filters to your queries is important to manage this effectively and avoid unnecessary processing unless you are trying to retrieve all the events at once. A filter is a set of criteria that you specify when making a request for logs. The blockchain node will then only return the logs that match these criteria, reducing the amount of data returned and making the query more efficient. > [!NOTE] > > ### Further reading > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about the `eth_getLogs` method and how to encode the filter parameters. For example, if you're only interested in a specific type of event, such as `Transfer` events, you can specify this in your filter. The node will then only return logs for `Transfer` events, ignoring all others. Similarly, if you're only interested in events from a particular address, you can specify this address in your filter. The node will then only return logs that involve this address, ignoring events from all other addresses. Using filters effectively can significantly reduce the amount of data you need to handle, making your application more efficient and responsive. ## Real-world example Let’s take it a step further and show an example of how we can fetch and store `Transfer` event logs from the [BAYC](https://etherscan.io/token/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d) smart contract. This project stores event logs in a MongoDB instance. This is a good starting point for creating your own BAYC API. ### Prerequisites * node.js * MongoDB Atlas account with database setup and dedicated account to it * [Chainstack Ethereum node](/docs/manage-your-networks) ### Setup For this example, we will be using node.js, so let’s set up a project: ```shell Shell theme={"system"} npm init -y ``` > [!NOTE] > > Check out [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project) to learn best practices when working with node.js. As the next step, we should install dependencies: ```shell Shell theme={"system"} npm install web3 dotenv mongodb ``` Now we can create `.env` and `main.js` files and fill in the following information: ```jsx .env theme={"system"} MONGODB_CONNECTION_STRING="YOUR_MONGO_DB_CONNECTION" CHAINSTACK_URL="YOUR_CHAINSTACK_ENDPOINT" ``` ```javascript main.js theme={"system"} require("dotenv").config(); const Web3 = require("web3"); const MongoClient = require("mongodb").MongoClient; async function connectToMongoDB(connectionString) { const client = new MongoClient(connectionString); try { await client.connect(); console.log("Connected to MongoDB"); return client; } catch (err) { console.error(`Failed to connect to MongoDB: ${err}`); return null; } } // Connect to your Chainstack Ethereum node console.log("Connecting to Ethereum node..."); const web3 = new Web3( new Web3.providers.HttpProvider(process.env.CHAINSTACK_URL) ); console.log("Connected to Ethereum node"); // Set the BAYC contract address const contractAddress = "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"; // BAYC contract address // BAYC contract was created at this block const startBlock = 12686718; const batch_size = 5000; // Stay within Ethereum network limits // Connect to MongoDB connectToMongoDB(process.env.MONGODB_CONNECTION_STRING).then(async (client) => { if (client) { const db = client.db("BAYC"); const collection = db.collection("bayc-logs"); // Get latest block number console.log("Fetching latest block number..."); const endBlock = await web3.eth.getBlockNumber(); console.log(`Latest block number is ${endBlock}`); // Query logs in batches for (let i = startBlock; i < endBlock; i += batch_size) { const toBlock = Math.min(i + batch_size - 1, endBlock); try { console.log(`Fetching events from block ${i} to ${toBlock}...`); /* We can use getPastLogs or getPastEvents getPastLogs return raw data, getPastEvents will do some formatting on the data returned */ const logs = await web3.eth.getPastLogs({ fromBlock: web3.utils.toHex(i), toBlock: web3.utils.toHex(toBlock), address: contractAddress, topics: [web3.utils.keccak256("Transfer(address,address,uint256)")], }); console.log( `Fetched ${logs.length} logs from block ${i} to ${toBlock}` ); // Process logs and store them in MongoDB for (let log of logs) { await collection.insertOne(log); console.log(`Stored log ${log.id} in MongoDB`); } console.log(`Stored logs from block ${i} to ${toBlock} in MongoDB`); } catch (err) { console.error( `Error fetching logs from block ${i} to ${toBlock}: ${err}` ); } } client.close(); } }); ``` Once we have the files ready, we can run the script with the following command: ```shell Shell theme={"system"} node main.js ``` If you set up everything properly, you will see the following output: ```jsx jsx theme={"system"} ❯ node main.js Connecting to Ethereum node... Connected to Ethereum node Connected to MongoDB Fetching latest block number... Latest block number is 17401394 Fetching events from block 12686718 to 12691717... Fetched 183 logs from block 12686718 to 12691717 Stored log log_5b71b7bd in MongoDB Stored log log_22da2c2a in MongoDB Stored log log_6c6c5129 in MongoDB Stored log log_9b606429 in MongoDB Stored log log_de39154c in MongoDB Stored log log_a49334ff in MongoDB ... ... Stored logs from block 12686718 to 12691717 in MongoDB Fetching events from block 12691718 to 12696717... Fetched 125 logs from block 12691718 to 12696717 Stored log log_e19237a9 in MongoDB Stored log log_7611f848 in MongoDB Stored log log_a8e6d7b7 in MongoDB Stored log log_999b2ede in MongoDB Stored log log_2f4fb9f4 in MongoDB Stored log log_51054a82 in MongoDB ... ... ``` Congratulations, you just set up your own data-processing script! ## Conclusion Understanding the nuances and best practices of using `eth_getLogs` is vital for efficiently working with Ethereum logs and enhancing your DApp performance. Being mindful of block range limitations and implementing methods to optimize log retrieval will provide a robust foundation for handling log data in a Web3 environment. ### About the author > [card] Edin Drazevicanin > > ![Edin Drazevicanin](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/juOi73j.png?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=8d80cce654921d5a7a6373a939ebf2af) > > Technical Support Engineer @ Chainstack > \ > JUST BUIDL IT! > > [https://github.com/0x6564](https://github.com/0x6564) > > [https://twitter.com/edeenn22](https://twitter.com/edeenn22) > > [https://www.linkedin.com/in/edindr/](https://www.linkedin.com/in/edindr/) # Fix Ethereum's filter not found error Source: https://docs.chainstack.com/docs/understanding-ethereums-filter-not-found-error-and-how-to-fix-it Resolve Ethereum filter not found errors with proper error handling. Learn about filter expiration, automated recreation, and eth_getFilterChanges. **TLDR:** * Filters let you retrieve specific blockchain data with minimal overhead. * The "Filter Not Found" error often occurs due to filter expiration, node restarts, or manual deletion. * Proper error handling, automated filter recreation, or alternative data retrieval (eth\_getLogs) can reduce this issue. * Always manage filter lifecycles in your code to ensure reliable and robust dApp performance. ## Introduction EVM-compatible blockchains, such as Ethereum, BNB Smart Chain, Polygon, and others are decentralized networks that store vast amounts of data. Filters are one of the ways developers can retrieve some of this data. > [!NOTE] > > Find an example of the [logs filter](/reference/ethereum-newfilter) in the API reference. ## What are filters? In EVM-compatible blockchains, filters play a crucial role in data retrieval. They are queries or conditions set by developers to monitor specific events on the blockchain. For instance, a developer might set a filter to watch for a specific token transfer event or function call in a smart contract. Filters are especially useful because blockchains are continuously growing ledgers, and sifting through all the data manually or programmatically without filters would be inefficient. Using filters, developers can efficiently pinpoint the event they are interested in without parsing through irrelevant information. ## The role of `eth_getFilterChanges` method The `eth_getFilterChanges` method is a standard RPC call used across EVM-compatible blockchains. Its primary purpose is to retrieve changes or updates that occurred concerning a previously set filter. In simpler terms, once a filter is set to watch for a specific event, the `eth_getFilterChanges` method can fetch any new occurrences. Learn more about [eth\_getFilterChanges](/reference/ethereum-getfilterchanges). However, developers might occasionally encounter the "Filter Not Found" error while working with this method. ## The "Filter Not Found" error explained The "Filter Not Found" error is a common issue developers encounter when working with filters on EVM-compatible blockchains. This error arises when the system cannot locate the specified filter ID during a query. Several reasons can trigger this error: * **Filter expiration**. Filters are not permanent. They have a lifespan and can expire after a certain period of inactivity, usually 5 minutes. Once a filter expires, querying it will result in the "Filter Not Found" error. * **Node restarts**. If the blockchain node is restarted, the filters created before the restart might be lost. Subsequent queries to these filters will then yield the error. * **Manual deletion**. Filters can be manually removed using specific methods like \`\`. If a filter is uninstalled and then queried, it will naturally result in the error. * **State pruning**. Some nodes, for optimization purposes, might prune or remove older states, including filters, to save space. This can also lead to an error when querying a pruned filter. Understanding the root cause of the "Filter Not Found" error is crucial. It helps developers take appropriate corrective actions, whether recreating the filter, ensuring longer filter lifespans, or maintaining node stability. ## Reproducing the "Filter Not Found" error Let’s go over the steps to reproduce this error so we can understand it better. ### Step 1. Create a filter Start by creating a filter; in this case, we’ll use the [`eth_newBlockFilter`](/reference/ethereum-newblockfilter) method. This filter watches for new blocks on the blockchain. ```bash cURL theme={"system"} curl --request POST \ --url YOUR_CHAINSTACK_ENDPOINT/ \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "jsonrpc": "2.0", "method": "eth_newBlockFilter", "id": 1 } ' ``` This will return a filter ID: ```bash Bash theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x0700000000000000ab622e24d29ffa1f" } ``` ### Step 2. Use `eth_getFilterChanges` After creating the filter, you can use `eth_getFilterChanges`, passing the filter ID to retrieve changes matching the filter's criteria: ```bash cURL theme={"system"} curl --request POST \ --url YOUR_CHAINSTACK_ENDPOINT/ \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": [ "0x0700000000000000ab622e24d29ffa1f" ], "id": 1 } ' ``` This is usually called "polling" the filter and will return changes between polls, in this case, the hashes of new blocks: ```bash Bash theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": [ "0x4a25e03a0dd4070e80f261e24fbd9d345f67e327989dc302c2d3e78ff32199cb", "0x837d4321129cea26fa2f17972bbbdad1e3d737bc8ea78d78fddefbeeb47a7f39", "0x48f84d66d5b824da7c97c1f1d67ff346a24d7076acc8a96ab3b1a2ede7f22419" ] } ``` ### Step 3. Delay or inactivity The "Filter Not Found" error often occurs when there is a delay between creating the filter and using `eth_getFilterChanges`. Ethereum nodes keep filters for 5 minutes, according to the [Go Ethereum (Geth) GitHub page](https://github.com/ethereum/go-ethereum/blob/36874b63a1b56eed2b8e4b47ccea0337920b84b8/eth/filters/filter_system.go). If you wait too long to check for changes, or a situation listed in the previous paragraph happens, the filter may no longer exist, returning `Filter Not Found`. ## How to handle filters In EVM-compatible blockchain development, errors are a common occurrence. The way these errors are handled can make a significant difference in the robustness and reliability of your applications. The "Filter Not Found" error, while prevalent, can be managed effectively with the right strategies. There are several recommended solutions using web3.py. Let us look at both of them further. ### Solution 1. Immediate change retrieval with error handling After setting up a filter, it's essential to fetch the changes using the `get_filter_changes` method from web3.py immediately. Implementing error handling at this stage can prevent potential issues: ```python Python theme={"system"} from web3 import Web3 import time # Initialize web3 connection node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') return blocks_filter except Exception as e: print(e) blocks = get_new_blocks() filter_id = blocks.filter_id print('Filter ID:', filter_id) # Fetch changes and handle potential errors try: time.sleep(12) # Wait 12 seconds for higher chances of changes. changes = web3.eth.get_filter_changes(filter_id) print(changes) # Process the changes accordingly except ValueError as error: if "filter not found" in str(error).lower(): # Gracefully address the "Filter Not Found" error print("The filter seems to have expired or is non-existent. Consider recreating it.") # Optionally, you can recreate the filter or take alternative measures else: # Address other potential errors print(f"Encountered an error during change retrieval: {error}") ``` In the provided code, the `try-except` block is instrumental in identifying and managing errors during the `get_filter_changes` invocation. By examining the error message for `filter not found`, we can specifically address this error. For other discrepancies, a generic error message aids in debugging. ### Solution 2. Automated filter recreation If a filter is found to have expired or is missing, an effective strategy is to automate its recreation within the error-handling logic. This involves invoking `new_filter` once more and preserving the new filter ID: ```python Python theme={"system"} if "filter not found" in str(error).lower(): print("The filter appears to have expired or is missing. Initiating filter recreation...") new_filter_id = get_new_blocks() print(f"Successfully generated a new filter with ID: {new_filter_id.filter_id}") # Proceed to process changes using the new_filter_id else: # Address other potential errors print(f"Encountered an error during change retrieval: {error}") ``` By incorporating this proactive approach to error handling, you can ensure the smooth operation of your EVM-compatible applications using `web3.py`. ### Solution 3. Persistent filter monitoring Instead of creating a filter and checking it only once, you can set up a loop that continuously monitors the filter for changes. If the filter expires or is removed, you can detect it immediately and take corrective action. ```python Python theme={"system"} import time interval = 30 def monitor_filter(filter_id, interval=10): while True: try: changes = web3.eth.get_filter_changes(filter_id) if changes: # Process the changes print(changes) time.sleep(interval) except ValueError as error: if "filter not found" in str(error).lower(): print("Filter expired or removed. Recreating...") filter_id = get_new_blocks().filter_id else: print(f"Error: {error}") ``` ### Solution 4. Use event logs instead of filters If you are trying to filter logs, instead of relying solely on filters, you can use the [eth\_getLogs](/reference/ethereum-getlogs) method to get historical data. This method is especially useful if you're interested in past events or transactions. ### Solution 5. Backup filters on multiple nodes You can create the same filter on multiple nodes by accessing several endpoints. This redundancy ensures that you can still retrieve data from another node if one of them fails or the filter is removed from one node. ### Solution 6. Regularly refresh filters Instead of waiting for a filter to expire, proactively refresh or recreate filters regularly. This approach ensures that your filters are always active and reduces the chances of encountering the "Filter Not Found" error. ### Solution 7. Implement comprehensive logging Incorporate detailed logging into your application. Logging all interactions with the blockchain and any errors that arise, you can quickly diagnose issues, understand their root causes, and implement fixes. ## Conclusion Connecting to a dependable node is paramount to ensure seamless interaction with a blockchain network. Nodes prone to frequent restarts or experiencing intermittent downtimes can disrupt the functionality of filters. Always verify the filter ID when invoking `eth_getFilterChanges`. Each filter possesses a unique ID, and discrepancies in using the correct ID will inevitably trigger the "Filter Not Found" error. Ethereum's inherent design includes a brief filter expiration duration. While this can be a source of the "Filter Not Found" error, adept error handling can substantially reduce its adverse effects, paving the way for a resilient Ethereum-based application. # Solana: Blocks vs slots explained Source: https://docs.chainstack.com/docs/understanding-the-difference-between-blocks-and-slots-on-solana Learn Solana blocks vs slots concepts. Understand transaction expiry, getLatestBlockhash RPC calls, and why slot numbers exceed block numbers by millions. **TLDR:** * Solana reports two metrics: **slots**, which are scheduled opportunities to produce a block, and **blocks**, which only increment when a block is actually produced. * This leads to a discrepancy: the slot count typically exceeds the block count by millions because many slots go unfilled. * For [transaction expiry](/docs/solana-how-to-handle-the-transaction-expiry-error), Solana uses **block height** (150 blocks), not slots, so empty slots do not affect how long your transaction remains valid. * Be mindful that RPC calls and explorers often mix the terminology, so always verify whether you’re dealing with a slot or a block. ## Main article Solana’s architecture can be a bit confusing at first glance—especially when you notice that the chain reports two different numbers for **blocks** and **slots**. Understanding how these concepts differ is key to interpreting the output of Solana’s RPC methods correctly. In this article, we’ll walk through: * What a **slot** is. * What a **block** is. * Why slot numbers and block numbers differ (sometimes by millions). * How this affects transaction validity, especially when using the `getLatestBlockhash` RPC call. ## What is a Solana slot? A **slot** is essentially a scheduled spot for a validator to produce a block. Solana runs on a proof-of-stake model (it's proof-of-history with a lot of modifications, but for this article's context it's the underlying proof-of-stake model that matters), where validators take turns producing blocks in these scheduled slots. However, a validator **may not produce a block** for each slot. When a slot is missed (i.e., no block is produced), it still counts toward the slot count, but it does not generate a corresponding block. So you can think of each slot as: > A timeslot allocated for a potential block. ## What is a Solana block? A **block** is only created if the validator assigned to the slot actually produces a block. Blocks contain the transactions, state changes, and other essential data that get committed to the chain. Because not every slot results in a block, the total block count tends to lag behind the slot count. This is why **block heights** and **slot heights** almost always diverge—and can differ by millions. In the past, there have been observations that some Solana validators had the incentives and the means to skip block production for some slots in return for a higher reward. This seems to have been fixed since then. You can read about it here: [Everstake claims some validators were optimized to skip block production](https://everstake.one/blog/some-solana-validators-may-be-optimized-to-skip-blocks-for-higher-rewards-but-v117-seems-to-have-fix). If you've been on Solana for some time, you'll remember a few network outages. These have also contributed to the block vs slot numbers disparity. As the network halted and no blocks were produced, the slots kept incrementing. As an example, let's take the very first outage of September 14, 2021—it lasted for 17 hours and was restarted around block `96542804`. So if we take a few blocks before that, we'll see that there are empty slots with not blocks produced: * Solana Explorer: [96542000](https://explorer.solana.com/block/96542000) * Solscan: [96542000](https://solscan.io/block/96542000) * SolanaFM: [96542000](https://solana.fm/block/96542000?cluster=mainnet-alpha) You can also run a [getBlock | Solana](/reference/solana-getblocks) call and provide the slot number and you'll see that it's missing. Note that you need a Chainstack Solana archive node to be able to make the call, since it's a slot far in the past: ```shell Shell theme={"system"} % curl --request POST \ --url CHAINSTACK_SOLANA_ARCHIVE_NODE \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "getBlock", "params": [ 96542000, { "encoding": "jsonParsed", "maxSupportedTransactionVersion": 1 } ] } ' {"jsonrpc":"2.0","error":{"code":-32009,"message":"Slot 96542000 was skipped, or missing in long-term storage"},"id":1} ``` ## Example: Slot height vs. block height As of January 1, 2025, here’s an example of Solana’s reported values: * **Slot height:** `311118891` * **Block height:** `289445888` Notice how the block height is \~22 million behind the slot height. This is normal on Solana due to the presence of empty (unproduced) slots. ## Where this matters: `getLatestBlockhash` method The [`getLatestBlockhash`](/reference/solana-getlatestblockhash) RPC method returns three key pieces of data: * **`slot`**: The slot number at which the block hash was produced. * **`blockhash`**: The latest block hash to use for a new transaction. * **`lastValidBlockHeight`**: The block height until which the transaction is considered valid before expiring (150 blocks from the “current” block height). If you call [getLatestBlockhash | Solana](/reference/solana-getlatestblockhash), you’ll see something like: ```shell Shell theme={"system"} curl --request POST \ --url https://solana-mainnet.core.chainstack.com/2ef4e47bc937d6b758f7c582e8e82a1e/ \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "getLatestBlockhash" } ' ``` ```Json JSON theme={"system"} { "jsonrpc": "2.0", "result": { "context": { "apiVersion": "2.0.18", "slot": 311115149 }, "value": { "blockhash": "AAU3aKZUQbddu7Q6sP8sggmQme7B9H1FAZvjVvkz5MZq", "lastValidBlockHeight": 289442306 } }, "id": 1 } ``` Here: * The `"slot": 311115149` matches the slot height. * The `"lastValidBlockHeight": 289442306` is about 22 million behind the slot number. The gap underscores how many slots have gone by without a block being produced in them. ## How Solana decides transaction validity Solana nodes consider **150 blocks** to determine if a transaction is still valid. Specifically, a transaction lives in the pending pool until the chain surpasses `lastValidBlockHeight`. After 150 blocks have passed from the latest block height at the time of your transaction creation, your transaction is dropped if it wasn’t included. Importantly: * **Blocks**, not slots, are used in these calculations. * If some slots are empty, they do not count toward that total of 150—because no block was produced there! Hence, when you see `lastValidBlockHeight`, it’s really telling you: “You have until block N + 150 to have your transaction included on-chain.” ### Quick illustration with `getBlock` Let’s do a [`getBlock`](/reference/solana-getblocks) call using the **slot** number from our `getLatestBlockhash` example above: ```shell Shell theme={"system"} curl --request POST \ --url https:// \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "getBlock", "params": [ 311115149, { "encoding": "jsonParsed", "maxSupportedTransactionVersion": 1 } ] } ' | jq '.' ``` ```Json JSON theme={"system"} { "jsonrpc": "2.0", "result": { "blockHeight": 289442156, "blockTime": 1735703553, "blockhash": "AAU3aKZUQbddu7Q6sP8sggmQme7B9H1FAZvjVvkz5MZq", ... }, "id": 1 } ``` Notice the result: * The slot passed in as a parameter is `311115149`. * The block height returned is `289442156`. Now, if you compare `289442156` with our `lastValidBlockHeight` (`289442306`), you’ll see there’s exactly a difference of 150 blocks—just as expected for the transaction validity window. ## Getting the latest slot vs. the latest block * [getSlot | Solana](/reference/solana-getslot) gives you the latest **slot height**. * [getBlockHeight | Solana](/reference/solana-getblockheight)gives you the latest **block height**. * [getBlockTime | Solana](/reference/solana-getblocktime) gives you the latest **block time** but you need to use the slot as a parameter in the call. If you are not using an archive node, make sure you provide the slot that is about within the last 24 hours. Try them out yourself: ```shell Shell theme={"system"} curl --request POST \ --url https://solana-mainnet.core.chainstack.com/2ef4e47bc937d6b758f7c582e8e82a1e/ \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "getSlot" } ' ``` ```shell Shell theme={"system"} curl --request POST \ --url https://solana-mainnet.core.chainstack.com/2ef4e47bc937d6b758f7c582e8e82a1e/ \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "getBlockHeight" } ' ``` ```shell Shell theme={"system"} curl --request POST \ --url https://solana-mainnet.core.chainstack.com/2ef4e47bc937d6b758f7c582e8e82a1e/ \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "getBlockTime", "params": [ 166974442 ] } ' ``` ## Common pitfalls 1. **Mistaking a slot for a block** Many Solana explorers incorrectly label URLs or pages with the word “block” while they’re actually referencing a **slot**. For instance: * Solscan’s URL might say `block/311115149`, but that number is a slot. * The official Solana explorer also says `block/311115149`, but again, it’s a slot, not a block. 2. **Transaction expiration** If you assume 150 **slots** for expiration instead of 150 **blocks**, you’ll miscalculate how long your transaction can remain in the mempool. In reality, empty slots do not affect transaction expiry. Only blocks do. 3. **RPC naming confusion** The [`getBlock`](/reference/solana-getblocks) method uses a slot number as its parameter. But the response includes the **block** height (`blockHeight`). Don’t let the naming fool you—this is a “fetch block by slot” call. ## Why slot and block numbers are always different * **Slots**: Every slot increments by one whether or not a block is produced. * **Blocks**: Only increment when a block is actually produced. Over time, the gap between slot height and block height keeps growing because some validators will miss (skip) their assigned slot, causing no block to form. That means your transaction expiry (150 blocks) counts only those slots where a block was produced. ## Conclusion Solana’s design means there will always be a discrepancy between slot height and block height. Slots are simply a schedule, while blocks represent actual produced data. Once you grasp this distinction—and remember that transaction validity is measured in blocks, not slots—you’ll avoid confusion and make the most of Solana’s RPC methods. > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Unichain: Collecting Uniswap v4 ETH-USDC trades Source: https://docs.chainstack.com/docs/unichain-collecting-uniswapv4-eth-usdc-trades Collect and decode Uniswap v4 ETH-USDC swap events on Unichain mainnet using Python event log parsing and a Chainstack Unichain RPC node endpoint. **TLDR:** * Demonstrates how to collect Uniswap v4 ETH-USDC trading data on Unichain using a multi-threaded Python approach with Chainstack nodes. * Monitors the Uniswap v4 Pool Manager contract for swap events and filters trades originating from the Universal Router. * Includes production-ready features like persistence with resume capability, error handling with exponential backoff, and parallel block processing. * Shows how to parse Uniswap v4 swap events from the singleton architecture and save comprehensive trading data to CSV files for analytics or LLM training. ## Overview There are different use cases for why you may want to collect the Uniswap v4 ETH-USDC trades on the Unichain mainnet, and data analytics and synthetic data generation for LLM fine-tuning is one of them. Uniswap v4, however, with its singleton architecture, despite the name, can be tricky. This tutorial will guide you through setting up a multi-threaded persistent Python script to quickly and without failure collect the precious trading data off Uniswap v4. We are going to do this Python. ## Prerequisites * [web3.py](https://web3py.readthedocs.io/) * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Unichain RPC endpoint](https://chainstack.com/build-better-with-unichain/) ## Implementation We will be collecting Uniswap v4 ETH-USDC swap events by monitoring the Uniswap v4 Pool Manager contract on Unichain. The script uses a multi-threaded approach to efficiently collect trading data while handling network issues gracefully. Here's how the implementation works: * Event monitoring — We monitor the `Swap` event from the Uniswap v4 Pool Manager contract. * Router filtering — We only collect trades that originate from the Universal Router. * Multi-threading — The script uses parallel processing to handle multiple block ranges simultaneously. * Persistence — Data is saved to CSV files with resume capability if the script is interrupted. * Error Handling — Comprehensive retry mechanisms handle network issues and connection failures. Key parameters in our example: * CHAINSTACK\_NODE\_ENDPOINT — Your Chainstack Unichain Mainnet node endpoint. * UNISWAP\_V4\_POOL\_MANAGER — The Uniswap v4 Pool Manager contract address. As taken from the [Unichain docs](https://docs.unichain.org/docs/technical-information/contract-addresses) — [0x1f98400000000000000000000000000000000004](https://uniscan.xyz/address/0x1f98400000000000000000000000000000000004). * UNISWAP\_V4\_UNIVERSAL\_ROUTER — The Universal Router contract address. As taken from the [Unichain docs](https://docs.unichain.org/docs/technical-information/contract-addresses) — [0xef740bf23acae26f6492b10de645d6b98dc8eaf3](https://uniscan.xyz/address/0xef740bf23acae26f6492b10de645d6b98dc8eaf3). * UNISWAP\_V4\_ETH\_USDC\_POOL — The ETH-USDC pool ID — [0x3258f413c7a88cda2fa8709a589d221a80f6574f63df5a5b6774485d8acc39d9](https://app.uniswap.org/explore/pools/unichain/0x3258f413c7a88cda2fa8709a589d221a80f6574f63df5a5b6774485d8acc39d9). * BATCH\_SIZE — The number of blocks to process in each batch. * START\_BLOCK — The block number to start collecting from. * UNICHAIN\_BLOCK\_TIME — The time it takes for a block to be produced on Unichain The script: ```python theme={"system"} import os from datetime import datetime import time import pandas as pd from web3 import Web3 from web3.exceptions import TransactionNotFound, BlockNotFound from requests.exceptions import ConnectionError from concurrent.futures import ThreadPoolExecutor, as_completed # Configuration constants UNICHAIN_RPC_URL = "CHAINSTACK_NODE_ENDPOINT" UNISWAP_V4_POOL_MANAGER = "0x1f98400000000000000000000000000000000004" UNISWAP_V4_UNIVERSAL_ROUTER = "0xef740bf23acae26f6492b10de645d6b98dc8eaf3" UNISWAP_V4_ETH_USDC_POOL = "0x3258f413c7a88cda2fa8709a589d221a80f6574f63df5a5b6774485d8acc39d9" BATCH_SIZE = 100 START_BLOCK = 17910700 UNICHAIN_BLOCK_TIME = 1 # seconds per block on Unichain # Retry configuration MAX_RETRIES = 5 BASE_RETRY_DELAY = 5 # Base delay between retries (will be multiplied by retry attempt number) MAX_RETRY_DELAY = 60 # Maximum delay between retries class UniswapV4DataCollector: def __init__(self): self.setup_web3_connection() # Convert address to checksum format self.pool_manager_address = Web3.to_checksum_address(UNISWAP_V4_POOL_MANAGER) self.universal_router_address = Web3.to_checksum_address(UNISWAP_V4_UNIVERSAL_ROUTER) self.target_pool_id = UNISWAP_V4_ETH_USDC_POOL.replace('0x', '') # Uniswap v4 PoolManager ABI for Swap event self.pool_manager_abi = [ { "anonymous": False, "inputs": [ {"indexed": True, "internalType": "PoolId", "name": "id", "type": "bytes32"}, {"indexed": True, "internalType": "address", "name": "sender", "type": "address"}, {"indexed": False, "internalType": "int128", "name": "amount0", "type": "int128"}, {"indexed": False, "internalType": "int128", "name": "amount1", "type": "int128"}, {"indexed": False, "internalType": "uint160", "name": "sqrtPriceX96", "type": "uint160"}, {"indexed": False, "internalType": "uint128", "name": "liquidity", "type": "uint128"}, {"indexed": False, "internalType": "int24", "name": "tick", "type": "int24"}, {"indexed": False, "internalType": "uint24", "name": "fee", "type": "uint24"} ], "name": "Swap", "type": "event" } ] self.setup_contract() def setup_web3_connection(self): """Initialize Web3 connection with retry mechanism.""" retry_count = 0 while True: try: self.w3 = Web3(Web3.HTTPProvider(UNICHAIN_RPC_URL)) # Test connection self.w3.eth.block_number print(f"Successfully connected to Unichain network using {UNICHAIN_RPC_URL}") break except Exception as e: retry_count += 1 if retry_count > MAX_RETRIES: raise Exception(f"Failed to connect to Unichain network RPC after {MAX_RETRIES} attempts") delay = min(BASE_RETRY_DELAY * retry_count, MAX_RETRY_DELAY) print(f"Connection to {UNICHAIN_RPC_URL} failed: {str(e)}") print(f"Retrying in {delay} seconds... (Attempt {retry_count}/{MAX_RETRIES})") time.sleep(delay) def setup_contract(self): """Initialize contract with retry mechanism.""" retry_count = 0 while True: try: self.contract = self.w3.eth.contract( address=self.pool_manager_address, abi=self.pool_manager_abi ) break except Exception as e: retry_count += 1 if retry_count > MAX_RETRIES: raise Exception(f"Failed to setup contract after {MAX_RETRIES} attempts") delay = min(BASE_RETRY_DELAY * retry_count, MAX_RETRY_DELAY) print(f"Contract setup failed. Retrying in {delay} seconds... (Attempt {retry_count}/{MAX_RETRIES})") time.sleep(delay) def get_last_processed_block(self, output_file: str) -> int: """Get the last successfully processed block from the CSV file.""" try: if os.path.exists(output_file) and os.path.getsize(output_file) > 0: df = pd.read_csv(output_file) if not df.empty: return df['block_number'].max() except Exception as e: print(f"Error reading last processed block: {e}") return None def collect_swap_events( self, start_block: int, end_block: int, batch_size: int = None ) -> pd.DataFrame: """Collect swap events with retry mechanism.""" all_events = [] current_block = start_block if batch_size is None: batch_size = BATCH_SIZE event_signature = "0x" + Web3.keccak( text="Swap(bytes32,address,int128,int128,uint160,uint128,int24,uint24)" ).hex() pool_id_bytes = bytes.fromhex(self.target_pool_id) pool_id_topic = '0x' + pool_id_bytes.hex().zfill(64) while current_block < end_block: retry_count = 0 success = False while not success and retry_count < MAX_RETRIES: try: batch_end = min(current_block + batch_size, end_block) event_filter = { 'address': self.pool_manager_address, 'fromBlock': current_block, 'toBlock': batch_end, 'topics': [event_signature, pool_id_topic] } events = self.w3.eth.get_logs(event_filter) event_count = 0 for event in events: try: processed_event = self.contract.events.Swap().process_log(event) # Skip if not from the Universal Router if processed_event.args.sender.lower() != self.universal_router_address.lower(): continue # Get transaction details tx = self.w3.eth.get_transaction(processed_event.transactionHash.hex()) event_data = { 'block_number': processed_event.blockNumber, 'transaction_hash': processed_event.transactionHash.hex(), 'pool_id': processed_event.args.id.hex(), 'router_address': processed_event.args.sender, 'original_sender': tx['from'], 'amount0': str(processed_event.args.amount0), 'amount1': str(processed_event.args.amount1), 'sqrt_price_x96': str(processed_event.args.sqrtPriceX96), 'liquidity': str(processed_event.args.liquidity), 'tick': processed_event.args.tick, 'fee': processed_event.args.fee, 'timestamp': self.w3.eth.get_block(processed_event.blockNumber)['timestamp'] } all_events.append(event_data) event_count += 1 except Exception as e: print(f"Error processing event: {e}") continue print(f"Processed blocks {current_block} to {batch_end}, found {event_count} events") success = True current_block = batch_end + 1 except (ConnectionError, TransactionNotFound, BlockNotFound) as e: retry_count += 1 if retry_count >= MAX_RETRIES: print(f"Failed to process blocks {current_block} to {batch_end} after {MAX_RETRIES} attempts") # Save the current progress if all_events: return pd.DataFrame(all_events) return pd.DataFrame() delay = min(BASE_RETRY_DELAY * retry_count, MAX_RETRY_DELAY) print(f"Error: {e}. Retrying in {delay} seconds... (Attempt {retry_count}/{MAX_RETRIES})") time.sleep(delay) # Reconnect to the network self.setup_web3_connection() self.setup_contract() return pd.DataFrame(all_events) if all_events else pd.DataFrame() def collect_uniswap_v4_data( self, start_block: int = None, ) -> pd.DataFrame: """Main data collection pipeline with resume capability.""" if start_block is None: start_block = START_BLOCK batch_size = BATCH_SIZE # Create output file timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') os.makedirs('data/raw', exist_ok=True) output_file = f'data/raw/swap_events_{timestamp}.csv' print(f"\n🔧 Configuration:") print(f" Start block: {start_block}") print(f" Batch size: {batch_size}") print(f" Output file: {output_file}") print(f" Pool ID: {UNISWAP_V4_ETH_USDC_POOL}") # Check for last processed block last_block = self.get_last_processed_block(output_file) if last_block is not None: print(f"Resuming collection from block {last_block + 1}") start_block = last_block + 1 return self._collect_and_save(start_block, batch_size, output_file) def _collect_and_save(self, current_block: int, batch_size: int, output_file: str) -> pd.DataFrame: """Continuously collect data with error handling and resume capability.""" blocks_per_chunk = int(600 / UNICHAIN_BLOCK_TIME) # 600 seconds = 10 minutes max_workers = 8 # Number of parallel threads max_workers_cap = 16 # Maximum allowed workers to prevent system overload try: while True: try: latest_block = self.w3.eth.block_number if current_block >= latest_block: print("Waiting for new blocks...") time.sleep(30) continue # Calculate how many chunks to process in parallel total_blocks_to_process = min(blocks_per_chunk * max_workers, latest_block - current_block) blocks_per_parallel_chunk = total_blocks_to_process // max_workers if blocks_per_parallel_chunk < batch_size: blocks_per_parallel_chunk = min(batch_size, latest_block - current_block) max_workers = min(max_workers_cap, max(1, (latest_block - current_block) // blocks_per_parallel_chunk)) # Ensure max_workers doesn't exceed the cap max_workers = min(max_workers, max_workers_cap) print(f"\nProcessing {total_blocks_to_process} blocks using {max_workers} workers") print(f"Each worker will process chunks of {blocks_per_parallel_chunk} blocks in batches of {batch_size}") # Create block ranges for parallel processing block_ranges = [] for i in range(max_workers): start = current_block + (i * blocks_per_parallel_chunk) end = min(start + blocks_per_parallel_chunk, latest_block) if start < end: block_ranges.append((start, end)) if not block_ranges: print("No valid block ranges to process") time.sleep(30) continue # Process block ranges in parallel all_swap_dfs = [] total_events_found = 0 with ThreadPoolExecutor(max_workers=max_workers) as executor: future_to_range = { executor.submit(self.collect_swap_events, start, end, batch_size): (start, end) for start, end in block_ranges } for future in as_completed(future_to_range): block_range = future_to_range[future] try: swap_df = future.result() if len(swap_df) > 0: all_swap_dfs.append(swap_df) total_events_found += len(swap_df) print(f"Completed blocks {block_range[0]} to {block_range[1]}, found {len(swap_df)} events") else: print(f"Completed blocks {block_range[0]} to {block_range[1]}, found 0 events") except Exception as e: print(f"Error processing blocks {block_range[0]} to {block_range[1]}: {e}") print(f"\n📊 Summary: {len(all_swap_dfs)} DataFrames collected with {total_events_found} total events") # Combine results and save if all_swap_dfs: combined_df = pd.concat(all_swap_dfs, ignore_index=True) file_exists = os.path.isfile(output_file) combined_df.to_csv(output_file, mode='a', header=not file_exists, index=False, float_format='%.0f') print(f"\n✅ SAVED TO FILE: {output_file}") print(f"Events collected in this round: {len(combined_df)}") # Count total events if needed (optional, can be removed for performance) # if file_exists: # try: # total_events = len(pd.read_csv(output_file)) # print(f"Total events collected so far: {total_events}") # except Exception as e: # print(f"Could not count total events: {e}") else: print(f"\n⚠️ No events found in this chunk, nothing to save") # Update current block for next iteration current_block = max(end for _, end in block_ranges) + 1 except (ConnectionError, TransactionNotFound, BlockNotFound) as e: print(f"Network error: {e}") print("Attempting to reconnect...") time.sleep(BASE_RETRY_DELAY) self.setup_web3_connection() self.setup_contract() continue except Exception as e: print(f"Unexpected error: {e}") print("Continuing with next chunk...") # Move forward by one chunk to avoid getting stuck current_block = current_block + blocks_per_chunk continue except KeyboardInterrupt: print("\nData collection stopped by user.") if os.path.isfile(output_file): final_df = pd.read_csv(output_file) print(f"\nFinal Collection Summary:") print(f"Total events collected: {len(final_df)}") return final_df return pd.DataFrame() if __name__ == '__main__': while True: try: collector = UniswapV4DataCollector() collector.collect_uniswap_v4_data( start_block=START_BLOCK ) except Exception as e: print(f"Fatal error: {e}") print("Restarting data collection in 60 seconds...") time.sleep(60) continue ``` ## Conclusion This tutorial demonstrated how to build a robust Uniswap v4 data collection system for ETH-USDC trades on Unichain using Python and Chainstack's Unichain nodes. This event-based monitoring approach provides reliable and fast trade data collection from the Uniswap v4 singleton architecture. This pattern can be easily adapted for other Uniswap v4 pools by simply changing the pool ID parameter, or extended to monitor multiple pools simultaneously. The collected data is perfect for analytics, research, or synthetic data generation for LLM fine-tuning purposes. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Unichain tooling Source: https://docs.chainstack.com/docs/unichain-tooling Unichain development tools guide. Use MetaMask, web3.py, ethers.js, Hardhat, Remix IDE, and Foundry with Chainstack Unichain nodes and endpoints. Get started with a [reliable Unichain RPC endpoint](https://chainstack.com/build-better-with-unichain/) to use the tools below. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Unichain nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Unichain nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.JsonRpcProvider(urlInfo.url, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * NETWORK\_ID — Unichain network ID: * Mainnet: `130` * Sepolia Testnet: `1301` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Unichain nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # Unlimited Node Source: https://docs.chainstack.com/docs/unlimited-node Convert any Chainstack RPC node into a flat-fee Unlimited Node with predictable monthly costs, capacity-based billing, and no per-request overage charges. ## Overview The **Unlimited Node add‑on** lets you turn **any** existing RPC node into a flat‑fee, capacity‑capped powerhouse. Instead of paying per request, you pick an RPS (requests per second) tier, activate the add‑on, and enjoy predictable monthly costs. ### Available RPS tiers * 25 RPS * 100 RPS * 250 RPS * 500 RPS * 1000 RPS > [!NOTE] > > ### Add‑on vs. extra usage > > With Unlimited Node, you **do not need the extra usage** option for the attached node. > > The node **consumes zero plan quota** and never incurs overage. > > If you exceed the tier limit, you will simply hit a message that the rate exceeds the limit. ## Benefits * **Fixed monthly bill** — no surprises, no overage math. * **Instant node upgrade to Unlimited** — flip the switch on any existing node. * **Plan quota preserved** — keep your remaining requests for other nodes or services. * **Growth‑first** — available from the **Growth** plan upward. ## Enable Unlimited Node 1. In **Nodes**, open the node you want to upgrade. 2. Click **Add-ons** > **Unlimited Node**. 3. Pick an RPS tier and click **Apply changes**. 4. Confirm payment. The add‑on starts within seconds. # Unlimited Node add-on Source: https://docs.chainstack.com/docs/unlimited-node-add-on Enable the Unlimited Node add-on to convert any existing Chainstack RPC node into a flat-fee endpoint with predictable monthly costs and capped capacity. See [Unlimited Node](/docs/unlimited-node) for full description. # Using Chainstack as a Hummingbot Gateway RPC provider Source: https://docs.chainstack.com/docs/using-chainstack-with-hummingbot-gateway Configure Chainstack as the RPC provider for Hummingbot Gateway. One Platform API key auto-discovers your deployed Solana and EVM nodes, with no manual endpoint URLs. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). **TLDR:** * Hummingbot Gateway v2.15 adds Chainstack as a built-in RPC provider for both Solana and EVM networks. * You set `rpcProvider: chainstack` and add one Platform API key. Gateway calls the Chainstack Platform API and routes to a node you already deployed, so there are no per-chain endpoint URLs to paste or rotate. * It is the only Gateway provider that spans Solana and all nine supported EVM chains from a single key. * This guide covers deploying nodes, configuring the provider, and verifying live swap quotes on Solana and Ethereum. ## What this integration does [Hummingbot](https://hummingbot.org/) is an open-source framework for building market-making and arbitrage bots. [Gateway](https://hummingbot.org/gateway/) is its standalone API server that connects those bots to DEXs such as Jupiter, Meteora, Raydium, Orca, and Uniswap. Every quote, swap, and liquidity action Gateway performs runs through an RPC node. By default Gateway points at public RPC endpoints, which rate-limit and drop requests under trading load. As of v2.15 you can route Gateway through your Chainstack nodes instead, for lower latency and more reliable execution. > [!NOTE] > > **What makes Chainstack different here** > > Other providers want a manually configured endpoint per network. Chainstack uses the [Chainstack Platform API](/reference/platform-api-getting-started) to discover and configure your deployed nodes automatically. One key covers both Solana and EVM, with no endpoint URLs to manage. ## Prerequisites Before starting, ensure you have: * A [Chainstack account](https://console.chainstack.com/) with at least one deployed node for the chain you trade on. * A [Chainstack Platform API key](/reference/platform-api-getting-started#create-api-key). * Hummingbot with Gateway v2.15 or later. The key here is the **Platform API key**, not a node endpoint authorization key. The Platform API key is what lets Gateway discover your nodes automatically. ## Supported networks Chainstack discovery in Gateway covers: * EVM — Ethereum, Arbitrum, Polygon, Optimism, Base, Avalanche, BNB Smart Chain, Celo, and Ethereum Sepolia. * Solana — mainnet-beta and devnet. ## Configure Gateway **1. Deploy your nodes** Deploy a node for each network your bot trades on. For production market making and arbitrage, use [Trader Nodes](/docs/trader-node) — dedicated resources keep latency low and avoid the rate limits of shared endpoints. For example, deploy a Solana mainnet node and an Ethereum mainnet node in the same project. **2. Create a Platform API key** On your [Chainstack account](https://console.chainstack.com/), navigate to **Settings** > **API keys** > **Create key**. Give it a descriptive name, such as `Hummingbot Gateway`, and copy the generated key. See [Chainstack Platform API: getting started](/reference/platform-api-getting-started#create-api-key) for details. **3. Add the key to apiKeys.yml** In your Gateway `conf/apiKeys.yml`, set the `chainstack` field to your Platform API key: ```yaml conf/apiKeys.yml theme={"system"} chainstack: 'YOUR_CHAINSTACK_PLATFORM_API_KEY' ``` **4. Set the RPC provider per chain** In the chain configuration files, set `rpcProvider` to `chainstack`. There are no endpoint URLs to add — Gateway resolves them from your account. ```yaml conf/chains/solana.yml theme={"system"} defaultNetwork: mainnet-beta rpcProvider: chainstack ``` ```yaml conf/chains/ethereum.yml theme={"system"} defaultNetwork: mainnet rpcProvider: chainstack ``` **5. Start Gateway and verify** Start Gateway as you normally would (see the [Hummingbot Gateway docs](https://hummingbot.org/gateway/)). On startup, each configured chain discovers its node: ```text theme={"system"} Chainstack API key configured Chainstack service initialized: node ND-123-456-789 (solana/solana-mainnet) for solana/mainnet-beta Solana (defaultNetwork: mainnet-beta): Block #427,223,441 Chainstack service initialized: node ND-987-654-321 (ethereum/ethereum-mainnet) for ethereum/mainnet Ethereum (defaultNetwork: mainnet): Block #25,342,330 ``` Confirm a chain is live with a status call: ```bash theme={"system"} curl "http://localhost:15888/chains/solana/status?network=mainnet-beta" ``` Then pull a read-only swap quote through a connector to confirm the full path works. The quote is routed through your Chainstack node: ```bash Solana (Jupiter) theme={"system"} curl "http://localhost:15888/connectors/jupiter/router/quote-swap?network=mainnet-beta&baseToken=SOL"eToken=USDC&amount=1&side=SELL" ``` ```bash Ethereum (Uniswap) theme={"system"} curl "http://localhost:15888/connectors/uniswap/router/quote-swap?network=mainnet&baseToken=WETH"eToken=USDC&amount=1&side=SELL&walletAddress=YOUR_WALLET_ADDRESS" ``` > [!TIP] > > A successful quote returns the output amount, route, and price impact — confirming Gateway is reading chain state through your Chainstack node. ## How auto-discovery works When Gateway initializes a chain with `rpcProvider: chainstack`, it calls the Chainstack Platform API, finds the running node that matches the chain and network, and uses that node's endpoint for all RPC. You never paste a node URL. > [!TIP] > > Gateway selects the first running node that matches the chain and network. For predictable selection, keep one node per network in the project tied to your API key. ## Use a paid node for production trading Free Developer-plan nodes are rate-limited and restrict some [Solana methods](/docs/solana-methods). In particular, [`getProgramAccounts`](/reference/solana-getprogramaccounts) and [`getTokenAccountsByOwner`](/reference/solana-gettokenaccountsbyowner) — used for SPL token balances — require a paid plan, so token-balance and some swap operations fail on the free tier. For live trading, run a paid [Trader Node](/docs/trader-node). It serves these methods and gives you dedicated throughput. See [pricing](https://chainstack.com/pricing/) for plan limits. ## Troubleshooting ### No running Chainstack node found Gateway could not match a running node to the chain and network. Check that the node exists in the account tied to your Platform API key, that it is running, and that its network matches the Gateway `defaultNetwork` (for example, a `solana-mainnet` node for `mainnet-beta`). ### Chainstack API key is missing or invalid Confirm the `chainstack` field in `conf/apiKeys.yml` holds a **Platform API key** from **Settings** > **API keys**, not a node endpoint authorization key. ### Method requires plan upgrade The node is on the free Developer plan and the connector called a paid-only method, such as `getProgramAccounts` on Solana. Move to a paid [Trader Node](/docs/trader-node). # Using eRPC with Chainstack: Quickstart Source: https://docs.chainstack.com/docs/using-erpc-with-chainstack-quickstart Set up eRPC fault-tolerant proxy with Chainstack auto-discovery. Configure multi-chain RPC endpoints, caching, and failover using Platform API integration. **TLDR:** * Set up eRPC as a fault-tolerant proxy that automatically discovers and configures all your Chainstack nodes using the Platform API. * Unlike other providers, Chainstack integration requires only a Platform API key to automatically fetch and configure all deployed nodes across multiple chains. * Benefit from automatic node discovery, multi-chain support, smart filtering by project/region/provider, and dynamic configuration without manual endpoint management. * Deploy using Docker with simple YAML configuration and test with standard JSON-RPC calls for production-ready blockchain infrastructure. ## Main article eRPC is a fault-tolerant EVM RPC proxy with permanent caching and reorg awareness. One of its most powerful features is the built-in support for multiple blockchain infrastructure providers, including Chainstack. > [!NOTE] > > **What makes Chainstack unique in eRPC?** > > Unlike other providers that require you to manually configure each blockchain network endpoint, Chainstack's integration with eRPC uses the [Chainstack Platform API](/reference/platform-api-getting-started) to automatically discover and configure all your deployed nodes. This means: * Automatic node discovery — eRPC fetches all your Chainstack nodes via API. * Multi-chain support — Automatically supports all chains where you have nodes. * Smart filtering — Filter nodes by project, organization, region, provider, and type. * Dynamic configuration — No need to manually update configs when you deploy new nodes. This tutorial will guide you through setting up eRPC as a fault-tolerant proxy for your Chainstack RPC endpoints, providing enhanced reliability, caching, and failover capabilities. ## Prerequisites Before starting, ensure you have: * A [Chainstack account](https://chainstack.com/) with deployed nodes. * A [Chainstack Platform API key](/reference/platform-api-getting-started#create-api-key). * Docker installed. And that's about it. > [!TIP] > > You don't need to clone the eRPC repository. This tutorial uses the pre-built Docker image and creates configuration files from scratch. ## Step 1: Get your Chainstack Platform API key The key difference with Chainstack is that you need a **Platform API key**, not a regular node endpoint API key. ### Creating a Platform API key 1. Log into your [Chainstack console](https://console.chainstack.com). 2. Navigate to **Settings** > **API keys**. 3. Click **Create key**. 4. Give it a descriptive name (e.g., "eRPC Integration"). 5. Copy the generated API key. > [!NOTE] > > This is different from the authorization keys you get with individual node endpoints. The Platform API key lets eRPC discover and manage all your nodes automatically. ## Step 2: Deploy nodes Deploy [Global Nodes](/docs/global-elastic-node) for EVM networks. For this tutorial, let's deploy nodes on a few different networks: Ethereum Mainnet, Polygon Mainnet, Arbitrum Mainnet. ## Step 3: Configure Chainstack integration If you want a quick start, you can just stick with the basic configuration and explore other options later. ### Basic configuration Create or edit your `erpc.yaml` file: ```yaml theme={"system"} projects: - id: main networks: - architecture: evm evm: chainId: 1 - architecture: evm evm: chainId: 137 upstreams: # Chainstack auto-discovery — will find all nodes automatically - id: chainstack-auto endpoint: chainstack://YOUR_CHAINSTACK_PLATFORM_API_KEY rateLimitBudget: chainstack-developer failsafe: timeout: duration: 15s retry: maxAttempts: 2 delay: 1000ms backoffMaxDelay: 10s backoffFactor: 0.3 jitter: 500ms rateLimiters: budgets: - id: chainstack-developer rules: - method: '*' maxCount: 25 # The lowest limit of the free Developer plan https://chainstack.com/pricing/ period: 1s - method: 'eth_getLogs' maxCount: 10 # The lowest limit of the free Developer plan https://chainstack.com/pricing/ period: 1s ``` Replace `YOUR_CHAINSTACK_PLATFORM_API_KEY` with your actual Platform API key. ### Advanced configuration with filtering Chainstack's integration supports powerful filtering options: ```yaml theme={"system"} projects: - id: main networks: - architecture: evm evm: chainId: 1 - architecture: evm evm: chainId: 137 upstreams: # Chainstack auto-discovery — will find all nodes automatically - id: chainstack-auto endpoint: chainstack://YOUR_CHAINSTACK_PLATFORM_API_KEY?project=PROJECT-ID rateLimitBudget: chainstack-developer failsafe: timeout: duration: 15s retry: maxAttempts: 2 delay: 1000ms backoffMaxDelay: 10s backoffFactor: 0.3 jitter: 500ms rateLimiters: budgets: - id: chainstack-developer rules: - method: '*' maxCount: 25 # The lowest limit of the free Developer plan https://chainstack.com/pricing/ period: 1s - method: 'eth_getLogs' maxCount: 10 # The lowest limit of the free Developer plan https://chainstack.com/pricing/ period: 1s ``` Replace `YOUR_CHAINSTACK_PLATFORM_API_KEY` with your actual Platform API key and `PROJECT-ID` with the ID of your project—for example, `PR-123-456`. ### Step 4: Run eRPC Start eRPC using Docker: ```bash theme={"system"} docker run --rm -v $(pwd)/erpc.yaml:/erpc.yaml -p 4000:4000 -p 4001:4001 ghcr.io/erpc/erpc:main ``` ### Step 5: Test your setup Test the proxy with a simple request: ```bash theme={"system"} # Test Ethereum mainnet curl --location 'http://localhost:4000/main/evm/1' \ --header 'Content-Type: application/json' \ --data '{ "method": "eth_getBlockByNumber", "params": ["latest", false], "id": 1, "jsonrpc": "2.0" }' # Test Polygon mainnet curl --location 'http://localhost:4000/main/evm/137' \ --header 'Content-Type: application/json' \ --data '{ "method": "eth_blockNumber", "params": [], "id": 2, "jsonrpc": "2.0" }' ``` ## Advanced configuration For more advanced configurations and features, refer to the [eRPC documentation](https://docs.erpc.cloud/) and the [eRPC GitHub repository](https://github.com/erpc/erpc). Don't forget to give them a star :star: for this awesome solution. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # eth_getStorageAt vs debug_storageRangeAt on Reth Source: https://docs.chainstack.com/docs/using-eth_getstorageat-instead-of-debug_storagerangeat-on-reth Work around the unsupported debug_storageRangeAt method in Reth by using eth_getStorageAt with known storage slot positions for contract state access. **TLDR:** * Provides a Python-based workaround for `debug_storageRangeAt` RPC method, which is not supported in Reth nodes. * Uses standard `eth_getStorageAt` calls to sequentially query storage slots and replicate debug functionality. * Includes pagination support to handle large storage queries efficiently with configurable result limits. * Demonstrates practical comparison between native `debug_storageRangeAt` on Base testinprod vs. the workaround approach on Reth. In this article, we'll walk through a practical Python-based workaround script to replicate the essential functionality of `debug_storageRangeAt` using standard Ethereum RPC calls (`eth_getStorageAt`). This approach ensures you can still achieve your debugging goals without relying on methods unavailable in Reth. We'll use Base and Python as an example. We'll replicate specifically the following call on the Base mainnet: ```shell Shell theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "jsonrpc": "2.0", "method": "debug_storageRangeAt", "id": 1, "params": [ "0xc40b7058b5b80e565dfb986fe852c047733291291c8de1be8888ae64b5457bbd", 25, "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "0x00000000000000000000000000000000", 2 ] } ' ``` ## Workaround overview The workaround leverages the standard Ethereum RPC method `eth_getStorageAt` to mimic the storage exploration functionality: * Sequentially query storage slots. * Retrieve non-zero storage entries. * Implement pagination to handle large storage queries efficiently. ## Full script in Python ```python Python theme={"system"} #!/usr/bin/env python3 """ Workaround for debug_storageRangeAt RPC method on Reth nodes. This script provides similar functionality using standard eth_getStorageAt calls. """ # ============================================================================= # Configuration - Edit these values # ============================================================================= # Your reliable Ethereum RPC endpoint from Chainstack (required) # Get yours at: https://chainstack.com/build-better-with-ethereum/ RPC_URL = "CHAINSTACK_NODE_URL" # Block hash or number to query storage at BLOCK_HASH = "YOUR_TARGET_BLOCK_HASH" # Transaction index in the block TX_INDEX = INDEX_NUMBER # Contract address to inspect storage for CONTRACT_ADDRESS = "0x833589fcd6eYOUR_CONTRACT_ADDRESSdb6e08f4c7c32d4f71b54bda02913" # Starting storage key (32 bytes hex string) START_KEY = "0x00000000000000000000000000000000" # Maximum number of storage entries to return MAX_RESULTS = 10 # ============== # Implementation # ============== from web3 import Web3 from eth_utils import keccak, to_bytes, to_hex import json from typing import Optional, Dict, Any, Union from dataclasses import dataclass @dataclass class StorageRangeResult: """Container for storage range query results.""" storage: Dict[str, Dict[str, Optional[str]]] # Storage slot -> {key, value} mapping next_key: Optional[str] = None # Next key for pagination class StorageRangeWorkaround: """Implements debug_storageRangeAt functionality using eth_getStorageAt.""" def __init__(self, rpc_url: str): """Initialize with RPC URL for the Ethereum node.""" self.w3 = Web3(Web3.HTTPProvider(rpc_url)) if not self.w3.is_connected(): raise ConnectionError("Failed to connect to Ethereum node") def get_storage_range_at( self, block_hash: Union[str, bytes], tx_index: int, contract_address: str, start_key: str, max_results: int ) -> StorageRangeResult: """ Emulate debug_storageRangeAt functionality using eth_getStorageAt. Args: block_hash: Block hash or number tx_index: Transaction index in the block (ignored in this implementation) contract_address: Address of the contract to inspect start_key: Starting storage key (32 bytes hex string) max_results: Maximum number of storage entries to return Returns: StorageRangeResult containing storage entries and next key if any """ # Normalize parameters if isinstance(block_hash, str) and block_hash.startswith('0x'): block_hash = block_hash[2:] block_hash = bytes.fromhex(block_hash) if isinstance(block_hash, str) else block_hash contract_address = self.w3.to_checksum_address(contract_address) start_key = start_key if start_key.startswith('0x') else '0x' + start_key # Get block information try: block = self.w3.eth.get_block(block_hash) except Exception as e: raise Exception(f"Failed to get block: {e}") # Get storage entries storage_entries = {} current_key_int = int(start_key, 16) found_entries = 0 # Keep trying keys until we find max_results non-zero entries or hit a reasonable limit max_attempts = max_results * 10 # Try up to 10x max_results to find non-zero values attempts = 0 while found_entries < max_results and attempts < max_attempts: current_key_hex = hex(current_key_int) if not current_key_hex.startswith('0x'): current_key_hex = '0x' + current_key_hex[2:].zfill(64) try: value = self.w3.eth.get_storage_at( contract_address, current_key_hex, block['number'] ) value_hex = '0x' + value.hex() # Only include non-zero values if value_hex != '0x' + '00' * 32: storage_entries[current_key_hex] = value_hex found_entries += 1 except Exception as e: print(f"Warning: Failed to get storage at {current_key_hex}: {e}") current_key_int += 1 attempts += 1 # Determine next key for pagination next_key = None if found_entries >= max_results: next_key = hex(current_key_int) if not next_key.startswith('0x'): next_key = '0x' + next_key[2:].zfill(64) # Format result result = StorageRangeResult( storage={ k: {"key": None, "value": v} for k, v in storage_entries.items() }, next_key=next_key ) return result def compute_mapping_slot(mapping_slot: int, key: Union[str, int, bytes]) -> str: """ Compute the storage slot for a mapping entry. Args: mapping_slot: Slot number where the mapping is declared key: The key in the mapping (address, uint, etc.) Returns: The 32-byte hex string of the storage slot """ if isinstance(key, str) and key.startswith('0x'): key = bytes.fromhex(key[2:]) elif isinstance(key, int): key = key.to_bytes(32, 'big') elif not isinstance(key, bytes): raise ValueError("Key must be hex string, integer, or bytes") key = key.rjust(32, b'\x00') # pad to 32 bytes slot_bytes = mapping_slot.to_bytes(32, 'big') slot_hash = keccak(key + slot_bytes) return '0x' + slot_hash.hex() def main(): """Main entry point for the script.""" try: storage_range = StorageRangeWorkaround(RPC_URL) result = storage_range.get_storage_range_at( block_hash=BLOCK_HASH, tx_index=TX_INDEX, contract_address=CONTRACT_ADDRESS, start_key=START_KEY, max_results=MAX_RESULTS ) # Print results in a JSON-RPC style response response = { "jsonrpc": "2.0", "id": 1, "result": { "storage": result.storage, "nextKey": result.next_key } } print(json.dumps(response, indent=2)) except Exception as e: # Format error as JSON-RPC error response error_response = { "jsonrpc": "2.0", "id": 1, "error": { "code": -32000, "message": str(e) } } print(json.dumps(error_response, indent=2)) if __name__ == '__main__': main() ``` ## Configuration Adjust these parameters according to your context: ```python python theme={"system"} # Your Chainstack node URL (replace with your own) RPC_URL = "CHAINSTACK_NODE_URL" # Target block hash for querying storage BLOCK_HASH = "YOUR_TARGET_BLOCK_HASH" # Transaction index in the block TX_INDEX = INDEX_NUMBER # Contract address you want to inspect CONTRACT_ADDRESS = "YOUR_CONTRACT_ADDRESS" # Starting storage key START_KEY = "0x00000000000000000000000000000000" # Maximum results per run MAX_RESULTS = 10 ``` 3. **Interpret the Output:** You'll see a JSON-formatted output similar to what `debug_storageRangeAt` provides, including storage entries and the next key for pagination. Use this snippet to identify and query the exact slots needed. ## Pagination explained Due to the potentially massive size of contract storage, the script includes pagination. If more entries exist beyond the queried range, `nextKey` is returned. Use this as your new `START_KEY` in the subsequent query. ## Limitations & Best Practices * **Performance:** This workaround may be slower than native implementations due to multiple RPC calls. * **Accuracy:** It returns hashed storage keys without preimage (original key). You'll need additional logic or external knowledge to map these hashes to original keys. ## Full example comparison Let's do the same parameters call `debug_storageRangeAt` on Base testinprod and `eth_getStorageAt` on Reth. ### debug\_storageRangeAt on Base testinprod Call: ```bash Shell theme={"system"} curl --request POST \ --url https://base-mainnet.core.chainstack.com/2fc1de7f08c0465f6a28e3c355e0cb14/ \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "jsonrpc": "2.0", "method": "debug_storageRangeAt", "id": 1, "params": [ "0xc40b7058b5b80e565dfb986fe852c047733291291c8de1be8888ae64b5457bbd", 25, "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "0x00000000000000000000000000000000", 2 ] } ' ``` Output: ```Json JSON theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": { "storage": { "0x0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9": { "key": "0x000000000000000000000000000000000000000000000000000000000000000b", "value": "0x000000000000000000000000000000000000000000000000000224743a3e0b18" }, "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0": { "key": "0x0000000000000000000000000000000000000000000000000000000000000005", "value": "0x5553444300000000000000000000000000000000000000000000000000000008" }, "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": { "key": "0x0000000000000000000000000000000000000000000000000000000000000000", "value": "0x0000000000000000000000003abd6f64a422225e61e435bae41db12096106df7" }, "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace": { "key": "0x0000000000000000000000000000000000000000000000000000000000000002", "value": "0x0000000000000000000000004d15e70518a20fc8828b5c3853f32e35238d0b77" }, "0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": { "key": "0x0000000000000000000000000000000000000000000000000000000000000004", "value": "0x55534420436f696e000000000000000000000000000000000000000000000010" }, "0x8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac802": { "key": "0x000000000000000000000000000000000000000000000000000000000000000f", "value": "0x02fa7265e7c5d81118673727957699e4d68f74cd74b7db77da710fe8a2c7834f" }, "0xa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688": { "key": "0x0000000000000000000000000000000000000000000000000000000000000007", "value": "0x5553440000000000000000000000000000000000000000000000000000000006" }, "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": { "key": "0x0000000000000000000000000000000000000000000000000000000000000001", "value": "0x000000000000000000000000d3571b3bc51cecff49194ad67afffc648d5e07b4" }, "0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3": { "key": "0x0000000000000000000000000000000000000000000000000000000000000008", "value": "0x0000000000000000000000012230393edad0299b7e7b59f20aa856cd1bed52e1" }, "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f": { "key": "0x0000000000000000000000000000000000000000000000000000000000000006", "value": "0x0000000000000000000000000000000000000000000000000000000000000006" } }, "nextKey": "0x0000000000000000000000000000000000000000000000000000000000000012" } } ``` ### eth\_getStorageAt Call: (Note the Chainstack Node URL is a public one for this Developer portal, so it's rate-limited and suitable for one-off example calls only). ```python Python theme={"system"} #!/usr/bin/env python3 """ Workaround for debug_storageRangeAt RPC method on Reth nodes. This script provides similar functionality using standard eth_getStorageAt calls. """ # ============================================================================= # Configuration - Edit these values # ============================================================================= # Your Chainstack node URL (required) RPC_URL = "https://base-mainnet.core.chainstack.com/2fc1de7f08c0465f6a28e3c355e0cb14/" # Block hash or number to query storage at BLOCK_HASH = "0xc40b7058b5b80e565dfb986fe852c047733291291c8de1be8888ae64b5457bbd" # Transaction index in the block TX_INDEX = 25 # Contract address to inspect storage for CONTRACT_ADDRESS = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913" # Starting storage key (32 bytes hex string) START_KEY = "0x00000000000000000000000000000000" # Maximum number of storage entries to return MAX_RESULTS = 10 # ============================================================================= # Implementation - Do not modify below this line unless you know what you're doing # ============================================================================= from web3 import Web3 from eth_utils import keccak, to_bytes, to_hex import json from typing import Optional, Dict, Any, Union from dataclasses import dataclass @dataclass class StorageRangeResult: """Container for storage range query results.""" storage: Dict[str, Dict[str, Optional[str]]] # Storage slot -> {key, value} mapping next_key: Optional[str] = None # Next key for pagination class StorageRangeWorkaround: """Implements debug_storageRangeAt functionality using eth_getStorageAt.""" def __init__(self, rpc_url: str): """Initialize with RPC URL for the Ethereum node.""" self.w3 = Web3(Web3.HTTPProvider(rpc_url)) if not self.w3.is_connected(): raise ConnectionError("Failed to connect to Ethereum node") def get_storage_range_at( self, block_hash: Union[str, bytes], tx_index: int, contract_address: str, start_key: str, max_results: int ) -> StorageRangeResult: """ Emulate debug_storageRangeAt functionality using eth_getStorageAt. Args: block_hash: Block hash or number tx_index: Transaction index in the block (ignored in this implementation) contract_address: Address of the contract to inspect start_key: Starting storage key (32 bytes hex string) max_results: Maximum number of storage entries to return Returns: StorageRangeResult containing storage entries and next key if any """ # Normalize parameters if isinstance(block_hash, str) and block_hash.startswith('0x'): block_hash = block_hash[2:] block_hash = bytes.fromhex(block_hash) if isinstance(block_hash, str) else block_hash contract_address = self.w3.to_checksum_address(contract_address) start_key = start_key if start_key.startswith('0x') else '0x' + start_key # Get block information try: block = self.w3.eth.get_block(block_hash) except Exception as e: raise Exception(f"Failed to get block: {e}") # Get storage entries storage_entries = {} current_key_int = int(start_key, 16) found_entries = 0 # Keep trying keys until we find max_results non-zero entries or hit a reasonable limit max_attempts = max_results * 10 # Try up to 10x max_results to find non-zero values attempts = 0 while found_entries < max_results and attempts < max_attempts: current_key_hex = hex(current_key_int) if not current_key_hex.startswith('0x'): current_key_hex = '0x' + current_key_hex[2:].zfill(64) try: value = self.w3.eth.get_storage_at( contract_address, current_key_hex, block['number'] ) value_hex = '0x' + value.hex() # Only include non-zero values if value_hex != '0x' + '00' * 32: storage_entries[current_key_hex] = value_hex found_entries += 1 except Exception as e: print(f"Warning: Failed to get storage at {current_key_hex}: {e}") current_key_int += 1 attempts += 1 # Determine next key for pagination next_key = None if found_entries >= max_results: next_key = hex(current_key_int) if not next_key.startswith('0x'): next_key = '0x' + next_key[2:].zfill(64) # Format result result = StorageRangeResult( storage={ k: {"key": None, "value": v} for k, v in storage_entries.items() }, next_key=next_key ) return result def compute_mapping_slot(mapping_slot: int, key: Union[str, int, bytes]) -> str: """ Compute the storage slot for a mapping entry. Args: mapping_slot: Slot number where the mapping is declared key: The key in the mapping (address, uint, etc.) Returns: The 32-byte hex string of the storage slot """ if isinstance(key, str) and key.startswith('0x'): key = bytes.fromhex(key[2:]) elif isinstance(key, int): key = key.to_bytes(32, 'big') elif not isinstance(key, bytes): raise ValueError("Key must be hex string, integer, or bytes") key = key.rjust(32, b'\x00') # pad to 32 bytes slot_bytes = mapping_slot.to_bytes(32, 'big') slot_hash = keccak(key + slot_bytes) return '0x' + slot_hash.hex() def main(): """Main entry point for the script.""" try: storage_range = StorageRangeWorkaround(RPC_URL) result = storage_range.get_storage_range_at( block_hash=BLOCK_HASH, tx_index=TX_INDEX, contract_address=CONTRACT_ADDRESS, start_key=START_KEY, max_results=MAX_RESULTS ) # Print results in a JSON-RPC style response response = { "jsonrpc": "2.0", "id": 1, "result": { "storage": result.storage, "nextKey": result.next_key } } print(json.dumps(response, indent=2)) except Exception as e: # Format error as JSON-RPC error response error_response = { "jsonrpc": "2.0", "id": 1, "error": { "code": -32000, "message": str(e) } } print(json.dumps(error_response, indent=2)) if __name__ == '__main__': main() ``` Output: ```Json JSON theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": { "storage": { "0x0": { "key": null, "value": "0x0000000000000000000000003abd6f64a422225e61e435bae41db12096106df7" }, "0x1": { "key": null, "value": "0x000000000000000000000000d3571b3bc51cecff49194ad67afffc648d5e07b4" }, "0x2": { "key": null, "value": "0x0000000000000000000000004d15e70518a20fc8828b5c3853f32e35238d0b77" }, "0x4": { "key": null, "value": "0x55534420436f696e000000000000000000000000000000000000000000000010" }, "0x5": { "key": null, "value": "0x5553444300000000000000000000000000000000000000000000000000000008" }, "0x6": { "key": null, "value": "0x0000000000000000000000000000000000000000000000000000000000000006" }, "0x7": { "key": null, "value": "0x5553440000000000000000000000000000000000000000000000000000000006" }, "0x8": { "key": null, "value": "0x0000000000000000000000012230393edad0299b7e7b59f20aa856cd1bed52e1" }, "0xb": { "key": null, "value": "0x000000000000000000000000000000000000000000000000000224743a3e0b18" }, "0xf": { "key": null, "value": "0x02fa7265e7c5d81118673727957699e4d68f74cd74b7db77da710fe8a2c7834f" } }, "nextKey": "0x10" } } ``` **References & further reading:** > [card] [Ethereum JSON-RPC specification](https://ethereum.org/en/developers/docs/apis/json-rpc/) > [card] [Reth JSON-RPC docs](https://reth.rs/jsonrpc/intro.html) > [card] [Understanding Ethereum Storage Layout](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html) ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Warp transactions Source: https://docs.chainstack.com/docs/warp-transactions Send Warp transactions through Chainstack Trader Nodes for faster propagation via bloXroute's relay network on Ethereum and BNB Smart Chain networks. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). With the *Warp transactions* add-on installed on your node, your transactions are distributed either through the high-speed transaction relay network if it's an EVM network or directly to the leader if it's Solana. This makes your transaction available for the validators to pick up and include in the block much faster than with the regular propagation. > [!NOTE] > > The Warp transactions feature is available starting from the [paid plans](https://chainstack.com/pricing/). > > Warp transactions (not requests, just the transactions) consumed are billed separately. For details, see [Pricing](https://chainstack.com/pricing/) with the [extra usage](/docs/manage-your-billing#manage-the-extra-usage-setting) setting enabled. ## Why use with Chainstack With Chainstack Trader Nodes and Global Nodes, you get the convenience of both worlds — reliable nodes for everything up to sending the transaction & the high speed of bloXroute for the transaction itself. In other words, if you have a bot running, switching your setup to a Chainstack [Trader Node](/docs/trader-node) or Global Node with Warp transactions is as easy as switching the endpoint in your code. The way Chainstack Warp transactions work is all calls go through the Chainstack infrastructure, except for one — `eth_sendRawTransaction` for EVMs or `sendTransaction` for Solana — this call is always routed directly to bloXroute. Normally, you'd face two challenging scenarios: * Using bloXroute alone would require crafting your own transaction and using different node endpoints from providers, necessitating a custom setup. * Relying solely on nodes would mean creating your own low-latency setup. However, with Chainstack, everything is done for you. You can simply use one endpoint, streamlining the entire process. ## Sample scenario The use cases are numerous, but here's a sample one: * Liquidity is deployed on a chain * You get a signal on the deployed liquidity through third-party monitoring systems * On the signal, your bot acts on the liquidity (e.g. does a purchase transaction) and the transaction is immediately propagated through the Chainstack Trader Node or Global Node with Warp transactions enabled for any validator node to pick it up * Once your transaction is committed by a validator to a block, you win the race thanks to the Chainstack Warp transactions propagation For best results and reduced latency, deploy a Trader Node or Global Node with Warp transactions in the location nearest to your bot. ## Availability The Warp transactions feature is available starting from the [paid plans](https://chainstack.com/pricing/). Warp transactions (not requests, just the transactions) consumed are billed separately. For details, see [Pricing](https://chainstack.com/pricing/) with the [extra usage](/docs/manage-your-billing#manage-the-extra-usage-setting) setting enabled. ## Enable Warp transactions Warp transactions is a node [add-on](/docs/add-ons) you install per node on Trader and Global Nodes. **1. Open the Add-ons tab** On Chainstack, open your Trader or Global Node and select the **Add-ons** tab. **2. Install the add-on** Click **Install** next to **Warp transactions**, select the plan, and click **Apply changes**. Once installed, the node shows a **Warp** badge, and `eth_sendRawTransaction` (EVM) or `sendTransaction` (Solana) calls route through bloXroute. To remove it, click **Manage** > **Uninstall**, type `delete app` to confirm, and click **Yes, I'm sure**. ## Usage Under the hood, a Warp transaction is a transaction sent to your node with: * EVM — `eth_sendRawTransaction` * Solana — `sendTransaction` For example, if you send your transaction with your MetaMask connected to a Chainstack node, the transaction will be sent using `eth_sendRawTransaction`. With the *Warp transactions* add-on installed, only `eth_sendRawTransaction` on EVM networks or `sendTransaction` transactions are consumed as Warp transactions. Other requests to the node are consumed as regular requests. For EVMs, see also [Sending Trader Node warp transaction with web3.js, ethers.js, web3.py, and ethClient.go](/docs/sending-warp-transaction-with-web3js-ethersjs-web3py-and-ethclientgo). # Web3 [De]Coded: tutorials, guides, and best practices Source: https://docs.chainstack.com/docs/web3-decoded-introduction Web3 [De]Coded is a collection of articles that covers an extensive range of Web3 topics written by experts at Chainstack with hands-on experience. Here you can find industry best practices and advanced coding approaches to help develop your Web3 expertise and hone your BUIDLing skills, as well as try out the entry-level friendly tutorials for each protocol available on the Chainstack platform. If you have questions or need some help, [talk to us on Discord](https://discord.gg/Cymtg2f7pX). You can also follow the Chainstack developer advocates [on Twitter](https://twitter.com/i/lists/1544610911402557440) to stay up to date with the latest Web3 tutorials. ## Web3 \[De]Coded summary Discover our comprehensive collection of expertly crafted guides; here, you can find a brief summary of the topic and its potential benefits. Explore the list below to find the perfect resource tailored to your needs. ### Chainstack Marketplace guides These articles show [how you can utilize Chainstack Marketplace](/docs/chainstack-marketplace-tutorials) and its top applications to boost your DApp performance and enlarge its functionalities. ### Blockchain APIs guides Here you can find guides to help you how to leverage the power of Blockchain APIs by giving you deeper insights and practical examples. > [card] [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) > [card] [Uncovering the power of eth\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) > [card] [Expanding your blockchain horizons: The eth\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) > [card] [Deep dive into Merkle proofs and eth\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) > [card] [Geth vs Erigon: Deep dive into RPC methods on Ethereum clients](/docs/geth-vs-erigon-deep-dive-into-rpc-methods-on-ethereum-clients) > [card] [Ethereum logs tutorial series: Logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters) > [card] [Mastering custom JavaScript tracing for Ethereum virtual machine](/docs/mastering-custom-javascript-tracing-for-ethereum-virtual-machine) > [card] [Develop a battleship game using zero-knowledge concepts on Ethereum](/docs/develop-a-battleship-game-using-zero-knowledge-concepts-on-ethereum) > [card] [Sending Warp transaction with web3.js, ethers.js, web3.py, and ethClient.go](/docs/sending-warp-transaction-with-web3js-ethersjs-web3py-and-ethclientgo) > [card] [Harnessing Chainlink oracles with Chainstack: Fetching real-time crypto prices from Ethereum](/docs/harnessing-chainlink-oracles-with-chainstack-fetching-real-time-crypto-prices-from-ethereum) > [card] [Fetching transfer events with getPastEvents for a BAYC NFT](/docs/fetching-transfer-events-with-getpastevents-for-a-bayc-nft) > [card] [Understanding Ethereum's 'Filter not found' error and how to fix it](/docs/understanding-ethereums-filter-not-found-error-and-how-to-fix-it) > [card] [Introducing Bun: The future of JavaScript runtimes](/docs/introducing-bun-the-future-of-javascript-runtimes) > [card] [Fetching transactions to and from a specific address with eth\_getBlockByNumber](/docs/fetching-transactions-to-and-from-a-specific-address-with-eth_getblockbynumber) > [card] [cryo: Your gateway to blockchain data](/docs/cryo-your-gateway-to-blockchain-data) ### Best practices handbook These guides will help you implement best practices to build more secure and efficient decentralized applications. > [card] [HTTP batch request VS multicall contract](/docs/http-batch-request-vs-multicall-contract) > [card] [How to store your Web3 DApp secrets: Guide to environment variables](/docs/how-to-store-your-web3-dapp-secrets-guide-to-environment-variables) > [card] [Introduction to smart contract auditing](/docs/introduction-to-smart-contract-manual-auditing-with-foundry-and-slither) > [card] [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project) > [card] [Get the most out of Chainstack platform API](/docs/guide-get-the-most-out-of-the-chainstack-platform-api) > [card] [Understanding eth\_getLogs limitations](/docs/understanding-eth-getlogs-limitations) > [card] [Monitoring transaction propagation from node to mempool in EVM networks with Python](/docs/monitoring-transaction-propagation-from-node-to-mempool-in-evm-networks-with-python) > [card] [Handle real-time data using WebSocket with JavaScript and Python](/docs/handle-real-time-data-using-websockets-with-javascript-and-python) > [card] [Make your DApp more reliable with Chainstack](/docs/make-your-dapp-more-reliable-with-chainstack) > [card] [Best practices for error handling in API requests](/docs/best-practices-for-error-handling-in-api-requests) > [card] [Mastering multithreading in Python for Web3 requests: A comprehensive guide](/docs/mastering-multithreading-in-python-for-web3-requests-a-comprehensive-guide) > [card] [Authentication methods available on Chainstack](/docs/authentication-methods-for-different-scenarios) > [card] [Scaling your DApp with Kubernetes: A comprehensive guide](/docs/tutorial-on-how-to-make-your-dapp-reliable-and-scalable-with-kubernetes) > [card] [Navigating the Web3 landscape: How to choose the right blockchain network for your DApp](/docs/navigating-the-web3-landscape-how-to-choose-the-right-blockchain-network-for-your-dapp) ### Protocols tutorials These tutorials provide a basic introduction to the protocols supported by Chainstack, enabling you to dive into Web3 development. # Development frameworks and libraries Source: https://docs.chainstack.com/docs/web3-development-frameworks-and-libraries-glossary Glossary of Web3 development frameworks and libraries including web3.js, ethers.js, web3.py, Hardhat, Foundry, Truffle, and Remix for blockchain apps. ## Web3 libraries [Web3 libraries](/docs/protocols-tooling-introduction) are a set of tools that enable developers to connect with the blockchain through third-party providers like Chainstack. Popular libraries in this domain include web3.js, ethers.js, and web3.py. ## web3.py web3.py is a Python library designed for seamless interaction with the Ethereum network. It is commonly utilized in decentralized applications (DApps) to facilitate transaction sending, smart contract interaction, block data retrieval, and various other functionalities. Initially inspired by the web3.js JavaScript library, web3.py has evolved to cater specifically to Python developers. > [!NOTE] > > Learn how to install the [web3.py library](/reference/web3-libraries#python-libraries). ## ethers.js ethers.js aims to be a comprehensive and compact library for seamless interaction with the Ethereum blockchain and its ecosystem. Originally designed for ethers.io, it has expanded to become a versatile library applicable to a wide range of use cases. > [!NOTE] > > Learn how to install the [ethers.js library](/reference/web3-libraries#javascript-libraries). ## web3.js web3.js comprises a collection of libraries that enable developers to interact with local or remote Ethereum nodes using HTTP, IPC, or WebSocket protocols. These libraries provide extensive capabilities for engaging with the Ethereum network. > [!WARNING] > > web3.js is deprecated. Consider using ethers.js for new projects as it provides better performance, smaller bundle size, and more modern JavaScript features. > [!NOTE] > > Learn how to install the [web3.js library](/reference/web3-libraries#javascript-libraries). ## Web3 development frameworks Web3 development frameworks assist developers in building decentralized applications (DApps). Some prominent frameworks in this space are Hardhat, Truffle, Remix, Brownie, and Foundry. ## Foundry Foundry serves as a smart contract development toolchain, offering features such as dependency management, project compilation, test execution, deployment, and command-line and Solidity script-based interaction with the blockchain. > [!NOTE] > > Learn more about Foundry from our [workshop with Learn Web3 DAO](https://github.com/chainstacklabs/learnweb3dao-foundry-workshop). ## Hardhat Hardhat provides a development environment for Ethereum software, encompassing various components for editing, compiling, debugging, and deploying smart contracts and DApps. These components work harmoniously to create a complete development environment. > [!NOTE] > > Learn how to [develop and deploy smart contracts with Hardhat](/docs/avalanche-tutorial-aavev3-flash-loans-with-hardhat). ## Moralis SDK Moralis SDK allows seamless implementation of Moralis' products in backend systems. It includes features like data querying for EVM chains and Solana, Web3-based authentication integration, and utilities for data transformation and formatting. ## OpenZeppelin OpenZeppelin is a reputable provider of crypto cybersecurity technology and services. They offer open-source frameworks for secure smart contract development and conduct comprehensive security audits for prominent DeFi and NFT projects. ## Remix IDE Remix IDE is a user-friendly development tool equipped with a graphical interface for smart contract development. It is favored by both beginners and experts and expedites the development process. Remix integrates well with other tools and simplifies deployment to the blockchain of choice. Its visual debugger is widely acclaimed. ## Truffle > [!WARNING] > > Truffle is deprecated and no longer actively maintained. Consider using [Hardhat](/docs/web3-development-frameworks-and-libraries-glossary#hardhat) or [Foundry](/docs/web3-development-frameworks-and-libraries-glossary#foundry) for new projects. Truffle is a comprehensive development environment, testing framework, and asset pipeline designed for blockchains utilizing the Ethereum Virtual Machine (EVM). It aims to enhance the developer experience and streamline the development process. ## Wei The smallest unit of Ether, where 1 Ether is equivalent to 10^18 Wei. This unit is often used when calculating gas costs, transaction fees, and values at the most granular level. Wei allows for precise transactions on the Ethereum network, facilitating a more accurate computation of transaction costs without the need for floating-point numbers. ## Gwei Also known as Gigawei, it is a denomination of Ether, where 1 Gwei equals 10^9 Wei. It's the most commonly used unit when discussing gas prices on the Ethereum network. Gwei balances granularity and usability, making it easier to understand and calculate gas fees for transactions and smart contract executions. ## Ether Ether is the unit in which the value of transactions and smart contracts are denominated and executed on the Ethereum blockchain. 1 Ether is equivalent to 10^18 Wei. As the "fuel" for the Ethereum ecosystem, Ether is essential for deploying and interacting with smart contracts and DApps on the platform. # Infrastructure Source: https://docs.chainstack.com/docs/web3-infrastructure-glossary Glossary of Web3 infrastructure terms covering zero-knowledge rollups, optimistic rollups, sidechains, RPC nodes, and blockchain scaling solutions. ## Zero-knowledge rollup scaling solutions Zero-knowledge rollups, or ZK rollups, are layer 2 scaling solutions designed to improve the scalability and throughput of blockchain networks like Ethereum. They achieve this by moving most of the computational and storage burdens off-chain while still maintaining the security guarantees of the underlying layer 1 blockchain. ZK rollups bundle multiple transactions into a single proof, "roll it up", and submit only the essential information to the main chain, reducing the data size and cost of on-chain operations. The term "zero-knowledge" refers to the cryptographic technique used to validate bundled transactions without revealing individual transaction details. > [!NOTE] > > Learn more about [ZK scaling solutions](https://chainstack.com/zkevm-and-zkrollups-explained/). ## The Graph The Graph is an indexing protocol specifically designed for querying and retrieving data from blockchain networks such as Ethereum and IPFS (InterPlanetary File System). It allows developers to create and publish subgraphs, which are open APIs that define how to extract and organize specific data from the blockchain. By indexing and abstracting the blockchain data, The Graph provides developers with an efficient way to access decentralized data and build decentralized applications (DApps) that require real-time and reliable information. ## Blocks In the context of blockchain technology, a block refers to a collection or batch of transactions that are bundled together and added to the blockchain as a single unit. Each block contains a unique identifier called a hash, which is generated based on the block's content and the hash of the previous block in the chain. This chaining of blocks with their hashes creates an immutable and tamper-evident ledger. Blocks serve as the building blocks of the blockchain and provide a chronological order for the transactions. > [!NOTE] > > Learn how to [retrieve blocks data](/reference/ethereum-blocks-rpc-methods) using Chainstack. ## Wallet A wallet, in the context of cryptocurrencies, is a software application or a physical device that allows users to securely store, manage, and interact with their digital assets. It stores the private keys that are necessary to access and control the cryptocurrencies associated with those keys. Wallets enable users to send, receive, and manage their cryptocurrencies, and they come in various forms, including software wallets (desktop, mobile, or web-based) and hardware wallets (physical devices). > [!NOTE] > > Learn more about crypto wallets with our [Wallets 101 series](https://chainstack.com/crypto-hot-wallets-vs-cold-wallets-explained/). ## Nodes In a blockchain network, a node refers to a computer or device that participates in the network by maintaining a copy of the blockchain, validating transactions, and relaying information to other nodes. Nodes play a crucial role in ensuring the security, integrity, and decentralization of the network. There are different types of nodes, including full nodes, archive nodes, and light nodes, each with varying levels of data storage and processing capabilities. > [!NOTE] > > Learn more about [different type of nodes](https://chainstack.com/evm-nodes-a-dive-into-the-full-vs-archive-mode/), and how to [deploy your own node]() using Chainstack. ## Consensus mechanism Consensus mechanisms are algorithms or protocols used by blockchain networks to achieve agreement among network participants on the validity of transactions and the state of the blockchain. Consensus mechanisms enable decentralized networks to reach a shared truth without relying on a central authority. Examples of popular consensus mechanisms include proof-of-work (PoW), where participants compete to solve complex mathematical problems to validate blocks (used mostly by Bitcoin), and proof-of-stake (PoS), where participants "stake" their coins to gain the right to validate blocks based on their stake (used by networks like Ethereum Post-Merge and Cardano). ## Full nodes Full nodes are blockchain nodes that maintain a complete copy of the blockchain's transaction history and validate each transaction and block according to the network's consensus rules. Full nodes independently verify the integrity of the blockchain and can relay transactions and blocks to other nodes, but they only store a [limited amout of the blockchain states](/docs/protocols-modes-and-types). They provide a high level of security and decentralization but require significant storage space and computational resources to operate. ## Archive nodes Archive nodes are a type of node that not only store the complete transaction history but also retain the historical state of the blockchain at any given point in time, starting from the genesis block. Archive nodes are useful for in-depth analysis, research, and retrieving historical data from the blockchain. They consume significant storage resources and are often used by researchers, analysts, and developers working with historical blockchain data. ## Light nodes Light nodes, also known as lightweight nodes or thin clients, are blockchain nodes that don't store the entire blockchain's transaction history. Instead, they only download and validate block headers, which contain summarized information about each block. Light nodes rely on full nodes or other trusted sources to access the complete blockchain data when necessary. Light nodes require fewer resources and can be run on devices with limited storage and computing power, making them more accessible for users who don't need to interact with the blockchain at a deep level. ## Maximal extractable value Maximal extractable value (MEV) refers to the potential additional value that miners or validators can extract from the process of ordering and including transactions in a block. Miners have the power to decide the order in which transactions are included and can prioritize transactions that benefit them financially. MEV arises from various activities, such as front-running, arbitrage opportunities, and transaction reordering. It has become a topic of interest and research in the blockchain community as it can affect fairness, market dynamics, and user experience. ## Ethereum improvement proposal Ethereum improvement proposals (EIPs) are formal documents that propose changes, enhancements, or new features to the Ethereum blockchain. EIPs define standards, protocols, and improvements to the core Ethereum protocol, client APIs, and contract standards. EIPs undergo a review process by the Ethereum community, including developers, researchers, and users, before they can be accepted and implemented. ## Fork In the context of blockchain, a fork occurs when a blockchain network undergoes a significant divergence, creating two or more separate chains with a shared history up to a certain point. There are two types of forks: soft forks and hard forks. A soft fork introduces backward-compatible changes that allow new rules while maintaining compatibility with old ones. In contrast, a hard fork introduces changes that are not backward-compatible, resulting in a permanent divergence of the blockchain. Forks can occur due to protocol upgrades, consensus changes, or disagreements within the community. ## Proof of Staked Authority (PoSA) Proof of Staked Authority (PoSA) is a hybrid consensus mechanism that combines aspects of Proof of Stake (PoS) and Proof of Authority (PoA). In PoSA, validators must hold a certain stake (PoS element) while being approved as a trusted node (PoA element). This aims to provide security by having validators risk both economic stake and reputation. PoSA is designed to be more decentralized than PoA while being more energy-efficient than Proof of Work. A potential risk is centralization if the validator set is not diverse enough. PoSA is used by the BNB Chain. ## Proof-of-stake Proof-of-stake (PoS) is a consensus mechanism used by blockchain networks to achieve agreement on the validity of transactions and create new blocks. In PoS, validators are chosen to create new blocks based on the amount of cryptocurrency they "stake" or lock up as collateral. The probability of being chosen to create a block is proportional to the validator's stake. PoS is considered an energy-efficient alternative to proof-of-work (PoW) as it eliminates the need for extensive computational work and reduces the environmental impact. ## Proof-of-work Proof-of-work (PoW) is a consensus mechanism used by blockchain networks, including Bitcoin and Ethereum pre-Merge, to validate and secure transactions. In PoW, miners compete to solve complex mathematical puzzles, requiring significant computational power. The first miner to solve the puzzle successfully verifies the transactions in a block and adds it to the blockchain. PoW ensures the security and immutability of the blockchain but requires substantial energy consumption and computational resources. ## Ethereum virtual machine The Ethereum virtual machine (EVM) is a runtime environment for executing smart contracts on the Ethereum blockchain. It is a decentralized, Turing-complete virtual machine that allows developers to write and deploy smart contracts in various programming languages. The EVM ensures that smart contracts on the Ethereum network behave consistently and securely by executing bytecode instructions. ## Layer 1 blockchains Layer 1 blockchain refers to the main blockchain network that serves as the foundation layer for a decentralized ecosystem. Examples of layer 1 blockchains include Ethereum, Avalanche, Fantom, Solana, and Aptos. These blockchains have their own consensus mechanisms, native currencies, and smart contract capabilities. Layer 1 blockchains handle the fundamental operations of transaction validation, block creation, and network security. ## Layer 2 blockchains Layer 2 blockchains are built on top of layer 1 networks and provide scalability solutions to improve transaction throughput and reduce fees. Layer 2 solutions, such as ZK rollups, enable off-chain computation and state-storage while leveraging the security and decentralization of the layer 1 blockchain. These solutions aim to alleviate congestion and enhance the scalability of layer 1 networks. Examples of layer 2 blockchains include Polygon, which is built on top of Ethereum, acting as a scaling solution. > [!NOTE] > > Learn more about [layer 2 scaling solutions](https://chainstack.com/solving-the-blockchain-trilemma-scaling-solutions-for-ethereum/). ## Faucet A faucet is a tool or service that provides free tokens, typically on a testnet, to developers or users for testing and experimentation purposes. Web3 developers often require testnet tokens, such as test ether on the Ethereum network, to deploy and interact with their smart contracts without using real cryptocurrencies. Faucets distribute these testnet tokens, allowing developers to experiment with smart contract functionality without incurring any real-world financial costs. You can take our own [faucet](https://faucet.chainstack.com/) as an example. ## Validator In a proof-of-stake (PoS) consensus mechanism, a validator is a participant who holds a stake or a certain amount of cryptocurrency in the network and is selected to propose and validate new blocks. Validators are responsible for verifying transactions, maintaining network security, and participating in the consensus process. By staking their tokens as collateral, validators have a financial incentive to act honestly and ensure the integrity of the blockchain network. ## Difficulty Difficulty refers to the level of complexity or computational effort required to mine or validate a new block in a proof-of-work (PoW) blockchain network. The difficulty is adjusted periodically to maintain a consistent block creation rate. As more miners join the network, the difficulty increases to ensure that new blocks are added to the blockchain at a consistent and predetermined rate. Difficulty adjustments help maintain the security and stability of the PoW blockchain network. ## Gas Gas is a unit of measurement representing the computational effort required to execute a transaction or perform an operation on a blockchain network. Gas is used to quantify the computational resources consumed by each transaction. When sending a transaction on the Ethereum network, users include a specified amount of gas to cover the computational cost of executing the transaction. Gas is priced in small denominations called gwei. ## Gwei Gwei is a denomination of ether on the Ethereum network. It stands for gigawei and represents one billionth of an ether. Gwei is commonly used to denote the price of gas, which determines the transaction fee on the Ethereum network. Gas prices are generally measured in gwei per unit of gas. ## Gas limit The gas limit refers to the maximum amount of gas a user is willing to pay for a specific transaction on the blockchain network. It represents the computational resources allocated to execute the transaction. The gas limit serves as a safety measure to prevent infinite loops or excessive resource consumption. If a transaction exceeds the specified gas limit, it will be automatically reverted, and the user will not be charged for the unused gas. ## Gas war A gas war occurs when there is high demand for block space on the blockchain, leading to increased competition among users to have their transactions included in the next block. Users bid higher gas fees to incentivize miners or validators to prioritize their transactions. As the competition intensifies, gas fees can rise significantly, resulting in a gas war. Gas wars often happen during periods of network congestion or when there is a surge in transaction activity. ## Hash A hash is an alphanumeric string, and it acts as a digital fingerprint. In the context of blockchain, hashes play a crucial role in confirming the integrity and immutability of data. Each block in a blockchain contains the hash value of the previous block, forming a chain. Any modification to the data of a previous block would result in a different hash value, making it evident that the blockchain has been tampered with. ## Liquidity pool A liquidity pool is a smart contract found on decentralized exchanges (DeXs) that enables users to contribute funds in the form of token pairs. Liquidity providers deposit amounts of different tokens into the pool, establishing a balanced liquidity reserve for those tokens. This pool allows other users to trade or swap between the tokens. Liquidity providers are incentivized to contribute by receiving a portion of the trading fees generated by the DeX, based on their share of the liquidity pool. ## Liquidity providers Liquidity providers are individuals or entities that deposit their tokens into a liquidity pool on a decentralized exchange. By doing so, they contribute liquidity to the pool, facilitating trading and swapping between different tokens for other users. Liquidity providers earn a proportionate share of the trading fees generated by the DeX as compensation for their contribution. Participating as a liquidity provider offers the opportunity to earn passive income while supporting the liquidity and functionality of decentralized finance (DeFi) platforms. ## Mainnet Mainnet refers to the primary network of a blockchain platform where real transactions occur and are permanently recorded on the distributed ledger. It is the live and operational version of the blockchain, as distinguished from testnets or development environments. For instance, the Ethereum Mainnet serves as the public blockchain network where users can engage with smart contracts, perform transactions with ether, and engage in various decentralized activities. Other blockchain networks, such as Binance Smart Chain and Polygon, also have their respective mainnets serving as the primary operational networks. ## Mining Mining involves validating and adding new blocks or transactions to a blockchain, typically executed in a proof-of-work (PoW) consensus mechanism. Miners employ powerful computers to solve complex cryptographic puzzles and find the nonce, which validates and verifies transactions. Upon successfully solving the puzzle, a miner adds a new block to the blockchain containing a set of verified transactions. Miners are rewarded for their computational efforts with incentives like newly created cryptocurrencies (e.g., Bitcoin) and transaction fees. In PoW-based blockchains, mining is crucial in upholding network security and confirming transaction integrity. ## Sybil In the context of blockchain and decentralized systems, a Sybil attack refers to a situation where a single entity or individual creates multiple identities or nodes on the network to gain a disproportionately large influence over the system. This can enable the attacker to subvert the consensus mechanism, disrupt the network, or carry out other malicious activities. ## Sybil-resistant A Sybil-resistant system or mechanism is designed to prevent or mitigate the effectiveness of Sybil attacks. A Sybil-resistant system makes it difficult or economically unfeasible for an attacker to create and maintain many identities or nodes on the network. This is typically achieved through various techniques, such as: 1. Proof-of-Work (PoW): In PoW-based blockchains like Bitcoin, creating new identities (mining nodes) requires significant computational power, which is an economic barrier to Sybil attacks. 2. Proof-of-Stake (PoS): In PoS-based blockchains, a node's influence is proportional to the amount of cryptocurrency (stake) held by its owner. Creating multiple identities with significant stakes is economically expensive, making Sybil's attacks less feasible. 3. Identity-based systems: Some blockchains use identity-based systems, where identities are tied to real-world identities or reputations, making it harder to create multiple anonymous identities. Sybil resistance is an important consideration in the design of decentralized systems, as it helps to ensure the integrity and security of the network by preventing any single entity from gaining excessive influence or control. # Web3 Lingo Source: https://docs.chainstack.com/docs/web3-language-and-acronyms Glossary of Web3 terms and acronyms including airdrops, DeFi, DAOs, gas fees, MEV, oracles, staking, and common blockchain industry terminology. ## Airdrop Airdrops are rewards distributed to a project's community members as an incentive mechanism for early adoption. For instance, individuals who used the Arbitrum chain before it had a token became eligible for an airdrop. When the Arbitrum Foundation launched its token, a portion was given away to the community for free. ## Altcoin Altcoin refers to any cryptocurrency other than Bitcoin. It can be understood as an alternative coin, encompassing all cryptocurrencies that are not Bitcoin. ## Blockchain Blockchain is a ledger that stores transactions in chronological order and cannot be altered. Transactions are grouped into "blocks" and these blocks are linked together in chronological order using cryptographic signatures. ## Alpha Alpha refers to having an advantage over other market participants. This advantage often stems from information not yet available to all market participants. In trading, being privy to early information allows one to stay ahead of the market. ## Alpha groups Alpha groups are communities of traders who collaborate to gain a collective edge over the market by sharing valuable information, networking, obtaining whitelists, and more. ## Aping Aping refers to buying into a project without conducting proper research. It involves investing in a project first and conducting research later. Although this behavior is not recommended, it is prevalent in the crypto market due to its dynamics. ## Arbitrage Arbitrage is the practice of buying and selling assets on different exchanges to profit from price differences. For example, if Bitcoin is trading at $40,000 on one exchange and $38,000 on another, purchasing BTC at $38,000 and selling it at $40,000 allows the trader to keep the price difference as profit. ## ATH ATH is an acronym for "All-Time High", which refers to the highest price a token or asset has ever reached historically. ## Bag holder Bag holder is a term used to describe a trader who bought a token at a high price and failed to sell it quickly enough to make a profit. As a result, they are left with a devalued "bag" of tokens. It's important to note that this does not imply the tokens are worthless but rather worth less than their initial purchase price. ## BTFD BFTD is an acronym for "Buy The Fucking Dip"; it is a term used within the crypto community to encourage other traders to buy when prices are declining. ## BUIDL BUIDL, a play on the term HODL, is used by Web3 development teams to indicate their commitment to actively building projects. ## Coin Coin refers to a cryptocurrency that operates on its own blockchain, such as Ethereum, Avalanche, Fantom, etc. ## Decentralized Application (DApp) A decentralized application (DApp) is a software application that runs on a distributed computing system, typically a blockchain, ensuring operations are decentralized, transparent, and censorship-resistant. ## DeFi DeFi is an abbreviation for "Decentralized Finance, " encompassing various financial applications built on decentralized networks. ## DeX DeX stands for "Decentralized Exchange" and refers to platforms like Uniswap, Sushiswap, Pancakeswap, Apeswap, and Quickswap. ## Double spend Double spending refers to attempting to spend the same cryptocurrency twice. It involves selling or transferring the same crypto to two different parties simultaneously. ## Circulating supply Circulating supply represents the number of tokens that have been released into the market and are currently in circulation. For example, if a token has a total supply of 100 tokens but only 80 have been mined, the circulating supply is 80. ## ENS The Ethereum Name Service (ENS) is a service that enables users to purchase unique names for their wallets. Instead of providing the entire wallet address (a long string of characters), users can simply share the `name.eth` address for others to send funds. ## CeX CeX stands for "Centralized Exchange" like Coinbase, Binance, Etoro, and Bitfinex. ## DApp DApp stands for "Decentralized Application", which refers to an application built using decentralized technologies. ## DAO DAO stands for "Decentralized Autonomous Organization", a novel structure without a central leader. DAOs are formed by individuals from a community who collaborate towards a common goal. If BTC represents the new form of "money", DAOs can be considered the new form of companies or LLCs. ## FUD FUD stands for "Fear, Uncertainty, and Doubt" and is commonly used in the crypto community when negative news or skepticism arises. It refers to attempts to create fear or doubt about a particular cryptocurrency or the market as a whole. ## ICO ICO stands for "Initial Coin Offering", which is similar to traditional IPOs (initial public offerings). It involves investing in a project at an early stage before it gets listed on any exchange. ## IEO IEO stands for "Initial Exchange Offering", which is a fundraising event conducted by a cryptocurrency exchange. The exchange acts as an intermediary between the project and investors. ## IDO IDO stands for "Initial DeX Offering", which is a fundraising event conducted on a decentralized exchange (DeX). Similar to an IEO, the DeX acts as a middleman between the project and investors. ## HODL HODL is a misspelling of "hold" that became a meme within the crypto community. It refers to the act of holding onto coins despite price declines, expressing a long-term investment strategy. ## Market cap Market cap, short for market capitalization, is a metric used in the cryptocurrency market to indicate a coin's dominance or value relative to the entire market. ## Moon Moon refers to a cryptocurrency or token's price rapidly surging to a significantly higher level within a short period. For example, a 50% increase in price in a single day. ## NFT NFT stands for "Non-Fungible Token". It represents a unique digital asset that cannot be replicated or interchanged on a one-to-one basis. Each NFT possesses distinct characteristics and ownership. ## NFT flipping NFT flipping refers to buying NFTs at a certain price and selling them at a higher price. It involves minting or acquiring an NFT and subsequently selling it on the open market for a profit. ## NGMI NGMI is an acronym for "Not Gonna Make It". It is used by the crypto community on platforms like Twitter to express pessimism or acknowledge a trade or investment that has a high probability of resulting in losses. ## Ponzinomics Ponzinomics refers to projects designed to enrich early investors at the expense of later participants. The name alludes to the Ponzi scheme, a fraudulent investment scheme that promises high returns but relies on new investors' funds to pay existing participants. ## Pump and dump Pump and dump refers to a scenario where a large group of investors collectively buy an asset, causing its price to "pump" or increase significantly. Subsequently, they sell their holdings, causing the price to "dump" or collapse. This often leads to losses for inexperienced investors who bought in at inflated prices. ## Rug pull Rug pull refers to scams in the cryptocurrency space. It describes a situation where a project initially presents an enticing roadmap but, after gaining funds from investors, fails to deliver on its promises. The team behind the project abandons it, taking the funds with them. ## Satoshi Nakamoto Satoshi Nakamoto is the pseudonymous individual or group credited with creating the Bitcoin protocol. Nakamoto published the Bitcoin whitepaper in 2008 and released the first Bitcoin software a year later. The true identity of Satoshi Nakamoto remains unknown. ## Shill Shill refers to the act of unsolicitedly endorsing a coin or project in public. Traders who have invested in a particular coin may have an interest in shilling it, hoping to generate interest and increase its value. ## Stablecoin A stablecoin is a type of cryptocurrency that is pegged to a stable asset, such as a fiat currency (e.g., USD, EUR) or a commodity like gold. Stablecoins aim to maintain a stable value and reduce price volatility by backing each unit with a known amount of the underlying asset. ## Staking Staking refers to the act of holding and "locking up" tokens for a specific purpose. For example, on decentralized exchanges (DeXs), users stake their tokens to provide liquidity for other investors who wish to purchase that specific token. In return, the stakers receive a portion of the transaction fees generated. ## Web3 Web3 or Web 3.0 refers to the concept of the decentralized web. It encompasses the blockchain and decentralized technology ecosystems as a whole. The term is used to describe the next generation of the internet, where decentralized applications (DApps) and peer-to-peer interactions play a significant role. ## Weak hands Weak hands refers to individuals who lack patience and sell their assets, often at a loss, during market downturns or price declines. In contrast, diamond hands refers to those who hold onto their investments regardless of market volatility. ## WAGMI WAGMI (we're all gonna make it) or WGMI (we gonna make it) are phrases used to express optimism within the crypto community. They convey the belief that everyone involved in the community will ultimately achieve success. They serve as a counterpoint to NGMI (not gonna make it). ## Whale Whale refers to a large investor or entity holding a substantial amount of cryptocurrency or capital. Whales often have the ability to influence market movements, particularly for smaller altcoins, due to their significant capital holdings. ## Wen Wen is a slang term derived from "when" and is used within Web3-specific lingo on social media platforms. # Web3 node.js: From zero to a full-fledged project Source: https://docs.chainstack.com/docs/web3-nodejs-from-zero-to-a-full-fledged-project Complete Web3 Node.js development guide. Learn nvm setup, web3.js best practices, security, testing with Mocha/Chai, and production deployment strategies. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR:** * Discusses how to set up a Node.js environment (via nvm) for smooth version management and package handling (via npm). * Covers Web3.js usage, project structure tips, and key security practices like environment variable storage and address checksums. * Emphasizes robust error handling, test coverage (Mocha, Chai, Truffle), and CI/CD integration for safer, production-grade DApps. * Concludes with suggestions on gas optimization, deployment practices, and monitoring/logging strategies to maintain a reliable Web3 project. ## Main article This is an in-depth guide outlining the best practices for establishing a Web3 [node.js](https://nodejs.org/en) project. Our focus will primarily be on the Ethereum blockchain; however, the core principles discussed are applicable to other blockchains and non-Web3 projects as well. ## Setting up a node.js project Using the ideal Node version based on your project and its dependencies, it’s a key step to consider. Using a tool like [Node Version Manager](https://github.com/nvm-sh/nvm) (nvm), managing node.js versions becomes easy, reducing the potential for version-related issues and enhancing the overall development experience. ### Managing your environment nvm is a robust tool that serves as a comprehensive solution for managing multiple active node.js versions. This open-source tool allows developers to install, update, switch between, and manage different versions of node.js in their development environment. It facilitates maintaining control over your node.js environment, which is essential to ensure compatibility across diverse systems and to prevent conflicts between projects with differing node.js version requirements. nvm provides several key functionalities: * Multiple version management — it enables the installation of multiple node.js versions on the same machine, accommodating the needs of different projects requiring various node.js versions. * Easy version switching — nvm allows for effortless swapping between different installed node.js versions, enabling compatibility testing on multiple node.js versions. * Version isolation — each node.js version installed via nvm operates independently, ensuring that global packages, npm installations, and configurations are specific to each version and thus eliminating potential conflicts. * Version-specific installations — nvm supports the installation of specific node.js versions, catering to projects with particular version requirements for optimal performance and compatibility. * Default version setting — you can set a default node.js version to be used in every new terminal session, streamlining your workflow if you primarily work with a specific version. Here is a brief guide on how to install and use nvm: ```shell Shell theme={"system"} # Installing NVM curl -o- | bash # Installing a specific version of Node.js nvm install 14.15.1 # Utilizing the installed version nvm use 14.15.1 ``` ### Node version manager quick reference guide Here's a quick reference guide for some of the most common nvm commands: ```shell Shell theme={"system"} # Installing NVM curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash # Checking the installed NVM version nvm --version # Installing a specific version of Node.js nvm install # Installing the latest Node.js version nvm install node # Using a specific installed version nvm use # Using the latest installed version nvm use node # Setting a version as the default for new sessions nvm alias default # Listing installed Node.js versions nvm ls # Checking the version of Node.js currently in use node -v # Uninstalling a specific Node.js version nvm uninstall ``` ### Managing your packages A package manager, like [npm](https://www.npmjs.com/) or [Yarn](https://yarnpkg.com/), is a programming tool that automates the process of installing, upgrading, configuring, and removing software packages in a consistent manner. In this article, we’ll consider npm as it is more widely adopted. #### Initialize a Node project using npm To initialize a new node.js project with npm use the following command: ```shell Shell theme={"system"} npm init -y ``` The `-y` flag is a convenient option that automatically responds 'yes' to all the prompts presented by npm during the initialization of a new project. If you prefer to provide individual responses to these prompts, which typically include details such as the project's name, description, repository URL, and others, you can simply omit the `-y` flag. When initiating a node.js project with the `npm init` command, you're preparing the project to use npm, Node's default package manager. This command creates a `package.json` file in your project directory, which stores important metadata about your project. This includes the name and version of your application, description, entry point, test command, git repository, keywords, author, and license. Additionally, the `package.json` file manages project dependencies by listing all Node modules/packages installed via `npm install`, and it allows for the creation of scripts to automate tasks such as starting your application or running tests. Here's an example of what a `package.json` file might look like after running `npm init`: ```json package.json theme={"system"} { "name": "my-dapp", "version": "1.0.0", "description": "A basic Node.js web3 application.", "main": "index.js", "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Your Name", "license": "MIT", "dependencies": { "web3": "^1.8.2" } } ``` ### Project structure A well-structured project directory is essential for maintaining the readability and scalability of your codebase, making it easier for both you and others to understand, navigate, and simplifying maintenance and modules adding in the log run. Here is an example of a clean project structure: ```bash Bash theme={"system"} Project │ .gitignore │ package.json │ README.md │ └───src │ │ │ └───contracts │ │ │ └───lib │ │ │ └───routes │ │ │ └───utils │ └───test ``` * `/src` — houses all source code, the core of your project. It's the main directory for development. * `/contracts` — stores Ethereum smart contract (.sol) files; used for defining the rules of transactions within your blockchain project. * `/lib` — contains library scripts. These scripts provide reusable functionalities and reduce code duplication. * `/routes` — holds server route files. They map URLs to specific functions that handle HTTP requests and responses. * `/utils` — stores utility scripts and helper functions. These generally simplify complex tasks and improve code readability. * `/test` — houses testing scripts. These validate your code's functionality and help maintain software quality. * `.gitignore` — lists files/folders that git should ignore. Helps prevent unwanted files (e.g., temporary files, logs) from being versioned. * `package.json` — manages package dependencies and scripts. It's a manifest file for node.js projects that provides project metadata. * `README.md` — contains project documentation. A good README explains what the project does, how to use it, and other pertinent information. ## web3.js library best practices web3.js is a JavaScript library that allows developers to interact with a local or remote Ethereum node using HTTP, IPC, or WebSocket. It provides a complete set of tools to work with the Ethereum blockchain and its associated functionalities like smart contracts, accounts, and transactions. > [!NOTE] > > Find examples of how to use [Web3 libraries in the Chainstack API documentation](/reference/web3-libraries). web3.js is widely used, so let’s go over some best practices here. ### 1: Versioning Install the latest stable version of web3.js, which has the most recent features and security patches: ```shell Shell theme={"system"} npm install web3 ``` You can also install a specific version of a library by specifying the version in the install command: ```shell Shell theme={"system"} npm install web3@1.8.2 ``` ### 2: Connection to Ethereum node Connect to an Ethereum node using the Web3 provider. In this example, we're connecting to a Chainstack node. ```js JavaScript theme={"system"} const Web3 = require("web3"); // Use your Chainstack endpoint URL const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; // This line establishes a connection instance const web3 = new Web3(NODE_URL); ``` The following example shows how to retrieve the latest block numbers using web3.js: ```js JavaScript theme={"system"} const Web3 = require("web3"); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const web3 = new Web3(NODE_URL); async function getLatestBlockNumber() { const block = await web3.eth.getBlockNumber(); console.log(`Latest block: ${block}`); } getLatestBlockNumber() ``` > [!NOTE] > > Explore more blockchain APIs and Web3 libraries examples in the [Chainstack API documentation](/reference/ethereum-getting-started). ### 3: Error handling It is imperative to implement effective error handling during blockchain interactions to ensure smooth operation and troubleshoot potential issues. Let's consider a scenario where we're fetching the gas price, a common operation in Ethereum transactions. Proper error handling, in this case, can greatly enhance the robustness of your application. Here's an illustrative example: ```js JavaScript theme={"system"} async function fetchGasPrice() { try { const gasPrice = await web3.eth.getGasPrice(); console.log(`Base fee: ${gasPrice}`) return gasPrice; } catch (error) { console.error(`Failed to get gas price due to an error: ${error}`); // Fallback to a default gas price if the fetch fails. const defaultGasPrice = web3.utils.toWei('50', 'gwei'); console.log(`Using default gas price: ${defaultGasPrice}`); return defaultGasPrice; } } fetchGasPrice() ``` This code includes a fallback to a default gas price when the fetch operation fails. This allows the application to continue running even when the gas price fetch fails. ## Coding best practices ### 1: Promises and async/await web3.js operations are asynchronous. Use promises and async/await syntax for handling these operations. Here's an example of fetching an account's balance: ```js JavaScript theme={"system"} const Web3 = require("web3"); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const web3 = new Web3(NODE_URL); async function getBalance(address, block) { const balance = await web3.eth.getBalance(address, block) console.log(balance) return balance } getBalance("0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324", "latest" ) ``` The `async` and `await` keywords in the code are used to handle asynchronous operations in a more readable and cleaner way. The function `getBalance` is marked as `async`, making it return a `Promise`. Within this function, `await` is used to pause execution until the `web3.eth.getBalance(address, block)` Promise resolves, effectively waiting for the balance of an Ethereum address at a specific block to be retrieved from the blockchain. Once this value is obtained, it's logged into the console. This makes the asynchronous code appear as though it's synchronous, simplifying its structure and readability. ### 2: Gas optimization Gas optimization is crucial in Ethereum transactions. Always estimate gas before sending transactions: ```js JavaScript theme={"system"} const myContract = new web3.eth.Contract(abi, contractAddress); async function estimateGas(data) { const gasAmount = await myContract.methods.myMethod(data).estimateGas({ from: myAccount }); return gasAmount; } ``` ### 3: Confirmation blocks Wait for enough block confirmations before considering a transaction final. The number of confirmations depends on the level of security you require. ```js JavaScript theme={"system"} web3.eth.sendTransaction({ from: senderAddress, to: receiverAddress, value: web3.utils.toWei("1", "ether") }) .on('confirmation', (confirmationNumber, receipt) => { if (confirmationNumber >= 3) { console.log('Transaction is confirmed'); } }) .on('error', console.error); ``` ### 4: Event listening Events are used for monitoring contract state changes. Always listen to 'error' events on your subscriptions. ```js JavaScript theme={"system"} myContract.events.MyEvent({ filter: {}, // Using an empty filter object will retrieve all events fromBlock: 0 }, (error, event) => console.log(event)) .on('error', console.error); ``` > [!NOTE] > > Learn more about retrieving events by reading [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial). ## Security best practices When developing applications on the blockchain, it's crucial to prioritize security. This is not only to protect your assets but also to ensure the integrity and reliability of your application. Here are some key areas to consider: ### 1: Private key management Private keys should never be exposed in your code or version control systems. Use environment variables or secure vault services. ```js JavaScript theme={"system"} # .env file PRIVATE_KEY=YOUR_PRIVATE_KEY ``` ```jsx jsx theme={"system"} // In your code require('dotenv').config(); const privateKey = process.env.PRIVATE_KEY; ``` > [!NOTE] > > Check out our extensive guide, [How to store your Web3 DApp secrets: Guide to environment variables](/docs/how-to-store-your-web3-dapp-secrets-guide-to-environment-variables). This guide goes deep into how to avoid exposing keys and endpoints in the frontend. ### 2: Address checksums Ethereum addresses come with built-in checksums to prevent transactions from being sent to the wrong addresses. Always validating and using checksum addresses is a best practice that can prevent costly mistakes. A checksum in the context of an Ethereum address is a form of error detection that's built into the address itself. It's used to detect errors that may have been introduced during its transmission or storage. Ethereum addresses are typically shown as a 40-character hexadecimal string. While Ethereum addresses are not case-sensitive, a certain case format is used to encode some redundancy into the address, allowing for error checking. This format is known as the ICAP (international bank account number) format. ```js JavaScript theme={"system"} async function addressCheck() { const address = '0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13'; if (web3.utils.isAddress(address)) { // The address is valid const checksumAddress = web3.utils.toChecksumAddress(address); if (address === checksumAddress) { console.log('The address is checksummed correctly'); } else { console.error('The address is not checksummed correctly'); } } else { console.error('Invalid address'); } } addressCheck() ``` ### 3: Smart contract interaction Always validate the ABI and contract addresses before interacting with them. ```js JavaScript theme={"system"} if (web3.utils.isAddress(contractAddress) && Array.isArray(abi)) { const myContract = new web3.eth.Contract(abi, contractAddress); } else { console.error('Invalid contract address or ABI'); } ``` ### 4: Rate limits and throttling When using services like Chainstack, keep the rate limits in mind. Implement retry logic or request throttling as necessary. ```js JavaScript theme={"system"} const axios = require('axios'); const retry = require('async-retry') async function getBlockNumber() { return retry(async bail => { try { const response = await axios.post( 'https://ethereum-mainnet.core.chainstack.com', { jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }, { headers: { 'Authorization': 'Basic ' + Buffer.from('YOUR_USERNAME:YOUR_PASSWORD').toString('base64') } } ); return response.data.result; } catch (error) { console.error(`Failed to get block number: ${error}`); if (error.response && error.response.status === 429) { bail(new Error('Too many requests, throttling...')); return; } throw error; } }, { retries: 5, minTimeout: 3000 }); } ``` ## Testing best practices ### 1: Unit testing Use libraries like Mocha/Chai for unit testing. ```js JavaScript theme={"system"} const assert = require('chai').assert; describe('Array', function() { describe('#indexOf()', function() { it('should return -1 when the value is not present', function() { assert.equal([1, 2, 3].indexOf(4), -1); }); }); }); ``` ### 2: Smart contract testing Use libraries like Truffle to write tests for your smart contracts. ```js JavaScript theme={"system"} const MyContract = artifacts.require('MyContract'); contract('MyContract', (accounts) => { it('should do something', async () => { const myContractInstance = await MyContract.deployed(); // Test myContractInstance methods... }); }); ``` ### 3: Test coverage Strive for high test coverage. Tools like Istanbul can be used for coverage reports. ```js JavaScript theme={"system"} # Install nyc npm install --save-dev nyc # Add a test script in package.json "scripts": { "test": "nyc mocha" } ``` ### 4: Continuous integration Use CI tools like Jenkins or Travis CI to automate testing and deployment processes. Here's a sample `.travis.yml` file for a node.js project: ```yaml yaml theme={"system"} language: node_js node_js: - "14" cache: directories: - "node_modules" script: - npm test ``` ## Deployment best practices ### 1: Environment variables Use different environment variables for different stages of your deployment (dev, test, prod). Tools like `dotenv` can help manage environment variables. ### 2. Minification Use tools like [Webpack](https://webpack.js.org/) or [Babel](https://babeljs.io/) to transpile and minify your code for production. ```js JavaScript theme={"system"} // webpack.config.js module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist'), }, }; ``` ### 3: Monitoring Set up monitoring and logging services for your application. Tools like [Winston](https://github.com/winstonjs/winston) can be used for logging in node.js applications. ```js JavaScript theme={"system"} const winston = require('winston'); const logger = winston.createLogger({ level: 'info', format: winston.format.json(), defaultMeta: { service: 'user-service' }, transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }), ], }); logger.error('Error log'); logger.warn('Warn log'); logger.info('Info log'); logger.verbose('Verbose log'); logger.debug('Debug log'); logger.silly('Silly log'); ``` ## Conclusion This document provides a comprehensive guide on best practices for establishing a Web3 node.js project, with a primary focus on the EVM blockchains. The topics covered include setting up a node.js project using tools like nvm and npm, creating a well-structured project directory, using the web3.js library to interact with Ethereum nodes, following coding best practices, ensuring security, testing the application, and deploying the project. # Security Source: https://docs.chainstack.com/docs/web3-security-glossary Glossary of Web3 security terms covering 51% attacks, flash loan exploits, reentrancy, front-running, rug pulls, and smart contract vulnerabilities. ## 51% attack A 51% attack occurs when a single entity or group of individuals controls more than 50% of the validating power or mining hash rate on a network. In such cases, the controlling entity can manipulate transactions, double-spend coins, and potentially disrupt the blockchains' operations. ## Cold wallet A cold wallet, also known as cold storage, refers to an offline wallet that is not connected to the internet. It provides a high level of security because it is not susceptible to online attacks. Cold wallets are typically hardware devices, such as Ledger or Trezor, which store private keys and allow users to securely manage their cryptocurrencies. They are similar to USB sticks and often require physical interaction to sign transactions. ## Hot wallet A hot wallet, or hot storage, is a cryptocurrency wallet that is connected to the internet and accessible for regular transactions. Hot wallets are convenient for frequent use and quick access to funds but have a higher security risk compared to cold storage. Examples of hot wallets include software wallets like MetaMask, Trust Wallet, and WalletConnect. These wallets are often used for day-to-day transactions and interact with decentralized applications (DApps) on the blockchain. ## Multi-signature wallet A multi-signature wallet, or multiSig, is a type of cryptocurrency wallet that requires multiple signatures or keys to authorize transactions. It enhances security and mitigates the risk of a single point of failure. In a multiSig wallet, a predetermined number of signatures, typically a majority, is required to approve a transaction. For example, if a wallet requires 3 signatures and there are 5 key holders, at least 3 of the 5 key holders must provide their signatures to initiate a transaction. MultiSig wallets are commonly used for shared accounts, corporate wallets, or community-managed funds. ## Reentrancy attack A reentrancy attack is a type of vulnerability that can occur in smart contracts. It involves an attacker exploiting a flaw in a contract's logic that allows them to repeatedly call back into the same contract before the previous call completes. By doing so, the attacker can manipulate the contract's state and potentially drain its funds or cause unintended behavior. The reentrancy attack gained attention with the infamous DAO Hack in 2016 on the Ethereum blockchain. Solidity, the programming language used for Ethereum smart contracts, has since implemented measures to prevent reentrancy attacks, and developers are encouraged to follow secure coding practices to mitigate this risk. # Web3.js: How to use the Chainstack plugin Source: https://docs.chainstack.com/docs/web3js-how-to-use-the-new-chainstack-plugin Integrate Chainstack nodes with web3.js using @chainsafe/web3-plugin-chainstack. Securely authenticate, fetch node details, and use Platform API endpoints. > [!NOTE] > > This tutorial uses **web3.js**, which is [no longer maintained](https://github.com/web3/web3.js) (ChainSafe sunset it in 2025). For new projects, use [ethers.js or viem](/docs/ethereum-tooling) — the concepts here still apply; only the library calls differ. **TLDR:** * Seamlessly integrate Chainstack-managed nodes with web3.js using the @chainsafe/web3-plugin-chainstack plugin. * Securely authenticate with Chainstack’s API to fetch node details and dynamically build endpoints. * Combine web3.js for on-chain interactions with Chainstack’s platform API for node management. * Enjoy fewer credentials to manage, simpler environment setups, and robust, high-performance blockchain infrastructure. ## Main article **Web3.js** is a popular JavaScript library that provides an easy-to-use interface for interacting with Ethereum and other EVM-compatible blockchains. It allows developers to send transactions, interact with smart contracts, and retrieve blockchain data. On the other hand, **Chainstack** is a powerful platform that provides managed blockchain services, offering reliable and scalable infrastructure for various blockchain networks. By integrating Chainstack with web3.js, developers can leverage the benefits of secure, high-performance nodes with minimal setup and maintenance. [Chainsafe](https://github.com/ChainSafe), the maintainer of web3.js, recently released a [plugin for Chainstack](https://github.com/ChainSafe/web3-plugin-chainstack). This plugin allows users to easily and securely access their Chainstack nodes and interact with the platform API. This tutorial will guide you through integrating Chainstack's managed blockchain nodes with web3.js using the Chainstack plugin. ## Prerequisites Before diving into this tutorial, it is essential to have a basic understanding of the following concepts: 1. **JavaScript**: This tutorial will involve writing and understanding JavaScript code, so familiarity with JavaScript syntax and fundamental programming concepts is necessary. 2. **Blockchain technology**: A general understanding of blockchain, including concepts such as nodes, transactions, and smart contracts. 3. **web3.js**: Basic knowledge of web3.js and its purpose in interacting with Ethereum and other EVM-compatible blockchains. If you are new to web3.js, you can find many examples in the [Chainstack API reference](/reference/ethereum-getblocknumber). **Tools and libraries** To follow along with this tutorial, ensure you have the following tools and libraries installed on your system: 1. **Node.js**: Node.js is a JavaScript runtime that allows you to run JavaScript code on the server side. You can download and install Node.js from the [official website](https://nodejs.org/). 2. **npm (Node Package Manager)**: npm is included with Node.js and is used to manage packages and dependencies for your Node.js projects. You can verify the installation by running the following commands in your terminal: ```bash Shell theme={"system"} node -v npm -v ``` 3. **web3.js** and the **Chainstack plugin**: This JavaScript library provides an interface for interacting with the Ethereum blockchain. You can install it using npm, create a new project, and run: ``` npm i web3 @chainsafe/web3-plugin-chainstack ``` Then, Create a new file named `index.js`. We'll also need a Chainstack node and API key. > [!NOTE] > > [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required. You can sign up with your GitHub, X, Google, or Microsoft account. > [card] [Create an API key](/docs/guide-get-the-most-out-of-the-chainstack-platform-api#create-and-safeguard-your-api-keys) Once you have met these prerequisites, you will be ready to set up your development environment and start integrating Chainstack with web3.js. > [!NOTE] > > Check out our guide on how to set up a Web3 Node.js project: [Web3 node.js: From zero to a full-fledged project](/docs/web3-nodejs-from-zero-to-a-full-fledged-project). ## Connect to a Chainstack node At this point, we can start coding; the project will be very basic but will show you how to use your Chainstack node and platform API. ### Import web3 and authentication The first step, like in many web3 libraries, is to import the packages and create a provider instance; in this case, we'll use the **Password-protected access and credentials** from where you deploy your Chainstack node; in this example, I'm using a Base mainnet [Global Node](/docs/global-elastic-node). This is how you do it, and it's straightforward: ```javascript Javascript theme={"system"} import { Web3 } from "web3"; import { ChainstackPlugin } from "@chainsafe/web3-plugin-chainstack"; const web3 = new Web3(); // RPC auth plus API key const userAuth = { username: "USERNAME", password: "PASSWORD", apiKey: "API_KEY", }; // Use the base URL here; in this case, it's for Base manner. const chainstackPlugin = new ChainstackPlugin( "https://base-mainnet.core.chainstack.com", userAuth ); web3.registerPlugin(chainstackPlugin); ``` The provider instance is set at this point, and we can use the `web3.js` library as we normally do. Here is an example of fetching the `chain_id`: ```javascript Javascript theme={"system"} async function main() { const result = await web3.eth.getChainId(); console.log(result); } main(); ``` Giving this result: ``` Chain ID fetched: 8453 ``` Find more examples of how to use `web3.js` in the [Chainstack API docs](/reference/ethereum-getblocknumber). > [!NOTE] > > Note that you might receive the following warning in the console: > > `NOTE: web3.js is running without provider. You need to pass a provider in order to interact with the network!` > > But you can ignore it. ## Interact with the Chainstack platform API Chainstack offers a unique feature, the [platform API](/reference/platform-api-getting-started); this means you can use a Chainstack API key to interact with your projects and nodes, fetch data about them, and even deploy nodes. The web3.js plugin integrates some of the platform API endpoints to get data. Where we create the `web3` instance, we include a Chainstack API key; this key is used to interact with the platform API, so you can use the regular `web3` instance: ``` const userAuth = { username: "USERNAME", password: "PASSWORD", apiKey: "API_KEY", }; ``` Let's explore some of them and how to use them. ### Fetch your Chainstack organization ``` async function main() { // Fetch the Chainstack organization based on the API key. const org = await web3.chainstack.getOrganization(); console.log(org); } main(); ``` The result will be like this: ``` { id: 'RG-XXX-XXX', name: 'david' } ``` ### Fetch your projects You can get details about the projects you created in the Chainstack console: ``` async function main() { const projects = await web3.chainstack.getProjects(); console.log(projects.results); } main(); ``` With a similar result: ``` [ { id: 'PR-XXX-XXX', name: 'gm', description: '', type: 'public', networks: 1, creator: { id: 'UR-XXX-XXX-XXX', email: 'user@email.com', first_name: '', last_name: '', organization: [Object] }, created_at: '2024-01-25T13:20:25.431049Z' } ] ``` ### Get networks info Now, this endpoint can be used in your projects to dynamically fetch the endpoints you need. This endpoint returns information on all the networks you deployed. For example, if you re-deploy a node in the network or add a new one, you can fetch the endpoints from the API instead of manually finding them in the console. ```javascript Javascript theme={"system"} async function main() { const networks = await web3.chainstack.getNetworks(); console.log(networks.results); } main(); ``` This will return: ``` [ { id: 'NW-XXX-XXX-X', name: 'Base Mainnet', project: 'PR-XXX-XXX', protocol: 'base', status: 'ok', configuration: { network: 'base-mainnet' }, details: {}, nodes: [ [Object] ], created_at: '2024-05-20T21:25:49.743468Z', creator: { id: 'UR-173-258-070', email: 'user@email.com', first_name: '', last_name: '', organization: [Object] } } ] ``` As you can see, it includes a `nodes` object, and that is where you'll find all the data on your nodes. You can extract the node's data like this: ```javascript Javascript theme={"system"} async function main() { const networks = await web3.chainstack.getNetworks(); const networkResults = networks.results; networkResults.forEach((network) => { console.log(`Network: ${network.name}`); console.log(`Nodes:`); network.nodes.forEach((node) => { // console.log(node) // Enable this log to see all the fields. console.log(`Node ID: ${node.id}`); console.log(`Node name: ${node.name}`); console.log(`Node Status: ${node.status}`); console.log(`Node base endpoint: ${node.details.https_endpoint}`); console.log(`Node base key: ${node.details.auth_key}`); console.log( `Full endpoint: ${node.details.https_endpoint}/${node.details.auth_key}` ); console.log(`--------`); }); }); } main(); ``` This will return all your node information: ``` Network: Base Mainnet Nodes: Node ID: ND-XXX-XXX-XXX Node name: Base Chainstack Global Node Status: running Node base endpoint: https://base-mainnet.core.chainstack.com Node base key: AUTH_KEY Full endpoint: https://base-mainnet.core.chainstack.com/AUTH_KEY ``` Now, you can use this info to fetch endpoints every time dynamically. Here is an example implementation: > [!NOTE] > > This example reconstructs the full endpoint to use in a `web3` instance, but you can also fetch the `username` and `password` to use in `userAuth` for the Chainstack plugin directly like in the first example. ```javascript Javascript theme={"system"} import { Web3 } from "web3"; import { ChainstackPlugin } from "@chainsafe/web3-plugin-chainstack"; // Auth const userAuth = { apiKey: "API_KEY", }; // Create a plugin instance const chainstackPlugin = new ChainstackPlugin(userAuth); web3.registerPlugin(chainstackPlugin); async function main() { // Fetch the networks const networks = await web3.chainstack.getNetworks(); const networkResults = networks.results; let fullEndpoint = ""; // Find and construct the full endpoint from the first available node for (const network of networkResults) { if (network.nodes && network.nodes.length > 0) { const node = network.nodes[0]; const httpsEndpoint = node.details.https_endpoint; const authKey = node.details.auth_key; fullEndpoint = `${httpsEndpoint}/${authKey}`; console.log(`Network: ${network.name}`); console.log(`Node ID: ${node.id}`); console.log(`Full endpoint: ${fullEndpoint}`); console.log("--------"); break; // Exit the loop after finding the first valid node } } if (fullEndpoint) { // Create a new web3 instance using the full endpoint const web3 = new Web3(fullEndpoint); try { // Perform an operation using the new web3 instance const chainId = await web3.eth.getChainId(); console.log(`Chain ID: ${chainId}`); } catch (error) { console.error("Error performing web3 operation:", error); } } else { console.error("No valid nodes found to construct the endpoint."); } } main().catch(console.error); ``` This now ensures your endpoint is always correct, and you have fewer chances to expose it. This is the result: ```shell Shell theme={"system"} Network: Base Mainnet Node ID: ND-XXX-XXX-XXX Full endpoint: https://base-mainnet.core.chainstack.com/AUTH_KEY -------- Chain ID: 8453 ``` ### More endpoints The web3.js plugin implements more endpoints; the full list is on the [GitHub repository](https://github.com/ChainSafe/web3-plugin-chainstack?tab=readme-ov-file#features). ## Conclusion Integrating Chainstack's managed blockchain nodes with web3.js using the Chainstack plugin allows you to leverage secure, high-performance infrastructure with minimal setup and maintenance. Following this tutorial, you have learned how to set up your development environment, authenticate with Chainstack, and use the platform API to interact with your nodes dynamically. This integration ensures you can efficiently manage and use your blockchain resources, making your development process smoother and more robust. # Web3pedia Source: https://docs.chainstack.com/docs/web3pedia-introduction Browse the Chainstack Web3pedia for glossary terms, concept definitions, and reference guides covering blockchain technology and decentralized development. As this dynamic field continues to expand, our team of experts will add new definitions, ensuring that Web3pedia grows from a hundred definitions to thousands and beyond. We like to explain complex ideas and make them accessible to everyone. Whether you're an industry professional, a curious enthusiast, or a newcomer, our crypto glossary will guide you through the blockchain world. Web3pedia covers a wide range of topics, including: > [card] [Web3 lingo](/docs/web3-language-and-acronyms) > [card] [Development frameworks](/docs/web3-development-frameworks-and-libraries-glossary) > [card] [Smart contracts](/docs/smart-contracts-glossary) > [card] [Security](/docs/web3-security-glossary) > [card] [Infrastructure](/docs/web3-infrastructure-glossary) > [card] [Solana](/docs/solana-glossary) Our dedicated team of researchers, developers, and blockchain enthusiasts are constantly monitoring the latest developments in the Web3 space. As new terms, projects, and innovations emerge, we will add them to Web3pedia, ensuring that you have access to the most relevant information. As an evolving document, Web3pedia encourages user participation. We value your input, and we welcome suggestions for new terms, definitions, and updates. By collaborating with our community, we create a vibrant platform that reflects the diverse knowledge and perspectives of Web3 enthusiasts worldwide. So, if you saw some crypto terms on Twitter and you don't know what they mean, you will probably find those crypto definitions here. # Work with Marketplace Source: https://docs.chainstack.com/docs/work-with-chainstack-marketplace Install and manage third-party blockchain tools and APIs from the Chainstack Marketplace using your API key for seamless integration and billing. ## Basic workflow To start using Chainstack Marketplace, do the following: 1. Go to the [Marketplace in your console](https://console.chainstack.com/marketplace/). 2. Purchase and install the app you decided to use. 3. [Generate a Chainstack API key](/reference/platform-api-getting-started#create-api-key) that you will use to retrieve your JSON web token (JWT). 4. [Retrieve your JWT](https://api.chainstack.com/reference/#operation/retrieveToken) using Chainstack Platform API. 5. Interact with your purchased app directly. ## Install an app To install the app in the Chainstack Marketplace, do the following: 1. Go to the [Marketplace in your console](https://console.chainstack.com/marketplace/). 2. Click **Install** on the app you want to install. 3. Select the app subscription plan and click **Complete order and begin installation**. The payment will be made and the app will get installed to your console. If a payment is unsuccessful, [check your balance](/docs/manage-your-billing) and try again. ## Get access to an app To get access to the installed app, you will use a JSON web token issued through Chainstack platform API. To interact with the platform API, you must use an API key. If you don't have an API key, you can create one either in **Settings** > **API keys** or right on the application page **Dashboard**. With your API key, retrieve a JSON web token by using the [corresponding method](https://api.chainstack.com/reference/#operation/retrieveToken). Now you can interact with the app API by using vendor's methods. > [!WARNING] > > ### JWT expiration > > Note that an issued token is valid 1 hour only. Make sure to renew it in time. ## Delete an app By deleting an app, you automatically cancel your subscription to it. To delete an app and cancel your subscription to it, do the following: 1. Open the app page. 2. Go to **Settings**. 3. Click **Delete app**. 4. Enter the requested phrase into the text field and click **Yes, I'm sure**. The app gets uninstalled and your subscription to it gets canceled. Unused period will be calculated and returned to your Chainstack balance automatically. # World Chain methods Source: https://docs.chainstack.com/docs/worldchain-methods Complete reference of World Chain JSON-RPC methods supported on Chainstack, with per-method availability for the eth, debug, trace, net, and web3 namespaces. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). World Chain is an [OP Stack](https://docs.optimism.io/) layer 2 built by [World](https://world.org/). Chain ID is `480`. Chainstack nodes run the `world-chain` execution client, a fork of [Reth](https://github.com/paradigmxyz/reth), and report `world-chain/v2.4.0` from `web3_clientVersion`. The availability below applies to World Chain Mainnet on Global Nodes. ## Both debug and trace are available World Chain exposes the `debug_*` and the `trace_*` namespaces in full. Every trace method works, including `trace_block`, `trace_transaction`, `trace_filter`, `trace_callMany`, and `trace_replayBlockTransactions`. Both namespaces need a [paid plan](https://chainstack.com/pricing/) — see [Debug and trace APIs](/docs/debug-and-trace-apis#world-chain). For tracer selection, the native tracers `callTracer`, `prestateTracer`, `4byteTracer`, and `noopTracer` are enabled. `flatCallTracer` and `muxTracer` are not — see [Available native tracers](/docs/limits#available-native-tracers). > [!NOTE] > > `rpc_modules` is not available on World Chain — it returns `-32601 Method not found`. Do not use it to work out which namespaces you have; use the table below. ## The transaction pool is not served The `txpool_*` namespace is not available on World Chain. `txpool_status`, `txpool_content`, `txpool_contentFrom`, and `txpool_inspect` all return `-32601`. There is no archive-mode or plan upgrade that turns them on, because World Chain is an OP Stack chain and pending transactions stay with the sequencer. ## Pending transactions are accepted but never delivered `eth_newPendingTransactionFilter` returns a filter ID and `eth_subscribe("newPendingTransactions")` returns a subscription ID, so both look like they worked. Neither ever delivers a transaction — the subscription produced zero events over a 16-second window against a chain that was confirming transactions throughout. `eth_getBlockByNumber("pending")` does return a block, unlike on [Arc](/docs/arc-methods), so that call is safe to make. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access) for how World Chain compares with the other EVM chains. ## Filters on a load-balanced endpoint A [Global Node](/docs/global-elastic-node) endpoint is load balanced across backends, and a filter created by `eth_newFilter` or `eth_newBlockFilter` lives on the single backend that created it. A follow-up `eth_getFilterChanges` that lands on a different backend returns `-32602 filter not found`. Poll with [eth\_getLogs](/reference/ethereum-getlogs) over an explicit block range instead, or use a WSS subscription, which holds one connection to one backend for its lifetime. ## Block time and archive billing World Chain produces a block roughly every 2 seconds. The standard EVM rule — 127 or more blocks behind the tip bills as archive — therefore covers about 4 minutes of history, against roughly 25 minutes on Ethereum. Plan for historical queries to bill as archive sooner than they would on Ethereum. ## Legacy debug methods The `world-chain` client inherits Reth's behavior of registering a number of Geth-era `debug_*` methods and then returning `null` for them rather than an error. They are marked unavailable below, because a `null` response carries no data. Use `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceBlockByHash`, and `debug_traceCall` instead, or `eth_getStorageAt` in place of `debug_storageRangeAt` — see [Using eth\_getStorageAt instead of debug\_storageRangeAt on Reth](/docs/using-eth_getstorageat-instead-of-debug_storagerangeat-on-reth). ## Method availability | Method | Availability | Comment | | - | - | - | | debug\_accountRange | | registered but returns `null` on Reth | | debug\_chaindbProperty | | returns `-32601` | | debug\_codeByHash | | registered but returns `null` on Reth | | debug\_dumpBlock | | returns `-32601` | | debug\_getAccessibleState | | returns `-32601` | | debug\_getBadBlocks | | returns an empty array | | debug\_getModifiedAccountsByHash | | registered but returns `null` on Reth | | debug\_getModifiedAccountsByNumber | | returns `-32602 Invalid params` for every argument form | | debug\_getRawBlock | | | | debug\_getRawHeader | | | | debug\_getRawReceipts | | | | debug\_getRawTransaction | | | | debug\_getTrieFlushInterval | | not implemented by Reth | | debug\_intermediateRoots | | | | debug\_preimage | | registered but returns `null` on Reth | | debug\_printBlock | | registered but returns `null` on Reth | | debug\_storageRangeAt | | registered but returns `null` on Reth — use `eth_getStorageAt` | | debug\_traceBadBlock | | returns `-32603 bad block not found in cache` when the chain has no bad blocks | | debug\_traceBlock | | takes an RLP-encoded block | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | eth\_accounts | | returns an empty array — no unlocked accounts on a hosted node | | eth\_blobBaseFee | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_coinbase | | not implemented by Reth — returns `-32603 unimplemented` | | eth\_createAccessList | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | filters are node-local — see [Filters on a load-balanced endpoint](#filters-on-a-load-balanced-endpoint) | | eth\_getFilterLogs | | | | eth\_getHeaderByHash | | | | eth\_getHeaderByNumber | | | | eth\_getLogs | | | | eth\_getProof | | | | eth\_getRawTransactionByHash | | | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionBySenderAndNonce | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleByBlockHashAndIndex | | always null — World Chain has no uncles | | eth\_getUncleByBlockNumberAndIndex | | always null — World Chain has no uncles | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_hashrate | | always `0x0` | | eth\_maxPriorityFeePerGas | | | | eth\_mining | | not implemented by Reth — returns `-32603 unimplemented` | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | accepts the filter but never returns a transaction — see [Pending transactions](#pending-transactions-are-accepted-but-never-delivered) | | eth\_protocolVersion | | | | eth\_sendRawTransaction | | | | eth\_sendRawTransactionSync | | waits for the receipt instead of returning the hash | | eth\_simulateV1 | | returns `-32603 Internal error` for every request shape tested | | eth\_subscribe | | WSS. `newHeads`, `logs`, and `syncing` deliver. `newPendingTransactions` subscribes but delivers nothing | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | WSS | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | rpc\_modules | | returns `-32601 Method not found` | | trace\_block | | | | trace\_call | | | | trace\_callMany | | | | trace\_filter | | | | trace\_get | | | | trace\_rawTransaction | | | | trace\_replayBlockTransactions | | | | trace\_replayTransaction | | | | trace\_transaction | | | | txpool\_content | | the `txpool_*` namespace is not served on World Chain | | txpool\_contentFrom | | the `txpool_*` namespace is not served on World Chain | | txpool\_inspect | | the `txpool_*` namespace is not served on World Chain | | txpool\_status | | the `txpool_*` namespace is not served on World Chain | | web3\_clientVersion | | | | web3\_sha3 | | | # World Chain tooling Source: https://docs.chainstack.com/docs/worldchain-tooling Connect MetaMask, ethers.js, viem, and web3.py to a World Chain node on Chainstack. Chain configuration, and the three behaviors that affect tooling. World Chain is an [OP Stack](https://docs.optimism.io/) layer 2, so the standard Ethereum libraries work against a World Chain node without modification. Gas is paid in ETH exactly as on Ethereum mainnet. ## Chain configuration | Property | Value | | - | - | | Network | World Chain Mainnet | | Chain ID | `480` | | Native gas currency | ETH | | Decimals | `18` | | Block time | \~2 seconds | | RPC URL | your Chainstack [World Chain endpoint](/docs/manage-your-node#view-node-access-and-credentials) | | Multicall3 | `0xcA11bde05977b3631167028862bE2a173976CA11` | | Explorer | [worldscan.org](https://worldscan.org) | See [World Chain methods](/docs/worldchain-methods) for per-method availability and [Debug and trace APIs](/docs/debug-and-trace-apis#world-chain) for the tracing namespaces. ## MetaMask Add World Chain Mainnet as a custom network. On Chainstack, get your [World Chain endpoint](/docs/manage-your-node#view-node-access-and-credentials), then in MetaMask select **Add a custom network** and fill in: * Network name — World Chain * Default RPC URL — your Chainstack World Chain endpoint * Chain ID — `480` * Currency symbol — ETH * Block explorer URL — `https://worldscan.org` ## ethers.js Install [ethers.js](https://docs.ethers.org/): ```shell Shell theme={"system"} npm install ethers ``` ```javascript index.js theme={"system"} const { JsonRpcProvider, formatEther } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function main() { const network = await provider.getNetwork(); console.log("Chain ID:", network.chainId.toString()); const block = await provider.getBlockNumber(); console.log("Block:", block); const balance = await provider.getBalance("0xC2Ca6E8f5764E377F72A206Ab6E2805607aC405a"); console.log("Balance:", formatEther(balance), "ETH"); } main(); ``` ## viem World Chain ships in viem's bundled chain list as `worldchain`, so you do not need `defineChain`: ```shell Shell theme={"system"} npm install viem ``` ```javascript index.mjs theme={"system"} import { createPublicClient, http, formatEther } from "viem"; import { worldchain } from "viem/chains"; const client = createPublicClient({ chain: worldchain, transport: http("CHAINSTACK_NODE_URL"), }); console.log("Chain ID:", await client.getChainId()); console.log("Block:", await client.getBlockNumber()); const balance = await client.getBalance({ address: "0xC2Ca6E8f5764E377F72A206Ab6E2805607aC405a", }); console.log("Balance:", formatEther(balance), "ETH"); ``` Pass your Chainstack endpoint to `http()` as above. The bundled chain definition carries a public RPC URL, and `http()` with no argument would use that instead of your node. ## web3.py Install [web3.py](https://web3py.readthedocs.io/): ```shell Shell theme={"system"} pip install web3 ``` ```python main.py theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider("CHAINSTACK_NODE_URL")) print("Connected:", web3.is_connected()) print("Chain ID:", web3.eth.chain_id) print("Block:", web3.eth.block_number) address = Web3.to_checksum_address("0xc2ca6e8f5764e377f72a206ab6e2805607ac405a") balance = web3.eth.get_balance(address) print("Balance:", web3.from_wei(balance, "ether"), "ETH") ``` > [!NOTE] > > web3.py rejects lowercase addresses with `InvalidAddress`. Wrap any address you did not get from the library itself in `Web3.to_checksum_address()`, as above. ## Three World Chain behaviors that affect tooling **There is no transaction pool to read.** The `txpool_*` namespace is not served — `txpool_status`, `txpool_content`, `txpool_contentFrom`, and `txpool_inspect` all return `-32601`. No mode or plan change turns them on, because World Chain is an OP Stack chain and pending transactions stay with the sequencer. **Pending-transaction subscriptions succeed and then deliver nothing.** `eth_subscribe("newPendingTransactions")` returns a subscription ID and `eth_newPendingTransactionFilter` returns a filter ID, so neither call looks like it failed. No transaction ever arrives. `newHeads` and `logs` subscriptions both work normally. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). **Filters are node-local.** A [Global Node](/docs/global-elastic-node) endpoint is load balanced across backends, and a filter created by `eth_newFilter` or `eth_newBlockFilter` lives on the backend that created it. A follow-up `eth_getFilterChanges` that lands elsewhere returns `-32602 filter not found`. Poll [eth\_getLogs](/reference/ethereum-getlogs) over an explicit block range instead, or hold a WSS subscription, which keeps one connection to one backend for its lifetime. # X Layer tooling Source: https://docs.chainstack.com/docs/xlayer-tooling Connect MetaMask, ethers.js, viem, web3.py, Foundry, and Hardhat to an X Layer Mainnet or Testnet node on Chainstack. Chain configuration, the OKB gas token, contract verification on Sourcify, and the X Layer behaviors that affect tooling. X Layer is OKX's Ethereum layer 2, built on the [OP Stack](https://docs.optimism.io/) with OKB as the gas token. It is EVM-equivalent, so the standard Ethereum libraries and frameworks work against an X Layer node without modification. Chainstack serves X Layer Mainnet and X Layer Testnet on Global Nodes with archive data and both the `debug_*` and `trace_*` namespaces. See [Debug and trace APIs](/docs/debug-and-trace-apis#x-layer). ## Chain configuration | Property | Mainnet | Testnet | | - | - | - | | Chain ID | `196` | `1952` | | Native gas currency | OKB | OKB | | Decimals | `18` | `18` | | Block time | 1 second | 1 second | | RPC URL | your Chainstack [X Layer endpoint](/docs/manage-your-node#view-node-access-and-credentials) | your Chainstack [X Layer endpoint](/docs/manage-your-node#view-node-access-and-credentials) | | Multicall3 | `0xcA11bde05977b3631167028862bE2a173976CA11` | `0xcA11bde05977b3631167028862bE2a173976CA11` | | Block explorer | [web3.okx.com/explorer/x-layer](https://web3.okx.com/explorer/x-layer) | [web3.okx.com/explorer/x-layer-testnet](https://web3.okx.com/explorer/x-layer-testnet) | | Faucet | — | [web3.okx.com/xlayer/faucet](https://web3.okx.com/xlayer/faucet) | ## MetaMask Add X Layer as a custom network. On Chainstack, get your [X Layer endpoint](/docs/manage-your-node#view-node-access-and-credentials), then in MetaMask select **Add a custom network** and fill in: * Network name — X Layer Mainnet * Default RPC URL — your Chainstack X Layer Mainnet endpoint * Chain ID — `196` * Currency symbol — OKB * Block explorer URL — `https://web3.okx.com/explorer/x-layer` For X Layer Testnet, use your Chainstack X Layer Testnet endpoint, chain ID `1952`, and the block explorer URL `https://web3.okx.com/explorer/x-layer-testnet`. ## ethers.js Install [ethers.js](https://docs.ethers.org/): ```shell Shell theme={"system"} npm install ethers ``` ```javascript index.js theme={"system"} const { JsonRpcProvider, formatEther } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function main() { const network = await provider.getNetwork(); console.log("Chain ID:", network.chainId.toString()); const block = await provider.getBlockNumber(); console.log("Block:", block); const balance = await provider.getBalance("0xe538905cf8410324e03A5A23C1c177a474D59b2b"); console.log("Balance:", formatEther(balance), "OKB"); } main(); ``` The address in the example is the wrapped OKB (WOKB) contract on X Layer Mainnet, which holds the OKB deposited into it. To receive new blocks as X Layer produces them, connect to the WSS endpoint and subscribe: ```javascript index.js theme={"system"} const { WebSocketProvider } = require("ethers"); const provider = new WebSocketProvider("CHAINSTACK_WSS_URL"); provider.on("block", (blockNumber) => { console.log("New block:", blockNumber); }); ``` ## viem viem ships `xLayer` (chain `196`) and `xLayerTestnet` (chain `1952`) chain definitions in `viem/chains`. Their default RPC URLs are OKX's public endpoints, so pass your Chainstack endpoint to `http()`. Both definitions include the Multicall3 address, so `client.multicall` batches contract reads into a single `eth_call`. ```shell Shell theme={"system"} npm install viem ``` ```javascript index.mjs theme={"system"} import { createPublicClient, http, formatEther, erc20Abi } from "viem"; import { xLayer } from "viem/chains"; const client = createPublicClient({ chain: xLayer, transport: http("CHAINSTACK_NODE_URL"), }); console.log("Chain ID:", await client.getChainId()); console.log("Block:", await client.getBlockNumber()); const wokb = "0xe538905cf8410324e03A5A23C1c177a474D59b2b"; const balance = await client.getBalance({ address: wokb }); console.log("Balance:", formatEther(balance), "OKB"); const [symbol, totalSupply] = await client.multicall({ contracts: [ { address: wokb, abi: erc20Abi, functionName: "symbol" }, { address: wokb, abi: erc20Abi, functionName: "totalSupply" }, ], allowFailure: false, }); console.log(symbol, "supply:", formatEther(totalSupply)); ``` For X Layer Testnet, import `xLayerTestnet` instead and pass your Chainstack X Layer Testnet endpoint. ## web3.py Install [web3.py](https://web3py.readthedocs.io/): ```shell Shell theme={"system"} pip install web3 ``` ```python main.py theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider("CHAINSTACK_NODE_URL")) print("Connected:", web3.is_connected()) print("Chain ID:", web3.eth.chain_id) print("Block:", web3.eth.block_number) address = Web3.to_checksum_address("0xe538905cf8410324e03a5a23c1c177a474d59b2b") balance = web3.eth.get_balance(address) print("Balance:", web3.from_wei(balance, "ether"), "OKB") ``` `from_wei(balance, "ether")` converts from the smallest unit with 18 decimals, which is the same conversion for OKB as for ETH. ## Foundry Install [Foundry](https://getfoundry.sh/) and pass your Chainstack endpoint with `--rpc-url`. Read chain data with `cast`: ```shell Shell theme={"system"} cast chain-id --rpc-url CHAINSTACK_NODE_URL cast block-number --rpc-url CHAINSTACK_NODE_URL cast balance 0xe538905cf8410324e03A5A23C1c177a474D59b2b --ether --rpc-url CHAINSTACK_NODE_URL ``` Deploy a contract with `forge create`. Without `--broadcast`, `forge create` only simulates the deployment. ```shell Shell theme={"system"} forge create src/Counter.sol:Counter \ --rpc-url CHAINSTACK_NODE_URL \ --private-key YOUR_PRIVATE_KEY \ --broadcast ``` Verify the deployed contract on [Sourcify](https://sourcify.dev/), which supports both X Layer networks and does not need an API key. For X Layer Testnet, pass `--chain 1952`. ```shell Shell theme={"system"} forge verify-contract DEPLOYED_CONTRACT_ADDRESS src/Counter.sol:Counter \ --chain 196 \ --verifier sourcify \ --watch ``` X Layer's own explorers take verifications through OKLink, which needs an OKLink API key. See X Layer's [contract verification guide](https://web3.okx.com/onchainos/dev-docs/xlayer/developer/verify-a-smart-contract/verifying-contract). ## Hardhat In a [Hardhat 3](https://hardhat.org/) project that uses `@nomicfoundation/hardhat-toolbox-viem`, add the X Layer networks to `hardhat.config.ts`: ```typescript hardhat.config.ts theme={"system"} import { configVariable, defineConfig } from "hardhat/config"; import hardhatToolboxViem from "@nomicfoundation/hardhat-toolbox-viem"; export default defineConfig({ plugins: [hardhatToolboxViem], solidity: "0.8.28", networks: { xlayer: { type: "http", url: configVariable("XLAYER_RPC_URL"), accounts: [configVariable("XLAYER_PRIVATE_KEY")], }, xlayerTestnet: { type: "http", url: configVariable("XLAYER_TESTNET_RPC_URL"), accounts: [configVariable("XLAYER_PRIVATE_KEY")], }, }, }); ``` `configVariable` reads the endpoints and the key from environment variables or the Hardhat keystore, which keeps them out of the config file. Deploy with an Ignition module, for example `ignition/modules/Counter.ts`: ```typescript ignition/modules/Counter.ts theme={"system"} import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; export default buildModule("CounterModule", (m) => { const counter = m.contract("Counter"); return { counter }; }); ``` Then verify on Sourcify with the `verify sourcify` task. Hardhat has no built-in Etherscan or Blockscout explorer for X Layer, so the plain `hardhat verify` task reports the chain as not supported for those two providers. ```shell Shell theme={"system"} npx hardhat ignition deploy ignition/modules/Counter.ts --network xlayer npx hardhat verify sourcify --network xlayer DEPLOYED_CONTRACT_ADDRESS ``` ## X Layer behaviors that affect tooling ### Transactions pay no L1 data fee X Layer sets the L1 fee scalars in the `GasPriceOracle` predeploy at `0x420000000000000000000000000000000000000F` to zero, so the OP Stack L1 data fee is zero. Transaction receipts still carry the `l1Fee`, `l1GasUsed`, `l1GasPrice`, and `l1BlobBaseFee` fields, with `l1Fee` at `0x0`, and the total cost of a transaction is `gasUsed × effectiveGasPrice`. ### Pending transactions are never delivered X Layer is an OP Stack chain, so pending transactions stay with the sequencer. `eth_subscribe("newPendingTransactions")` and `eth_newPendingTransactionFilter` return an ID but deliver nothing, and `txpool_status` always reads empty. To follow transactions before the sequencer seals their block on X Layer Mainnet, use [Flashblocks on X Layer Mainnet](#flashblocks-on-x-layer-mainnet). See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ### Flashblocks on X Layer Mainnet Chainstack X Layer Mainnet endpoints serve Flashblocks, the sequencer's preconfirmations of the block in progress. X Layer Testnet endpoints do not. * `eth_getBlockByNumber("pending")` on Mainnet returns the next block with the transactions preconfirmed so far, and the block gains transactions until the sequencer seals it. On Testnet, the `pending` tag returns the latest block. * `eth_flashblocksEnabled` returns `true` on Mainnet and `false` on Testnet. * `eth_subscribe("flashblocks", filter)` over the WebSocket endpoint streams preconfirmed block headers and transactions on Mainnet. Each event has a `type` of `header` or `transaction`. The `flashblocks` subscription filter takes these fields: * `headerInfo` — `true` to receive `header` events for the block in progress, several per block as preconfirmations arrive. * `subTxFilter.subscribeAddresses` — receive only transactions whose sender, recipient, or log-emitting contract is one of these addresses. * `subTxFilter.txInfo` — `true` to include the full transaction in each `transaction` event. * `subTxFilter.txReceipt` — `true` to include the receipt in each `transaction` event. This subscription streams every preconfirmed transaction that calls the L1 attributes predeploy, with its receipt, one transaction per block: ```json Request theme={"system"} {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["flashblocks",{"subTxFilter":{"subscribeAddresses":["0x4200000000000000000000000000000000000015"],"txInfo":true,"txReceipt":true}}]} ``` ### Block-count limits and the proof window X Layer produces a block every second, so a 10,000-block `eth_getLogs` request covers under 3 hours of chain time. `eth_getProof` serves the latest 256 blocks, about 4 minutes of chain time. See [EVM range limits](/docs/limits#evm-range-limits) and [EVM `eth_getProof` history limits](/docs/limits#evm-eth_getproof-history-limits). # XRP Ledger tooling Source: https://docs.chainstack.com/docs/xrpl-tooling Connect to your Chainstack XRP Ledger node over the rippled JSON-RPC API on HTTPS and the WebSocket API on WSS with curl, wscat, xrpl-py, and xrpl.js, including ledger history limits, transaction submission, and ledger subscriptions. Get started with a [reliable XRP Ledger RPC endpoint](https://chainstack.com/build-better-with-xrp-ledger/) to use the tools below. Your Chainstack XRP Ledger node serves the [rippled API](https://xrpl.org/docs/references/http-websocket-apis) two ways: JSON-RPC over the HTTPS endpoint, and the WebSocket API over the WSS endpoint. The official SDKs — [xrpl.js](https://github.com/XRPLF/xrpl.js) and [xrpl-py](https://github.com/XRPLF/xrpl-py) — connect over WSS, and xrpl-py also connects over HTTPS. XRP Ledger has its own method set and does not implement the Ethereum `eth_*` interface, so EVM tooling — ethers.js, web3.py, Hardhat — does not apply here. ## JSON-RPC over HTTPS Every call is an HTTP POST carrying a `method` and a `params` array. The `params` array holds exactly one object, even when a method takes no arguments — a bare `{}` rather than an empty array. ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H 'Content-Type: application/json' \ -d '{"method":"server_info","params":[{}]}' ``` The `result` object reports the client version, the ledgers this node holds, and its sync state: ```json Response (excerpt) theme={"system"} { "build_version": "3.3.0", "complete_ledgers": "106106987-107266543", "server_state": "full" } ``` Replace `YOUR_CHAINSTACK_ENDPOINT` with your node's HTTPS endpoint. For the credential in that endpoint and the other ways to authenticate, see [Authentication methods for different scenarios](/docs/authentication-methods-for-different-scenarios). ## WebSocket API over WSS The WSS endpoint serves the rippled WebSocket API, which frames requests differently from JSON-RPC: each message names the method in a `command` field and carries its arguments at the top level, with an optional `id` that the response echoes. A WebSocket connection also supports [subscriptions](https://xrpl.org/docs/references/http-websocket-apis/public-api-methods/subscription-methods/subscribe), which push events such as closed ledgers and validated transactions as they happen. ```bash wscat theme={"system"} wscat -c YOUR_CHAINSTACK_WSS_ENDPOINT > {"id": 1, "command": "subscribe", "streams": ["ledger"]} ``` The node answers with the subscription result, then sends a `ledgerClosed` message for every new validated ledger: ```json Response theme={"system"} {"id":1,"result":{"fee_base":10,"ledger_hash":"9FF17722348FDC9F54AC383A8EF40B62BD37B12153D77C611351FDD75C1CCF44","ledger_index":107266586,"ledger_time":843812440,"network_id":0,"reserve_base":1000000,"reserve_inc":200000,"validated_ledgers":"106106987-107266586"},"status":"success","type":"response"} {"fee_base":10,"ledger_hash":"582D06B6C94C4F897F1B0F5411DD5FDF523EDA20B4D2895D2920D265FF9F2D9A","ledger_index":107266587,"ledger_time":843812441,"network_id":0,"reserve_base":1000000,"reserve_inc":200000,"txn_count":75,"type":"ledgerClosed","validated_ledgers":"106106987-107266587"} ``` Replace `YOUR_CHAINSTACK_WSS_ENDPOINT` with your node's WSS endpoint, which carries the same auth token as the HTTPS endpoint. ## How much ledger history your node holds XRP Ledger Global Nodes are [Extended](/docs/extended-nodes) — they keep more recent ledgers than a default `rippled` server holds, but not the full chain. The node deletes old ledgers in rotation and holds between 750,000 and 1,500,000 of the most recent ledgers. At the Mainnet close rate of about 3.8 seconds per ledger, that is about 33 to 66 days of history. Read `complete_ledgers` from `server_info` at runtime and work within it. The lower bound jumps forward each time the node rotates out old ledgers, so treat any specific depth as a moving target rather than a guarantee. Requesting anything below the window returns `lgrNotFound`: ```json Response theme={"system"} { "result": { "error": "lgrNotFound", "error_message": "ledgerNotFound", "status": "error" } } ``` The same floor applies to transaction history and to account state alike — `account_tx` clamps its search range to the retained window rather than reaching further back. For queries older than the window, use a full-history source such as an [XRP Ledger public full-history server](https://xrpl.org/docs/concepts/networks-and-servers/ledger-history). ## Python Use the official [xrpl-py](https://github.com/XRPLF/xrpl-py) SDK. Its `JsonRpcClient` talks to your node over HTTPS: ```python Python theme={"system"} # pip install xrpl-py from xrpl.clients import JsonRpcClient from xrpl.models.requests import AccountInfo, ServerInfo client = JsonRpcClient("YOUR_CHAINSTACK_ENDPOINT") info = client.request(ServerInfo()).result["info"] print("rippled", info["build_version"], "| ledgers on this node:", info["complete_ledgers"]) account = client.request(AccountInfo( account="rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", ledger_index="validated", )).result["account_data"] print("balance (drops):", account["Balance"], "| sequence:", account["Sequence"]) ``` This prints the node's client version, its retained range, and the queried account's balance in drops: ```text Output theme={"system"} rippled 3.3.0 | ledgers on this node: 106106987-107266543 balance (drops): 56775133590 | sequence: 44196 ``` For subscriptions, use `AsyncWebsocketClient` over the WSS endpoint. This script subscribes to the `ledger` stream and prints each closed ledger: ```python Python theme={"system"} # pip install xrpl-py import asyncio from xrpl.asyncio.clients import AsyncWebsocketClient from xrpl.models.requests import StreamParameter, Subscribe async def main(): async with AsyncWebsocketClient("YOUR_CHAINSTACK_WSS_ENDPOINT") as client: await client.send(Subscribe(streams=[StreamParameter.LEDGER])) async for message in client: if message.get("type") == "ledgerClosed": print("ledger", message["ledger_index"], "closed with", message["txn_count"], "transactions") asyncio.run(main()) ``` ```text Output theme={"system"} ledger 107266567 closed with 95 transactions ledger 107266568 closed with 76 transactions ledger 107266569 closed with 110 transactions ``` ## JavaScript Use the official [xrpl.js](https://github.com/XRPLF/xrpl.js) SDK. Its `Client` connects to your node over the WSS endpoint: ```javascript JavaScript theme={"system"} // npm install xrpl import { Client } from "xrpl"; const client = new Client("YOUR_CHAINSTACK_WSS_ENDPOINT"); await client.connect(); const { result } = await client.request({ command: "server_info" }); console.log("rippled", result.info.build_version, "| ledgers:", result.info.complete_ledgers); const { result: account } = await client.request({ command: "account_info", account: "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", ledger_index: "validated", }); console.log("balance (drops):", account.account_data.Balance, "| sequence:", account.account_data.Sequence); await client.disconnect(); ``` ```text Output theme={"system"} rippled 3.3.0 | ledgers: 106106987-107266543 balance (drops): 56775133590 | sequence: 44196 ``` ### Sign and submit a transaction `submitAndWait` fills in `Fee`, `Sequence`, and `LastLedgerSequence` from the node, signs the transaction with the wallet locally, submits it, and resolves once a validated ledger contains it: ```javascript JavaScript theme={"system"} // npm install xrpl import { Client, Wallet, xrpToDrops } from "xrpl"; const client = new Client("YOUR_CHAINSTACK_WSS_ENDPOINT"); await client.connect(); const wallet = Wallet.fromSeed("YOUR_SEED"); const { result } = await client.submitAndWait( { TransactionType: "Payment", Account: wallet.classicAddress, Destination: "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe", Amount: xrpToDrops("1"), }, { wallet }, ); console.log(result.meta.TransactionResult, "in validated ledger", result.ledger_index); await client.disconnect(); ``` On Testnet, a successful payment prints: ```text Output theme={"system"} tesSUCCESS in validated ledger 21087026 ``` ### Track new ledgers Subscribe to the `ledger` stream and handle the `ledgerClosed` event. The node pushes one message for every validated ledger, every few seconds: ```javascript JavaScript theme={"system"} // npm install xrpl import { Client } from "xrpl"; const client = new Client("YOUR_CHAINSTACK_WSS_ENDPOINT"); await client.connect(); client.on("ledgerClosed", (ledger) => { console.log(`ledger ${ledger.ledger_index} closed with ${ledger.txn_count} transactions`); }); await client.request({ command: "subscribe", streams: ["ledger"] }); ``` ```text Output theme={"system"} ledger 107266544 closed with 87 transactions ledger 107266545 closed with 86 transactions ledger 107266546 closed with 115 transactions ``` To follow validated transactions instead, subscribe to the `transactions` stream and handle the `transaction` event. # Yellowstone gRPC Geyser plugin Source: https://docs.chainstack.com/docs/yellowstone-grpc-geyser-plugin Stream real-time Solana data with the Yellowstone gRPC Geyser plugin and learn how to build analytics, trading, and indexing tools. *Solana is fast. Geyser is faster.* Yellowstone gRPC Geyser is a high-speed, plugin-based interface that streams live Solana blockchain data directly from validator memory, eliminating the latency of traditional RPC methods. > [!TIP] > > ### Geyser is available starting from Growth > > Geyser is available as a separate paid add-on starting from the [Growth plan](https://chainstack.com/pricing). > [!NOTE] > > ### Mainnet only > > The Yellowstone gRPC Geyser plugin is only available on Solana mainnet. It is not available on Solana devnet. Benefits * Real-time streaming of transactions, account updates, blocks, and slot changes. * Uses a standard gRPC interface with protobuf-formatted payloads. * Accessible from any language supporting gRPC (Python, Go, Rust, JavaScript). * Ideal for analytics, sniping bots, MEV strategies, data indexing, real-time dashboards, and compliance monitoring. * `from_slot`/`fromSlot` support to replay recent slots and recover events missed during a brief disconnect. For an example, see `learning-examples/recover_missed_slots_with_from_slot.py` in [Solana Geyser Python tutorial](https://github.com/chainstacklabs/grpc-geyser-tutorial). The replay buffer holds a limited number of recent slots, not full history. See [Replay depth with from\_slot](#replay-depth-with-from_slot). ## Replay depth with from\_slot The `from_slot` field replays recent slots from a server-side ring buffer, so a client that briefly disconnects can reconnect and catch up without missing events. It is a reconnection-recovery mechanism, not a historical backfill. On Global Nodes, the buffer holds approximately the last 100 slots (around a minute). Requesting a `from_slot` older than the buffer returns an error: ```text theme={"system"} broadcast from is not available, last available: ``` For arbitrary historical data beyond the buffer, use the standard JSON-RPC methods ([`getBlock`](/reference/solana-getblock), [`getSignaturesForAddress`](/reference/solana-getsignaturesforaddress), and [`getTransaction`](/reference/gettransaction)) instead of `from_slot`. > [!NOTE] > > Need a deeper replay window? [Dedicated Nodes](/docs/dedicated-node) can be configured with a larger `from_slot` buffer for latency-sensitive workloads. [Contact us](https://chainstack.com/contact/) to set this up. ## Enable the Yellowstone gRPC Geyser plugin 1. Deploy a Solana [Global Node](/docs/global-elastic-node) 2. Click on your deployed Solana node. 3. Click **Add-ons** > **Yellowstone gRPC Geyser Plugin** > **Install**. ## Connect to your endpoint Once the add-on is installed, the node's **Access and credentials** view gains a **Yellowstone gRPC** section with the gRPC endpoint and the x-token. This endpoint is separate from the node's HTTPS and WSS endpoints, and the token travels in request metadata rather than in the URL — see [gRPC access via x-token](/docs/authentication-methods-for-different-scenarios#grpc-access-via-x-token). The x-token is the same value as the node's auth token, so if your RPC endpoint already works you already have it. Geyser listens on port **443** with TLS. On Global Nodes, the console shows the full endpoint, `https://yellowstone-solana-mainnet.core.chainstack.com:443`. On Dedicated Nodes, it shows a bare hostname with no scheme or port. Whichever form the console shows, pass your client the form it expects, where `HOST` is the hostname alone — `yellowstone-solana-mainnet.core.chainstack.com` on Global Nodes: | Client | Endpoint form | | - | - | | `@triton-one/yellowstone-grpc` (TypeScript), `yellowstone-grpc-client` (Rust) | `https://HOST:443` — the scheme is required | | `grpc` (Python), `grpc-go` (Go), `grpcurl` | `HOST:443` — host and port only, no scheme | The two forms are not interchangeable. A TypeScript or Rust client given a bare host fails to connect, and a Python or Go client given a scheme fails to resolve it. ```js Node.js theme={"system"} const { default: Client } = require("@triton-one/yellowstone-grpc"); const client = new Client("https://yellowstone-solana-mainnet.core.chainstack.com:443", "YOUR_X_TOKEN"); console.log(await client.getVersion()); ``` ```python Python theme={"system"} import grpc auth = grpc.metadata_call_credentials( lambda ctx, cb: cb((("x-token", "YOUR_X_TOKEN"),), None) ) creds = grpc.composite_channel_credentials(grpc.ssl_channel_credentials(), auth) channel = grpc.aio.secure_channel("yellowstone-solana-mainnet.core.chainstack.com:443", creds) ``` For full working examples, see [Listening to programs using Geyser and Yellowstone gRPC (Node.js)](/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js) and [Solana: transactions and block time with Yellowstone gRPC](/docs/solana-transactions-block-time-yellowstone-grpc). ## Limits \$49/month: * Unlimited stream events * 2 concurrent streams available per platform account * Up to 200 accounts per filter \$149/month: * Unlimited stream events * 7 concurrent streams available per platform account * Up to 200 accounts per filter \$449/month: * Unlimited stream events * 25 concurrent streams available per platform account * Up to 200 accounts per filter ### Filter limits On Global Nodes, regardless of the plan, a single subscribe request can include up to the following number of filters of each type: | Filter type | Maximum filters per request | | - | - | | `accounts` | 5 | | `slots` | 5 | | `transactions` | 20 | | `transactions_status` | 10 | | `blocks` | 10 | | `blocks_meta` | 5 | | `entries` | 5 | Within those filters, the following limits apply: * Each public key list in a filter holds up to 200 keys — `account` and `owner` in `accounts` filters; `account_include`, `account_exclude`, and `account_required` in `transactions` and `transactions_status` filters; and `account_include` in `blocks` filters. * A subscribe request can include up to 2 `accounts_data_slice` entries. * An `accounts` filter must set `account` or `owner`, and a `transactions` or `transactions_status` filter must set at least one condition, such as `account_include` or `vote`. A filter without conditions, which would stream every update of its type, is rejected. * The SPL Token Program (`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) is rejected in the `account` field of `accounts` filters and in the `account_include` field of `transactions`, `transactions_status`, and `blocks` filters. * The System Program (`11111111111111111111111111111111`) is rejected in the `owner` field of `accounts` filters. A subscribe request that exceeds any of these limits is rejected. On a [Dedicated Node](/docs/dedicated-node) with the Yellowstone gRPC Geyser plugin, the filter limits can be customized. ## Tooling See: * [Solana Geyser Python tutorial](https://github.com/chainstacklabs/grpc-geyser-tutorial) * [Pump fun and Bonk fun sniping bot with Geyser](https://github.com/chainstacklabs/pumpfun-bonkfun-bot) * [Solana: Listening to pump.fun token mint using Geyser](/docs/solana-listening-to-pumpfun-token-mint-using-geyser) * [Solana: Listening to programs using Geyser and Yellowstone gRPC (Node.js)](/docs/solana-listening-to-programs-using-geyser-and-yellowstone-grpc-node-js) * [Yellowstone repository](https://github.com/rpcpool/yellowstone-grpc) * [Rust example](https://github.com/rpcpool/yellowstone-grpc/tree/master/examples/rust) * [Go example](https://github.com/rpcpool/yellowstone-grpc/tree/master/examples/golang) # Zcash tooling Source: https://docs.chainstack.com/docs/zcash-tooling Connect to a Chainstack Zcash node over JSON-RPC with curl, Python, and JavaScript on Mainnet and Testnet. Network details, the transparent address index, shielded pool state methods, and the methods available on Zebra. Chainstack Zcash nodes run [Zebra](https://github.com/ZcashFoundation/zebra), the Zcash Foundation's node, and serve its Bitcoin-style [JSON-RPC API](https://docs.rs/zebra-rpc/latest/zebra_rpc/methods/trait.RpcServer.html) over HTTPS. Send requests with curl or any HTTP client. The same calls work on Zcash Mainnet and Testnet, and the nodes serve transparent transaction history from the first block. ## Network details | Property | Mainnet | Testnet | | - | - | - | | `chain` in `getblockchaininfo` | `main` | `test` | | Currency | ZEC | TAZ | | Smallest unit | zatoshi, 1 ZEC = 100,000,000 zatoshis | zatoshi | | Transparent address prefixes | `t1`, `t3` | `tm`, `t2` | | Block explorer | [mainnet.zcashexplorer.app](https://mainnet.zcashexplorer.app) | [testnet.zecblock.com](https://testnet.zecblock.com) | | Faucet | — | [faucet.testnet.valargroup.dev](https://faucet.testnet.valargroup.dev) | Zcash Testnet activated Network Upgrade 7 (NU7) at block 4,465,026. Software without NU7 support stayed on a chain that split from Testnet at that block, so before you compare results with an explorer or another node, check that it returns the same block hash at height 4,465,026 as your Chainstack endpoint. ## JSON-RPC API To get basic network information from your Zcash node HTTPS endpoint, call `getblockchaininfo`: ```bash cURL theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"1.0","id":1,"method":"getblockchaininfo","params":[]}' ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). The response lists the network upgrades and their activation heights in `upgrades`, and the balances of the transparent and shielded pools in `valuePools`. ## Address index Zebra indexes transparent addresses, so you can look up an address's balance, transaction history, and unspent outputs without running an indexer. Each method takes an object with an `addresses` array: * `getaddressbalance` — the current balance and the total received, in zatoshis. * `getaddresstxids` — the IDs of the transactions that touch the addresses, optionally limited to a block range with `start` and `end`. * `getaddressutxos` — the unspent outputs of the addresses, with the amount of each in the `satoshis` field, in zatoshis. ```bash getaddressbalance theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"1.0","id":1,"method":"getaddressbalance","params":[{"addresses":["t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd"]}]}' ``` ```bash getaddresstxids theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"1.0","id":1,"method":"getaddresstxids","params":[{"addresses":["t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd"],"start":1,"end":1000}]}' ``` The address index covers transparent addresses only. Shielded balances and shielded transaction history are visible only to the holder of the viewing key, through a wallet that scans the chain. ## Shielded pool state Zebra serves the state of the Sapling, Orchard, and Ironwood note commitment trees, which wallets use to sync and to build shielded transactions: * `z_gettreestate` — the note commitment tree state of the Sapling, Orchard, and Ironwood pools at a block, by height or hash. * `z_getsubtreesbyindex` — the roots of complete note commitment subtrees for `sapling`, `orchard`, or `ironwood`, from a start index. * `z_validateaddress` — validates a transparent, Sapling, or unified address and returns its type. * `z_listunifiedreceivers` — returns the receivers that a unified address contains. ```bash z_getsubtreesbyindex theme={"system"} curl YOUR_CHAINSTACK_ENDPOINT \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"1.0","id":1,"method":"z_getsubtreesbyindex","params":["orchard",0,2]}' ``` ## Python Install [requests](https://pypi.org/project/requests/): ```shell Shell theme={"system"} pip install requests ``` ```python main.py theme={"system"} import requests ENDPOINT = "YOUR_CHAINSTACK_ENDPOINT" ZATOSHIS_PER_ZEC = 100_000_000 def rpc(method, params=None): response = requests.post( ENDPOINT, json={"jsonrpc": "1.0", "id": 1, "method": method, "params": params or []}, timeout=30, ) if "application/json" not in response.headers.get("Content-Type", ""): raise RuntimeError(f"HTTP {response.status_code}: {response.text.strip()}") body = response.json() if body.get("error"): raise RuntimeError(body["error"]["message"]) return body["result"] info = rpc("getblockchaininfo") print("Chain:", info["chain"]) print("Blocks:", info["blocks"]) address = "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd" balance = rpc("getaddressbalance", [{"addresses": [address]}]) print("Balance (ZEC):", balance["balance"] / ZATOSHIS_PER_ZEC) print("Received (ZEC):", balance["received"] / ZATOSHIS_PER_ZEC) txids = rpc("getaddresstxids", [{"addresses": [address], "start": 1, "end": 1000}]) print("Transactions in blocks 1 to 1000:", len(txids)) ``` On Mainnet, the `main.py` example prints `Chain: main`, the block height, and the balance and history of the address that received the founders' reward in block 1. On Testnet, replace the address with `t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi`, which received the founders' reward in Testnet block 1, and read the amounts as TAZ. For a password-protected endpoint, put the credentials in the URL, as in `https://USERNAME:PASSWORD@HOSTNAME`, and requests sends them as HTTP basic authentication. ## JavaScript The example uses the built-in `fetch` in Node.js 18 or later, so it needs no packages. ```javascript index.mjs theme={"system"} const ENDPOINT = "YOUR_CHAINSTACK_ENDPOINT"; const ZATOSHIS_PER_ZEC = 100_000_000; async function rpc(method, params = []) { const response = await fetch(ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "1.0", id: 1, method, params }), signal: AbortSignal.timeout(30000), }); if (!response.headers.get("content-type")?.includes("application/json")) { throw new Error(`HTTP ${response.status}: ${(await response.text()).trim()}`); } const body = await response.json(); if (body.error) throw new Error(body.error.message); return body.result; } const blocks = await rpc("getblockcount"); console.log("Blocks:", blocks); const address = "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd"; const utxos = await rpc("getaddressutxos", [{ addresses: [address] }]); const total = utxos.reduce((sum, utxo) => sum + utxo.satoshis, 0); console.log("Unspent outputs:", utxos.length); console.log("Spendable (ZEC):", total / ZATOSHIS_PER_ZEC); const mempool = await rpc("getrawmempool"); console.log("Transactions in mempool:", mempool.length); ``` The built-in `fetch` rejects a URL that includes credentials, with `TypeError: Request cannot be constructed from a URL that includes credentials`. For a password-protected endpoint, send the credentials in an `Authorization` header instead: ```javascript auth.mjs theme={"system"} const ENDPOINT = "https://HOSTNAME"; const AUTHORIZATION = "Basic " + Buffer.from("USERNAME:PASSWORD").toString("base64"); const response = await fetch(ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", Authorization: AUTHORIZATION }, body: JSON.stringify({ jsonrpc: "1.0", id: 1, method: "getblockcount", params: [] }), }); console.log("Blocks:", (await response.json()).result); ``` ## Available methods Chainstack Zcash nodes serve these Zebra JSON-RPC methods on Mainnet and Testnet: | Category | Methods | | - | - | | Blockchain | `getblockchaininfo`, `getblockcount`, `getbestblockhash`, `getbestblockheightandhash`, `getblockhash`, `getblock`, `getblockheader`, `getdifficulty` | | Transactions | `getrawtransaction`, `gettxout`, `sendrawtransaction` | | Address index | `getaddressbalance`, `getaddresstxids`, `getaddressutxos` | | Shielded pool state | `z_gettreestate`, `z_getsubtreesbyindex`, `z_validateaddress`, `z_listunifiedreceivers` | | Mempool | `getrawmempool`, `getmempoolinfo` | | Mining information | `getmininginfo`, `getnetworksolps`, `getnetworkhashps`, `getblocksubsidy` | | Node and network | `getinfo`, `getnetworkinfo`, `getpeerinfo`, `ping`, `getdeprecationinfo` | | Utilities | `validateaddress`, `getstandardfee` | The mining methods `getblocktemplate` and `submitblock` and the node control methods `stop`, `addnode`, `generate`, `invalidateblock`, and `reconsiderblock` are not available. A call returns HTTP status `403` with error code `-32601`. ## Zcash behaviors that affect tooling ### JSON-RPC over HTTPS only Zebra serves JSON-RPC over HTTP, so Chainstack Zcash endpoints have no WebSocket endpoint and no subscriptions. To follow new blocks, poll `getbestblockhash` or `getblockcount`. Mainnet produces a block about every 75 seconds. Testnet produces a block about every 25 seconds, the NU7 block time. ### Amounts come in two units Transaction outputs report each amount twice, as `value` in ZEC and `valueZat` in zatoshis. The address index methods report amounts in zatoshis only, and `getaddressutxos` names the field `satoshis`. Use the zatoshi fields for arithmetic to avoid floating-point rounding. ### The address index does not check the network The address index methods accept an address from the other network and return an empty result instead of an error. A Mainnet address sent to a Testnet endpoint returns a zero balance and no transactions. Check an address with `validateaddress` first, which returns `"isvalid": false` for an address from the other network. ### `getbestblockheightandhash` returns the hash as bytes `getbestblockheightandhash` returns the block hash as an array of 32 numbers in reverse byte order, not as a hex string. For the hex string, call `getbestblockhash`, or reverse the array and hex-encode it. ### Wallet methods are not part of Zebra Zebra is a full node without a built-in wallet, so the wallet methods of the deprecated `zcashd` node, such as `z_sendmany`, `z_getbalance`, and `listunspent`, do not exist. Build and sign transactions with a Zcash wallet library, then broadcast them with `sendrawtransaction`. # ZKsync Era methods Source: https://docs.chainstack.com/docs/zksync-era-methods Complete ZKsync Era RPC API methods reference. Includes eth_, debug_, zks_ methods for L2 transactions, batches, bridges, and zero-knowledge proof queries. Our pricing is simple: 1 RU per full request, 2 RUs per archive. For which EVM methods are full vs archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). See also [interactive ZKsync Era API call examples](/reference/getting-started-zksync). | Method | Availability | Comment | | - | - | - | | eth\_accounts | | | | eth\_blockNumber | | | | eth\_call | | | | eth\_chainId | | | | eth\_estimateGas | | | | eth\_feeHistory | | | | eth\_gasPrice | | | | eth\_getAccount | | | | eth\_getBalance | | | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockReceipts | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getCode | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getProof | | Use `zks_getProof` | | eth\_getStorageAt | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getTransactionByHash | | | | eth\_getTransactionCount | | | | eth\_getTransactionReceipt | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_maxPriorityFeePerGas | | | | eth\_newBlockFilter | | | | eth\_newFilter | | | | eth\_newPendingTransactionFilter | | | | eth\_signTransaction | | | | eth\_subscribe | | | | eth\_syncing | | | | eth\_uninstallFilter | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | net\_listening | | | | net\_peerCount | | | | net\_version | | | | web3\_clientVersion | | | | web3\_sha3 | | | | debug\_getBadBlocks | | | | debug\_storageRangeAt | | | | debug\_getTrieFlushInterval | | | | debug\_traceBlock | | | | debug\_traceBlockByHash | | | | debug\_traceBlockByNumber | | | | debug\_traceCall | | | | debug\_traceTransaction | | | | zks\_estimateFee | | | | zks\_estimateGasL1ToL2 | | | | zks\_getAllAccountBalances | | | | zks\_getBlockDetails | | | | zks\_getBridgeContracts | | | | zks\_getBytecodeByHash | | | | zks\_getConfirmedTokens | | | | zks\_getL1BatchBlockRange | | | | zks\_getL1BatchDetails | | | | zks\_getL2ToL1LogProof | | | | zks\_getL2ToL1MsgProof | | | | zks\_getMainContract | | | | zks\_getProof | | | | zks\_getRawBlockTransactions | | | | zks\_getTestnetPaymaster | | | | zks\_getTokenPrice | | | | zks\_getTransactionDetails | | | | zks\_L1BatchNumber | | | | zks\_L1ChainId | | | | admin\_addPeer | | | | admin\_addTrustedPeer | | | | admin\_datadir | | | | admin\_exportChain | | | | admin\_importChain | | | | admin\_nodeInfo | | | | admin\_peerEvents | | | | admin\_peers | | | | admin\_removePeer | | | | admin\_removeTrustedPeer | | | | admin\_startHTTP | | | | admin\_startWS | | | | admin\_stopHTTP | | | | admin\_stopWS | | | # zkSync Era tooling Source: https://docs.chainstack.com/docs/zksync-era-tooling zkSync Era development tools guide. Use zksync-ethers SDK, Hardhat, MetaMask, ethers.js, and Remix IDE with Chainstack zkSync Era nodes. Get started with a [reliable zkSync Era RPC endpoint](https://chainstack.com/build-better-with-zksync-era/) to use the tools below. ## Interaction tools ### MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## Development tools > [!NOTE] > > ZKsync Era runs standard EVM bytecode through its [EVM Bytecode Interpreter](https://docs.zksync.io/zksync-era/tooling/hardhat), so for most new projects you can use standard Ethereum tooling — Hardhat, Foundry, ethers, or viem — pointed at your Chainstack ZKsync Era endpoint, exactly like any other EVM chain. The EraVM-native workflow below (the `hardhat-zksync` plugin suite with the `zksolc` compiler) remains for contracts that target EraVM directly. ### zksync-ethers SDK See [zksync-ethers](https://github.com/zksync-sdk/zksync-ethers). ### Hardhat (EraVM-native) To compile and deploy contracts that target EraVM natively, configure the [hardhat-zksync](https://docs.zksync.io/zksync-era/tooling/hardhat) plugin suite to build and interact through your zkSync Era nodes. For standard EVM contracts, use a normal Hardhat setup pointed at your Chainstack endpoint instead (see the note above). 1. Install [Hardhat](https://docs.zksync.io/zksync-era/tooling/hardhat) and create a project. ```shell Shell theme={"system"} yarn add -D typescript ts-node ethers@^5.7.2 zksync-web3 hardhat @matterlabs/hardhat-zksync-solc @matterlabs/hardhat-zksync-deploy @matterlabs/zksync-contracts @openzeppelin/contracts @matterlabs/hardhat-zksync-verify @nomicfoundation/hardhat-verify dotenv ``` 2. Install the [dotenv](https://www.npmjs.com/package/dotenv) package to securely load your sensitive variables from a `.env` file 3. Create a new environment in `hardhat.config.ts`: ```js hardhat.config.ts theme={"system"} import { HardhatUserConfig } from "hardhat/config"; require("dotenv").config(); import "@matterlabs/hardhat-zksync-deploy"; import "@matterlabs/hardhat-zksync-solc"; import "@matterlabs/hardhat-zksync-verify"; const config: HardhatUserConfig = { zksolc: { version: "1.3.13", // Use latest available compilerSource: "binary", settings: {}, }, defaultNetwork: "zkSyncTestnet", networks: { hardhat: { zksync: true, }, zkSyncTestnet: { url: process.env.ZKSYNC_TESTNET_CHAINSTACK, ethNetwork: "sepolia", zksync: true, verifyURL: "https://explorer.sepolia.era.zksync.dev/contract_verification", }, }, solidity: { version: "0.8.17", }, }; export default config; ``` > [!NOTE] > > Follow the tutorial for a better understanding: [zkSync Era∎: Develop a custom paymaster contract](/docs/zksync-tutorial-develop-a-custom-paymaster-contract) ### web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and zkSync Era nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). #### HTTPS Use `HTTPProvider` to connect to your node endpoint and get the latest block number. ```py Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```py Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * HOSTNAME — your node HTTPS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) #### WSS Use the `WebsocketProvider` object to connect to your node WSS endpoint and get the latest block number. ```py Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```py Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.WebsocketProvider('wss://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * HOSTNAME — your node WSS endpoint hostname * USERNAME — your node access username (for password-protected endpoints) * PASSWORD — your node access password (for password-protected endpoints) See also [WebSocket connection to an EVM node](https://support.chainstack.com/hc/en-us/articles/900001918763-WebSocket-connection-to-an-Ethereum-node). ### node.js You can build a web app to query data using node.js and [axios](https://www.npmjs.com/package/axios): ```js Javascript theme={"system"} const axios = require("axios"); const payload = { jsonrpc: "2.0", id: 0, method: "eth_blockNumber", params: [] }; (async () => { const response = await axios.post(`YOUR_CHAINSTACK_ENDPOINT`, payload) console.log(response.data) })(); ``` * YOUR\_CHAINSTACK\_ENDPOINT — your Chainstack node endpoint protected either with the key or password. See node access details. * `query` — your JSON-RPC query. In this case, to get the latest block number. ### ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and zkSync Era nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTPS or WebSocket. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). #### HTTPS Use the `JsonRpcProvider` object to connect to your node endpoint and get the balance of any address: ```js Javascript theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL, NETWORK_ID); const eth_getBalance = async () => { const balance = await provider.getBalance("0x439356Ad40D2f2961c99FFED4453f482AEC453Af"); console.log(balance); }; eth_getBalance() ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * NETWORK\_ID — zkSync Era network ID: * Mainnet: `324` * Sepolia Testnet:`300` #### WebSocket Use the `WebSocketProvider` object to connect to your node WSS endpoint and get the latest block number: ```js Javascript theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.WebSocketProvider(NODE_URL, NETWORK_ID); const eth_getBalance = async () => { const balance = await provider.getBalance("0x439356Ad40D2f2961c99FFED4453f482AEC453Af"); console.log(balance); }; eth_getBalance() ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node WSS endpoint protected either with the key or password * NETWORK\_ID — zkSync Era network ID: * Mainnet: `324` * Sepolia Testnet:`300` ### Remix IDE To make Remix IDE interact with the network through a zkSync Era node deployed with Chainstack: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. # zkSync Era: Develop a custom paymaster contract Source: https://docs.chainstack.com/docs/zksync-tutorial-develop-a-custom-paymaster-contract Build custom paymaster on zkSync Era with account abstraction. Sponsor user transactions using ERC-20 tokens instead of ETH with Hardhat and TypeScript. **TLDR:** * Shows how to deploy a custom paymaster on zkSync Era, sponsoring user transactions in ERC-20 tokens. * Uses Hardhat, TypeScript, and zkSync’s account abstraction to bypass ETH-only gas fees. * Demonstrates token minting to a zero-ETH wallet, with the paymaster contract covering transaction costs. * Provides a complete workflow for contract setup, funding, and gasless (sponsored) interaction on zkSync Era. ## Introduction This tutorial provides a step-by-step guide on creating and deploying a custom paymaster smart contract on the zkSync Era Testnet using Hardhat and TypeScript. By leveraging zkSync Era's account abstraction feature, you'll be able to facilitate transactions where gas fees are paid using a custom ERC-20 token, rather than ETH. The tutorial will guide you through deploying two smart contracts: a paymaster contract and an ERC-20 token contract. The paymaster contract, once deployed and funded with zkSync Era Testnet ETH, will act as a sponsor for users' gas fees. In this setup, users can execute transactions without directly paying for gas. Instead, they will transfer a token to the paymaster contract, which handles the gas fee payment. This approach can, for example, offer a monthly quota of sponsored transactions for your DApp users. This enhances the user experience and makes DApps more accessible. ## What is zkSync Era? zkSync is a trustless zkEVM protocol based on ZK rollups that offers scalable and low-cost transactions on Ethereum by conducting computation and storing most data off-chain. As a subset of zkSync, zkSync Era is designed to emulate Ethereum's functionality but at a lower cost. zkSync Era supports existing Ethereum wallets without separate registration and permits smart contract usage, like its Ethereum counterpart. Transactions in zkSync Era undergo a lifecycle that includes user initiation, operator processing, block commitment, and final block verification, all of which ensure the same level of security as Ethereum's mainchain. The current version of zkSync Era caters to the needs of most applications on Ethereum, with features such as native support of ECDSA signatures, Solidity 0.8.x support, compatibility with Ethereum's Web3 API, support for Ethereum cryptographic primitives, and L1 to L2 smart contract messaging. It also offers high security and usability compared to other L2 scaling solutions, with unique properties like safely withdrawing assets even when the user or ZK rollup validators are offline. > [!NOTE] > > Learn more about zkEVM and rollups in [zkEVM and rollups explained](https://chainstack.com/zkevm-and-zkrollups-explained/). ## What is account abstraction? zkSync Era natively supports account abstraction, a feature designed to address inefficiencies in Ethereum's account system, which currently divides into externally owned accounts (EOAs) and smart contract accounts. These two types have limitations that can lead to friction in certain applications like smart-contract wallets or privacy protocols. However, account abstraction allows accounts on zkSync Era, an EVM-compatible chain, to initiate transactions akin to EOAs and implement arbitrary logic akin to smart contracts. zkSync Era introduces the concept of smart accounts and paymasters, designed to enhance flexibility, user experience, and security by offering fully programmable accounts and transaction sponsorship in ERC-20 tokens, respectively. Despite some similarities with Ethereum's EIP 4337, key differences shape zkSync's unique account management approach. ## Prerequisites * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable zkSync Era RPC endpoint](https://chainstack.com/build-better-with-zksync-era/) * [node.js](https://nodejs.org/en/download) at least V16 on your machine * [yarn installed](https://classic.yarnpkg.com/lang/en/docs/install/) on your machine ## Overview To get from zero to a paymaster-sponsored transaction, do the following: 1. With Chainstack, create a public chain project (public chain project-A project to join public networks). 2. With Chainstack, join the zkSync Era Sepolia Testnet. 3. With Chainstack, access your zkSync Era Sepolia Testnet node credentials. 4. Setup the project and install the required dependencies. 5. Create the smart contracts. 6. Get zkSync Era Sepolia Testnet funds from the faucet. 7. Set up the Hardhat scripts to deploy and interact. 8. Deploy the smart contracts. 9. Use the paymaster to mint tokens to a new address without using gas. ## Step-by-step ### Create a public chain project See [Create a project](/docs/manage-your-project#create-a-project). ### Join the zkSync Era Sepolia Testnet See [Join a public network](/docs/manage-your-networks). ### Get your zkSync Era Sepolia Testnet node endpoint See [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ### Setup the project and install the required dependencies Create a new directory where you want to develop your project: ```shell Shell theme={"system"} mkdir zksync-paymaster cd zksync-paymaster ``` Initialize a new yarn project: ```shell Shell theme={"system"} yarn init -y ``` To create the project structure, we'll need a `contracts` and a `deploy` directories: ```shell Shell theme={"system"} mkdir contracts deploy ``` Install the required dependencies: ```shell Shell theme={"system"} yarn add -D typescript ts-node ethers@^5.7.2 zksync-web3 hardhat @matterlabs/hardhat-zksync-solc @matterlabs/hardhat-zksync-deploy @matterlabs/zksync-contracts @openzeppelin/contracts @matterlabs/hardhat-zksync-verify @nomicfoundation/hardhat-verify dotenv ``` Create a `.env` file and add your Chainstack zkSync Era endpoint and the private key for the account you want to use to deploy the smart contracts and get some gas tokens initially minted to. ```Text .env theme={"system"} ZKSYNC_TESTNET_CHAINSTACK="YOUR_CHAINSTACK_ZKSYNC_ENDPOINT" PRIVATE_KEY="YOUR_DEPLOYER_PRIVATE_KEY" ``` Create a new `hardhat.config.ts`file and paste the following: ```typescript hardhat.config.ts theme={"system"} import { HardhatUserConfig } from "hardhat/config"; require("dotenv").config(); import "@matterlabs/hardhat-zksync-deploy"; import "@matterlabs/hardhat-zksync-solc"; import "@matterlabs/hardhat-zksync-verify"; const config: HardhatUserConfig = { zksolc: { version: "1.3.13", // Use latest available compilerSource: "binary", settings: {}, }, defaultNetwork: "zkSyncTestnet", networks: { hardhat: { zksync: true, }, zkSyncTestnet: { url: process.env.ZKSYNC_TESTNET_CHAINSTACK, ethNetwork: "sepolia", zksync: true, verifyURL: "https://zksync2-testnet-explorer.zksync.dev/contract_verification", }, }, solidity: { version: "0.8.17", }, }; export default config; ``` > [!NOTE] > > Check the `zksolc` compiler [GitHub repository](https://github.com/matter-labs/zksolc-bin/) to find the latest version available. ### Create the smart contracts Now that the project is set up let's create the smart contracts starting from the ERC-20 token that users will use to pay 'pay' for transactions. #### ERC-20 smart contract In the `contracts` directory, create a new file named `CSgas.sol` and paste the following Solidity code: ```sol CSgas.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract CSgas is ERC20 { uint8 public _decimals; constructor( string memory name_, string memory symbol_, uint8 decimals_ ) ERC20(name_, symbol_) { _decimals = decimals_; } function mint(address _to, uint256 _amount) public returns (bool) { _mint(_to, _amount); return true; } } ``` This is a simple implementation of an ERC-20 smart contract using the OpenZeppelin library, which provides secure and community-audited implementations for parts of the Ethereum protocol. The smart contract is written in version 0.8.0 of Solidity, specified at the top of the file. The caret symbol `^` means it's compatible with any new minor releases under version 0.8. The contract is importing something important—the ERC-20 token standard from the OpenZeppelin library. The contract itself is named `CSgas`. When `CSgas` is deployed, it calls a constructor function. This function sets up the token's name, symbol, and the number of decimal places the token will have. The deploying code will pass through these details. The `_decimals` variable is used to determine how the token's value is displayed in wallets and user interfaces. Most tokens go with 18 decimal places, matching the precision of Ethereum's own currency, ether. The mint function is public, meaning anyone can call it. When called, it mints new tokens. You need to input who should receive the tokens and how many to create. It uses the `_mint` function from the ERC20 contract to create the token and update the balance. #### Paymaster smart contract Now let's go over the interesting part, the `Paymaster` smart contract. Create a new file in the `contract` directory named `Paymaster.sol` and paste the following: ```sol Paymaster.sol theme={"system"} // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IPaymaster, ExecutionResult, PAYMASTER_VALIDATION_SUCCESS_MAGIC} from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymaster.sol"; import {IPaymasterFlow} from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymasterFlow.sol"; import {TransactionHelper, Transaction} from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; contract Paymaster is IPaymaster { uint256 constant PRICE_FOR_PAYING_FEES = 1 ether; address public allowedToken; modifier onlyBootloader() { require( msg.sender == BOOTLOADER_FORMAL_ADDRESS, "Only bootloader can call this method" ); _; } constructor(address _erc20) { allowedToken = _erc20; } function validateAndPayForPaymasterTransaction( bytes32, bytes32, Transaction calldata _transaction ) external payable onlyBootloader returns (bytes4 magic, bytes memory context) { // By default the transaction is considered accepted. magic = PAYMASTER_VALIDATION_SUCCESS_MAGIC; require( _transaction.paymasterInput.length >= 4, "The standard paymaster input must be at least 4 bytes long" ); bytes4 paymasterInputSelector = bytes4( _transaction.paymasterInput[0:4] ); if (paymasterInputSelector == IPaymasterFlow.approvalBased.selector) { (address token, uint256 amount, bytes memory data) = abi.decode( _transaction.paymasterInput[4:], (address, uint256, bytes) ); // Verify if token is the allowed one require(token == allowedToken, "Invalid token"); // We verify that the user has provided enough allowance address userAddress = address(uint160(_transaction.from)); address thisAddress = address(this); uint256 providedAllowance = IERC20(token).allowance( userAddress, thisAddress ); require( providedAllowance >= PRICE_FOR_PAYING_FEES, "Min allowance too low" ); // Note, that while the minimal amount of ETH needed is tx.gasPrice * tx.gasLimit, // neither paymaster nor account are allowed to access this context variable. uint256 requiredETH = _transaction.gasLimit * _transaction.maxFeePerGas; try IERC20(token).transferFrom(userAddress, thisAddress, amount) {} catch (bytes memory revertReason) { // If the revert reason is empty or represented by just a function selector, // we replace the error with a more user-friendly message if (revertReason.length <= 4) { revert("Failed to transferFrom from users' account"); } else { assembly { revert(add(0x20, revertReason), mload(revertReason)) } } } // The bootloader never returns any data, so it can safely be ignored here. (bool success, ) = payable(BOOTLOADER_FORMAL_ADDRESS).call{ value: requiredETH }(""); require( success, "Failed to transfer tx fee to the bootloader. Paymaster balance might not be enough." ); } else { revert("Unsupported paymaster flow"); } } function postTransaction( bytes calldata _context, Transaction calldata _transaction, bytes32, bytes32, ExecutionResult _txResult, uint256 _maxRefundedGas ) external payable override onlyBootloader { // Refunds are not supported yet. } receive() external payable {} } ``` Let's go over it. This contract is named `Paymaster` and it's designed to manage transaction fees on behalf of users and uses Solidity version 0.8.0. The contract starts by importing dependencies. It uses the OpenZeppelin library for the `IERC20` interface, which is a standard interface for interacting with ERC-20 tokens. It's also importing several interfaces and libraries from the `@matterlabs/zksync-contracts` package, which is used for handling zkSync transactions. The `Paymaster` contract itself implements the `IPaymaster` interface. This interface defines the methods that a paymaster contract should have. The contract has a constant `PRICE_FOR_PAYING_FEES` set to 1 ether. This is the amount of tokens the user needs to allow the paymaster to spend on their behalf to pay for transaction fees. The `allowedToken` variable stores the address of the ERC-20 token that the paymaster will accept for transaction fees. This is passed through the deploying script after the ERC-20 token is deployed. The `onlyBootloader` modifier is a security feature. It ensures that only the bootloader can call certain functions. In the constructor, the contract sets the `allowedToken` to the address of the ERC-20 token that's passed in when the contract is deployed. The `validateAndPayForPaymasterTransaction` function is where the magic happens. It's called by the bootloader to validate and pay for a transaction. This function checks that the transaction is using the correct token and that the user has allowed the paymaster to spend enough tokens to cover the transaction fees. If everything checks out, the paymaster pays the transaction fees. The `postTransaction` function is called after a transaction is executed. It doesn't do anything right now because refunds are not supported yet. Finally, the`receive` function is a special function called when someone sends ether to the contract without providing any data. This function is empty, so the contract accepts ether without doing anything else. ### Get zkSync Era Sepolia Testnet funds from the faucet Before moving on, let's make sure we have testnet funds to deploy smart contracts and interact with them on the zkSync Testnet. You can request funds from the [Chainstack zkSync faucet](https://faucet.chainstack.com/zksync-testnet-faucet). > [!NOTE] > > Make sure to select **zkSync Era Testnet** on the bottom right of the screen. ### Set up the Hardhat scripts to deploy and interact At this stage, we can make the scripts to interact with zkSync Era. We'll make two scripts: * A deployment script that will deploy and fund the smart contracts. * An interaction script that will use the paymaster to allow a user with no ETH mint tokens to a new wallet. #### Script to deploy the smart contracts Create a script to deploy those smart contracts in the `deploy` directory create a new file named `deploy.ts` and paste the following code: > [!NOTE] > > Note that the directory must be named `deploy`, otherwise the zkSync version of Hardhat will not find the files. ```typescript deploy.ts theme={"system"} import { Provider, Wallet } from "zksync-web3"; import * as ethers from "ethers"; import { HardhatRuntimeEnvironment } from "hardhat/types"; import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; require("dotenv").config(); // Define constants and environment variables const ZKSYNC_CHAINSTACK_ENDPOINT = process.env.ZKSYNC_TESTNET_CHAINSTACK; const PRIVATE_KEY = process.env.PRIVATE_KEY; const initialFunding = 0.01; const tokensToMint = 10; const CONFIRMATIONS = 4; const TOKEN_NAME = "Chainstack gas"; const TOKEN_SYMBOL = "CSG"; const TOKEN_DECIMALS = 18; const TOKEN_CONTRACT_PATH = "contracts/CSgas.sol:CSgas"; const PAYMASTER_CONTRACT_PATH = "contracts/Paymaster.sol:Paymaster"; // Check if environment variables are set if (!ZKSYNC_CHAINSTACK_ENDPOINT) { throw new Error( "ZKSYNC_TESTNET_CHAINSTACK is not set in the environment variables" ); } if (!PRIVATE_KEY) { throw new Error("Private key is not set in the environment variables"); } // Function to create a new wallet async function createWallet() { console.log("Creating new test wallet..."); const wallet = Wallet.createRandom(); console.log(`New wallet address: ${wallet.address}`); console.log(`New wallet private key: ${wallet.privateKey}`); return wallet; } // Function to deploy a contract async function deployContract( deployer: Deployer, contractName: string, args: any[] ) { console.log(`Deploying ${contractName}...`); const contractArtifact = await deployer.loadArtifact(contractName); const contractInstance = await deployer.deploy(contractArtifact, args); await contractInstance.deployTransaction.wait(CONFIRMATIONS); console.log(`${contractName} deployed at: ${contractInstance.address}`); return contractInstance; } // Function to verify a contract async function verifyContract( hre: HardhatRuntimeEnvironment, address: string, contractPath: string, args: any[] ) { console.log(`Verifying contract...`); return await hre.run("verify:verify", { address: address, contract: contractPath, constructorArguments: args, }); } // Function to fund the Paymaster contract async function fundPaymaster(deployer: Deployer, paymasterAddress: string) { console.log(`Funding Paymaster with ${initialFunding} ETH on zkSync...`); return await ( await deployer.zkWallet.sendTransaction({ to: paymasterAddress, value: ethers.utils.parseEther(String(initialFunding)), }) ).wait(CONFIRMATIONS); } // Function to mint tokens async function mintTokens(contract: any, address: string) { console.log(`Minting ${tokensToMint} tokens to ${address}`); return await ( await contract.mint(address, ethers.utils.parseEther(String(tokensToMint))) ).wait(); } // Main function for deployment export default async function (hre: HardhatRuntimeEnvironment) { try { // Initialize provider, wallet, and deployer const provider = new Provider(ZKSYNC_CHAINSTACK_ENDPOINT); const wallet = new Wallet(PRIVATE_KEY); const deployer = new Deployer(hre, wallet); // Create a new wallet, this is only for showing how an wallet with no ETH can be sponsored by the paymaster const newWallet = await createWallet(); //Deploy the Gas token const deployedChainstackGas = await deployContract(deployer, "CSgas", [ TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS, ]); // Deploy the Paymaster const deployedPaymaster = await deployContract(deployer, "Paymaster", [ deployedChainstackGas.address, ]); // Verify the 2 contracts await verifyContract( hre, deployedChainstackGas.address, TOKEN_CONTRACT_PATH, [TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS] ); await verifyContract( hre, deployedPaymaster.address, PAYMASTER_CONTRACT_PATH, [deployedChainstackGas.address] ); // Fund the Paymaster contract and mint tokens await fundPaymaster(deployer, deployedPaymaster.address); await mintTokens(deployedChainstackGas, newWallet.address); console.log(`Paymaster and new wallet funded!`); // Get and log the balance of the Paymaster contract const paymasterBalance = await provider.getBalance( deployedPaymaster.address ); console.log( `Paymaster balance: ${ethers.utils.formatEther(paymasterBalance)}` ); // Get and log the balance of the new wallet const newWalletTokenBalance = await deployedChainstackGas.balanceOf( newWallet.address ); const newWalletTokenBalanceInEther = ethers.utils.formatEther( newWalletTokenBalance ); console.log( `New wallet's Chainstack gas token balance: ${newWalletTokenBalanceInEther}` ); } catch (error) { console.error(`Error during deployment: ${error}`); } } ``` This script has a lot going on, so let's break it down: 1. **Imports and anvironment variables**. The script begins by importing necessary modules and types. It then defines several constants and retrieves environment variables. Here is how we set up the script and keep it somewhat easier to maintain and edit: ``` // Take endoint and private key from the .env file const ZKSYNC_CHAINSTACK_ENDPOINT = process.env.ZKSYNC_TESTNET_CHAINSTACK; const PRIVATE_KEY = process.env.PRIVATE_KEY; // This is the amount of ETH that will be transfered to the contract to pay for gas fees const initialFunding = 0.01; // This is the amount of gas ERC20 tokens that will be minted to the new wallet const tokensToMint = 10; // This is how many blocks to wait after deployment, it helps making sure the deployment is propagated before moving to verification const CONFIRMATIONS = 4; // These are the details of the ERC20 token const TOKEN_NAME = "Chainstack gas"; const TOKEN_SYMBOL = "CSG"; const TOKEN_DECIMALS = 18; // These are the paths to the contract for verification const TOKEN_CONTRACT_PATH = "contracts/CSgas.sol:CSgas"; const PAYMASTER_CONTRACT_PATH = "contracts/Paymaster.sol:Paymaster"; ``` 2. **Environment variable checks**. The script checks if the necessary environment variables are set. If they are not, it throws an error. This is to accommodate the fact that TypeScript does not allow for strings to have `undefined` values. 3. **Function definitions**. The script defines several helper functions to keep the code clean and maintainable: * `createWallet()` — creates a new random wallet and logs the wallet's address and private key. We create a new wallet to demonstrate how an account can initiate transactions sponsored by the paymaster without having any ETH. * `deployContract()` — deploys a contract to the zkSync Era network. It takes a `Deployer` instance, the name of the contract, and an array of arguments for the contract's constructor. It loads the contract's artifact, deploys the contract, waits for a certain number of confirmations, and then logs the contract's address. * `verifyContract()` — verifies a contract on the zkSync Era network. It takes a `HardhatRuntimeEnvironment` instance, the contract's address, the path to the contract's source code, and an array of arguments passed to the contract's constructor. It runs the `verify:verify` task provided by the Hardhat environment. * `fundPaymaster()` — funds the paymaster contract with a certain amount of ETH. It takes a `Deployer` instance and the paymaster contract's address. It sends a transaction to the paymaster contract and waits for a certain number of confirmations. * `mintTokens()` — mints a certain number of tokens and sends them to a specified address. It takes a contract instance and an address. It calls the `mint` function on the contract and waits for the transaction to be confirmed. 4. **Main function**. The script exports a default async function that performs the main deployment process: * It initializes a `Provider` instance, a `Wallet` instance, and a `Deployer` instance. * It creates a new wallet. * It deploys the `CSgas` contract, the token sent to the paymaster to indicate the account is allowed to get the transaction sponsored, and the `Paymaster` contract. * It verifies both contracts. * It funds the paymaster contract with ETH and mints tokens to the new wallet. * It retrieves and logs the balance of the paymaster contract and the new wallet's token balance. 5. **Error handling**. If any errors occur during deployment, the script catches them and logs the error message. #### Script to interact In the same `deploy` directory, create a new file named `gasless-tx.ts` and paste the following code: ```typescript gasless-tx.ts theme={"system"} import { Provider, utils, Wallet } from "zksync-web3"; import * as ethers from "ethers"; import { HardhatRuntimeEnvironment } from "hardhat/types"; require("dotenv").config(); // Constants const ZKSYNC_CHAINSTACK_ENDPOINT = process.env.ZKSYNC_TESTNET_CHAINSTACK; const PAYMASTER_ADDRESS = "DEPLOYED_PAYMASTER_ADDRESS"; const TOKEN_ADDRESS = "DEPLOYED_GAS_TOKEN"; const USER_WALLET = "PRIVATE_KEY_OF_THE_WALLET_GENERATED_DURING_DEPLOYMENT"; const TOKEN_NAME = "CSgas"; const TOKENS_TO_MINT = "5"; const TOKENS_TO_MINT_WEI = ethers.utils.parseEther(TOKENS_TO_MINT); // Function to get the Chainstack gas token instance function getToken(hre: HardhatRuntimeEnvironment, wallet: Wallet) { const artifact = hre.artifacts.readArtifactSync(TOKEN_NAME); return new ethers.Contract(TOKEN_ADDRESS, artifact.abi, wallet); } // Function to generate a random new wallet async function createWallet() { console.log("Creating new test wallet..."); const wallet = Wallet.createRandom(); console.log(`New wallet address: ${wallet.address}`); console.log(`New wallet private key: ${wallet.privateKey}`); console.log(""); return wallet; } // Function to display balances async function displayBalances( provider, chainstackGastoken, user, paymasterAddress ) { console.log( `The first user has no ETH to pay for gas! Balance: ${await user.getBalance()}` ); console.log( `The first user holds ${ethers.utils.formatEther( await user.getBalance(TOKEN_ADDRESS) )} Chainstack gas tokens` ); console.log( `Paymaster ETH balance is ${ethers.utils.formatEther( await provider.getBalance(paymasterAddress) )}` ); console.log( `Paymaster Chainstack gas token balance is: ${ethers.utils.formatEther( await chainstackGastoken.balanceOf(paymasterAddress) )}` ); console.log(""); } // Function to mint tokens async function mintTokens(chainstackGastoken, user, paymasterParams) { console.log( `Minting ${TOKENS_TO_MINT} tokens to the new user sponsored by the paymaster...` ); console.log(""); await ( await chainstackGastoken.mint(user.address, TOKENS_TO_MINT_WEI, { customData: { paymasterParams: paymasterParams, gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT, }, }) ).wait(); } export default async function (hre: HardhatRuntimeEnvironment) { const provider = new Provider(ZKSYNC_CHAINSTACK_ENDPOINT); const firstUser = new Wallet(USER_WALLET, provider); const chainstackGastoken = getToken(hre, firstUser); // Display initial balances await displayBalances( provider, chainstackGastoken, firstUser, PAYMASTER_ADDRESS ); // Create a new user to send tokens to const secondUser = await createWallet(); const gasPrice = await provider.getGasPrice(); // Setup the "ApprovalBased" paymaster flow's const paymasterParams = utils.getPaymasterParams(PAYMASTER_ADDRESS, { type: "ApprovalBased", token: TOKEN_ADDRESS, // The minimum required amount of Chainstack tokens is 1, as set up in the smart contract minimalAllowance: ethers.BigNumber.from("1000000000000000000"), innerInput: new Uint8Array(), }); // Estimate gas fee for minting new tokens const gasLimit = await chainstackGastoken.estimateGas.mint( firstUser.address, TOKENS_TO_MINT_WEI, { customData: { gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT, paymasterParams: paymasterParams, }, } ); const fee = gasPrice.mul(gasLimit.toString()); console.log("Gas fee estimate: ", ethers.utils.formatEther(fee)); // Mint tokens await mintTokens(chainstackGastoken, secondUser, paymasterParams); // Display final balances await displayBalances( provider, chainstackGastoken, firstUser, PAYMASTER_ADDRESS ); console.log( `The second user now has ${ethers.utils.formatEther( await chainstackGastoken.balanceOf(secondUser.address) )} Chainstack gas tokens` ); } ``` Let's break down the code and the logic: 1. **Imports and environment variables**. The script begins by importing necessary modules and types. It then defines several constants and retrieves environment variables. Let's go over what's happening with the constants as is our setup point. ```typescript TypeScript theme={"system"} // Get the szSync endpoint from the env file const ZKSYNC_CHAINSTACK_ENDPOINT = process.env.ZKSYNC_TESTNET_CHAINSTACK; // Add the address of the deployed paymaster and token contracts const PAYMASTER_ADDRESS = "DEPLOYED_PAYMASTER_ADDRESS"; const TOKEN_ADDRESS = "DEPLOYED_GAS_TOKEN"; // The private key of the new wallet generated running the previous script const USER_WALLET = "PRIVATE_KEY_OF_THE_WALLET_GENERATED_DURING_DEPLOYMENT"; // The name of the token const TOKEN_NAME = "CSgas"; // How many tokens to mint to another account, this is just to show how the paymaster can sponsor transactions const TOKENS_TO_MINT = "5"; const TOKENS_TO_MINT_WEI = ethers.utils.parseEther(TOKENS_TO_MINT); ``` 2. **Function definitions**. The script defines several helper functions: * `getToken()` — retrieves the instance of the Chainstack gas token. It takes a `HardhatRuntimeEnvironment` instance and a `Wallet` instance reads the token's artifact and returns a new contract instance with the token's address and ABI. This is used to interact with the token contract. * `createWallet()` — creates a new random wallet and logs the wallet's address and private key. We create another random wallet to use as a second user, where basically the first user we created in the deployment script mints new tokens for this second new user. * `displayBalances()` — retrieves and logs the balances of the user and the paymaster contract in both ETH and Chainstack gas tokens. It takes a `Provider` instance, a contract instance, a `Wallet` instance, and the paymaster contract's address. This is just used to give insights into the process and logic. * `mintTokens()` — mints a certain number of tokens and sends them to a specified user. It takes a contract instance, a `Wallet` instance, and the paymaster parameters. It calls the `mint` function on the contract and waits for the transaction to be confirmed. 3. **Main function**. The script exports a default async function that performs the main process: * It initializes a `Provider` instance and a `Wallet` instance, and retrieves the Chainstack gas token instance. * It displays the initial balances of the user and the paymaster contract. * It creates a new wallet. * It retrieves the current gas price and sets the paymaster parameters for an ApprovalBased flow. * It estimates the gas fee for minting new tokens and logs the estimated fee. * It mints tokens to the new wallet. * It displays the user's final balances, the paymaster contract, and the new wallet's token balance. 4. **Error handling**. If errors occur during the process, the script catches them and logs the error message. ### Deploy the smart contracts At this point, everything is set up to deploy the contracts. Make sure to edit the configuration constants as you need: ```typescript TypeScript theme={"system"} const initialFunding = 0.001; const tokensToMint = 10; const CONFIRMATIONS = 4; const TOKEN_NAME = "Chainstack gas"; const TOKEN_SYMBOL = "CSG"; const TOKEN_DECIMALS = 18; const TOKEN_CONTRACT_PATH = "contracts/CSgas.sol:CSgas"; const PAYMASTER_CONTRACT_PATH = "contracts/Paymaster.sol:Paymaster"; ``` Then in the terminal from the root of your project, compile the smart contracts: ```shell Shell theme={"system"} yarn hardhat compile ``` This will compile the smart contracts in the `contracts` directory. Then run the `deploy.ts` script: ```shell Shell theme={"system"} yarn hardhat deploy-zksync --script deploy.ts ``` This process will generate a new wallet and deploy the contracts while transferring 0.001 ETH to the paymaster contract. It will also mint 10 `CSgas` tokens to the new wallet. Please note that this ETH is drawn from the account associated with the private key you specified in the `.env` file. The console will log the following result: ```shell Shell theme={"system"} Creating new test wallet... New wallet address: 0x05C6C4F137f71bd3018a6a63d9E7961a5611DE67 New wallet private key: 0xdb9cc9081ec7af13869ab121880f7abe352a8d3643a27839231f324b3b7f9666 Deploying CSgas... CSgas deployed at: 0x0E5E64716321cd013E6814f06A8c42475506331E Deploying Paymaster... Paymaster deployed at: 0x17a27157E6CE8FdD50bede091d63Df8Ab6A44f93 Verifying contract... Your verification ID is: 31187 Contract successfully verified on zkSync block explorer! Verifying contract... Your verification ID is: 31188 Contract successfully verified on zkSync block explorer! Funding Paymaster with 0.01 ETH on zkSync... Minting 10 tokens to 0x05C6C4F137f71bd3018a6a63d9E7961a5611DE67 Paymaster and new wallet funded! Paymaster balance: 0.01 New wallet's Chainstack gas token balance: 10.0 ✨ Done in 91.72s. ``` You can now verify the contracts on the [zkSync Era Sepolia explorer](https://sepolia.explorer.zksync.io/). You will see the deployment transactions and the verified contracts. Now the smart contracts are deployed. The paymaster should have 0.001 ETH to sponsor gas fees, and the new account should have 10 `CSG` tokens. ### Use the paymaster to mint tokens to a new address without using gas Now we can use the second script to interact with the paymaster contract and see the magic happening. First, make sure to update the configuration constants in the `gasless-tx.ts` file with the smart contract addresses, token name, and generated walled private key. In my specific case, it will look like this; note that you will have different parameters: ```typescript TypeScript theme={"system"} const PAYMASTER_ADDRESS = "0x17a27157E6CE8FdD50bede091d63Df8Ab6A44f93"; const TOKEN_ADDRESS = "0x0E5E64716321cd013E6814f06A8c42475506331E"; const USER_WALLET = "0xdb9cc9081ec7af13869ab121880f7abe352a8d3643a27839231f324b3b7f9666"; const TOKEN_NAME = "CSgas"; ``` You can find those parameters in the deployment log; note that the token name depends on which name you gave: ```shell Shell theme={"system"} Creating new test wallet... New wallet address: 0x05C6C4F137f71bd3018a6a63d9E7961a5611DE67 New wallet private key: 0xdb9cc9081ec7af13869ab121880f7abe352a8d3643a27839231f324b3b7f9666 Deploying CSgas... CSgas deployed at: 0x0E5E64716321cd013E6814f06A8c42475506331E Deploying Paymaster... Paymaster deployed at: 0x17a27157E6CE8FdD50bede091d63Df8Ab6A44f93 ``` Run the script with the following command: ```shell Shell theme={"system"} yarn hardhat deploy-zksync --script gasless-tx.ts ``` The console will output the following logs: ```shell Shell theme={"system"} The first user has no ETH to pay for gas! Balance: 0 The first user holds 10.0 Chainstack gas tokens Paymaster ETH balance is 0.01 Paymaster Chainstack gas token balance is: 0.0 Creating new test wallet... New wallet address: 0x6dA3EB8C5B3a779ab7bB1Bda674eD5cA165E8a6D New wallet private key: 0xbea2c6f8424d252b268ce989fe2a56c61bec4a5a2db5fa88270033e3c75318a7 Gas fee estimate: 0.00028722875 Minting 5 tokens to the new user sponsored by the paymaster... The first user has no ETH to pay for gas! Balance: 0 The first user holds 9.0 Chainstack gas tokens Paymaster ETH balance is 0.00980312875 Paymaster Chainstack gas token balance is: 1.0 The second user now has 5.0 Chainstack gas tokens ✨ Done in 38.76s. ``` Now, let's dive into the sequence of events and the underlying logic: Initially, the script assesses the balances of all accounts. This is done to illustrate that the first user (created during the deployment phase) has a zero balance, rendering it incapable of sending a transaction due to the lack of gas. Despite this, the account holds 10 Chainstack gas tokens. On the other hand, the paymaster account holds a balance of 0.01 ETH but lacks any Chainstack gas tokens. ``` The first user has no ETH to pay for gas! Balance: 0 The first user holds 10.0 Chainstack gas tokens Paymaster ETH balance is 0.01 Paymaster Chainstack gas token balance is: 0.0 ``` Next, the script generates a new wallet, which will serve as the recipient of the freshly minted tokens. The first account initiates the minting process, which, interestingly, does not pay for the gas in ETH. Instead, it transfers 1 CSG token to the paymaster contract. The paymaster contract, recognizing the token as an acceptable form of payment, sponsors the gas for the minting transaction. It's important to note that if the script is configured to send less than 1 CSG token to the paymaster, or a different token, the transaction will fail and be reverted. This is a really powerful concept as it allows your DApp to onboard users more easily. In this specific example, we don't do anything particularly meaningful, but you can build on top of this. ## Conclusion This tutorial guided you through setting up Hardhat to work with zkSync Era and develop a paymaster contract to sponsor users' gas fees. ### About the author > [card] Davide Zambiasi > > ![Davide Zambiasi](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1533079085001363457/1VvXp1m0_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=d7763d47c087f55eebcf51d07e52dc55) > > Developer Advocate @ Chainstack > \ > BUIDLs on EVM, The Graph protocol, and Starknet > \ > Helping people understand Web3 and blockchain development > > [https://github.com/soos3d](https://github.com/soos3d) > > [https://twitter.com/web3Dav3](https://twitter.com/web3Dav3) > > [https://www.linkedin.com/in/davide-zambiasi/](https://www.linkedin.com/in/davide-zambiasi/) # Zora: Real-time creator token detection and monitoring Source: https://docs.chainstack.com/docs/zora-creator-token-detection-tutorial Detect and monitor creator token deployments on Zora mainnet in real time using Python event log filtering and a Chainstack Zora RPC node for indexing. **TLDR:** * Demonstrates how to build a real-time creator token detection system on Zora mainnet using Python and Chainstack nodes. * Monitors ERC-20 Transfer events across the network while filtering out standard tokens like WETH and USDzC to focus on creator tokens. * Includes automatic token metadata discovery and real-time transfer tracking for creator economy analytics. * Shows how to leverage Zora's creator-first blockchain infrastructure to identify emerging creator tokens and track their adoption. ## Overview Zora is focused on a paradigm shift in the creator economy, built as a blockchain specifically designed to empower creators and their communities. Unlike traditional platforms that extract value from creators, Zora's philosophy centers on giving creators ownership of their work, their audience, and their economic destiny. At the heart of Zora's creator economy are creator tokens—digital assets that represent ownership, participation, or support for a creator's work. These tokens can represent anything from art collections to community membership, fan engagement rewards, or revenue sharing mechanisms. The creator tokens per Zora are unique in their ability to create direct economic relationships between creators and their supporters, bypassing traditional gatekeepers. This tutorial will guide you through building a very simple real-time detection system for creator tokens on Zora Mainnet. By monitoring ERC-20 transfer events and filtering out standard tokens like [WETH (Wrapped Ethereum)](https://explorer.zora.energy/token/0x4200000000000000000000000000000000000006) and [USDzC (Zora's native USDC)](https://explorer.zora.energy/token/0xCccCCccc7021b32EBb4e8C08314bD62F7c653EC4), we can focus specifically on the creator tokens that drive Zora's unique ecosystem. We'll implement this using Python with error handling and real-time monitoring capabilities. ## Prerequisites * [web3.py](https://web3py.readthedocs.io/) * [Chainstack account](https://console.chainstack.com/) to deploy a [reliable Zora RPC endpoint](https://chainstack.com/build-better-with-zora/) * Basic understanding of ERC-20 tokens and blockchain events ## Implementation Our creator token detection system works by monitoring the Zora mainnet for ERC-20 transfer events in real-time. The script filters out standard infrastructure tokens (WETH and USDzC) to focus specifically on creator tokens that represent the heart of Zora's creator economy. Here's how the implementation works: * Event monitoring — We monitor all ERC-20 `Transfer` events across the Zora mainnet in real-time. * Creator token focus — By excluding WETH and USDzC, we isolate creator tokens and community-specific assets. * Automatic discovery — When a new token is detected, the script automatically fetches its metadata (name, symbol, decimals). * Real-time tracking — All transfer events are logged with full transaction details for analysis. * Persistent monitoring — The script runs continuously, processing new blocks as they're mined. Key parameters in our implementation: * `YOUR_CHAINSTACK_HTTP_ENDPOINT` — Your Chainstack Zora Mainnet node endpoint. * `EXCLUDED_TOKENS` — Standard tokens we filter out: * `0xCccCCccc7021b32EBb4e8C08314bD62F7c653EC4` — USDzC (Zora's bridged USDC) * `0x4200000000000000000000000000000000000006` — WETH (Wrapped Ethereum) * `TRANSFER_EVENT_SIG` — The keccak256 hash of the `Transfer` event signature * `ERC20_ABI` — Standard ERC-20 ABI fragments for token metadata queries The script: ```python theme={"system"} import time from web3 import Web3 from web3.middleware import ExtraDataToPOAMiddleware # Chainstack Zora mainnet endpoint ZORA_RPC = "YOUR_CHAINSTACK_HTTP_ENDPOINT" # Exclude these tokens (USDzC and WETH) EXCLUDED_TOKENS = { "0xCccCCccc7021b32EBb4e8C08314bD62F7c653EC4".lower(), # USDzC "0x4200000000000000000000000000000000000006".lower(), # WETH } # Standard ERC-20 ABI fragments ERC20_ABI = [ { "anonymous": False, "inputs": [ {"indexed": True, "name": "from", "type": "address"}, {"indexed": True, "name": "to", "type": "address"}, {"indexed": False, "name": "value", "type": "uint256"}, ], "name": "Transfer", "type": "event", }, {"constant": True, "inputs": [], "name": "name", "outputs": [{"name": "", "type": "string"}], "type": "function"}, {"constant": True, "inputs": [], "name": "symbol", "outputs": [{"name": "", "type": "string"}], "type": "function"}, {"constant": True, "inputs": [], "name": "decimals", "outputs": [{"name": "", "type": "uint8"}], "type": "function"}, ] # Connect to Zora mainnet w3 = Web3(Web3.HTTPProvider(ZORA_RPC)) # Zora is OP Stack, so it needs POA middleware w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) print(f"Connected to Zora: {w3.is_connected()}") # Get the Transfer event signature hash TRANSFER_EVENT_SIG = "0x" + w3.keccak(text="Transfer(address,address,uint256)").hex() # Track seen token addresses seen_tokens = set() # Helper to get token info def get_token_info(token_address): # Convert to checksum address for web3.py compatibility checksum_address = Web3.to_checksum_address(token_address) contract = w3.eth.contract(address=checksum_address, abi=ERC20_ABI) try: name = contract.functions.name().call() except Exception: name = "?" try: symbol = contract.functions.symbol().call() except Exception: symbol = "?" try: decimals = contract.functions.decimals().call() except Exception: decimals = "?" return name, symbol, decimals # Start from latest block start_block = w3.eth.block_number print(f"Starting from block {start_block}") while True: latest_block = w3.eth.block_number for block_num in range(start_block, latest_block + 1): block = w3.eth.get_block(block_num, full_transactions=False) logs = w3.eth.get_logs({ "fromBlock": block_num, "toBlock": block_num, "topics": [TRANSFER_EVENT_SIG], }) for log in logs: token_address = log["address"].lower() if token_address in EXCLUDED_TOKENS: continue # Only fetch token info once per token if token_address not in seen_tokens: name, symbol, decimals = get_token_info(token_address) seen_tokens.add(token_address) print(f"\nNew token detected: {token_address}") print(f" Name: {name}") print(f" Symbol: {symbol}") print(f" Decimals: {decimals}") # Parse transfer event from_addr = "0x" + log["topics"][1].hex()[-40:] to_addr = "0x" + log["topics"][2].hex()[-40:] value = int.from_bytes(log["data"].rjust(32, b"\x00"), byteorder="big") print(f"Transfer | Token: {token_address} | From: {from_addr} | To: {to_addr} | Value: {value}") start_block = latest_block + 1 time.sleep(2) ``` ## Conclusion This tutorial demonstrated how to build a real-time creator token detection system for Zora mainnet using Python and Chainstack's infrastructure. By monitoring ERC-20 transfer events and filtering out standard tokens, we can focus specifically on the creator tokens that embody Zora's vision of an empowered creator economy. The monitoring approach provides valuable insights into how creators are experimenting with tokenization, community building, and new economic models. This pattern can be extended to include additional filtering criteria, token analysis, or integration with other data sources to provide even deeper insights into Zora's creator ecosystem. ### About the author > [card] Ake > > ![Ake](https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1719912994363326464/8_Bi4fdM_400x400.jpg?fit=max\&auto=format\&n=UN3rP7zhB69idvnC\&q=85\&s=792a24ab1b4682406fa589c0ecd88e5d) > > Director of Developer Experience @ Chainstack > \ > Talk to me all things Web3 > \ > 20 years in technology | 8+ years in Web3 full time years experience > > [https://github.com/akegaviar/](https://github.com/akegaviar/) > > [https://twitter.com/akegaviar](https://twitter.com/akegaviar) > > [https://www.linkedin.com/in/ake/](https://www.linkedin.com/in/ake/) # Zora tooling Source: https://docs.chainstack.com/docs/zora-tooling Zora blockchain development tools guide. Use MetaMask, web3.py, ethers.js, Hardhat, Remix IDE, and Foundry with Chainstack Zora nodes and endpoints. Get started with a [reliable Zora RPC endpoint](https://chainstack.com/build-better-with-zora/) to use the tools below. ## MetaMask On [node access details](/docs/manage-your-node#view-node-access-and-credentials), click **Connect wallet**. ## web3.py Build DApps using [web3.py](https://github.com/ethereum/web3.py) and Zora nodes deployed with Chainstack. 1. Install [web3.py](https://web3py.readthedocs.io/). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `HTTPProvider` to connect to your node endpoint and get the latest block number. ```python Key Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('YOUR_CHAINSTACK_ENDPOINT')) print(web3.eth.block_number) ``` ```python Password Protected theme={"system"} from web3 import Web3 web3 = Web3(Web3.HTTPProvider('https://%s:%s@%s'% ("USERNAME", "PASSWORD", "HOSTNAME"))) print(web3.eth.block_number) ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## ethers.js Build DApps using [ethers.js](https://github.com/ethers-io/ethers.js/) and Zora nodes deployed with Chainstack. 1. Install [ethers.js](https://www.npmjs.com/package/ethers). 2. Connect over HTTP. See also [EVM node connection: HTTP vs WebSocket](https://support.chainstack.com/hc/en-us/articles/900002187586-Ethereum-node-connection-HTTP-vs-WebSocket). ### HTTP Use the `JsonRpcProvider` object to connect to your node endpoint and get the latest block number: ```javascript Key Protected theme={"system"} const { ethers } = require("ethers"); var urlInfo = { url: 'YOUR_CHAINSTACK_ENDPOINT' }; var provider = new ethers.JsonRpcProvider(urlInfo.url, NETWORK_ID); provider.getBlockNumber().then(console.log); ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password * NETWORK\_ID — Zora network ID: * Mainnet: `7777777` See also [node access details](/docs/manage-your-node#view-node-access-and-credentials). ## Hardhat Configure [Hardhat](https://hardhat.org/) to deploy contracts and interact through your Zora nodes. 1. Install [Hardhat](https://hardhat.org/) and create a project. 2. Create a new environment in `hardhat.config.js`: ```javascript Javascript theme={"system"} require("@nomiclabs/hardhat-waffle"); ... module.exports = { solidity: "0.7.3", networks: { chainstack: { url: "YOUR_CHAINSTACK_ENDPOINT", accounts: ["YOUR_PRIVATE_KEY"] }, } }; ``` where * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS or WSS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials). * YOUR\_PRIVATE\_KEY — the private key of the account that you use to deploy the contract 3. Run `npx hardhat run scripts/deploy.js --network chainstack` and Hardhat will deploy using Chainstack. See also [Forking EVM-compatible mainnet with Hardhat](https://support.chainstack.com/hc/en-us/articles/900004242406). ## Remix IDE To make Remix IDE interact with the network through a Chainstack node: 1. Get [MetaMask](https://metamask.io/) and set it to interact through a Chainstack node. See [Interacting through MetaMask](#metamask). 2. In Remix IDE, navigate to the **Deploy** tab. Select **Injected Provider - MetaMask** in **Environment**. This will engage MetaMask and make Remix IDE interact with the network through a Chainstack node. ## Foundry 1. Install [Foundry](https://getfoundry.sh/). 2. Use `--rpc-url` to run the operation through your Chainstack node. ### Forge Use `forge` to develop, test, and deploy your smart contracts. To deploy a contract: ```shell Shell theme={"system"} forge create CONTRACT_NAME --contracts CONTRACT_PATH --private-key YOUR_PRIVATE_KEY --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where * CONTRACT\_NAME — name of the contract in the Solidity source code * CONTRACT\_PATH — path to your smart contract * YOUR\_PRIVATE\_KEY — the private key to your funded account that you will use to deploy the contract * YOUR\_CHAINSTACK\_ENDPOINT — your node HTTPS endpoint protected either with the key or password ### Cast Use `cast` to interact with the network and the deployed contracts. To get the latest block number: ```shell Shell theme={"system"} cast block-number --rpc-url YOUR_CHAINSTACK_ENDPOINT ``` where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password # accountSubscribe | Solana Source: https://docs.chainstack.com/reference/accountsubscribe-solana Subscribe to real-time Solana account changes with accountSubscribe. Receive WebSocket notifications whenever a monitored account is modified. ## Parameters * `publicKey` - The public key of the account to monitor, as a base-58 encoded string * `` - (optional) The level of commitment desired when querying state. Default is "finalized". * `encoding` - (optional) Data encoding for the account data: * `"base58"` - Base-58 encoding (default) * `"base64"` - Base-64 encoding * `"base64+zstd"` - Compressed Base-64 encoding * `"jsonParsed"` - JSON format (if program supports it) ## Response * `result` - An integer subscription id (needed to unsubscribe) * `subscription` - An object containing account information: * `lamports` - Number of lamports assigned to the account * `owner` - Base-58 encoded public key of the program this account has been assigned to * `data` - Account data, encoded according to specified encoding parameter * `executable` - Boolean indicating if the account contains a program * `rentEpoch` - The epoch at which this account will next owe rent ## `accountSubscribe` code examples This example subscribes to changes in a token account. > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"accountSubscribe","params":["39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg",{"encoding":"jsonParsed","commitment":"finalized"}]} ``` ## Use case The `accountSubscribe` method in Solana is essential for applications that need to monitor account state changes in real-time. Common use cases include: * Token tracking: Monitor token account balances for changes to update user interfaces instantly * Program monitoring: Track program-owned accounts to detect state changes in smart contracts * Wallet integration: Keep wallet balances and account states synchronized with the blockchain * DeFi applications: Monitor liquidity pools, lending positions, or other DeFi-related accounts * Gaming: Track in-game asset accounts and player state changes in real-time By subscribing to account changes, applications can provide responsive user experiences without constantly polling the blockchain for updates. This is particularly useful for applications requiring real-time updates like trading interfaces, gaming platforms, or wallet applications. # accountUnsubscribe | Solana Source: https://docs.chainstack.com/reference/accountunsubscribe-solana Cancel a Solana account subscription with accountUnsubscribe. Stop receiving real-time WebSocket notifications for previously monitored account changes. See also [accountSubscribe | Solana](/reference/accountsubscribe-solana). ## Parameters * `integer` - The ID of the subscription that you want to cancel. This is the subscription ID that was returned when calling `accountSubscribe`. ## Response * `boolean` - A boolean value indicating if the subscription was removed successfully. `true` if removed successfully, `false` if not. ## `accountUnsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection. You can use a WebSocket client library for your programming language of choice to interact with Solana's WebSocket API. ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":" {"jsonrpc":"2.0","id":1,"method":"accountUnsubscribe","params":[0]}","params":[0]} ``` # Accounts info | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-accounts-info-rpc-methods Arbitrum account information JSON-RPC methods — retrieve balances, nonces, contract code, and storage data for accounts on the Arbitrum network. Retrieving account information from the Arbitrum blockchain can be done by using the following methods: > [card] [eth\_getTransactionCount](/reference/arbitrum-gettransactioncount) > [card] [eth\_getBalance](/reference/arbitrum-getbalance) > [card] [eth\_getCode](/reference/arbitrum-getcode) > [card] [eth\_getStorageAt](/reference/arbitrum-getstorageat) # arbtrace_block | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-arbtrace-block openapi/arbitrum_node_api/debug_and_trace/arbtrace_block.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that enables the tracing of the execution of a specific block using its number. Chainstack Arbitrum reference. Arbitrum API method that enables the tracing of the execution of a specific block using its number. This method can be used to troubleshoot and analyze smart contracts and transactions on the Arbitrum blockchain. It provides an in-depth trace of the block execution, with details on all the interactions, such as transactions and calls, that took place. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > Blocks older than 22,207,815 were added to the chain before the Nitro migration and cannot be queried with Geth `debug_*` methods. Starting from block 22,207,815, Arbitrum migrated to Nitro which made Geth `debug_*` methods available for newer blocks. > > Use the `arbtrace_block` method for calling blocks prior to 22,207,815. ## Parameters * `quantity` — the integer of a block encoded as hexadecimal or the string. ## Response * `result` — an object containing the traces of all of the transactions in the block: * `action` — an object that describes the action taken by the transaction: * `from` — the address of the sender who initiated the transaction. * `callType` — the type of call, `call` or `delegatecall`, two ways to invoke a function in a smart contract. `call` creates a new environment for the function to work in, so changes made in that function won't affect the environment where the function was called. `delegatecall` doesn't create a new environment. Instead, it runs the function within the environment of the caller, so changes made in that function will affect the caller's environment. * `gas` — the units of gas included in the transaction by the sender. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is null. * `value` — the value of the native token transferred along with the transaction in Wei. * `blockHash` — the hash of the block in which the transaction was included. * `blockNumber` — the number of the block in which the transaction was included. * `error` — a string that indicates whether the transaction was successful or not. `null` if successful, `Reverted` if not. * `result` — an object that contains additional data about the execution of the transaction: * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `output` — the return value of the call, encoded as a hexadecimal string. * `subtraces` — the number of sub-traces created during execution. When a transaction is executed on the EVM, it may trigger additional sub-executions, such as when a smart contract calls another smart contract or when an external account is accessed. * `traceAddress` — an array that indicates the position of the transaction in the trace. * `transactionHash` — the hash of the transaction. * `transactionPosition` — the position of the transaction in the block. * `type` — the type of action taken by the transaction, `call` or `create`. `call` is the most common type of trace and occurs when a smart contract invokes another contract's function. `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain. ## `arbtrace_block` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const arbtraceBlock = async (block) => { const traces = await provider.send("arbtrace_block", [block]); console.log(traces); }; arbtraceBlock("0xE4E1C0") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3.HTTPProvider(node_url) block = "0xE4E1C0" block_traces = web3.provider.make_request('arbtrace_block', [block]) print(block_traces) ``` ## Use case The `arbtrace_block` method on Arbitrum can be used in analyzing the blocks on the network. By observing the gas supplied by transactions and the corresponding gas consumption, developers can gather valuable information about the behavior of the block. This analysis serves as an initial step toward creating a chain explorer. # arbtrace_call | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-arbtrace-call openapi/arbitrum_node_api/debug_and_trace/arbtrace_call.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that traces the execution of eth_call within the context of a specific block's execution. Arbitrum via Chainstack. Arbitrum API method that traces the execution of [eth\_call](/reference/arbitrum-ethcall) within the context of a specific block's execution. This method uses the final state of the parent block as its base and allows developers to trace the execution of a particular call. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. > [!WARNING] > > Blocks older than 22,207,815 were added to the chain before the Nitro migration and cannot be queried with Geth `debug_*` methods. Starting from block 22,207,815, Arbitrum migrated to Nitro which made Geth `debug_*` methods available for newer blocks. > > Use the `arbtrace_call` method for calling blocks prior to 22,207,815. ## Parameters * `object` — the transaction call object: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity` — the integer of a block, encoded as hexadecimal. * `array` — an array identifying the type of tracer and its configuration: * `trace` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. ## Response * `output` — the data returned as a result of the transaction, encoded in hexadecimal format. * `stateDiff` — reveals changes to the state resulting from the execution of the given transaction. * `trace` — the basic trace of specific information. * `action` — the operation to be performed on the recipient. * `from` — the address initiating the transaction. * `callType` — the type of method, such as `call` or `delegatecall`. * `gas` — the units of gas supplied by the sender, encoded in hexadecimal format. * `input` — the data transmitted along with the transaction, typically used for interaction with smart contracts. * `to` — the recipient's address. If it's a contract creation transaction, this field is `null`. * `value` — the amount sent with the transaction, encoded as a hexadecimal. * `result` — the value of the gas price used, encoded as hexadecimal. * `gasUsed` — the total amount of gas used by all the transactions in the block, encoded as hexadecimal. * `output` — the return value from the contract call, encoded in hexadecimal. If the `RETURN` method isn't executed, the output will be empty bytes. * `subtraces` — a list of contract calls made by the transaction, each represented as a nested `call`frame object. * `traceAddress` — a list of addresses where the call was executed, the addresses of the parent calls, and the order of the current sub-call. * `type` — the value of the method, such as `call` or `create`. * `vmTrace` — a comprehensive trace of the virtual machine's state during the execution of a given transaction, including any sub-calls. ## `arbtrace_call` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const arbtraceCall = async () => { const call = { "from": '0xeff678bf68ca0da9dfdac0c88f431e8d0e2f7116', "to": '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506', "gas": '0xfa4d9', "gasPrice": '0x9c1e25d', "data": '0x18cbafe5000000000000000000000000000000000000000000000000000000003038624200000000000000000000000000000000000000000000000006ff882fdeb13bd100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000eff678bf68ca0da9dfdac0c88f431e8d0e2f711600000000000000000000000000000000000000000000000000000000630f76620000000000000000000000000000000000000000000000000000000000000002000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1' } const block = '0xE4E1C0' const tracer = 'trace'; const traces = await provider.send("arbtrace_call", [call, [tracer], block]); console.log(traces); }; arbtraceCall() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3.HTTPProvider(node_url) call = { 'from': '0xeff678bf68ca0da9dfdac0c88f431e8d0e2f7116', 'to': '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506', 'gas': '0xfa4d9', 'gasPrice': '0x9c1e25d', 'data': '0x18cbafe5000000000000000000000000000000000000000000000000000000003038624200000000000000000000000000000000000000000000000006ff882fdeb13bd100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000eff678bf68ca0da9dfdac0c88f431e8d0e2f711600000000000000000000000000000000000000000000000000000000630f76620000000000000000000000000000000000000000000000000000000000000002000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1' } block = 'latest' # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. tracer = ['trace'] tx_traces = web3.provider.make_request('arbtrace_call', [call, tracer, block]) print(tx_traces) ``` # arbtrace_callMany | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-arbtrace-callmany openapi/arbitrum_node_api/debug_and_trace/arbtrace_callMany.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that executes multiple call traces on the same block within the blockchain. Use it on Arbitrum via Chainstack. Arbitrum API method that executes multiple call traces on the same block within the blockchain. Before executing a transaction, all preceding transactions are first applied and traced to ensure a clear, sequential record of transactions and their interdependencies. This allows for the tracing of dependent transactions and is essential for integrity and accountability. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > Blocks older than 22,207,815 were added to the chain before the Nitro migration and cannot be queried with Geth `debug_*` methods. Starting from block 22,207,815, Arbitrum migrated to Nitro which made Geth `debug_*` methods available for newer blocks. > > Use the `arbtrace_callMany` method for calling blocks prior to 22,207,815. ## Parameters * `array` — transaction call objects: * `object` — the transaction call object to trace; can be multiple call objects for different transactions: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `array` — the string `trace` to indicate the type of tracer. * `quantity` — the integer of a block, encoded as hexadecimal. ## Response * `array` — an array with the traces objects: * `output` — the data returned as a result of the transaction, encoded in hexadecimal format. * `stateDiff` — reveals changes to the state resulting from the execution of the given transaction. * `trace` — the basic trace of specific information. * `action` — the operation to be performed on the recipient. * `from` — the address initiating the transaction. * `callType` — the type of method, such as `call` or `delegatecall`. * `gas` — the units of gas supplied by the sender, encoded in hexadecimal format. * `input` — the data transmitted along with the transaction, typically used for interaction with smart contracts. * `to` — the recipient's address. If it's a contract creation transaction, this field is `null`. * `value` — the amount sent with the transaction, encoded as a hexadecimal. * `result` — the value of the gas price used, encoded as hexadecimal. * `gasUsed` — the total amount of gas used by all the transactions in the block, encoded as hexadecimal. * `output` — the return value from the contract call, encoded in hexadecimal. If the `RETURN` method isn't executed, the output will be empty bytes. * `subtraces` — a list of contract calls made by the transaction, each represented as a nested `call`frame object. * `traceAddress` — a list of addresses where the call was executed, the addresses of the parent calls, and the order of the current sub-call. * `type` — the value of the method, such as `call` or `create`. * `vmTrace` — a comprehensive trace of the virtual machine's state during the execution of a given transaction, including any sub-calls. ## `arbtrace_callMany` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const arbtraceCallMany = async () => { const calls = [ [ { "from": "0xb8351B61Fa1Eb007A9f80144C489d513e6A76b14", "to": "0x478fa4C971a077038B4Fc5C172c3Af5552224ccc", "value": "0xb1a2bc2ec50000" }, [ "trace" ] ], [ { "from": "0xb8351B61Fa1Eb007A9f80144C489d513e6A76b14", "to": "0x988aA44E12c7BCE07E449A4156b4A269d6642B3A", "value": "0x6f05b59d3b20000" }, [ "trace" ] ] ]; const block = "latest"; const traces = await provider.send("arbtrace_callMany", [calls, block]); console.log(traces); }; arbtraceCallMany(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3.HTTPProvider(node_url) calls = [ [ { 'from': '0xb8351B61Fa1Eb007A9f80144C489d513e6A76b14', 'to': '0x478fa4C971a077038B4Fc5C172c3Af5552224ccc', 'value': '0xb1a2bc2ec50000' }, ['trace'] ], [ { 'from': '0xb8351B61Fa1Eb007A9f80144C489d513e6A76b14', 'to': '0x988aA44E12c7BCE07E449A4156b4A269d6642B3A', 'value': '0x6f05b59d3b20000' }, ['trace'] ] ] block = 'latest' response = web3.provider.make_request('arbtrace_callMany', [calls, block]) print(response) ``` ## Use case The `arbtrace_callMany` method is a powerful tool for retrieving execution traces of multiple transactions or contract calls in a single batch. It offers efficiency and convenience by reducing the number of requests required to fetch trace data, thereby improving performance. # arbtrace_filter | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-arbtrace-filter openapi/arbitrum_node_api/debug_and_trace/arbtrace_filter.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that allows to retrieve internal transaction data by defining a specific filter object. Arbitrum via Chainstack. Arbitrum API method that allows to retrieve internal transaction data by defining a specific filter object. This method is a powerful tool for gaining insight into the intricate operations of complex transactions, particularly those associated with smart contracts. It provides you with the capability to seamlessly access all internal transactions connected to a particular address within a predefined block range. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > Blocks older than 22,207,815 were added to the chain before the Nitro migration and cannot be queried with Geth `debug_*` methods. Starting from block 22,207,815, Arbitrum migrated to Nitro which made Geth `debug_*` methods available for newer blocks. > > Use the `arbtrace_filter` method for calling blocks prior to 22,207,815. ## Parameters * `object` — the filter object used for specifying the following filter criteria: * `fromBlock` — (optional) specifies the starting block from which to retrieve data. * `toBlock` — (optional) specifies the ending block until which data should be retrieved. * `fromaddress` — (optional) array of addresses of the senders from which the transactions should be filtered. * `toaddress` — (optional) array of addresses of the receivers to which the transactions should be filtered. * `after` — (optional) specifies the offset trace number to start retrieving traces. * `count` — (optional) specifies the number of traces to display in a batch. ## Response * `array` — represents the block traces, which consist of the following fields (note that all return types are hexadecimal representations unless otherwise stated): * `action` — represents the `ParityTrace` object, which provides details on the following: * `from` — indicates the address of the sender. * `callType` — specifies the type of method used, such as `call` or `delegatecall`. * `gas` — represents the gas provided by the sender, encoded in hexadecimal. * `input` — contains the data sent along with the transaction. * `to` — specifies the address of the receiver. * `value` — indicates the value sent with this transaction, encoded as an integer in hexadecimal. * `blockHash` — provides the hash of the block where this transaction was included. * `blockNumber` — represents the block number in which this transaction was included. * `result` — refers to the `ParityTraceResult` object, which includes the following information: * `gasUsed` — represents the amount of gas used by this specific transaction alone. * `output` — contains the value returned by the contract call. It includes the actual value sent by the `RETURN` method. If the `RETURN` method was not executed, the output is empty bytes. * `subtraces` — represents the traces of contract calls made by the transaction. * `traceAddress` — specifies the list of addresses where the call executed, including the address of the parents and the order of the current sub-call. * `transactionHash` — refers to the hash of the transaction. * `transactionPosition` — indicates the position of the transaction. * `type` — specifies the type of method used, such as `call` or `create`. ## `arbtrace_filter` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const arbtraceFilter = async () => { const filter = { "fromBlock": "0xE4E1C0", "toBlock": "0xE4E1C2", "fromaddress": ["0x9e6ef7f75ad88d4edb4c9925c94b769c5b0d6281"], }; const traces = await provider.send("arbtrace_filter", [filter]); console.log(traces); }; arbtraceFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3.HTTPProvider(node_url) filter = { "fromBlock": "0xE4E1C0", "toBlock": "0xE4E1C2", "fromaddress": ["0x9e6ef7f75ad88d4edb4c9925c94b769c5b0d6281"], }; response = web3.provider.make_request('arbtrace_filter', [filter]) print(response) ``` ## Use case The `arbtrace_filter` method on the Arbitrum blockchain allows you to retrieve detailed traces of smart contract execution. Think of traces as a step-by-step account of what happens when a smart contract is called or interacts with other contracts. With this method, you can specify filter criteria, such as the range of blocks you're interested in. When you invoke the `arbtrace_filter` method, it returns traces that match your criteria. Traces are useful for various purposes. For example, you can debug smart contract transactions by analyzing the sequence of function calls, state changes, and events emitted during execution. This helps you identify and fix issues in your contract's logic. Traces also provide insights into gas consumption, allowing you to optimize gas usage in your contracts by identifying gas-intensive operations. # arbtrace_get | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-arbtrace-get openapi/arbitrum_node_api/debug_and_trace/arbtrace_get.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the traces of a transaction based on its traces position. arbtrace_get on Arbitrum via Chainstack. Arbitrum API method that returns the traces of a transaction based on its traces position. The index positions of the traces within a transaction refer to the individual steps or actions that occurred during the execution of that transaction. Each trace represents a specific operation or event within the transaction, such as a contract function call or a state change. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. > [!WARNING] > > Blocks older than 22,207,815 were added to the chain before the Nitro migration and cannot be queried with Geth `debug_*` methods. Starting from block 22,207,815, Arbitrum migrated to Nitro which made Geth `debug_*` methods available for newer blocks. > > Use the `arbtrace_get` method for calling blocks prior to 22,207,815. ## Parameters * `hash` — the transaction hash for which you want to retrieve the trace. It is a unique identifier for the transaction. * `array` — an array of index positions representing the traces within the transaction that you want to retrieve. The index positions indicate the specific steps or actions within the transaction. ## Response * `result` — an object containing the traces of all of the transaction: * `action` — an object that describes the action taken by the transaction: * `calltype` — the type of call, `call` or `delegatecall`, two ways to invoke a function in a smart contract. `call` creates a new environment for the function to work in, so changes made in that function won't affect the environment where the function was called. `delegatecall` doesn't create a new environment. Instead, it runs the function within the environment of the caller, so changes made in that function will affect the caller's environment. * `from` — the address that initiated the action. * `gas` — the amount of gas allocated for the action. * `input` — the input data provided for the action. * `to` — the address of the contract or account receiving the action. * `value` — the value in Wei sent along with the action. * `blockhash` — the hash of the block where the action was included. * `blocknumber` — the number of the block where the action was included. * `result` — an object that contains additional data about the execution of the transaction: * `gasused` — the amount of gas used during the action. * `output` — the output data returned by the action. * `subtraces` — the number of sub-traces created during execution. When a transaction is executed on the EVM, it may trigger additional sub-executions, such as when a smart contract calls another smart contract or when an external account is accessed. * `traceaddress` — an array representing the path to the current action within the trace. * `transactionhash` — the hash of the transaction associated with the trace. * `transactionposition` — the position of the transaction within the block. * `type` — the type of action taken by the transaction, `call` or `create`. `call` is the most common type of trace and occurs when a smart contract invokes another contract's function. `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain. ## `arbtrace_get` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const arbtraceGet = async () => { const transactionHash = "0xe8648e3ad982a3d67ef0880d6631343cffff364786994b34e5fa292cfef0e680"; const traceOptions = ["0x0"]; const traces = await provider.send("arbtrace_get", [transactionHash, traceOptions]); console.log(traces); }; arbtraceGet(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3.HTTPProvider(node_url) transactionHash = "0xe8648e3ad982a3d67ef0880d6631343cffff364786994b34e5fa292cfef0e680"; traceOptions = ["0x0"]; response = web3.provider.make_request('arbtrace_get', [transactionHash, traceOptions]) print(response) ``` ## Use case The `arbtrace_get` method is valuable for transaction analysis and debugging on the Arbitrum blockchain. By retrieving a detailed trace of a specific transaction, you can gain insights into the sequence of actions and their outcomes. Practical use cases include: * Debugging smart contract transactions — analyzing the trace helps identify issues and unexpected behavior by examining the specific function calls, parameters, and values involved. * Identifying execution errors — the trace provides information on where errors occurred within the transaction, helping pinpoint the cause of failures or issues. Gas consumption and output data can also be examined for error messages. * Gas usage optimization — by analyzing the `gasUsed` field in the trace's result, you can identify gas-intensive operations and optimize gas usage in your smart contract code. * Auditing and security analysis — the `arbtrace_get` method enables thorough audits of smart contract transactions. Analyzing the trace ensures that the transaction aligns with expected behavior, state changes, and security requirements. # arbtrace_replayBlockTransactions | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-arbtrace-replayblocktransactions openapi/arbitrum_node_api/debug_and_trace/arbtrace_replayBlockTransactions.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that replays all the transactions within a specific block and returns the traces. Chainstack Arbitrum reference. Arbitrum API method that replays all the transactions within a specific block and returns the traces. Developers can use the `arbtrace_replayBlockTransactions` method to gain insight into the behavior of smart contracts within a block, analyze gas usage, and optimize their contracts accordingly. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. > [!WARNING] > > Blocks older than 22,207,815 were added to the chain before the Nitro migration and cannot be queried with Geth `debug_*` methods. Starting from block 22,207,815, Arbitrum migrated to Nitro which made Geth `debug_*` methods available for newer blocks. > > Use the `arbtrace_replayBlockTransactions` method for calling blocks prior to 22,207,815. ## Parameters * `quantity` — the integer of a block, encoded as hexadecimal or the string. * `array` — an array identifying the type of tracer and its configuration: * `trace` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. ## Response * `output` — the data returned as a result of the transaction, encoded in hexadecimal format. * `stateDiff` — reveals changes to the state resulting from the execution of the given transaction. * `trace` — the basic trace of specific information. * `action` — the operation to be performed on the recipient. * `from` — the address initiating the transaction. * `callType` — the type of method, such as `call` or `delegatecall`. * `gas` — the units of gas supplied by the sender, encoded in hexadecimal format. * `input` — the data transmitted along with the transaction, typically used for interaction with smart contracts. * `to` — the recipient's address. If it's a contract creation transaction, this field is `null`. * `value` — the amount sent with the transaction, encoded as a hexadecimal. * `result` — the value of the gas price used, encoded as hexadecimal. * `gasUsed` — the total amount of gas used by all the transactions in the block, encoded as hexadecimal. * `output` — the return value from the contract call, encoded in hexadecimal. If the `RETURN` method isn't executed, the output will be empty bytes. * `subtraces` — a list of contract calls made by the transaction, each represented as a nested `call`frame object. * `traceAddress` — a list of addresses where the call was executed, the addresses of the parent calls, and the order of the current sub-call. * `type` — the value of the method, such as `call` or `create`. * `vmTrace` — a comprehensive trace of the virtual machine's state during the execution of a given transaction, including any sub-calls. * `destroyedContracts` — a list of the smart contract destroyed during the block execution; `null` if no contract was destroyed. ## `arbtrace_replayBlockTransactions` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const arbtraceReplayBlockTransactions = async () => { const block = '0xE4E1C0' const tracer = 'trace'; const traces = await provider.send("arbtrace_replayBlockTransactions", [block, [tracer]]); console.log(traces); }; arbtraceReplayBlockTransactions(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3.HTTPProvider(node_url) block = "0xE4E1C0"; traceOptions = ["trace"]; response = web3.provider.make_request('arbtrace_replayBlockTransactions', [transactionHash, traceOptions]) print(response) ``` ## Use case The `arbtrace_replayBlockTransactions` allows you to replay the transactions within a specific block and retrieve detailed execution traces for each transaction. It is particularly useful for analyzing the state changes and interactions that occurred during the execution of transactions within a block. Here are the key points to understand about the `arbtrace_replayBlockTransactions` method: * Retrieving execution traces — the method replays the transactions within a block and returns execution traces for each transaction. These traces provide a detailed account of the state changes, internal transactions, and events triggered during the execution. * Block-level analysis — the method focuses on a specific block and replays all the transactions within that block. It is different from individual transaction tracing methods, as it provides a comprehensive view of the interactions and effects of multiple transactions within a single block. * Contextual understanding — by using `arbtrace_replayBlockTransactions`, developers can gain a deeper understanding of the sequence and effects of transactions within a block. This can be valuable for debugging complex interactions, analyzing reentrancy issues, or understanding the overall impact of transactions on the blockchain state. # arbtrace_replayTransaction | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-arbtrace-replaytransaction openapi/arbitrum_node_api/debug_and_trace/arbtrace_replayTransaction.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that replays a specific transaction and returns the traces. arbtrace_replayTransaction on Arbitrum via Chainstack. Arbitrum API method that replays a specific transaction and returns the traces. Developers can use the `arbtrace_replayTransaction` method to gain insight into the behavior of smart contracts, analyze gas usage, and optimize their contracts accordingly. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. > [!WARNING] > > Blocks older than 22,207,815 were added to the chain before the Nitro migration and cannot be queried with Geth `debug_*` methods. Starting from block 22,207,815, Arbitrum migrated to Nitro which made Geth `debug_*` methods available for newer blocks. > > Use the `arbtrace_replayTransaction` method for calling blocks prior to 22,207,815. ## Parameters * `hash` — the hash identifying a transaction. * `array` — an array identifying the type of tracer and its configuration: * `trace` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. ## Response * `output` — the data returned as a result of the transaction, encoded in hexadecimal format. * `stateDiff` — reveals changes to the state resulting from the execution of the given transaction. * `trace` — the basic trace of specific information. * `action` — the operation to be performed on the recipient. * `from` — the address initiating the transaction. * `callType` — the type of method, such as `call` or `delegatecall`. * `gas` — the units of gas supplied by the sender, encoded in hexadecimal format. * `input` — the data transmitted along with the transaction, typically used for interaction with smart contracts. * `to` — the recipient's address. If it's a contract creation transaction, this field is `null`. * `value` — the amount sent with the transaction, encoded as a hexadecimal. * `result` — the value of the gas price used, encoded as hexadecimal. * `gasUsed` — the total amount of gas used by all the transactions in the block, encoded as hexadecimal. * `output` — the return value from the contract call, encoded in hexadecimal. If the `RETURN` method isn't executed, the output will be empty bytes. * `subtraces` — a list of contract calls made by the transaction, each represented as a nested `call`frame object. * `traceAddress` — a list of addresses where the call was executed, the addresses of the parent calls, and the order of the current sub-call. * `type` — the value of the method, such as `call` or `create`. * `vmTrace` — a comprehensive trace of the virtual machine's state during the execution of a given transaction, including any sub-calls. * `destroyedContracts` — a list of the smart contract destroyed during the block execution; `null` if no contract was destroyed. ## `arbtrace_replayTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const arbtraceReplayTransaction = async () => { const hash = '0xe8648e3ad982a3d67ef0880d6631343cffff364786994b34e5fa292cfef0e680' const tracer = 'trace'; const traces = await provider.send("arbtrace_replayTransaction", [hash, [tracer]]); console.log(traces); }; arbtraceReplayTransaction() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3.HTTPProvider(node_url) transactionHash = "0xe8648e3ad982a3d67ef0880d6631343cffff364786994b34e5fa292cfef0e680"; traceOptions = ["trace"]; response = web3.provider.make_request('arbtrace_replayTransaction', [transactionHash, traceOptions]) print(response) ``` ## Use case The `arbtrace_replayTransaction` allows you to replay a specific transaction and analyze its interactions. Suppose you are a smart contract developer working on a decentralized application that involves multiple contract interactions within a single transaction. During the development process, you encounter unexpected behaviors or errors when executing certain transactions, and you need to understand the root cause. In such a scenario, you can use the `arbtrace_replayTransaction` method to replay the problematic transaction and retrieve the traces associated with it. By analyzing these traces, you can gain valuable insights into the execution flow and pinpoint the cause of the issue. # arbtrace_transaction | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-arbtrace-transaction openapi/arbitrum_node_api/debug_and_trace/arbtrace_transaction.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that traces a specific transaction. arbtrace_transaction JSON-RPC method available on the Arbitrum blockchain via Chainstack. Arbitrum API method that traces a specific transaction. It provides a detailed record of all the steps taken by the Ethereum Virtual Machine (EVM) during the execution, including all the operations performed and the changes made to the blockchain state. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. > [!WARNING] > > Blocks older than 22,207,815 were added to the chain before the Nitro migration and cannot be queried with Geth `debug_*` methods. Starting from block 22,207,815, Arbitrum migrated to Nitro which made Geth `debug_*` methods available for newer blocks. > > Use the `arbtrace_transaction` method for calling blocks prior to 22,207,815. ## Parameters * `hash` — the hash identifying a transaction ## Response * `action` — an object that describes the action taken by the transaction: * `from` — the address of the sender who initiated the transaction. * `callType` — the type of call, `call` or `delegatecall`, two ways to invoke a function in a smart contract. `call` creates a new environment for the function to work in, so changes made in that function won't affect the environment where the function was called. `delegatecall` doesn't create a new environment. Instead, it runs the function within the environment of the caller, so changes made in that function will affect the caller's environment. * `gas` — the units of gas included in the transaction by the sender. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is null. * `value` — the value of the native token transferred along with the transaction, in wei. * `blockHash` — the hash of the block in which the transaction was included. * `blockNumber` — the number of the block in which the transaction was included. * `error` — a string that indicates whether the transaction was successful or not. `null` if successful, `Reverted` if not. * `result` — an object that contains additional data about the execution of the transaction: * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `output` — the return value of the call, encoded as a hexadecimal string. * `subtraces` — the number of sub-traces created during execution. When a transaction is executed on the EVM, it may trigger additional sub-executions, such as when a smart contract calls another smart contract or when an external account is accessed. * `traceAddress` — an array that indicates the position of the transaction in the trace. * `transactionHash` — the hash of the transaction. * `transactionPosition` — the position of the transaction in the block. * `type` — the type of action taken by the transaction, `call` or `create`. `call` is the most common type of trace and occurs when a smart contract invokes another contract's function. `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain. ## `arbtrace_transaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txHash) => { const traces = await provider.send("arbtrace_transaction", [txHash]); console.log(traces); }; traceTransaction("0xe8648e3ad982a3d67ef0880d6631343cffff364786994b34e5fa292cfef0e680") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3.HTTPProvider(node_url) tx_hash = "0xe8648e3ad982a3d67ef0880d6631343cffff364786994b34e5fa292cfef0e680" traces = web3.provider.make_request('arbtrace_transaction', [tx_hash]) print(traces) ``` ## Use case The `arbtrace_transaction` method in Arbitrum is useful for debugging and analyzing transactions on the blockchain. It allows developers to trace the execution of a specific transaction and retrieve detailed information about its steps, contract calls, state changes, and gas consumption. Analyzing the trace helps developers identify the specific step or contract interaction where the issue occurs. With this information, they can debug and resolve the problem, whether it's a logical error, incorrect input values, or unexpected interactions with other contracts. # Blocks info | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-blocks-rpc-methods Arbitrum block information JSON-RPC methods — query block details including transactions, timestamps, height, headers, and uncle counts. Retrieving block information from the Arbitrum blockchain can be done by using the following methods: > [card] [eth\_blockNumber](/reference/arbitrum-eth_blocknumber) > [card] [eth\_getBlockByHash](/reference/arbitrum-getblockbyhash) > [card] [eth\_getBlockByNumber](/reference/arbitrum-getblockbynumber) > [card] [eth\_getBlockTransactionCountByHash](/reference/arbitrum-getblocktransactioncountbyhash) > [card] [eth\_getBlockTransactionCountByNumber](/reference/arbitrum-getblocktransactioncountbynumber) > [card] [eth\_newBlockFilter](/reference/arbitrum-newblockfilter) This information can be used to track the state of the blockchain and for: * **Blockchain analysis** — insights into the overall activity on the network, such as the number of processed transactions and average transaction fees, can be obtained. * **Block explores** — web-based tools that provide access to information on a blockchain, including block data, transactions, and addresses, allowing users to search, view, and analyze the network's activity. * **Smart contract analysis** — the activity of specific smart contracts * **DApp development** — block information can be utilized in the development of decentralized applications. # Chain info | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-chain-data-rpc-methods Arbitrum chain data RPC methods for querying network details like chain ID, syncing status, and protocol version through Chainstack node endpoints. For example, if a developer wants to build a DApp on Arbitrum, they would use these methods to get information about the network they are connecting to. They could use the chain ID information to make sure they are connecting to the right network and to ensure the security of their DApp; for example, distinguishing between the main network and a test network. Retrieving chain information from the Arbitrum blockchain can be done by using the following methods: > [card] [eth\_chainId](/reference/chainid) > [card] [eth\_syncing](/reference/arbitrum-syncing) # clearSubscriptions | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-clearsubscriptions Use eth_clearSubscriptions on Arbitrum via ethers.js to remove all active WebSocket subscriptions from your Chainstack node connection at once. The subscriptions available using the ethers.js `provider.on` method are: > [card] [on('block')](/reference/arbitrum-subscribenewblockheaders) > [card] [on(filter)](/reference/arbitrum-subscribelogs) > [card] [syncing subscription](/reference/arbitrum-subscribesyncing) ## Parameters `boolean` — keep the [syncing subscription](/reference/arbitrum-subscribesyncing) if `true`. ## Response `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `clearSubscriptions` code example ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function clearSubscriptions() { try { await provider.removeAllListeners(); console.log("Subscriptions were canceled successfully"); return process.exit(1) } catch (error) { console.error("Failed to cancel subscriptions:", error); return process.exit(1) } } clearSubscriptions() ``` ## Use case A way to use the `clearSubscriptions` method is to place it in a timer to clear all of the subscriptions after a pre-determined about. The following code is an example using the [block subscription](/reference/arbitrum-subscribenewblockheaders), which will be stopped and removed after one minute using `removeAllListeners`: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToNewBlocks() { try { // Log a message when the block subscription is set up handleConnected(); // Subscribe to new blocks and attach event listeners provider.on("block", handleNewBlock); provider.on("error", handleError); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } /* Fallback functions to react to the different events */ // Function that logs a message when the subscription is connected function handleConnected() { console.log("New subscription to new blocks"); } // Event listener that logs the received block data async function handleNewBlock(blockNumber) { const block = await provider.getBlock(blockNumber); console.log(block); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } async function clearSubscriptions() { try { await provider.removeAllListeners(); console.log("Subscriptions were canceled successfully"); return process.exit(1) } catch (error) { console.error("Failed to cancel subscriptions:", error); return process.exit(1) } } async function main() { subscribeToNewBlocks(); // Run clearSubscriptions() once after 60 seconds setTimeout(clearSubscriptions, 60000); } main() ``` The code starts by defining an async function called `subscribeToNewBlocks` that subscribes to new blocks using the `provider.on("block")` method. This method registers a listener that fires on every new block, and the code also attaches a listener for the `error` event. The code defines three functions `handleConnected`, `handleNewBlock`, and `handleError` that log messages to the console when the subscription is set up, when a new block arrives, and when an error occurs. The `subscribeToNewBlocks` function is called from the `main` function, which is also defined in the code. The `main` function calls `subscribeToNewBlocks` to create a new subscription and then schedules the `clearSubscriptions` function to run once after 60 seconds using the `setTimeout` function. The `clearSubscriptions` function uses the `provider.removeAllListeners` method to cancel any existing subscriptions and log a message to the console if the cancellation was successful. # Client information | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-client-data-rpc-methods Arbitrum client information JSON-RPC methods — check client version, network ID, chain ID, listening status, and peer count for your node. Retrieving blockchain client information from an Arbitrum node can be done by using the following method: > [card] [web3\_clientVersion](/reference/arbitrum-clientversion) # web3_clientVersion | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-clientversion openapi/arbitrum_node_api/client_info/web3_clientVersion.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the client type and version running on the Arbitrum node. web3_clientVersion on Arbitrum via Chainstack. Arbitrum API method that returns the client type and version running on the Arbitrum node. This information can be useful to developers to verify that a node they are connecting to is compatible with their needs. ## Parameters * `none` ## Response * `string` — a string identifying the type of client, version, operating system, and language version running on the node ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const clientVersion = async () => { const version = await provider.send("web3_clientVersion"); console.log(`Client version: ${version}`); }; clientVersion(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) client_version = web3.provider.make_request('web3_clientVersion', []) print(client_version) ``` ## Use case A use case for the `web3_clientVersion` method can be to verify which client version is running to then decide which function to call. Let's say you have a DApp that needs to call a different function based on the client's version. You can use `web3_clientVersion` to build this logic. Here is an implementation of this use case using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Get client version async function getClientVersion() { const clientVersion = await provider.send("web3_clientVersion"); const clientName = clientVersion.split('/')[1]; return clientName } // define a function to be run if the client version is 'vv2.0.10' function runFunction1() { console.log('Running function 1...'); } // define a function to be run if the client version is 'vv2.0.11' function runFunction2() { console.log('Running function 2...'); } // define an async function to retrieve the client version and run the appropriate function async function runBasedOnClientVersion() { try { const clientVersion = await getClientVersion(); console.log(`Client version: ${clientVersion}`); if (clientVersion === 'vv2.0.10-73224e3') { runFunction1(); } else if (clientVersion === 'vv2.0.11-73224e3') { runFunction2(); } // log an error if the client version is not recognized else { console.error('Unrecognized client version.'); } } catch (error) { console.error(error); } } // call the async function to retrieve the client version and run the appropriate function runBasedOnClientVersion(); ``` This code uses the ethers.js library to connect to a blockchain node specified by the `NODE_URL` variable and retrieves the client version using the `web3_clientVersion` method. It then runs different functions based on the client version by defining two functions `runFunction1` and `runFunction2`, and using an if statement to check the client version and call the appropriate function. The `getClientVersion` function uses `provider.send` to call the `web3_clientVersion` method directly and returns the result. This function is called within the `runBasedOnClientVersion` function to retrieve the client version. Overall, this code demonstrates how to retrieve the client version on a blockchain node using the ethers.js library and how to use the client version to run different functions based on the version. The code can be modified to add additional functions and version checks or to perform other actions based on the client version. # debug_accountRange | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-accountrange openapi/arbitrum_node_api/debug_and_trace/debug_accountRange.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that enumerates all accounts at a given block with paging capability. debug_accountRange on Arbitrum via Chainstack. Arbitrum API method that enumerates all accounts at a given block with paging capability. This method returns a range of accounts stored in the state trie, providing account details such as balance, nonce, code hash, and storage. It is useful for state inspection and debugging purposes. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `blockNrOrHash` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`), or block hash. * `start` — the start key hash for pagination, encoded as hex bytes. Use `0x0000000000000000000000000000000000000000000000000000000000000000` to start from the beginning. * `maxResults` — the maximum number of accounts to return. Capped at 256. * `nocode` — if `true`, excludes contract bytecode from the result. * `nostorage` — if `true`, excludes storage data from the result. * `incompletes` — if `true`, includes accounts without known addresses. ## Response * `result` — an object containing the state dump: * `root` — the state root hash of the block. * `accounts` — a map of account addresses to account data: * `balance` — the account balance in Wei. * `nonce` — the account nonce. * `root` — the storage root hash. * `codeHash` — the hash of the account's contract code. * `code` — the contract bytecode (omitted if `nocode` is `true`). * `storage` — the account's storage slots (omitted if `nostorage` is `true`). * `address` — the account address. * `next` — the next key hash for pagination. Use this value as the `start` parameter in the next call to continue iterating. ## `debug_accountRange` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugAccountRange = async () => { const result = await provider.send("debug_accountRange", [ "latest", "0x0000000000000000000000000000000000000000000000000000000000000000", 10, true, true, false ]); console.log(result); }; debugAccountRange(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request('debug_accountRange', [ "latest", "0x0000000000000000000000000000000000000000000000000000000000000000", 10, True, True, False ]) print(result) ``` ## Use case The `debug_accountRange` method is useful for building state explorers and performing state audits. By paginating through all accounts at a specific block, developers can analyze the distribution of balances, identify contracts, and inspect the state trie structure. The pagination support via the `start` and `next` parameters allows iterating over the full account set without loading the entire state into memory. # Debug and trace | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-and-trace-rpc-methods Arbitrum debug and trace JSON-RPC methods — use debug_* for post-Nitro blocks and arbtrace_* for pre-Nitro blocks to inspect execution traces. Developers can access the debug and trace tools on the Arbitrum blockchain with the following methods: > [card] [debug\_accountRange](/reference/arbitrum-debug-accountrange) > [card] [debug\_dumpBlock](/reference/arbitrum-debug-dumpblock) > [card] [debug\_getAccessibleState](/reference/arbitrum-debug-getaccessiblestate) > [card] [debug\_getModifiedAccountsByHash](/reference/arbitrum-debug-getmodifiedaccountsbyhash) > [card] [debug\_getModifiedAccountsByNumber](/reference/arbitrum-debug-getmodifiedaccountsbynumber) > [card] [debug\_getRawBlock](/reference/arbitrum-debug-getrawblock) > [card] [debug\_getRawHeader](/reference/arbitrum-debug-getrawheader) > [card] [debug\_getRawReceipts](/reference/arbitrum-debug-getrawreceipts) > [card] [debug\_getRawTransaction](/reference/arbitrum-debug-getrawtransaction) > [card] [debug\_intermediateRoots](/reference/arbitrum-debug-intermediateroots) > [card] [debug\_preimage](/reference/arbitrum-debug-preimage) > [card] [debug\_printBlock](/reference/arbitrum-debug-printblock) > [card] [debug\_traceBadBlock](/reference/arbitrum-debug-tracebadblock) > [card] [debug\_traceBlockByHash](/reference/arbitrum-debug-traceblockbyhash) > [card] [debug\_traceBlockByNumber](/reference/arbitrum-debug-traceblockbynumber) > [card] [debug\_traceCall](/reference/arbitrum-debug-tracecall) > [card] [debug\_traceTransaction](/reference/arbitrum-debug-tracetransaction) Arbitrum uses the Nitro client (a Geth fork) and does not support the `trace_*` namespace. Instead, use the `arbtrace_*` methods for pre-Nitro blocks and `debug_*` methods for post-Nitro blocks. > [!NOTE] > > Blocks older than 22,207,815 were added to the chain before the Nitro migration and cannot be queried with Geth `debug_*` methods. Starting from block 22,207,815, Arbitrum migrated to Nitro which made Geth `debug_*` methods available for newer blocks. Use the following `arbtrace_*` methods for calling on blocks prior to 22,207,815: > [card] [arbtrace\_block](/reference/arbitrum-arbtrace-block) > [card] [arbtrace\_call](/reference/arbitrum-arbtrace-call) > [card] [arbtrace\_callMany](/reference/arbitrum-arbtrace-callmany) > [card] [arbtrace\_filter](/reference/arbitrum-arbtrace-filter) > [card] [arbtrace\_get](/reference/arbitrum-arbtrace-get) > [card] [arbtrace\_replayBlockTransactions](/reference/arbitrum-arbtrace-replayblocktransactions) > [card] [arbtrace\_replayTransaction](/reference/arbitrum-arbtrace-replaytransaction) > [card] [arbtrace\_transaction](/reference/arbitrum-arbtrace-transaction) ## Pre-built tracers > [!NOTE] > > ### Custom JavaScript tracers need a Dedicated Node > > The tracers listed below are native (built-in) tracers—you select one by name. Custom JavaScript tracers, where you pass raw JavaScript as the `tracer`, are available as customized solutions on the [Enterprise plan](https://chainstack.com/pricing/) on [Dedicated Nodes](/docs/dedicated-node). Developers can customize the type of tracing using various debug and trace methods, and there are numerous pre-built tracers available to choose from. Here is a list of the pre-built tracers that can be utilized: ### `4byteTracer` This tracer looks for function signatures that are 4 bytes long during contract execution and gathers information about the method identifiers and the size of the provided data. This information can be helpful in matching a signature against the data size. ### `callTracer` This tracer extracts information about all internal calls made by a transaction, providing visibility into how contracts interact with each other. ### `prestateTracer` This tracer returns sufficient information about an account to create a local execution of the transaction. This procedure runs the given transaction again and monitors every aspect of the state that is modified or accessed during execution. ### `noopTracer` This tracer does nothing but respond with a blank object. This a no operations tracer for testing setup. ### `stylusTracer` This tracer is for debugging Stylus contracts by tracing calls between the WasmVM and EVM, allowing developers to fully understand their Stylus transactions. # debug_dumpBlock | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-dumpblock openapi/arbitrum_node_api/debug_and_trace/debug_dumpBlock.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that retrieves the state of all accounts at a given block number. Not available on Chainstack Global Nodes and Trader Nodes. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Arbitrum API method that retrieves the state of all accounts at a given block number. This method returns a dump of the state trie, including account balances, nonces, code hashes, and storage. It is primarily used for debugging and state inspection. > [!WARNING] > > `debug_dumpBlock` is not available on Arbitrum Global Nodes and Trader Nodes — the call returns `-32601`. The method covers post-Nitro blocks only (block 22,207,815 and later); for pre-Nitro blocks, use the `arbtrace_*` methods. ## Parameters * `quantity` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`). ## Response * `result` — an object containing the state dump: * `root` — the state root hash of the block. * `accounts` — a map of account addresses to account data: * `balance` — the account balance in Wei. * `nonce` — the account nonce. * `root` — the storage root hash. * `codeHash` — the hash of the account's contract code. * `code` — the contract bytecode. * `storage` — the account's storage slots. * `address` — the account address. > [!WARNING] > > This method returns up to 256 accounts per call. For iterating over all accounts, use `debug_accountRange` which supports pagination. ## `debug_dumpBlock` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugDumpBlock = async (blockNumber) => { const result = await provider.send("debug_dumpBlock", [blockNumber]); console.log(result); }; debugDumpBlock("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request('debug_dumpBlock', ["latest"]) print(result) ``` ## Use case The `debug_dumpBlock` method is useful for inspecting the state of all accounts at a specific block. Developers can use it to verify account states during debugging, compare state snapshots between blocks, or build tools that analyze the distribution of contract deployments and account balances at a point in time. # debug_getAccessibleState | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-getaccessiblestate openapi/arbitrum_node_api/debug_and_trace/debug_getAccessibleState.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that finds the first block number in a given range where the state is accessible on disk. Arbitrum via Chainstack. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Arbitrum API method that finds the first block number in a given range where the state is accessible on disk. This is useful for determining which historical states are available for querying on the node, as nodes may prune older state data. > [!WARNING] > > `debug_getAccessibleState` is not available on Arbitrum Global Nodes and Trader Nodes — the call returns `-32601`. The method covers post-Nitro blocks only (block 22,207,815 and later); for pre-Nitro blocks, use the `arbtrace_*` methods. ## Parameters * `from` — the start of the block range to search, as a hex-encoded block number or block tag. * `to` — the end of the block range to search, as a hex-encoded block number or block tag. ## Response * `result` — the block number (as a decimal integer) of the first block in the range where the state is accessible. Returns an error if no accessible state is found in the range. ## `debug_getAccessibleState` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugGetAccessibleState = async (from, to) => { const result = await provider.send("debug_getAccessibleState", [from, to]); console.log(result); }; debugGetAccessibleState("0x1C9C380", "latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request('debug_getAccessibleState', ["0x1C9C380", "latest"]) print(result) ``` ## Use case The `debug_getAccessibleState` method is useful for determining the historical state availability of a node. Before calling state-dependent methods like `debug_accountRange` or `debug_dumpBlock` on a specific block, developers can use this method to verify that the node still has the state data for that block. This is particularly relevant for nodes that use state pruning, where older state data may have been removed. # debug_getModifiedAccountsByHash | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-getmodifiedaccountsbyhash openapi/arbitrum_node_api/debug_and_trace/debug_getModifiedAccountsByHash.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that retrieves a list of accounts that were modified in a specific block, identified by the block's hash. On Arbitrum. Arbitrum API method that retrieves a list of accounts that were modified in a specific block, identified by the block's hash. This method compares the state tries between the specified block and its parent to find all accounts with changed state. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `startHash` — the hash of the block for which to retrieve modified accounts. When only this parameter is provided, the method returns accounts modified in this single block (compared to its parent). * `endHash` — (optional) the hash of the end block. When provided, the method returns accounts modified between the `startHash` block and the `endHash` block. ## Response * `result` — an array of account addresses that were modified in the specified block or range. ## `debug_getModifiedAccountsByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getModifiedAccounts = async (blockHash) => { const result = await provider.send("debug_getModifiedAccountsByHash", [blockHash]); console.log(result); }; getModifiedAccounts("0x5765eab677d93b81a1c29de804e115d0e4db8dd40e0deabcf187e4e0d047c758"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0x5765eab677d93b81a1c29de804e115d0e4db8dd40e0deabcf187e4e0d047c758" result = web3.provider.make_request('debug_getModifiedAccountsByHash', [block_hash]) print(result) ``` ## Use case The `debug_getModifiedAccountsByHash` method is useful for tracking state changes at the account level. Developers debugging contract interactions can identify exactly which accounts were affected by transactions in a specific block. This is also valuable for building state change monitoring tools, forensic analysis, and audit systems that need to track account modifications. # debug_getModifiedAccountsByNumber | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-getmodifiedaccountsbynumber openapi/arbitrum_node_api/debug_and_trace/debug_getModifiedAccountsByNumber.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that retrieves a list of accounts that were modified in a specific block or between two blocks, identified by block number. Arbitrum API method that retrieves a list of accounts that were modified in a specific block or between two blocks, identified by block number. This method compares the state tries between the specified blocks to find all accounts with changed state. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `startNum` — the block number (as a decimal integer) for which to retrieve modified accounts. When only this parameter is provided, the method returns accounts modified in this single block (compared to its parent). * `endNum` — (optional) the end block number (as a decimal integer). When provided, the method returns accounts modified between `startNum` and `endNum`. ## Response * `result` — an array of account addresses that were modified in the specified block or range. ## `debug_getModifiedAccountsByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getModifiedAccounts = async (startBlock, endBlock) => { const result = await provider.send("debug_getModifiedAccountsByNumber", [startBlock, endBlock]); console.log(result); }; // Get accounts modified in a single block getModifiedAccounts(30000000); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) # Get accounts modified in a single block result = web3.provider.make_request('debug_getModifiedAccountsByNumber', [30000000]) print(result) ``` ## Use case The `debug_getModifiedAccountsByNumber` method is useful for monitoring state changes over a range of blocks. Developers can use it to build state diff tools, track which accounts are affected by a series of transactions, or perform forensic analysis on state changes across a block range. Unlike `debug_getModifiedAccountsByHash`, this method accepts block numbers, making it more convenient for iterating over sequential blocks. # debug_getRawBlock | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-getrawblock openapi/arbitrum_node_api/debug_and_trace/debug_getRawBlock.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the RLP-encoded block data for a given block number or tag. Available on Arbitrum via Chainstack. Arbitrum API method that returns the RLP-encoded block data for a given block number or tag. The response includes the complete block in its raw encoded format, including the header and all transactions. This method is useful for low-level block analysis and custom block processing. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`). ## Response * `data` — the RLP-encoded block data as a hex string, including the header and all transactions. ## `debug_getRawBlock` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugGetRawBlock = async (block) => { const result = await provider.send("debug_getRawBlock", [block]); console.log(result); }; debugGetRawBlock("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request('debug_getRawBlock', ["latest"]) print(result) ``` ## Use case The `debug_getRawBlock` method is useful for applications that need to process or archive raw block data. Developers building custom block explorers, data pipelines, or cross-chain verification tools can use this method to retrieve the complete block in its serialized format for custom parsing and analysis. # debug_getRawHeader | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-getrawheader openapi/arbitrum_node_api/debug_and_trace/debug_getRawHeader.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the RLP-encoded block header for a given block number or tag. Available on Arbitrum via Chainstack. Arbitrum API method that returns the RLP-encoded block header for a given block number or tag. Unlike `debug_getRawBlock`, this method returns only the header without transaction data, making it more efficient when only header information is needed. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`). ## Response * `data` — the RLP-encoded block header as a hex string. ## `debug_getRawHeader` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugGetRawHeader = async (block) => { const result = await provider.send("debug_getRawHeader", [block]); console.log(result); }; debugGetRawHeader("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request('debug_getRawHeader', ["latest"]) print(result) ``` ## Use case The `debug_getRawHeader` method is useful when you need to inspect or verify block headers without the overhead of retrieving full block data. This is particularly valuable for light client implementations, header chain verification, and tools that need to extract specific header fields like the state root, receipts root, or gas used from the raw RLP encoding. # debug_getRawReceipts | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-getrawreceipts openapi/arbitrum_node_api/debug_and_trace/debug_getRawReceipts.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the binary-encoded receipts for all transactions in a given block. Chainstack Arbitrum reference. Arbitrum API method that returns the binary-encoded receipts for all transactions in a given block. Each receipt is encoded using the typed-transaction envelope format. This method is useful for low-level receipt processing and analysis. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`). ## Response * `result` — an array of hex-encoded binary receipts, one per transaction in the block. ## `debug_getRawReceipts` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugGetRawReceipts = async (block) => { const result = await provider.send("debug_getRawReceipts", [block]); console.log(result); }; debugGetRawReceipts("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request('debug_getRawReceipts', ["latest"]) print(result) ``` ## Use case The `debug_getRawReceipts` method is useful for applications that need to process receipt data in its raw format. This is particularly valuable for building receipt proof systems, custom indexers that decode receipts directly from their binary encoding, and tools that need to verify receipt inclusion in the receipts trie. # debug_getRawTransaction | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-getrawtransaction openapi/arbitrum_node_api/debug_and_trace/debug_getRawTransaction.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the binary-encoded transaction data for a given transaction hash. Use it on Arbitrum via Chainstack. Arbitrum API method that returns the binary-encoded transaction data for a given transaction hash. The transaction is returned in the typed-transaction envelope format. This method checks both finalized (indexed) transactions and the transaction pool. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `hash` — the hash of the transaction to retrieve. ## Response * `data` — the binary-encoded transaction as a hex string. ## `debug_getRawTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugGetRawTransaction = async (txHash) => { const result = await provider.send("debug_getRawTransaction", [txHash]); console.log(result); }; debugGetRawTransaction("0x5765eab677d93b81a1c29de804e115d0e4db8dd40e0deabcf187e4e0d047c758"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) tx_hash = "0x5765eab677d93b81a1c29de804e115d0e4db8dd40e0deabcf187e4e0d047c758" result = web3.provider.make_request('debug_getRawTransaction', [tx_hash]) print(result) ``` ## Use case The `debug_getRawTransaction` method is useful for retrieving transactions in their raw serialized format. This is valuable for transaction replay tools, custom transaction decoders, and systems that need to verify transaction signatures by working directly with the raw transaction bytes rather than the decoded JSON representation. # debug_intermediateRoots | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-intermediateroots openapi/arbitrum_node_api/debug_and_trace/debug_intermediateRoots.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that computes the intermediate state roots for a given block by re-executing all transactions. Arbitrum via Chainstack. Arbitrum API method that computes the intermediate state roots for a given block by re-executing all transactions. For each transaction in the block, it returns the state root after that transaction was applied. This provides a detailed view of how the state trie evolves within a single block. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. > [!WARNING] > > This method re-executes all transactions in the block, which can be resource-intensive for blocks with many transactions. ## Parameters * `hash` — the hash of the block to compute intermediate roots for. * `object` — (optional) tracing configuration: * `timeout` — the timeout for the computation. Defaults to `5s`. See [Go time format](https://pkg.go.dev/time#ParseDuration) for accepted values. * `reexec` — the number of blocks to re-execute to reconstruct historical state. Defaults to 128. ## Response * `result` — an array of state root hashes (as hex strings), one per transaction in the block. Each root represents the state after the corresponding transaction was executed. ## `debug_intermediateRoots` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugIntermediateRoots = async (blockHash) => { const result = await provider.send("debug_intermediateRoots", [blockHash, {}]); console.log(result); }; debugIntermediateRoots("0x5765eab677d93b81a1c29de804e115d0e4db8dd40e0deabcf187e4e0d047c758"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0x5765eab677d93b81a1c29de804e115d0e4db8dd40e0deabcf187e4e0d047c758" result = web3.provider.make_request('debug_intermediateRoots', [block_hash, {}]) print(result) ``` ## Use case The `debug_intermediateRoots` method is useful for verifying state transitions within a block. By computing the intermediate state root after each transaction, developers can identify exactly which transaction caused a state change and verify the correctness of state transitions. This is particularly valuable for building state verification tools and debugging state-related issues in smart contracts. # debug_preimage | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-preimage openapi/arbitrum_node_api/debug_and_trace/debug_preimage.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the preimage for a given Keccak-256 hash. In Ethereum's state trie, account addresses are stored as their Keccak-256 hashes. Arbitrum API method that returns the preimage for a given Keccak-256 hash. In Ethereum's state trie, account addresses are stored as their Keccak-256 hashes. This method allows retrieving the original data (preimage) that was hashed. The node must have preimage recording enabled for this method to return results. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. > [!WARNING] > > Preimage recording must be enabled on the node (`--cache.preimages`). If preimages are not stored, this method returns an error. ## Parameters * `hash` — the Keccak-256 hash to look up the preimage for, encoded as a hex string. ## Response * `data` — the raw preimage bytes as a hex string. Returns an error if the preimage is not found. ## `debug_preimage` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugPreimage = async (hash) => { const result = await provider.send("debug_preimage", [hash]); console.log(result); }; debugPreimage("0x0000000000000000000000000000000000000000000000000000000000000000"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) hash_value = "0x0000000000000000000000000000000000000000000000000000000000000000" result = web3.provider.make_request('debug_preimage', [hash_value]) print(result) ``` ## Use case The `debug_preimage` method is useful for resolving Keccak-256 hashes back to their original values. In the state trie, account addresses and storage keys are stored as hashes. This method allows developers building state analysis tools to reverse-lookup hashes to their original addresses or data, which is essential for tools like state diff visualizers and trie explorers. # debug_printBlock | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-printblock openapi/arbitrum_node_api/debug_and_trace/debug_printBlock.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns a human-readable representation of a block. Unlike eth_getBlockByNumber which returns structured JSON. Arbitrum API method that returns a human-readable representation of a block. Unlike `eth_getBlockByNumber` which returns structured JSON, this method returns a verbose text dump of the entire block structure including all header fields, transactions, and uncles. The output format uses Go's `spew.Sdump` formatting. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `number` — the block number as a decimal integer. ## Response * `result` — a human-readable string representation of the block, including all internal fields and nested structures. ## `debug_printBlock` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugPrintBlock = async (blockNumber) => { const result = await provider.send("debug_printBlock", [blockNumber]); console.log(result); }; debugPrintBlock(30000000); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request('debug_printBlock', [30000000]) print(result) ``` ## Use case The `debug_printBlock` method is useful for quick debugging when you need a comprehensive view of a block's internal structure. The human-readable output format reveals all fields including those not typically exposed by standard RPC methods, making it valuable for diagnosing block-level issues and understanding the complete block composition. # debug_traceBadBlock | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-tracebadblock openapi/arbitrum_node_api/debug_and_trace/debug_traceBadBlock.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that traces all transactions in a block that has been marked as "bad" by the node. Chainstack Arbitrum reference. Arbitrum API method that traces all transactions in a block that has been marked as "bad" by the node. Bad blocks are blocks that failed validation during import. This method works similarly to `debug_traceBlockByHash` but looks up the block from the bad blocks cache instead of the canonical chain. It is primarily used for diagnosing why a block was rejected. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `hash` — the hash of the bad block to trace. * `object` — (optional) an object identifying the type of tracer and its configuration: * `tracer` — the name of the tracer to use (`callTracer`, `prestateTracer`, `4byteTracer`, or a custom JS tracer). * `timeout` — the timeout for the tracing operation. Defaults to `5s`. * `reexec` — the number of blocks to re-execute to reconstruct historical state. Defaults to 128. ## Response * `result` — an array of trace results, one per transaction in the block: * `txHash` — the hash of the transaction. * `result` — the trace output for the transaction (format depends on the tracer used). * `error` — an error message if the transaction trace failed. ## `debug_traceBadBlock` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const debugTraceBadBlock = async (blockHash) => { const result = await provider.send("debug_traceBadBlock", [ blockHash, { tracer: "callTracer" } ]); console.log(result); }; debugTraceBadBlock("0x0000000000000000000000000000000000000000000000000000000000000000"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" result = web3.provider.make_request('debug_traceBadBlock', [block_hash, {"tracer": "callTracer"}]) print(result) ``` ## Use case The `debug_traceBadBlock` method is essential for diagnosing block validation failures. When a node rejects a block, this method allows developers and node operators to re-execute and trace the transactions in the rejected block to identify the specific transaction or state transition that caused the validation failure. This is particularly useful for debugging consensus issues and understanding chain reorganization events. # debug_traceBlockByHash | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-traceblockbyhash openapi/arbitrum_node_api/debug_and_trace/debug_traceBlockByHash.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that traces the execution of all transactions in a block identified by its hash. Chainstack Arbitrum reference. Arbitrum API method that traces the execution of all transactions in a block identified by its hash. This method provides detailed traces for every transaction and call that took place in the block, including gas usage and performance metrics for each operation. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use [`arbtrace_block`](/reference/arbitrum-arbtrace-block) instead. ## Parameters * `hash` — the hash of the block to be traced. * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the EVM works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. ## `debug_traceBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByHash = async (blockHash) => { const tracer = { tracer: "callTracer" }; const traces = await provider.send("debug_traceBlockByHash", [blockHash, tracer]); console.log(traces); }; traceBlockByHash("0xb3a662bef958318d25b80ba2ce6932d7e2f10262086247fa848f2d96de75fa18"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0xb3a662bef958318d25b80ba2ce6932d7e2f10262086247fa848f2d96de75fa18" tracer = {"tracer": "callTracer"} block_traces = web3.provider.make_request('debug_traceBlockByHash', [block_hash, tracer]) print(block_traces) ``` ## Use case The `debug_traceBlockByHash` method is useful for auditing all transactions in a specific block. Developers can use it to analyze gas consumption patterns, identify function call frequencies using the `4byteTracer`, or inspect the full call tree of every transaction in a block using the `callTracer`. # debug_traceBlockByNumber | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-traceblockbynumber openapi/arbitrum_node_api/debug_and_trace/debug_traceBlockByNumber.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that traces the execution of all transactions in a block identified by its number. Chainstack Arbitrum reference. Arbitrum API method that traces the execution of all transactions in a block identified by its number. This method provides detailed traces for every transaction and call in the block, including gas usage and internal call information. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use [`arbtrace_block`](/reference/arbitrum-arbtrace-block) instead. ## Parameters * `quantity or tag` — the block number in hex format or block tag: * `latest` — the most recent block. * `safe` — the block that received justification from the beacon chain. * `finalized` — the block accepted as canonical by more than 2/3 of validators. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, returning account states needed to execute transactions. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. ## `debug_traceBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByNumber = async (block) => { const tracer = { tracer: "callTracer" }; const traces = await provider.send("debug_traceBlockByNumber", [block, tracer]); console.log(traces); }; traceBlockByNumber("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) block = "latest" tracer = {"tracer": "callTracer"} block_traces = web3.provider.make_request('debug_traceBlockByNumber', [block, tracer]) print(block_traces) ``` ## Use case The `debug_traceBlockByNumber` method is useful for analyzing all transactions in a block by number. Developers can use it to calculate average gas usage across transactions, monitor smart contract activity patterns, or build block-level analytics dashboards. # debug_traceCall | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-tracecall openapi/arbitrum_node_api/debug_and_trace/debug_traceCall.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that traces the execution of eth_call within the context of a specific block's execution. debug_traceCall on Arbitrum via Chainstack. Arbitrum API method that traces the execution of [eth\_call](/reference/arbitrum-ethcall) within the context of a specific block's execution. This method uses the final state of the parent block as its base and allows developers to trace the execution of a particular call without submitting an on-chain transaction. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use [`arbtrace_call`](/reference/arbitrum-arbtrace-call) instead. ## Parameters * `object` — the transaction call object: * `from` — (optional) the address used to send the transaction. * `to` — the address to which the transaction is directed, a wallet or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasPrice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in Wei. * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts. * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`). * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes. * `callTracer` — tracer that captures information on all call frames executed during a transaction. * `prestateTracer` — tracer with two modes: `prestate` and `diff`. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/arbitrum-debug-and-trace-rpc-methods#pre-built-tracers) in the debug and trace overview. You can also use additional configuration parameters: * `disableStorage` — when enabled, prevents tracing of storage changes. * `disableStack` — when enabled, skips tracing of stack changes. * `enableMemory` — when `false`, prevents tracing of memory changes. * `enableReturnData` — when `false`, prevents tracing of return data. * `timeout` — timeout period for JavaScript-based tracing calls. Default is `5s`. > [!NOTE] > > When using one of the built-in tracers, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData` settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the Struct/opcode logger. ## Response types ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. ## `debug_traceCall` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceCall = async () => { const call = { to: "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", data: "0x70a08231000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045" }; const block = "latest"; const tracer = { tracer: "callTracer" }; const traces = await provider.send("debug_traceCall", [call, block, tracer]); console.log(traces); }; traceCall(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) call = { 'to': '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', 'data': '0x70a08231000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045' } block = 'latest' tracer = {"tracer": "callTracer"} tx_traces = web3.provider.make_request('debug_traceCall', [call, block, tracer]) print(tx_traces) ``` ## Use case A practical use case for the `debug_traceCall` method is to simulate and trace a smart contract function call without submitting an actual transaction. Developers can use this to inspect internal calls, verify expected behavior, and debug contract interactions before committing transactions on-chain. This is particularly useful for testing complex DeFi interactions on Arbitrum. # debug_traceTransaction | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-debug-tracetransaction openapi/arbitrum_node_api/debug_and_trace/debug_traceTransaction.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns a transaction's traces by replaying it. Reference for debug_traceTransaction on Arbitrum via Chainstack. Arbitrum API method that returns a transaction's traces by replaying it. This method provides a detailed breakdown of every step in the execution of a transaction, including gas usage and opcode output. It accurately simulates the transaction's execution path by replaying any prior transactions, making it a powerful tool for developers to identify and diagnose issues. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#arbitrum) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro transactions, use [`arbtrace_transaction`](/reference/arbitrum-arbtrace-transaction) instead. > > Arbitrum does not support the `trace_*` namespace (e.g., `trace_transaction`). Use `debug_traceTransaction` for post-Nitro blocks or `arbtrace_transaction` for pre-Nitro blocks. ## Parameters * `hash` — the hash identifying a transaction. * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the EVM works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/arbitrum-debug-and-trace-rpc-methods#pre-built-tracers) in the debug and trace overview. You can also use additional configuration parameters: * `disableStorage` — when enabled, prevents tracing of storage changes made by the transaction. * `disableStack` — when enabled, skips tracing of stack changes. * `enableMemory` — when `false`, prevents tracing of memory changes. * `enableReturnData` — when `false`, prevents tracing of return data. * `timeout` — timeout period for JavaScript-based tracing calls. Default is `5s`. See [Go time format](https://pkg.go.dev/time#ParseDuration) for accepted values. > [!NOTE] > > When using one of the built-in tracers, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData` settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the Struct/opcode logger. ## Response types ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. ## `debug_traceTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txHash) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer const tracer = { tracer: "callTracer" }; const traces = await provider.send("debug_traceTransaction", [txHash, tracer]); console.log(traces); }; traceTransaction("0x2cb57e963111cf4d231ec8f66f0eb7a964f10625c2749bf8571d35332fd326c0"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) tx_hash = "0x2cb57e963111cf4d231ec8f66f0eb7a964f10625c2749bf8571d35332fd326c0" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer tracer = {"tracer": "callTracer"} tx_traces = web3.provider.make_request('debug_traceTransaction', [tx_hash, tracer]) print(tx_traces) ``` ## Use case A practical use case for the `debug_traceTransaction` method is to inspect failed transactions on the Arbitrum blockchain by using the built-in `callTracer` to extract the error and revert reason. By tracing a failed transaction, developers can identify exactly where and why the execution reverted, which is essential for debugging smart contract interactions. # eth_estimateGas | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-estimategas openapi/arbitrum_node_api/gas_data/eth_estimateGas.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns an estimate of gas units needed for a given transaction. eth_estimateGas on Arbitrum via Chainstack. Arbitrum API method that returns an estimate of gas units needed for a given transaction. It provides an estimate of the maximum amount of gas units required to complete the transaction based on the network's current state and the transaction's parameters. A client or wallet can use this estimate to determine the appropriate amount of gas to include in a transaction to ensure that the network processes it. ## Parameters * `object` — the transaction call object, same as in `eth_call`; the `from` field is optional, and the `nonce` field is omitted: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — the estimated amount of gas units needed, represented as a hexadecimal string. ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimateGas = async () => { // The transaction to estimate is a simple transfer in this example. const transactionObj = { from: "0x62383739D68Dd0F844103Db8dFb05a7EdED5BBE6", to: "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8", } const gasUnits = await provider.estimateGas(transactionObj); console.log(`Estimated gas units required: ${gasUnits}`); }; estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.estimate_gas({"from":"0x62383739D68Dd0F844103Db8dFb05a7EdED5BBE6","to":"0xbe0eb53f46cd790cd13851d5eff43d12404d33e8"}, "latest" )) ``` ## Use case A practical use case for `eth_estimateGas` is to calculate the gas cost required for a transaction that transfers a value from one Arbitrum address (`fromAddress`) to another (`toAddress`). The gas cost is an important factor in transactions, as it determines the amount of fees that need to be paid by the sender to the network for processing the transaction. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function estimateGasCost(from, to, value) { try { const gasEstimate = await provider.estimateGas({ from: from, to: to, value: value }); return gasEstimate } catch (error) { console.error(`Error estimating gas cost: ${error}`); } } async function main() { const fromAddress = '0x62383739D68Dd0F844103Db8dFb05a7EdED5BBE6'; const toAddress = '0x81ab6Fd4A68d0aDDFAD65F9F97Bc1427dCd40C8E'; const value = ethers.parseEther('25'); // 25 ETH in Wei const estimatedGas= await estimateGasCost(fromAddress, toAddress, value) if (estimatedGas) { console.log(`Estimated gas cost: ${estimatedGas}`); } } main() ``` The `estimateGasCost` function wraps the call to `provider.estimateGas` in a `try-catch` block so that any errors that occur during the estimation process can be handled gracefully. If an error occurs, the error message is logged to the console. If the estimate is successful, the estimated gas cost is returned. The main function is the entry point of the code. It sets the `fromAddress`, `toAddress`, and value for the transaction, and then calls the `estimateGasCost` function to get the estimated gas cost. The estimated gas cost is logged to the console if the estimate is successful, but it is available to be used for further processing in the `estimatedGas` constant. # eth_blockNumber | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-eth_blocknumber openapi/arbitrum_node_api/blocks_info/eth_blockNumber.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the latest block number of the blockchain. Reference for eth_blockNumber on Arbitrum via Chainstack. Arbitrum API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0x69B5B` means that the block is the 432,987th block in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockNumber = async () => { const block_Number = await provider.getBlockNumber(); console.log(block_Number); }; eth_getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.block_number) ``` ## Use case A possible use case for the `eth_blockNumber` method in Arbitrum is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. The following code uses the web3.js library and defines a function called `getCurrentBlockNumber`. When called, this function returns the latest block number from the network, and you can use it in your DApp. You can then use your application's `getCurrentBlockNumber` function to get the current block number whenever you need it. In this example, we use it to calculate on which block a special offer to a specific user will expire. We retrieve the block when we issue the offer to the user and add `288000`, which is approximately how many Arbitrum blocks are produced daily. Now, in the `offerExpires` constant, we have the last block when the user can exercise the special price. Use the `getCurrentBlockNumber` function to compare it to `offerExpires` to know if the offer is still valid. # eth_call | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-ethcall openapi/arbitrum_node_api/execute_transactions/eth_call.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. Arbitrum API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. This can be useful for testing and debugging by simulating transfers or smart contract transactions and retrieving data from the blockchain. > [!NOTE] > > **Disclaimer** > > The interactive example in this page uses `eth_call` to call the `owner` method from the [Uniswap V3: Factory smart contract on Arbitrum](https://arbiscan.io/address/0x1F98431c8aD98523631AE4a59f267346ea31F984#code). It returns the address owning the smart contract. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `object` — the transaction call object: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters. See the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `data` — the result of executing the specified call on the Ethereum Virtual Machine (EVM), encoded as a hexadecimal string. ## `eth_call` code examples The following code snippets provide a clear demonstration of how to simulate a standard ETH transfer between two accounts. Note that when using `eth_call` to simulate a transfer or a smart contract transaction, the sender account must have a sufficient balance to cover the associated gas fee. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const simulateTransfer = async () => { try { // Define the accounts to transfer between const fromAddress = "0x28165971E4d592CA6C45A3786786Ec2f6b4b8D23"; const toAddress = "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"; // Define the value to transfer const value = ethers.utils.parseEther("100"); // Build transaction object const transactionObject = { from: fromAddress, to: toAddress, gasLimit: ethers.utils.hexlify(21000), value: value } // Use eth_call to simulate the transfer on the latest block const result = await provider.call(transactionObject); console.log(`Result: ${result}`); } catch (error) { console.error(`Error: ${error.message}`); } } simulateTransfer(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the accounts to transfer between from_address = web3.to_checksum_address('0x28165971E4d592CA6C45A3786786Ec2f6b4b8D23') to_address = web3.to_checksum_address('0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a') # Define the value to transfer (in Ether) value = web3.to_wei(1, 'ether') # Build the transaction object transaction = { 'from': from_address, 'to': to_address, 'gas': 21000, # 21000 is the standard for a regular transfer 'value': value, 'data': '', } # Use eth_call to simulate the transfer result = web3.eth.call(transaction, 'latest') print(result) ``` Executing this code will produce a return value of `0x`, indicating a successful transaction. This implies that the transaction will be executed without any errors when the code is used to send the transaction. ## Use case A common use case for `eth_call` is calling smart contract functions. For example, interact with an ERC-20 token smart contract deployed on the Arbitrum network and retrieve the balance of a specific account. The following code uses the ethers.js library to call the `balanceOf` function of the [UNI token smart contract](https://arbiscan.io/token/0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0#readProxyContract) on the Arbitrum network. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); // Contract address and ABI for the token const CONTRACT_ADDRESS = "0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0"; const ABI = [ { "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "balance", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" } ]; // Asynchronously fetches the balance of an account for the given address async function getAccountBalance(address) { try { // Create a new contract instance const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider); // Call the balanceOf function to retrieve the account balance in wei using eth_call const balanceInWei = await contract.balanceOf(address); // Convert the balance from wei to ether const balanceInEth = ethers.formatEther(balanceInWei); // Log the account balance in ether to the console console.log(`The account balance for address ${address} is: ${balanceInEth} UNI`); } catch (error) { console.error("Error occurred while fetching account balance:", error); } } // Call the getAccountBalance function with a sample account address getAccountBalance("0x1b72bac3772050fdcaf468cce7e20deb3cb02d89"); ``` The `getAccountBalance` function is called with the address of the account whose balance we want to retrieve. This function creates a new contract instance, calls the `balanceOf` function of the smart contract with the provided address, and retrieves the account balance in wei. The balance is then converted to ether using the `formatEther` function of the `ethers` object. Finally, the account balance is printed on the console for easy viewing. # Executing transactions | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-evm-excecution-rpc-methods Arbitrum transaction execution JSON-RPC methods — call smart contracts, sign transactions, send raw transactions, and estimate gas on the network. Executing and simulating transactions on the Arbitrum blockchain can be done by using the following methods: > [card] [eth\_call](/reference/arbitrum-ethcall) > [card] [eth\_sendRawTransaction](/reference/arbitrum-sendrawtransaction) # Filter handling | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-filters-rpc-methods Arbitrum filter JSON-RPC methods — create event and block filters, poll for changes, retrieve filter logs, and manage subscriptions on-chain. Handling filters can be done by using the following methods: > [card] [eth\_getFilterChanges](/reference/arbitrum-getfilterchanges) > [card] [eth\_uninstallFilter](/reference/arbitrum-uninstallfilter) # Gas data | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-gas-data-rpc-methods Arbitrum gas data JSON-RPC methods — query current gas prices and estimate gas costs for transactions to build cost-effective dapps on Arbitrum. Retrieving gas information from the Arbitrum blockchain can be done by using the following methods: > [card] [eth\_estimateGas](/reference/arbitrum-estimategas) > [card] [eth\_gasPrice](/reference/gasprice) # eth_getBalance | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getbalance openapi/arbitrum_node_api/accounts_info/eth_getBalance.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the balance of a specific Arbitrum account in Wei, the smallest unit of ether. Arbitrum via Chainstack. Arbitrum API method that returns the balance of a specific Arbitrum account in [Wei](https://www.investopedia.com/terms/w/wei.asp), the smallest unit of ether. This method allows developers to retrieve the current balance of an Arbitrum account for various purposes, such as checking available funds or displaying balance information. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to check the balance of * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — the integer value of the current balance in Wei ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getBalance = async (address, block) => { const balance = await provider.send("eth_getBalance", [address, block]); console.log(balance); }; getBalance("0x0938C63109801Ee4243a487aB84DFfA2Bba4589e", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" address = web3.to_checksum_address("0x0938C63109801Ee4243a487aB84DFfA2Bba4589e") balance = web3.eth.get_balance(address, "latest") print(balance) ``` ## Use case One practical use case for `eth_getBalance` is to check the balance of an account for a program that scans the balance periodically and fills up the account when the balance drops below a certain value. Here's an example of how you can achieve this in ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const accountAddress = '0x0938C63109801Ee4243a487aB84DFfA2Bba4589e'; const minimumBalance = ethers.parseEther("10"); // 10 ETH in wei async function checkBalance(address) { const balance = await provider.getBalance(address, 'latest'); return balance; } async function fillUp(balance) { const minimumConverted = ethers.formatEther(minimumBalance); if (balance < minimumBalance) { console.log(`The balance of the account ${accountAddress} is below ${minimumConverted} ETH.`); console.log(`Sending more funds...`) // Call another function to fill up your account } else { console.log(`The balance of the account ${accountAddress} is above ${minimumConverted} ETH.`); console.log(`No need to send more funds.`) } } async function main() { const balance = await checkBalance(accountAddress); const balanceInEth = ethers.formatEther(balance); console.log(`The balance of the account ${accountAddress} is ${balanceInEth} ETH. \n`); await fillUp(balance); } main(); ``` The code is a simple script that checks the balance of an account on the Arbitrum network and takes action if the balance falls below a specified minimum. The `accountAddress` variable holds the address for which you want to check the balance. The `minimumBalance` variable holds the minimum balance in Wei you want to check against. The `checkBalance` function takes an address as an argument and returns the balance of the account in Wei. The `fillUp` function takes the balance as an argument and converts the minimum balance from Wei to ETH using the `formatEther` method from ethers.js, and checks if the balance is below the minimum balance. > [!NOTE] > > Note that the Wei value is converted only to display it, as it is more accurate to work with the Wei value. If the balance is below the minimum, it logs a message to the console indicating that more funds need to be sent and calls another function to fill up the account. If the balance is above the minimum, it logs a message indicating that no additional funds need to be sent. The main function calls the `checkBalance` and `fillUp` functions to convert the balance from Wei to ETH using the `formatEther` method, and log the balance and the result of the check to the console. In summary, this script allows you to easily check the balance of an account on the Arbitrum network and take action if the balance falls below a specified minimum. # eth_getBlockByHash | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getblockbyhash openapi/arbitrum_node_api/blocks_info/eth_getBlockByHash.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns information about the block matching the given block hash. eth_getBlockByHash on Arbitrum via Chainstack. Arbitrum API method that returns information about the block matching the given block hash. This method can be useful for analyzing a block and its transactions. ## Parameters * `hash` — the hash of the block * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — the hash of the previous block that was used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ethereum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ethereum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Ethereum network since the inception of the network. It measures the overall security and integrity of the Ethereum network. * `extradata` — the extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — the array of uncle hashes. ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByHash = async () => { const blockByNumber = await provider.send("eth_getBlockByHash", ["0x1a13c64b964eee3e95db96b5042d910e2d93097d8f35490aba114b0bc3a1ed34",false]); console.log(blockByNumber); }; eth_getBlockByHash(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block("0x1a13c64b964eee3e95db96b5042d910e2d93097d8f35490aba114b0bc3a1ed34", False)) ``` ## Use case The `eth_getBlockByHash` is excellent for analytics purposes. For example, it can be used to quickly find out the hashes of transactions that create new smart contracts. The `transactions` field, an array of all the transactions within the block, is returned in the response. By iterating through this array and examining each transaction's `to` field, a user can determine which transactions in the block are creating new smart contracts. The following example uses the web3.js library to create a function to extract this data: The previous example shows how the `eth_getBlockByHash` method might be used to know which transactions create a smart contract. When a new smart contract is created, the `to` field will be `null`. Running this function on a block hash with the transactions detail field set to `true` will inspect each transaction's `to` field. If the field is `null`, the transaction creates a smart contract, printing the transaction hash, which can then be used for further analysis. # eth_getBlockByNumber | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getblockbynumber openapi/arbitrum_node_api/blocks_info/eth_getBlockByNumber.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns information about the block matching the given block number. Available on Arbitrum via Chainstack. Arbitrum API method that returns information about the block matching the given block number. This method can retrieve a wide range of information about the block, such as its hash, timestamp, difficulty, and gas limit. You can also retrieve transaction objects included in the block if needed. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — a block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ethereum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ethereum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. As an Ethereum L2, Arbitrum inherits Ethereum's consensus mechanism and this value is no longer relevant. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — array of transaction objects. See [eth\_getTransactionByHash](/reference/arbitrum-gettransactionbyhash) for the exact shape. * `uncles` — array of uncle hashes. ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByNumber = async () => { const blockByNumber = await provider.send("eth_getBlockByNumber", ["latest",false]); console.log(blockByNumber); }; eth_getBlockByNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block("latest", False)) ``` ## Use case The `eth_getBlockByNumber` method can determine whether a block you are analyzing has been included in the main chain or was forked due to a 'reorg'. A block hash is generated through a cryptographic hash function, which takes the block's data, including transactions and other information, and the hash of the parent block (the previous block) and outputs a unique, fixed-length digest. This hash represents the current block within the blockchain. The script compares the parent hash of the following block with the hash under inspection. If the two values match, it can be concluded that the block was included in the main chain. Conversely, if the values do not match, it indicates that the block was not used to generate the next block's hash and has therefore been excluded from the main chain, resulting in a forked block. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // If the hash of the block to inspect is === to the parent hash of the next block, then this is not a forked block. async function isForked(block) { const nextBlock = blockToInspect + 1 console.log(`Block to inspect: ${block}`) console.log(`Next block: ${nextBlock} \n`) const blockToInspectHash = (await provider.getBlock(block, false)).hash console.log(`Block hash of the block you are inspecting: ${blockToInspectHash} \n`) const nextBlockParentHash = (await provider.getBlock(nextBlock, false)).parentHash console.log(`Parent hash of the next block: ${nextBlockParentHash} \n`) if (String(nextBlockParentHash) == String(nextBlockParentHash)) { console.log(`You are inspecting the correct block`) return false } else { console.log(`You are inspecting a forked block!`) return true } } const blockToInspect = 69800577 isForked(blockToInspect) ``` In this case, the `eth_getBlockByNumber` is used to retrieve the hash of a block you want to analyze and the parent hash of the next block. ## Try the `eth_getBlockByNumber` RPC method yourself # eth_getBlockTransactionCountByHash | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getblocktransactioncountbyhash openapi/arbitrum_node_api/blocks_info/eth_getBlockTransactionCountByHash.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. Arbitrum API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. ## Parameters * `hash` — the block hash of the requested block ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockHash) => { const count = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); console.log(count); }; getTransactionsCount('0x7709b7cacb9a3d647afe557d51891c00ad0c00e01d5bffdbcfe6c1730d36290c'); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block_transaction_count("0x7709b7cacb9a3d647afe557d51891c00ad0c00e01d5bffdbcfe6c1730d36290c")) ``` ## Use case `eth_getBlockTransactionCountByHash` is a useful tool for analyzing transaction volume on the Arbitrum blockchain. On average, a [new block is generated on the Arbitrum mainnet every 0.3 seconds](https://arbiscan.io/chart/blocktime), resulting in approximately 12,000 blocks per hour. Using the ethers.js library, one can inspect the past 12,000 blocks starting from the latest block, retrieve the hash of each block, and use `eth_getBlockTransactionCountByHash` to find the number of transactions in each block. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function retrieveTransactionsCount() { // Get the current block number const currentBlockNumber = await provider.getBlockNumber(); // Calculate the block number from one hour ago const oneHourAgoBlockNumber = currentBlockNumber - 12000; // 12000 blocks/hr on average on the Arbitrum mainnet // Initialize a variable to store the total number of transactions let totalTransactionCount = 0; // Loop through all blocks from the current block number to the block number from one hour ago for (let block = currentBlockNumber; block > oneHourAgoBlockNumber; block--) { // Get the hash of each block const blockData = await provider.getBlock(block); const blockHash = blockData.hash // Get the number of transactions in this block using each hash const transactionCount = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); // Add the number of transactions in this block to the total totalTransactionCount += Number(transactionCount); console.log(`Block #${block} has ${Number(transactionCount)} transactions.`); } console.log(`The total number of transactions in the previous hour is: ${totalTransactionCount}`); } retrieveTransactionsCount(); ``` This example connects to an Arbitrum node using the ethers.js library. The `retrieveTransactionsCount` function gets the current block number, calculates the block number from one hour ago, and uses a for loop to iterate through all blocks in that range. The function then retrieves the hash of each block and the number of transactions in each block and adds it to a total transaction count. The final result, the total number of transactions in the previous hour, is logged to the console. This is a good example of how you can combine different methods. # eth_getBlockTransactionCountByNumber | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getblocktransactioncountbynumber openapi/arbitrum_node_api/blocks_info/eth_getBlockTransactionCountByNumber.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the number of transactions in a block specified by block number or tag. Chainstack Arbitrum reference. Arbitrum API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — the integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockId) => { const count = await provider.send("eth_getBlockTransactionCountByNumber", [blockId]); console.log(count); }; getTransactionsCount('latest') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.get_block_transaction_count(69800577)) # A hex value starting with "0x" is accepted as well. ``` ## Use case `eth_getBlockTransactionCountByNumber` can be used to analyze how many transactions are included on the Arbitrum blockchain in a certain period. On average, a [new block is generated on the Arbitrum mainnet every 0.3 seconds](https://arbiscan.io/chart/blocktime), resulting in approximately 12,000 blocks per hour. Using the ethers.js library, one can inspect the past 12,000 blocks starting from the latest block, retrieve the hash of each block, and use `eth_getBlockTransactionCountByNumber` to find the number of transactions in each block to sum them. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function retrieveTransactionCounts() { // Get the current block number const currentBlockNumber = await provider.getBlockNumber(); // Calculate the block number from one hour ago const oneHourAgoBlockNumber = currentBlockNumber - 12000; // 1200/blocks/hr on average on the Arbitrum mainnet // Initialize a variable to store the total number of transactions let totalTransactionCount = 0; // Loop through all blocks from the current block number to the block number from one hour ago for (let block = currentBlockNumber; block > oneHourAgoBlockNumber; block--) { // Get the number of transactions in this block const transactionCount = await provider.send("eth_getBlockTransactionCountByNumber", [block]); // Make the necessery conversions from hex to decimal const stringCount = BigInt(transactionCount).toString(); const decimalCount = Number(stringCount) // Add the number of transactions in this block to the total totalTransactionCount += decimalCount; const stringBlock = BigInt(block).toString(); const decimalBlock = Number(stringBlock) console.log(`Block ${decimalBlock} has ${decimalCount} transactions.`); } console.log(`The total number of transactions in the previous hour is: ${totalTransactionCount}`); } retrieveTransactionCounts(); ``` Using `eth_getBlockTransactionCountByNumber` is a bit more straightforward compared to the [example shown using `eth_getBlockTransactionCountByHash`](/reference/arbitrum-getblocktransactioncountbyhash) for this same use case, you don't need to extract the block hash; instead, you can query the transaction count directly. This example is made using the ethers.js library, and you can notice that it requires more parsing compared to web3.js, and the results are given as hexadecimal. # eth_chainId | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getchainid openapi/arbitrum_node_api/chain_info/eth_chainId.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the current chain ID. eth_chainId JSON-RPC method available on the Arbitrum blockchain via Chainstack. Arbitrum API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and generally verify if a network is the desired one. It was introduced in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). ## Parameters * `none` ## Response * `quantity` — EIP-155 Chain ID ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const chainId = async () => { // This will return the value in Hex const chainId = await provider.send("eth_chainId"); console.log(`Hex Chain ID: ${chainId}`); }; chainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.chain_id) ``` ## Use case One possible use case for the `chain_id` method in Arbitrum is to determine which chain a user is connected to when using a DApp with the MetaMask browser extension. This can be useful to provide a customized user experience based on the specific chain that the user is interacting with. Here is an example of how the `chain_id` method might be used in this context with MetaMask. Keep in mind that the chain ID is returned as a hexadecimal value: ```js JavaScript theme={"system"} // Check which network is selected on MetaMask async function checkChain() { // Declare the desired chain ID to match const desiredChainId = '0xA4B1'; // Arbitrum mainnet chain ID // Retrieve the current Chain ID selected by the user ethereum.request({ method: 'eth_chainId' }).then(chainId => { // compare the actual chain ID it to the declared chain ID if (chainId !== desiredChainId) { console.log(`You are currently on the wrong network. Please switch to the mainnet.`); // Call the promtSwitch function promtSwitch() } else { console.log("This is the correct network") } }) } // Prompt user to switch to a determined network based on the chain ID async function promtSwitch() { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0xA4B1' // chainId must be in HEX with 0x in front }], }); } ``` In this case, the `checkChain` function is called to get the current chain ID using the Ethereum object provided by MetaMask. The chain ID is then checked to determine if the user is connected to the Arbitrum mainnet and calls the `promtSwitch` function to prompt the user to switch chains in case the chain ID returned does not match the `desiredChainId` constant. # eth_getCode | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getcode openapi/arbitrum_node_api/accounts_info/eth_getCode.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that retrieves the compiled bytecode of a smart contract providing its address as a parameter. Arbitrum via Chainstack. Arbitrum API method that retrieves the compiled bytecode of a smart contract providing its address as a parameter. This method returns a hexadecimal string representing the smart contract's bytecode. Developers can use this bytecode to verify if a smart contract is legitimate and to ensure that it performs the intended functions. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response `data` — the compiled bytecode of a smart contract. Returns `0x` if the address is not associated with a smart contract. ## `eth_getCode` code examples The following example retrieves the compiled bytecode from the [UNI token smart contract](https://arbiscan.io/token/0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0#code). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getCode = async (address, block) => { const code = await provider.send("eth_getCode", [address, block]); console.log(code); }; getCode("0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" code = web3.eth.get_code("0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0", "latest") print(web3.to_hex(code)) # Convert the Bytes result into HEX. ``` ## Use case A practical use case for the `eth_getcode` method is verifying whether an address is associated with a smart contract. This can be particularly useful for blockchain explorers, auditors, and DApp developers, who must ensure that the code they interact with is legitimate. Here is an example using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function verifyContractAddress(address) { try { const bytecode = await provider.getCode(address); return bytecode !== '0x'; } catch (error) { console.error(`Error: ${error.message}`); return false; } } async function main() { const addressToVerify = "0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0" const isContract = await verifyContractAddress(addressToVerify); if (isContract) { console.log(`📑The address ${addressToVerify} is a smart contract.`); } else { console.log(`🚨The address ${addressToVerify} is not a smart contract.`); } } main(); ``` The `verifyContractAddress` function takes an `address` as a parameter and returns a `boolean` value indicating whether the address is associated with a smart contract. The function works by using the `provider.getCode` method to retrieve the bytecode of the contract associated with the given address. If the returned bytecode is not equal to `0x`, then the function assumes that the address is associated with a smart contract and returns `true`. If an error occurs while calling `provider.getCode`, the function returns `false` and logs the error message to the console. The `main` function is responsible for calling `verifyContractAddress` with a specific address to verify and log the results to the console. In this example, `main` is set up to verify the address `0xc094fd7ef80be094f3d98e227a487490ab1f9b9c`. After calling `verifyContractAddress`, `main` logs either a success message indicating that the address is associated with a smart contract or a warning message indicating that it is not. Overall, this code provides a useful utility for verifying the legitimacy of smart contract addresses on the Arbitrum blockchain. It can be useful for auditors, blockchain explorers, and DApp developers who want to ensure that they are interacting with trustworthy code on the blockchain. # eth_getFilterChanges | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getfilterchanges openapi/arbitrum_node_api/filter_handling/eth_getFilterChanges.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Arbitrum API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Filters can be created using one of the following methods: * [eth\_newFilter](/reference/arbitrum-newfilter) * [eth\_newBlockFilter](/reference/arbitrum-newblockfilter) After creating a filter using one of the available methods, the resulting filter ID can be used to fetch changes by calling the `eth_getFilterChanges` method. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/arbitrum-newfilter) > * [eth\_newBlockFilter](/reference/arbitrum-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `string` — the filter ID of the filter for which you want to retrieve the changes ## Response * `array` — an array that represents the changes that have occurred on the blockchain since the last time the filter was polled: * For filters created with `eth_newBlockFilter`: * `blockHash` — the hashes of the new blocks since the last time the filter was polled. * For filters created with `eth_newFilter`, the following event logs: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getFilterChanges` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/arbitrum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getFilterChanges(filter) { try { const changes = await provider.send('eth_getFilterChanges', [filter]); console.log(changes); // Log the with the new data } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' getFilterChanges(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_filtered_events(filter_id): try: filter_changes = web3.eth.get_filter_changes(filter_id) print(filter_changes) # the array of events that match the filter except Exception as e: print(e) filter_id = '0x0' get_filtered_events(filter_id) ``` ## Use case The `eth_getFilterChanges` method can be used to periodically poll a filter to retrieve new data. In the following Python program, using the web3.py library creates a filter using [eth\_newBlockFilter](/reference/arbitrum-newblockfilter) to poll for new blocks, and uses `eth_getFilterChanges` to poll the filter and retrieve new blocks. Here is the implementation in Python: ```python main.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def create_block_filter(): try: # Create a new block filter block_filter = web3.eth.filter('latest') # Split the string at the space character parts = str(block_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(f'Error creating block filter: {e}') def get_filtered_events(filter_id): try: filter_changes = web3.eth.getFilterChanges(filter_id) return filter_changes # the array of events that match the filter except Exception as e: print(f'Error getting filter changes: {e}') def main(): # Create a block filter block_filter_id = create_block_filter() if block_filter_id is not None: print(f'New block filter ID: {block_filter_id}') else: print('Error creating block filter') return # Loop forever and poll the filter every 10 seconds while True: print('Polling for new blocks...') new_blocks = get_filtered_events(block_filter_id) if new_blocks is not None: print(new_blocks) else: print('Error getting filter changes') time.sleep(10) if __name__ == '__main__': main() ``` Here is the explanation of the code: The `create_block_filter` function creates a new block filter with the `web3.eth.filter('latest')` method, which matches against the latest block. The resulting filter object is converted to a string, split into parts using the `split` method and the filter ID is extracted from the second part using array indexing. The filter ID is then returned by the function. The `get_filtered_events` uses the `eth_getFilterChanges` method to retrieve the new blocks. The function takes a single parameter, `filter_id`, which is a string that represents the ID of the filter to be polled, the filter created by the `create_block_filter` function in this case. If the filter ID is valid, `get_filtered_events` calls `web3.eth.getFilterChanges(filter_id)` to retrieve the array of events that match the filter criteria. This array is then returned by the function, which is then printed to the console in the `main` function. The `main` function encapsulates the program logic, calling `create_block_filter` to create a new block filter and printing the resulting filter ID to the console. The function then enters an infinite loop, calling `get_filtered_events` every 10 seconds to retrieve the changes to the blockchain that match the filter criteria. If the changes are not `None`, they are printed to the console. An error message is printed to the console if an error occurs while creating the block filter or retrieving the filter changes. # eth_gasPrice | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getgasprice openapi/arbitrum_node_api/gas_data/eth_gasPrice.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the current gas base fee of the network. Arbitrum developers can call eth_gasPrice via Chainstack. Arbitrum API method that returns the current gas base fee of the network. The gas price is the quantity of the native token the transaction's sender must pay per unit of gas consumed. The value returned is in Wei. ## Parameters * `none` ## Response * `quantity` — the integer value of the current gas base fee, returned in Wei ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getGasPrice() { const baseFee = await provider.send("eth_gasPrice"); console.log(`The base gas fee is: ${baseFee} Wei`); } getGasPrice() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.gas_price) ``` ## Use case You can use `eth_gasPrice` to calculate the total gas value to send with a transaction based on the base and priority fee system. This concept was implemented with [EIP-1559](https://metamask.io/1559/) in Ethereum's London hard fork. EIP-1559 aimed to solve the problem of network congestion by implementing a dynamic fee market mechanism, which adjusts the fee required to process a transaction based on network demand. with the current system, the total gas price is composed of a **base fee** determined by the network's load and a **priority fee** added by the user. > [!NOTE] > > **Note** > > The `eth_gasPrice` method returns the base fee. The following script calculates the total fee to send with a transaction based on the base fee and an arbitrary priority fee that the user can choose. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getGas(priorityFee) { const priorityFeeWei = ethers.parseUnits(String(priorityFee), 'gwei'); const baseFee = (await provider.getFeeData()).gasPrice; const totalFee = priorityFeeWei + baseFee return [totalFee, baseFee] } async function main() { // The priority fee is constant at 0.1 Gwei, the user can change it. const PRIORITY_FEE = 0.1; const [totalFee, baseFee] = await getGas(PRIORITY_FEE) const baseFeeGwei = ethers.formatUnits(baseFee, 'gwei') console.log(`The priority fee: ${PRIORITY_FEE} Gwei.`) console.log(`The Arbitrum base fee at this moment is: ${Number(baseFeeGwei).toFixed(2)} Gwei.`) const gweiFee = ethers.formatUnits(totalFee, 'gwei') console.log(`The total gas fee should be at least: ${Number(gweiFee).toFixed(2)} Gwei.`) } main() ``` The `getGas` function calculates the total fee required to process a transaction. The function takes in a `priorityFee` as input representing the fee a user is willing to pay to prioritize their transaction processing. The function first converts the `priorityFee` from Gwei to Wei (the smallest unit of ether) using `ethers.parseUnits`. Then, it uses the `provider.getFeeData` method to retrieve the current base fee set by the network. The function finally adds the priority fee to the base fee and returns the total fee along with the base fee as an array. The `main` function is the entry point of the code. It first sets a constant `PRIORITY_FEE` to 10 Gwei. Then calls the `getGas` function with `PRIORITY_FEE` as an argument and destructures the returned array into two variables `totalFee` and `baseFee`. The code then converts `baseFee` and `totalFee` from Wei to Gwei using `ethers.formatUnits` for better readability, and logs the results to the console. The main function logs the priority fee, the base fee at that moment, and the minimum recommended total fee that should be paid to process a transaction. # eth_getLogs | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getlogs openapi/arbitrum_node_api/logs_and_events/eth_getLogs.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns an array of all logs that match a given filter object. Available on Arbitrum via Chainstack JSON-RPC nodes. Arbitrum API method that returns an array of all logs that match a given filter object. Logs are records of smart contract-generated events on the network and contain valuable information about transactions and smart contract interactions. Some common use cases for `eth_getLogs` include monitoring smart contract events, tracking token transfers, and analyzing blockchain data. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block > * `earliest` — the earliest available or genesis block > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `array` — an array of log objects that match the specified filter or an empty array if there have been no new events since the last poll: * `address` — the contract address from which the event originated * `topics` — an array of 32-byte data fields containing indexed event parameters * `data` — the non-indexed data that was emitted along with the event * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getLogs` code examples The following examples retrieve the logs of the `Transfer` event from the [WETH](https://arbiscan.io/token/0x82af49447d8a07e3bd95bd0d56f35241523fbab1#code) smart contract on Arbitrum. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getLogs = async () => { const latestBlock = await provider.getBlockNumber(); const filter = { fromBlock: latestBlock - 10, toBlock: 'latest', address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const logs = await provider.getLogs(filter); console.log(logs); }; getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) latest_block = web3.eth.block_number filter = { 'fromBlock': latest_block - 10, 'toBlock': 'latest', 'address': '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } logs = web3.eth.get_logs(filter) print(logs) ``` > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about the `eth_getLogs` method. ## Block range limitations While `eth_getLogs` is a powerful tool, it's crucial to understand its limitations, particularly when working with different EVM-compatible chains, as these networks often have different constraints. The `eth_getLogs` method allows you to select a range of blocks to get events from and is important to exercise proper management. `eth_getLogs` block range is capped by your [subscription plan](/docs/limits#evm-range-limits). On high-activity chains or contracts, query smaller ranges and paginate to avoid oversized responses or timeouts. ## Use case One use case for `eth_getLogs` is to retrieve the transfer events for a specific Token ID from an ERC-721 smart contract. ERC-721 is a token standard for non-fungible tokens (NFTs), and each token transfer is recorded as an event on the blockchain. By retrieving these events, developers can track the movement of NFTs and build applications that interact with them. The following program uses `eth_getLogs` and ethers.js to track the transfers of the token number '437066' from the [Arbitrum Odyssey NFT](https://arbiscan.io/token/0xfAe39eC09730CA0F14262A636D2d7C5539353752#code) collection on Arbitrum. This will retrieve the logs and display the address that initiated the transfer and the block number where it occurred. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTokenTransferLogs(contractAddress, tokenId) { try { const tokenIdTopic = ethers.zeroPadValue(ethers.toBeHex(tokenId), 32); const filter = { fromBlock: 'earliest', toBlock: 'latest', address: contractAddress, topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', null, null, tokenIdTopic] }; const logs = await provider.getLogs(filter); const transferLogs = logs.map((log) => { const topics = log.topics; const fromAddress = topics[1]; const formattedFromAddress = `0x${fromAddress.slice(26)}`; if (!ethers.isAddress(formattedFromAddress)) { throw new Error(`${formattedFromAddress} is an invalid address`); } const blockNumber = log.blockNumber; // Display a summary console.log(`Address ${formattedFromAddress} transferred the token ${tokenId} in block ${blockNumber}.`); return { fromAddress: formattedFromAddress, tokenId: tokenId, blockNumber: blockNumber }; }); return transferLogs; } catch (error) { throw new Error(`Error getting transfer logs: ${error.message}`); } } async function main() { const contractAddress = '0xfAe39eC09730CA0F14262A636D2d7C5539353752'; const tokenId = '437066'; try { const transferLogs = await getTokenTransferLogs(contractAddress, tokenId); console.log(transferLogs); } catch (error) { console.error(error.message); } } main(); ``` This code defines an async function called `getTokenTransferLogs` which takes a `contractAddress` and a `tokenId` as input parameters. Within the function, the `tokenId` is converted to a 32-byte hexadecimal value and then used to create a topic for a filter to get past logs for a specific token transfer. The `provider.getLogs` function is an API that returns logs that match a certain filter. Once the logs have been retrieved, the function loops through each log and extracts the relevant information, such as the sender's address and the block number. It also checks whether the sender's address is a valid address; this is in case the parsing logic fails. For each valid transfer log, the function logs a summary to the console and creates a transfer log object that includes the `fromAddress`, `tokenId`, and `blockNumber` properties. The function returns an array of transfer log objects, and an exception is thrown if there are any errors. The `main` function calls `getTokenTransferLogs` with a specific `contractAddress` and `tokenId`, and then logs the array of transfer log objects to the console. If there are any errors, an error message is logged to the console instead. # eth_getStorageAt | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-getstorageat openapi/arbitrum_node_api/accounts_info/eth_getStorageAt.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the data stored at a specific storage slot within a smart contract. Chainstack Arbitrum reference. Arbitrum API method that returns the data stored at a specific storage slot within a smart contract. It can help developers to read a smart contract's internal state, like user data or balances, to inform decisions and build more advanced applications that interact with smart contracts on the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity` — the index of the storage position to retrieve the data from * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response `data` — the data stored in the storage slot. Returned as a hexadecimal string. ## `eth_getStorageAt` code examples The following example returns the storage value at slot `10` from the [Arbitrum Odyssey NFT collection](https://arbiscan.io/token/0xfAe39eC09730CA0F14262A636D2d7C5539353752#code) on the Arbitrum mainnet. In this case, this is the address owning the smart contract. This method can retrieve the different storage values in time by querying different blocks in the past. Note that to query a state in the past, you need an [archive node](https://chainstack.com/evm-nodes-a-dive-into-the-full-vs-archive-mode). > [!NOTE] > > curl needs a `HEX String` starting with `0x` to identify the block if you want to use a hex integer block number as a parameter.\ > For example, block number `14000000` will be `0xD59F80`. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getstorage = async (address, slot, block) => { const storage = await provider.send("eth_getStorageAt", [address, slot, block]); console.log(storage); }; getstorage("0xfAe39eC09730CA0F14262A636D2d7C5539353752", "0xa", "latest" ) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" storage = web3.eth.get_storage_at("0xfAe39eC09730CA0F14262A636D2d7C5539353752",10, "latest") print(web3.to_hex(storage)) ``` ## Use case One use case for `eth_getStorageAt` is analyzing how the value stored at a specific storage slot of a smart contract changes over time. For example, a developer may want to track the change in a specific variable of a smart contract, such as the total supply of a token, and perform some analysis on it. Here's an example implementation of this use case using ethers.js. The following program analyzes the [MIM](https://arbiscan.io/token/0xfea7a6a0b346362bf88a9e4a88416b77a57d6c2a#code) smart contract on the Arbitrum chain to track how the total supply changes over time. The storage slot matches the position of the variable in the smart contract; the following code snippet shows the top of the MIM smart contract where the variables used in this program are declared. ```sol ERC20.sol theme={"system"} contract AnyswapV5ERC20 is IAnyswapV3ERC20 { using SafeERC20 for IERC20; string public name; // Slot 0 string public symbol; // Slot 1 uint8 public immutable override decimals; // Slot 2 address public immutable underlying; bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant TRANSFER_TYPEHASH = keccak256("Transfer(address owner,address to,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public immutable DOMAIN_SEPARATOR; /// @dev Records amount of AnyswapV3ERC20 token owned by account. mapping (address => uint256) public override balanceOf; uint256 private _totalSupply; // Slot 8 . . . ``` Here's an example implementation of this use case using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // You need an archive node is going more than 127 blocks in the past. async function getStorageValueOverTime() { // The address of the MIM smart contract const mimhAddress = '0xFEa7a6a0B346362BF88A9e4A88416B77a57D6c2A'; // The index of the storage slots where the total supply and name data are stored const totalSupplySlot = 8; const nameSlot = 0; // The range of blocks to analyze const startBlock = 16000000; const endBlock = await provider.getBlockNumber(); for (let blockNumber = startBlock; blockNumber <= endBlock; blockNumber++) { // Retrieve the name and total supply from the smart contract storage at each block const [name, supply] = await Promise.all([ provider.getStorage(mimhAddress, nameSlot, blockNumber), provider.getStorage(mimhAddress, totalSupplySlot, blockNumber) ]); // Decode the name and total supply from hexadecimal format to their original values const decodedName = ethers.toUtf8String("0x" + name.slice(2).replace(/(00)+$/, "")); const decodedSupply = BigInt(supply); // Convert the total supply from wei to ether and round to 4 decimal places const convertedSupply = ethers.formatEther(decodedSupply); const adjustedSupply = Number(convertedSupply).toFixed(4); // Output the block number, name, and total supply for each block console.log(`Block ${blockNumber}: Name = ${decodedName}, Total supply = ${adjustedSupply}`); } } async function main() { try { await getStorageValueOverTime(); } catch (error) { console.error(error); } } main(); ``` The bulk of the program is the `getStorageValueOverTime` function. The `getStorageValueOverTime` function retrieves and outputs the name and total supply of the WETH smart contract at each block height between a specified start block and the current block height. The function begins by declaring constants for the MIM contract address, the storage slot indices for the name and total supply data, the starting block height, and the current block height. Next, the function enters a `for` loop that iterates over every block between the start block and the current block height. Within the loop, the function retrieves the name and total supply from the smart contract storage at the current block height using the `provider.getStorage` method. The `Promise.all` method is used to retrieve the name and total supply in parallel for improved efficiency. The `ethers.toUtf8String` method is then used to convert the name from hexadecimal format to its original string value. The `BigInt` constructor is used to convert the total supply from hexadecimal format to a native `bigint` value, which can be used for mathematical operations. The `ethers.formatEther` method is then used to convert the total supply from wei to ether. Finally, the `Number` and `toFixed` methods are used to round the total supply to four decimal places and output the block number, name, and total supply for each block; note that this is for readability, and you should use the raw wei data to make calculations. Overall, the `getStorageValueOverTime` function provides a way to monitor changes in the state of a smart contract over time, which can be useful for understanding the contract's behavior and performance. # Arbitrum API reference: JSON-RPC quickstart on Chainstack Source: https://docs.chainstack.com/reference/arbitrum-getting-started Start building on Arbitrum L2 with optimistic rollups. Deploy RPC nodes, use JSON-RPC API, bridge assets, and connect to Ethereum scaling solution. ## What is the Arbitrum protocol? Arbitrum is a layer 2 scaling solution for Ethereum that enhances transaction processing efficiency and reduces costs by using optimistic rollups. Off-chain transactions are grouped together in a batch and processed before being sent to the Ethereum network. > [!NOTE] > > Read [Solving the blockchain trilemma: A look at some scaling solutions](https://chainstack.com/solving-the-blockchain-trilemma-scaling-solutions-for-ethereum/) to learn more about layer 2 chains. To bridge assets between Ethereum and Arbitrum, simply use the [Arbitrum bridge](https://bridge.arbitrum.io/?l2ChainId=42161). The process is easy and takes around 10 minutes to complete. Just connect to MetaMask and follow the steps. You can also try out Arbitrum on the Chainstack platform by deploying an Arbitrum Sepolia testnet node and bridging some Sepolia ETH using the [testnet Arbitrum bridge](https://bridge.arbitrum.io/?l2ChainId=421614). Find useful Arbitrum tools in the [Arbitrum tooling](/docs/arbitrum-tooling) section. ## What is the Arbitrum API The Arbitrum API allows developers to communicate with the Arbitrum blockchain to build applications. To read data from and send transactions to the Arbitrum blockchain, an application must connect to an Arbitrum RPC node. When communicating with an Arbitrum RPC node, the Arbitrum client implements a JSON-RPC specification, a communication protocol allowing one to make remote calls and execute them as if they were made locally. ## How to start using the Arbitrum API To use the Arbitrum API, you need access to an Arbitrum RPC node. Follow these steps to sign up on Chainstack, deploy an Arbitrum RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the Arbitrum blockchain and use the Arbitrum API to build. > [!NOTE] > > **See also** > > [Build better with Arbitrum](https://chainstack.com/build-better-with-arbitrum/) # eth_getTransactionByBlockHashAndIndex | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-gettransactionbyblockhashandindex openapi/arbitrum_node_api/transaction_info/eth_getTransactionByBlockHashAndIndex.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. Arbitrum API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Ethereum blockchain. ## Parameters * `hash` — the hash of the block * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal ## Response * `object` — a transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blockHash, index) => { const transaction = await provider.send("eth_getTransactionByBlockHashAndIndex", [blockHash, index]); console.log(transaction); } getTransaction("0x1b327cf80c50063ba59fdf1eb8f60d1397d8e6df75cceef43130c783f517d532", "0x4"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block("0x1b327cf80c50063ba59fdf1eb8f60d1397d8e6df75cceef43130c783f517d532", 4)) ``` ## Use case The `eth_getTransactionByBlockHashAndIndex` can be used to retrieve transaction details from a block. In the following example the `getSendersInBlock` checks how many transactions exist in a block, then receives the sender of each (`from` field) and displays it. > [!NOTE] > > Note that there are simpler ways to get this information, and this is only a practical example of how to use `eth_getTransactionByBlockHashAndIndex`, if the number of transactions in the block is already known, the first step can be skipped. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getSendersInBlock(blockHash) { const block = await provider.getBlock(blockHash); const transactions = block.transactions; console.log(`Block ${blockHash} contains ${transactions.length} transactions`); for (let i = 0; i < transactions.length; i++) { const transaction = await provider.send("eth_getTransactionByBlockHashAndIndex", [blockHash, "0x" + i.toString(16)]); console.log(`Sender of transaction at index ${i}:`, transaction.from); } } getSendersInBlock("0x1b327cf80c50063ba59fdf1eb8f60d1397d8e6df75cceef43130c783f517d532"); ``` The number of transactions in the block is determined using `transactions.length` and the total number of transactions logged to the console. A `for` loop is then used to retrieve information about each transaction in the block. For each iteration of the loop, the code calls `provider.send` with the `eth_getTransactionByBlockHashAndIndex` method to retrieve information about the transaction at the current index. The `from` property of the returned transaction object is logged to the console to show the sender of the transaction. # eth_getTransactionByBlockNumberAndIndex | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-gettransactionbyblocknumberandindex openapi/arbitrum_node_api/transaction_info/eth_getTransactionByBlockNumberAndIndex.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. Arbitrum API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Ethereum blockchain. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal. ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blocNumber, index) => { const transaction = await provider.send("eth_getTransactionByBlockNumberAndIndex", [blocNumber, index]); console.log(transaction); } getTransaction("latest", "0x3"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block(69841690, 2)) # Hex encoded parameters starting with "0x" are accepted as well. ``` ## Use case The `eth_getTransactionByBlockNumberAndIndex` can be used to retrieve transaction details from a block. In the following example the `getTypeInBlock` function checks how many transactions exist in a block, then retrieves the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of each (`type` field), and displays it. > [!NOTE] > > Note that there are simpler ways to get this information, and this is only a practical example of how to use `eth_getTransactionByBlockNumberAndIndex`, if the number of transactions in the block is already known, the first step can be skipped. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTypeInBlock() { const latestBlock = await provider.getBlockNumber(); const block = await provider.getBlock(latestBlock, false); const transactions = block.transactions; console.log(`The latest block is ${latestBlock} and contains ${transactions.length} transactions`); for (let i = 0; i < transactions.length; i++) { const transaction = await provider.send("eth_getTransactionByBlockNumberAndIndex", [`0x${latestBlock.toString(16)}`, `0x${i.toString(16)}`]); console.log(`The type of transaction at index ${i}:`, transaction.type); } } getTypeInBlock(); ``` The number of transactions in the block is determined using `transactions.length` and the total number of transactions logged to the console. A `for` loop is then used to retrieve information about each transaction in the block. For each iteration of the loop, the code calls the `eth_getTransactionByBlockNumberAndIndex` method to retrieve information about the transaction at the current index. The `type` property of the returned transaction object is logged to the console to show the sender of the transaction. # eth_getTransactionByHash | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-gettransactionbyhash openapi/arbitrum_node_api/transaction_info/eth_getTransactionByHash.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the information about a transaction from the transaction hash. Use it on Arbitrum via Chainstack. Arbitrum API method that returns the information about a transaction from the transaction hash. It is used to retrieve information about a specific transaction by its hash. The method returns an object with details about the transaction, such as the sender, recipient, gas used, transaction value, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet; it essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. This field is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (transactionHash) => { const transaction = await provider.send("eth_getTransactionByHash", [transactionHash]); console.log(transaction); }; getTransaction("0xc42973ff29cf47758c63b1e1edc687e87ac1afb1f256303638c24316d7515c3c") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction("0xc42973ff29cf47758c63b1e1edc687e87ac1afb1f256303638c24316d7515c3c")) ``` ## Use case One use case for `eth_getTransactionByHash` is to inspect a specific transaction and display information about it, such as the address that was sent, the block it was included in, and the number of transactions the sender sent so far (nonce). This information can be useful for monitoring and analyzing transactions. Here's an example of how you can achieve this in ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTransaction(transactionHash) { // Get the transaction using the provided transaction hash const transaction = await provider.getTransaction(transactionHash) // Store the desired parameters in constants const sender = transaction.from; const block = transaction.blockNumber const transactionsCount = transaction.nonce // Log the transaction information to the console console.log(`Transaction ${transactionHash} was sent from address ${sender}`); console.log(`It was included in block ${block}`); console.log(`The sender has sent ${transactionsCount} transactions so far\n`); } getTransaction("0x3985727a3dbb61eab145ad22cfa842562d9e633806057290a5403b2eb880e160") ``` In this example, the program starts by connecting to a node endpoint. Then, call the `eth_getTransactionByHash` method and pass in the transaction hash as an argument. The method returns a Promise that resolves to a transaction object, which is used to retrieve the sender address, the block number the transaction was included in, and the address nonce. The last step is to log the information to the console. > [!NOTE] > > Note that the desired parameters are stored in constants, so you can use them for further processing. # eth_getTransactionCount | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-gettransactioncount openapi/arbitrum_node_api/accounts_info/eth_getTransactionCount.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns the number of transactions sent from an address at the selected block. This value is also called nonce. Arbitrum API method that returns the number of transactions sent from an address at the selected block. This value is also called `nonce`. It is an important piece of information, especially to ensure that a transaction is not sent twice. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`— the address to retrieve the transaction count. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — an integer value identifying the number of transactions sent from an address at the specified block. ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getNonce = async (address, blockId) => { const nonce = await provider.send("eth_getTransactionCount", [address, blockId]); console.log(nonce); }; getNonce("0x4f4e13B91C31C6DB330e1c375B9c6f86eAeA411E", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_count("0x4f4e13B91C31C6DB330e1c375B9c6f86eAeA411E", "latest")) ``` ## Use case One of the most common use cases for `eth_getTransactionCount`is to create the transaction object built in a script designed to send a transaction. The nonce field is required, and it is retrieved using the `eth_getTransactionCount` method. The following code shows how to build a `rawTransaction` using ethers.js. > [!WARNING] > > **Security notice** > > You need your private key to sign the transaction; never share your private key with anyone. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Initialize the wallet with the private key const wallet = new ethers.Wallet("PRIVATE_KEY", provider); // Validate input parameters and return an error message if invalid const validateInputs = (fromAddress, toAddress) => { if (!ethers.isAddress(fromAddress)) { throw new Error(`Invalid fromAddress: ${fromAddress}`); } if (!ethers.isAddress(toAddress)) { throw new Error(`Invalid toAddress: ${toAddress}`); } }; // Async function to create a raw transaction async function createRawTransaction(fromAddress, toAddress, value) { validateInputs(fromAddress, toAddress); const nonce = await provider.getTransactionCount(fromAddress); const gasPrice = (await provider.getFeeData()).gasPrice; const gasLimit = await provider.estimateGas({ from: fromAddress, to: toAddress, }); // Build the transaction object const transactionObject = { to: toAddress, gasPrice: gasPrice, gasLimit: gasLimit, nonce: nonce, value: value, }; // Sign the transaction using the wallet and return it as a raw transaction const rawTransaction = await wallet.signTransaction(transactionObject); return rawTransaction; } async function main() { const rawTransaction = await createRawTransaction('0x4f4e13B91C31C6DB330e1c375B9c6f86eAeA411E', "0xe341b2f448eb190495ed4a89c01f20078b26b0f6" , "100000000000000") console.log(`Raw transaction: ${rawTransaction}`) } main() ``` In summary, the code creates a valid raw transaction that can be broadcasted to the network. During the process, the script retrieves important values such as the `nonce`, `gasPrice`, and `gasLimit`, builds a transaction object, and signs it using a private key. First, the `createRawTransaction` function calls `validateInputs` to ensure that the `fromAddress` and `toAddress` parameters are valid addresses. If either of these addresses is invalid, the function throws an error with a descriptive message. Next, the function makes a call to `provider.getTransactionCount` with the `fromAddress` as a parameter. This method returns the number of transactions sent from the `fromAddress`, and is used as the `nonce` for the transaction. The function retrieves the `gasPrice` and `gasLimit` for the transaction using `provider.getFeeData` and `provider.estimateGas`, respectively. The [eth\_gasPrice](/reference/gasprice) is the amount of gas that the transaction sender is willing to pay per unit of gas consumed by the transaction, while the [eth\_estimateGas](/reference/arbitrum-estimategas) is an estimate of the maximum amount of gas that the transaction will consume. With the `nonce`, `gasPrice`, and `gasLimit` values, the function builds a transaction object, which includes the `toAddress`, `gasPrice`, `gasLimit`, `nonce`, and `value` of the transaction. The value is the amount transferred from the `fromAddress` to the `toAddress`. The transaction object is then signed by the wallet using the private key. The signed transaction is returned as a `rawTransaction`, which can be broadcasted to the network to execute the transaction. # eth_getTransactionReceipt | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-gettransactionreceipt openapi/arbitrum_node_api/transaction_info/eth_getTransactionReceipt.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that retrieves the receipt of a transaction from a transaction hash. eth_getTransactionReceipt on Arbitrum via Chainstack. > [!NOTE] > > **No response in the interactive API** > > The response to the call might be too big for the web app to handle, so if you are not getting it here, copy the CURL example and run in your terminal—this will work. Arbitrum API method that retrieves the receipt of a transaction from a transaction hash. It can be used to verify the success or failure of a transaction, check the execution status of a smart contract, retrieve logs, check if a transaction deployed a smart contract, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `Transaction receipt` — the receipt object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the address of the contract created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects generated by this transaction, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionReceipt = async (transactionHash) => { const receipt = await provider.send("eth_getTransactionReceipt", [transactionHash]); console.log(receipt); }; getTransactionReceipt('0x3985727a3dbb61eab145ad22cfa842562d9e633806057290a5403b2eb880e160') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_receipt("0x3985727a3dbb61eab145ad22cfa842562d9e633806057290a5403b2eb880e160")) ``` ## Use case A practical use case for `eth_getTransactionReceipt` is to verify a transaction's status and whether it was successful or not. This can be especially useful when deploying a contract, as you can use the receipt to confirm that the contract was deployed successfully and has a valid contract address. Here's an example in ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function verifyTransaction(transactionHash) { // Get the transaction receipt const receipt = await provider.getTransactionReceipt(transactionHash); // Check if the receipt is not yet available if (!receipt) { console.log(`Transaction ${transactionHash} is still pending...`); return; } // Check if the transaction was successful if (receipt.status) { console.log(`✅The transaction was successful! \nHash: ${transactionHash}`); // Check if the transaction deployed a smart contract if (receipt.contractAddress != null) { console.log(`📑The smart contract was deployed at address: ${receipt.contractAddress}`); } else { console.log(`❗This transaction did not deploy a smart contract`); } } else { console.log(`💥The transaction failed. \nHash: ${transactionHash}`); } } verifyTransaction('0x1f001c4e6a2d2e8f5c7a7dfa479178a7a055dc02c1480a3324545410c8706522') ``` This script holds an async function called `verifyTransaction` that takes a transaction hash as input. The function uses the ethers.js library to retrieve the transaction receipt of the specified transaction using the `eth_getTransactionReceipt` method. The function first checks if the transaction receipt is not yet available, and if so, it logs a message to the console indicating that the transaction is still pending. If the receipt is available, the function checks the status property of the receipt to determine if the transaction was successful. If the transaction was successful, the function checks if a smart contract was deployed as part of the transaction. The function logs the smart contract's address to the console if a smart contract is deployed. If not, the function logs a message indicating that no smart contract was deployed. If the transaction was not successful, the function logs a message indicating that the transaction failed, along with the transaction hash. # Logs and events | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-logs-rpc-methods Arbitrum event log RPC methods for retrieving smart contract events, transaction logs, and filter-based data from your Chainstack node endpoints. Retrieving logs information from the Arbitrum blockchain can be done by using the following methods: > [card] [eth\_getLogs](/reference/arbitrum-getlogs) > [card] [eth\_newFilter](/reference/arbitrum-newfilter) > [!NOTE] > > Find logs limitations in [Understanding eth\_getLogs limitations](/docs/understanding-eth-getlogs-limitations) # eth_subscribe("logs") | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-native-subscribe-logs Subscribe to real-time Arbitrum event logs via WebSocket. Receive notifications whenever new logs matching your specified filter criteria are emitted. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) the event filter options: * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about topics and event logs. ## Response * `subscription` — the subscription ID. * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_subscribe("logs")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_subscribe", "params": ["logs", {"address":"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewLogs() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['logs', {"address":"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewLogs(); ``` This will generate a continuous stream of data displaying new event logs matching the filter. Use [eth\_unsubscribe](/reference/arbitrum-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("logs")` method on Arbitrum is used to subscribe to the logs generated by smart contracts on the blockchain. These logs contain information about events that have occurred on the blockchain, such as details of a smart contract function and outputs of transactions. A practical and real-world scenario for using the `eth_subscribe("logs")` method could be in the context of a decentralized exchange (DEX). In the context of a DEX, trades are executed using smart contracts that run on the Arbitrum blockchain. When a trade is executed on a DEX, a smart contract is called to process the transaction. This smart contract generates a log that contains information about the trade, such as the amount of cryptocurrency being traded, the parties involved, and so on. By using the `eth_subscribe("logs")` method, a user or application can subscribe to the logs generated by the DEX's smart contracts. This can be useful for tracking trades in real time, monitoring the performance of the DEX, and generating reports on trading activity. For example, a trader might use the `eth_subscribe("logs")` method to monitor the logs generated by a DEX in order to identify trends in trading activity. They might look for patterns in the volume and price of trades, which could help them make more informed trading decisions. Similarly, an analytics platform might use the `eth_subscribe("logs")` method to collect data on trading activity across multiple DEXs. By analyzing this data, they could generate insights into the performance of different DEXs, which could be useful for investors and traders looking to make informed decisions about where to trade. # eth_subscribe("newHeads") | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-native-subscribe-newheads Subscribe to new Arbitrum block headers via WebSocket. Receive real-time notifications whenever a new block is added to the Arbitrum blockchain. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `newHeads` in this case. ## Response * `subscription` — the subscription ID. * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Arbitrum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Arbitrum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Arbitrum network since the inception of the network. It measures the overall security and integrity of the Arbitrum network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/arbitrum-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `eth_subscribe("newHeads")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewBlocks() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewBlocks(); ``` This will generate a continuous stream of data displaying new block headers as they are added to the chain. Use [eth\_unsubscribe](/reference/arbitrum-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newHeads")` method can be useful in a variety of real-world scenarios, such as: * Real-time monitoring of blockchain activity. Companies or individuals who rely on the Arbitrum network for their business can use `eth_subscribe("newHeads")` to receive real-time notifications about new blocks being added to the chain. This can help them keep track of important transactions, such as high-value trades, in real time and respond quickly to any issues that may arise. * Decentralized Finance (DeFi) applications. DeFi applications, such as lending platforms, can use `eth_subscribe("newHeads")` to monitor the blockchain for new blocks that might contain important data, such as changes in interest rates or new lending opportunities. * Smart contract automation. Developers can use `eth_subscribe("newHeads")` to trigger automated smart contract actions whenever a new block is added to the chain. For example, a smart contract could automatically execute an action order whenever a new block is added that meets certain criteria. # eth_unsubscribe | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-native-unsubscribe Cancel any active Arbitrum WebSocket subscription with eth_unsubscribe. Pass the subscription ID to stop receiving real-time event notifications. When a client subscribes to a particular event, the Arbitrum node will send updates as soon as they occur. However, in some cases, the client may no longer be interested in receiving updates for a particular event. The `eth_unsubscribe` method can be used to cancel the following subscriptions: > [card] [eth\_subscribe('newHeads')](/reference/arbitrum-native-subscribe-newheads) > [card] [eth\_subscribe('logs')](/reference/arbitrum-native-subscribe-logs) ## Parameters * `string` — the ID of the subscription that you want to cancel ## Response * `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `eth_unsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_unsubscribe", "params": ["0x7529626735859f32962b10b19ee2e3eb"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function removeSubscription() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_unsubscribe', params: ['0x9de3135dd3dd6c6b99bd104061a5af4f'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } removeSubscription(); ``` This will remove the subscription matching the ID passed as a parameter. # eth_newBlockFilter | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-newblockfilter openapi/arbitrum_node_api/blocks_info/eth_newBlockFilter.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that creates a filter that watches for new blocks on the blockchain. eth_newBlockFilter on Arbitrum via Chainstack. Arbitrum API method that creates a filter that watches for new blocks on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/arbitrum-getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter The filters created are stored on the blockchain client instance. The filter fill is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/arbitrum-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/arbitrum-uninstallfilter) to remove the filter ## `eth_newBlockFilter` code examples > [!NOTE] > > **web3.eth.filterdeprecation** > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/arbitrum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newBlockFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') return blocks_filter except Exception as e: print(e) blocks = get_new_blocks() filter_id = blocks.filter_id print(filter_id) ``` ## Use case One use case for `eth_newBlockFilter` in a simple DApp is to listen for new blocks and update the user interface with the latest block information. When the DApp starts, it creates a new filter using `eth_newBlockFilter` to listen for new blocks. When a new block is added to the blockchain, the filter is triggered, and the DApp retrieves the latest block information using a Web3 library. Here is an implementation of this use case using web3.py: ```python main.py theme={"system"} import time from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Set up a new blocks filter def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) def watch_for_blocks(new_filter): # Wait for new blocks to arrive while True: # Retrieve the changes that match the filter changes = web3.eth.getFilterChanges(new_filter) # If there are no changes, wait for new blocks if len(changes) == 0: time.sleep(1) # Wait for 1 second continue # Process the new blocks for change in changes: block = web3.eth.getBlock(change) print('New block:', block) def main(): # Create a new filter to watch for new blocks new_filter = get_new_blocks() print(new_filter) # Watch for new blocks watch_for_blocks(new_filter) if __name__ == '__main__': main() ``` The code is divided into three main functions: `get_new_blocks`, `watch_for_blocks`, and `main`. Let's take a closer look at each of them: `get_new_blocks` is responsible for setting up a new filter to watch for new blocks. The `try` block sets up the filter by calling the `eth.filter` method of the web3.py library and passing it the string `latest`. The `split` method is then used to split the resulting string at each space character, and the second part of the resulting list is extracted as the filter ID. The filter ID is returned from the function. > [!NOTE] > > The `eth.filter` method of the web3.py library does not only return the filter ID but it returns a sentence saying `Filter for 0x3c3f5a5e637d29c033e4e7a3f7e870cd`. This is the reason for the string manipulation within the `get_new_blocks` function. `watch_for_blocks` is the function that watches for new blocks. It does this by first entering an infinite loop while `True`. Within this loop, the [eth\_getFilterChanges](/reference/arbitrum-getfilterchanges) method is used to retrieve any changes that match the filter ID passed to the function. If there are no changes, the code waits for 1 second using the `time.sleep` method and then continues with the loop. If there are changes, the new blocks are processed by looping through them and calling the `eth.getBlock` method of the web3.py library to retrieve the details of each block. Finally, the details of the new blocks are printed on the console. `main` is the function that ties everything together. It first calls `get_new_blocks` to set up a new filter to watch for new blocks, and then passes the resulting filter ID to `watch_for_blocks` to start watching for new blocks. The filter ID is also printed to the console to confirm that the filter has been set up correctly. # eth_newFilter | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-newfilter openapi/arbitrum_node_api/logs_and_events/eth_newFilter.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that generates a filter object based on the filter parameters. Available on Arbitrum via Chainstack JSON-RPC nodes. Arbitrum API method that generates a filter object based on the filter parameters. It returns a filter ID, which can be used to retrieve the results of the filter using the [eth\_getFilterChanges](/reference/arbitrum-getfilterchanges) method. By creating a filter for specific events, developers can receive notifications when those events occur and use them to trigger actions in their applications. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block > * `earliest` — the earliest available or genesis block > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `result` — a hexadecimal string representing the ID of the newly created filter > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter fill is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/arbitrum-getfilterchanges) to retrieve updated logs * [eth\_uninstallFilter](/reference/arbitrum-uninstallfilter) to remove the filter ## `eth_newFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/arbitrum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const createFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) filter = { 'toBlock': 'latest', 'address': '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } def create_logs_filter(filter_object): try: logs_filter = web3.eth.filter(filter_object) return logs_filter except Exception as e: print(e) logs_filter = create_logs_filter(filter) filter_id = logs_filter.filter_id print(f'New filter ID: {filter_id}') ``` ## Use case You can use `eth_newFilter` to create a filter for a specific action on a smart contract, for example, to monitor the transfer transactions from the [Wrapped ETH token](https://arbiscan.io/token/0x82af49447d8a07e3bd95bd0d56f35241523fbab1#code). The idea is to create a filter using the `eth_newFilter` method to monitor an ERC-20 smart contract, WETH in this case. The following filter parameters are selected: ```javascript filter_params theme={"system"} const filter = { // Poll the latest block available toBlock: 'latest', // WETH smart contract address address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', // Signature of the Transfer event. topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], }; ``` The program is then polling the filter every 2 seconds to check for any new changes using the `eth_getFilterChanges` method. Here is the implementation using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], }; const createAndPollFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(`The filter ID is: ${filterId}`); // the filter ID returned by eth_newFilter // Poll the filter every 30 seconds setInterval(async () => { const filterChanges = await provider.send('eth_getFilterChanges', [filterId]); if (filterChanges.length === 0) { console.log('No new logs since the last poll.'); } else { console.log(filterChanges); // the filter changes returned by eth_getFilterChanges } }, 2000); // 2 seconds in milliseconds return filterId; } catch (error) { console.log(error); } }; createAndPollFilter(); ``` Here's a breakdown of how the code works: The `createAndPollFilter` function is an asynchronous function that creates and polls a filter for the WETH smart contract on Arbitrum. The `eth_newFilter` method creates a new filter with the specified parameters. In this case, it passes in the filter object as the filter parameter. The method returns the ID of the newly created filter, which is then logged into the console. The `setInterval` method is used to poll the filter every 2 seconds. It takes a function as the first argument and an interval in milliseconds as the second argument. The function passed to `setInterval` is an asynchronous function that calls `eth_getFilterChanges` with the filter ID returned by `eth_newFilter`. This method returns an array of all the changes to the filter since the last time it was polled. If there are no changes, it logs "No new logs since the last poll." to the console. If there are changes, it logs the filter changes to the console. The `createAndPollFilter` function returns the filter ID, and if there are any errors thrown while creating or polling the filter, the catch block logs the error to the console. # eth_sendRawTransaction | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-sendrawtransaction openapi/arbitrum_node_api/execute_transactions/eth_sendRawTransaction.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that allows submitting a signed transaction to the network. eth_sendRawTransaction on Arbitrum via Chainstack. Arbitrum API method that allows submitting a signed transaction to the network. Once a transaction is signed, you can use the `eth_sendRawTransaction` method to submit the signed transaction to the Arbitrum network for processing. > [!WARNING] > > Note that the interactive example in this page will not work, due to the fact that `eth_sendRawTransaction` is used to modify the state of the blockchain, it is not possible to duplicate the same request. ## Parameters * `data` — the signed transaction. The serialized transaction data, which includes the transaction metadata such as the nonce, gas price, gas limit, recipient address, and data payload, as well as the digital signature generated using the private key associated with the address that is sending the transaction. Typically, signed with a library such as web3.py, web3.js, or ethers.js using the sender's private key. ## Response * `result` — the unique hash identifying the transaction or the zero hash if the transaction is not available yet. The zero hash is a special value that represents an invalid or null hash. ## `eth_sendRawTransaction` code examples The following examples demonstrate how to use Web3 libraries to make an ETH transfer on the Arbitrum Sepolia testnet. > [!NOTE] > > **Additional libraries requirement** > > Note that web3.js requires installing two extra libraries: > > * [ethereumjs-tx](https://www.npmjs.com/package/@ethereumjs/tx) > * [ethereumjs-common](https://www.npmjs.com/package/@ethereumjs/common) ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); async function sendEth(value) { // Define the sender and receiver addresses, and the private key const sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a'; const receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114'; const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Define the gas limit const gasLimit = await provider.estimateGas({ from: sender, to: receiver, value: ethers.utils.parseEther(value), }); // Get the transaction count for the sender address const nonce = await provider.getTransactionCount(sender); // Define the transaction object const transactionObject = { to: receiver, gasPrice: ethers.utils.parseUnits('50', 'gwei'), gasLimit: gasLimit.toHexString(), nonce: nonce, value: ethers.utils.parseEther(value), }; // Sign the transaction using the private key const wallet = new ethers.Wallet(privateKey, provider); console.log(`Sending transaction...`) const transaction = await wallet.sendTransaction(transactionObject); console.log('Transaction hash:', transaction.hash); } sendEth("2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" # Define the sender and receiver addresses, and the private key sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a' receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114' private_key = '1111111111111111111111111111111111111111111111111111111111111111' # Estimate gas limit gas_limit = web3.eth.estimate_gas({ 'from': sender, 'to': receiver }) # Build the transaction object transaction = { 'nonce': web3.eth.get_transaction_count(sender), 'to': receiver, 'value': web3.to_wei(1, 'ether'), # value to send 'gas': gas_limit, 'gasPrice': web3.eth.gas_price, 'chainId': web3.eth.chain_id } # Sign the transaction signed_tx = web3.eth.account.sign_transaction(transaction, private_key) print(f'Signed transaction: {signed_tx}') # Send the transaction print('Sending transaction...') tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) print(f'Transaction hash: {web3.toHex(tx_hash)}') ``` > [!NOTE] > > Note that you will need to edit the `sender`, `receiver`, and `privateKey` fields to be able to use this code. ## Use case One potential use case for `eth_sendRawTransaction` is to automatically swap ETH for a token when its price reaches a certain level. `eth_sendRawTransaction` will allow specifying custom transaction parameters, such as gas price or gas limit, to optimize the speed and cost of the transaction. For instance, you could build a program monitoring the price of a specific token and execute a `swapExactETHForTokens` transaction when the token reaches a certain price level, it could use `eth_sendRawTransaction` to submit a raw transaction with a custom gas price and limit that are optimized for the current network conditions. The following code shows how to call the `swapExactETHForTokens` function from the SushiSwap exchange on Arbitrum using ethers.js: > [!NOTE] > > Note that this code only shows how to execute the `swapExactETHForTokens` function on [SushiSwap on Arbitrum mainnet](https://arbiscan.io/address/0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506#code), and not how to track the prices. You need to build a separate part for this. This particular example shows how to swap ETH for UNI tokens. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); // ABI for the swapExactETHForTokens function const sushiRouterABI = [ { "inputs":[ { "internalType":"uint256", "name":"amountOutMin", "type":"uint256" }, { "internalType":"address[]", "name":"path", "type":"address[]" }, { "internalType":"address", "name":"to", "type":"address" }, { "internalType":"uint256", "name":"deadline", "type":"uint256" } ], "name":"swapExactETHForTokens", "outputs":[ { "internalType":"uint256[]", "name":"amounts", "type":"uint256[]" } ], "stateMutability":"payable", "type":"function" } ] async function swapETHForToken(value, token, privateKey) { // Create a new wallet using the private key const wallet = new ethers.Wallet(privateKey, provider); // Get the SushiSwap Router contract and create a contract instance const sushiRouterAddress = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; // SushiSwap Router address on Arbitrum Mainnet const sushiRouterContract = new ethers.Contract(sushiRouterAddress, sushiRouterABI, wallet); // Prepare the swap parameters const tokenToBuy = token; const amountOfETH = ethers.utils.parseEther(value); // Convert the input value to Wei const minAmountOfTokens = ethers.utils.parseUnits('0'); // Set the minimum output token amount to 0, this means that you will always receive something. const deadline = Math.floor(Date.now() / 1000) + 60 * 10; // Set the deadline to 10 minutes from now // Define the token path for the swap const path = ['0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', tokenToBuy]; // WETH to token to buy // Parameters for the transaction object const nonce = await wallet.getTransactionCount(); const gasPrice = await provider.getGasPrice(); const transactionData = sushiRouterContract.interface.encodeFunctionData('swapExactETHForTokens', [ minAmountOfTokens, path, wallet.address, deadline ]); // Calculate the gas limit based on the parameters const gasLimit = await provider.estimateGas({to: sushiRouterAddress, value: amountOfETH, data: transactionData}); // Get the chain ID for replay-protected (EIP-155) transactions const chainId = await provider.getNetwork().then(network => network.chainId); // Build the transaction object const transactionObject = { to: sushiRouterAddress, value: amountOfETH, nonce: nonce, gasPrice: gasPrice, gasLimit: gasLimit, data: transactionData, chainId: chainId }; // Sign and send the transaction console.log(`Swapping tokens...`); const signedTx = await wallet.signTransaction(transactionObject); const transaction = await provider.sendTransaction(signedTx); console.log(`Transaction sent; transaction hash: ${transaction.hash}\n`); // Wait for the transaction to be confirmed console.log(`Validating transaction...`); const receipt = await transaction.wait(); return receipt } async function main() { try { // Set the swap parameters and private key const quantity = '0.2'; // Amount of WETH to swap const addressToBuy = '0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0'; // Address of the token to buy (UNI in this case) const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Replace with your actual private key // Execute the swap and check if successful const receipt = await swapETHForToken(quantity, addressToBuy, privateKey); if (receipt.status === 1) { console.log(`The swap was successful!`); console.log(`Transaction receipt`, receipt); } else { console.log(`The swap failed with status ${receipt.status}.`); } } catch (error) { console.error(`An error occurred during the swap: ${error.message}`); } } main(); ``` > [!WARNING] > > **Security notice** > > You need your private key to sign the transaction; never share your private key with anyone. > > On a side note, the private key in this case must be imported without '0x' at the beginning of the string. In the `swapETHForToken` function, a new wallet is created using the provided `privateKey`. Then, the instance of the SushiSwap Router contract is obtained using its address and the ABI (Application Binary Interface) for the `swapExactETHForTokens` function. This function swaps a specified amount of ETH for a token at the current market price. The `tokenToBuy`, `amountOfETH`, `minAmountOfTokens`, and `deadline` variables are used to prepare the swap parameters. The `path` variable defines the input and output token path for the swap. The `nonce`, `gasPrice`, and the `transactionData` variables define the parameters for the transaction object and encode the `swapExactETHForTokens` function call with the specified parameters. The `gasLimit` variable is calculated based on the parameters, and the `chainId` variable is obtained for replay-protected (EIP-155) transactions. Then, the `transactionObject` variable is built, including the `to` address, the `value`, the `nonce`, the `gasPrice`, the `gasLimit`, the `data`, and the `chainId`. Finally, the transaction is signed and sent, and the receipt is returned. If the status of the receipt is equal to 1, the swap was successful, and the receipt object is logged to the console. If the status is not equal to 1, the swap failed, and an error message is logged to the console. # eth_sendRawTransactionSync | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-sendrawtransactionsync openapi/arbitrum_node_api/execute_transactions/eth_sendRawTransactionSync.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that submits a signed transaction and blocks until the transaction receipt is returned or the timeout expires. Arbitrum API method `eth_sendRawTransactionSync` allows for the submission of a pre-signed transaction to the Arbitrum network and waits synchronously for the transaction receipt or a configurable timeout. This method reduces transaction submission latency by combining transaction submission and receipt retrieval into a single RPC call, based on [EIP-7966](https://eips.ethereum.org/EIPS/eip-7966). ## Parameters * `signedTransactionData` — the signed transaction data in hexadecimal format. This data includes the transaction details such as nonce, gas price, gas limit, to address, value, data, and the signature. * `timeout` (optional) — maximum time to wait for the transaction receipt, in milliseconds, passed as a `0x`-prefixed hex-encoded quantity, for example `0x1388` for 5000 ms. If omitted, the node's default applies (20 seconds in the standard Nitro configuration). Requests above the node's maximum (60 seconds by default) are silently capped to the maximum rather than rejected. > [!NOTE] > > Although [EIP-7966](https://eips.ethereum.org/EIPS/eip-7966) defines `timeout` as a plain integer, Arbitrum Nitro—a [go-ethereum](https://github.com/OffchainLabs/go-ethereum) fork—types it as a hex-encoded quantity (`hexutil.Uint64`), so you must pass it as a `0x`-prefixed hex string. Plain integers (`5000`) and non-hex strings (`"5000"`) are rejected with an `invalid argument 1` error. ## Response * `result` — the complete transaction receipt object, the same structure returned by [eth\_getTransactionReceipt](/reference/arbitrum-gettransactionreceipt): transaction hash, block information, gas usage, contract address (if applicable), logs, and status. If the timeout elapses before the transaction is mined, the call returns an error instead of a receipt. Alongside the standard receipt fields, Arbitrum includes: * `gasUsedForL1` — the amount of gas spent on parent chain (L1) calldata, in units of child chain gas. * `l1BlockNumber` — the block number of the first non-Arbitrum ancestor chain, usable for `block.number` calls. * `timeboosted` — `true` if the transaction was sequenced through the Arbitrum [Timeboost](https://docs.arbitrum.io/how-arbitrum-works/timeboost/gentle-introduction) express lane, `false` otherwise. ## Use case The `eth_sendRawTransactionSync` method is particularly useful for: * High-frequency applications requiring immediate transaction confirmation feedback. * Layer 2 solutions with fast block times where rapid transaction throughput is critical. * DApps and wallets seeking to improve user experience with faster transaction confirmations. * Trading applications where transaction latency directly impacts performance. * Applications requiring stronger delivery guarantees than standard asynchronous transaction submission. # eth_simulateV1 | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-simulatev1 openapi/arbitrum_node_api/transaction_info/eth_simulateV1.json POST /249360a6043e7e070377eb6f345f5573 Arbitrum API method that enables simulation of transaction execution without actually committing it to the blockchain. On Arbitrum. Arbitrum API method that enables simulation of transaction execution without actually committing it to the blockchain. This method allows for testing and previewing the results of potential transactions, including their effects on state, gas usage, and potential errors. ## Parameters * `object` — the simulation parameters: * `blockStateCalls` — an array of transaction call objects: * `calls` — an array of transaction objects: * `from` — the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — the maximum amount of gas that can be used by the transaction. * `maxFeePerGas` — the maximum fee per gas the sender is willing to pay. * `maxPriorityFeePerGas` — the maximum priority fee per gas the sender is willing to pay. * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts. * `validation` — a boolean indicating whether to validate the transaction. * `traceTransfers` — a boolean indicating whether to trace token transfers. * `blockOverride` — (optional) an object to override block properties: * `number` — (optional) the block number to override. * `difficulty` — (optional) the block difficulty to override. * `time` — (optional) the block timestamp to override. * `gasLimit` — (optional) the block gas limit to override. * `coinbase` — (optional) the block coinbase address to override. * `random` — (optional) the block random value to override. * `baseFeePerGas` — (optional) the block base fee per gas to override. * `stateOverride` — (optional) an object to override account states: * `address` — the account address to override: * `balance` — (optional) the account balance to override. * `nonce` — (optional) the account nonce to override. * `code` — (optional) the account code to override. * `state` — (optional) the account storage to override. * `stateDiff` — (optional) the account storage diff to override. * `string` — the block parameter (e.g., "latest", "earliest", or a block number). ## Response * `result` — the result of the simulation, including: * Transaction execution details * Gas used * State changes * Trace information if requested * Any errors that would occur during actual execution ## Use case A common use case for `eth_simulateV1` is to test complex smart contract interactions before submitting them to the blockchain. This helps developers predict gas usage, identify potential errors, and understand how their transactions will affect the blockchain state. For example, you can use `eth_simulateV1` to: 1. Check if a transaction would revert before submitting it. 2. Estimate accurate gas costs for complex transactions. 3. Preview token transfers that would result from a contract call. 4. Test contract interactions without spending real funds. 5. Debug smart contract functions by examining execution traces. # subscribe ("logs") | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-subscribelogs Reference for the subscribe ("logs") JSON-RPC method on the Arbitrum blockchain via Chainstack nodes. Parameters, response, and examples included. ethers.js subscription equivalent to [eth\_newFilter](/reference/arbitrum-newfilter). `subscribe("logs")` allows developers to subscribe to real-time updates about new event logs on the Arbitrum blockchain; the application will receive notifications whenever new logs matching the filter are emitted. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) The subscription options: * `fromBlock` — an integer that specifies the starting block number from which the logs should be fetched. `null` by default. * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `subscribe("logs")` code example > [!NOTE] > > Note that ethers.js subscriptions require a WebSocket connection. Use the ethers.js `provider.on` method with a log filter to attach an event listener to the subscription object: * the filter callback — activates for each new log matching the filter. * `provider.on("error", ...)` — activates if an error is detected during the subscription. * `provider.off(filter)` — removes the listener and stops the subscription. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); // Only log transfer events from the WMATIC smart contract. const logsFilter = { address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } async function subscribeToLogs() { try { // Create a new subscription to logs matching the filter provider.on(logsFilter, handleLogs); // Attach an event listener for errors provider.on("error", handleError); console.log("Subscribed to logs"); } catch (error) { console.error(`Error subscribing to new logs: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs the filtered events async function handleLogs(log) { console.log(log) } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToLogs(); ``` ## Use case A practical use case for `subscribe("logs")` is a DApp that continuously listens for new specific event logs and isolates certain fields for analytics purposes. This is useful, for example, to only track events about a specific ERC-721 token. The following is an implementation of this concept using ethers.js subscriptions, this program will only track the `Transfer` event from the [Arbitrum Odyssey NFT](https://arbiscan.io/token/0xfAe39eC09730CA0F14262A636D2d7C5539353752#code) smart contract. The `PunkTransfer` looks like this in the smart contract: ```solidity IERC721.sol theme={"system"} event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); ``` The following code will log all of the new `Transfer` events and extract the block number. This can be useful to analyze when tokens are moved. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); // Only log Transfer events. const logsFilter = { address: '0xfAe39eC09730CA0F14262A636D2d7C5539353752', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } async function subscribeToLogs() { try { // Create a new subscription to logs matching the filter provider.on(logsFilter, handleLogs); // Attach an event listener for errors provider.on("error", handleError); console.log("Subscribed to logs"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } // Use this function to unsubscribe and exit the program function unsubscribe() { provider.off(logsFilter); console.log("Successfully unsubscribed!"); return process.exit(1) } /* Fallback functions to react to the different events */ // Event listener that logs the filtered events async function handleLogs(log) { const block = log.blockNumber console.log(`An Arbitrum NFT was transferred in this block: ${block}`) } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToLogs(); ``` This code creates a new subscription to logs matching the filter using the `provider.on` method with a log filter. This method registers a listener that is called every time a new log matching the filter is emitted. The code defines two event listener functions that are attached to the provider: `handleLogs` and `handleError`. The `handleLogs` function is called when a new log matching the filter is emitted, then extracts the block number field and logs it to the console. The `handleError` function is called when an error occurs, and it logs an error message. The code includes the `unsubscribe` function that can be implemented in the logic to unsubscribe and exit the program when a condition is met. Finally, the code calls the `subscribeToLogs` function, which creates the subscription and attaches the event listeners. When a new event log is received, the `handleLogs` function is called to extract the data and log it to the console. # subscribe ("newBlockHeaders") | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-subscribenewblockheaders Reference docs for the subscribe ("newBlockHeaders") JSON-RPC method on the Arbitrum blockchain, available via Chainstack JSON-RPC nodes. Subscription equivalent to [eth\_newBlockFilter](/reference/arbitrum-newblockfilter). `subscribe("newBlockHeaders")` allows developers to subscribe to real-time updates about new block headers on the Arbitrum blockchain; the application will receive notifications whenever a new block is added to the blockchain. The notification will include information about the new block, such as its block number, hash, and timestamp. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `newBlockHeaders` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Arbitrum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Arbitrum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Arbitrum network since the inception of the network. It measures the overall security and integrity of the Arbitrum network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/arbitrum-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `subscribe("newBlockHeaders")` code example > [!NOTE] > > Note that subscriptions require a WebSocket connection. Use the ethers.js `WebSocketProvider` to attach event listeners to the provider: * `provider.on("block", listener)` — activates for each new block header received. * `provider.on("error", listener)` — activates if an error is detected during the subscription. * `provider.off("block", listener)` — removes the listener and stops receiving new block headers. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToNewBlocks() { try { // Subscribe to the 'block' event to receive new block headers provider.on("block", handleNewBlock); // Attach an error listener to the provider provider.on("error", handleError); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that fetches and logs the received block header data async function handleNewBlock(blockNumber) { const blockHeader = await provider.getBlock(blockNumber); console.log(blockHeader); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToNewBlocks(); ``` ## Use case A practical use case for `subscribe("newBlockHeaders")` is a DApp that continuously listens for new block headers, then isolates the block number and `baseFeePerGas` for analytics purposes. The following is an implementation of this concept using ethers.js subscriptions: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); let blockCount = 0; async function subscribeToNewBlocks() { try { // Subscribe to the 'block' event to receive new block headers provider.on("block", handleNewBlock); // Attach an error listener to the provider provider.on("error", handleError); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } function unsubscribeFromNewBlocks() { provider.off("block", handleNewBlock); console.log("Successfully unsubscribed!"); return process.exit(1); } /* Fallback functions to react to the different events */ // Event listener that extracts block number and base fee per gas from the received block header data async function handleNewBlock(blockNumber) { const blockHeader = await provider.getBlock(blockNumber); const baseFeePerGas = blockHeader.baseFeePerGas; const baseFeeGwei = ethers.formatUnits(baseFeePerGas, "gwei"); blockCount++; console.log(`Block number: ${blockNumber} \n Base Fee per Gas: ${baseFeeGwei} Gwei \n`); if (blockCount === 100) { unsubscribeFromNewBlocks(); } } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToNewBlocks(); ``` This code creates a new subscription to the `block` event using the `provider.on` method on an ethers.js `WebSocketProvider`. The provider emits this event for every new block header received. The code defines two event listener functions that are attached to the provider: `handleNewBlock` and `handleError`. The `handleNewBlock` function is called when a new block header is received; it fetches the full block with `provider.getBlock`, then extracts the block number and base fee per gas, converting the latter to gwei with `ethers.formatUnits`. The `handleError` function is called when an error occurs, and it logs an error message. This code includes an `unsubscribeFromNewBlocks` function which unsubscribes from the `newBlockHeaders` subscription and quits the program using `return process.exit(1)` after it received 100 new blocks. This is to demonstrate its use. Finally, the code calls the `subscribeToNewBlocks` function, which creates the subscription and attaches the event listeners. When a new block header is received, the `handleNewBlock` function is called to extract the block data and log it to the console. # subscribe ("syncing") | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-subscribesyncing Monitor Arbitrum node sync status in real time with ethers.js. Subscribe to syncing updates to track block download progress and chain synchronization. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `logs` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `object` — the following sync object when the node is currently syncing: * `startingBlock` — the block number from which the node began syncing. * `currentBlock` — the latest block number the node has synced to. * `highestBlock` — the estimated highest block number that needs to be synced. * `pulledStates` — the number of state entries that have already been downloaded. * `knownStates` — the estimated number of state entries to be downloaded during the sync. * `boolean` — returns `False` when the node is already in sync. ## `subscribe("syncing")` code example > [!NOTE] > > Note that ethers.js subscriptions require a WebSocket connection. Use a `WebSocketProvider` and the provider event listeners to react to sync status updates: * `provider.send("eth_syncing", [])` — queries the current sync status on demand. * `provider.on("block", ...)` — activates for each new block, so you can re-check the sync status in real time. * `provider.on("error", ...)` — activates if an error is detected on the connection. * `provider.destroy()` — closes the connection and removes all listeners. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToSync() { try { // Log the initial sync status on connection const status = await provider.send("eth_syncing", []); handleConnected(status); // Re-check the sync status on every new block provider.on("block", handleSync); // React to connection errors provider.on("error", handleError); } catch (error) { console.error(`Error subscribing to sync: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs the initial sync status function handleConnected(status) { console.log(`Initial sync status: ${JSON.stringify(status)}`); } // Event listener that logs the current sync status async function handleSync() { const status = await provider.send("eth_syncing", []); console.log(status); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error: ${error}`); } subscribeToSync(); ``` ## Use case A practical use case for `subscribe("syncing")` is a DApp that continuously listens for the status of a node and notifies the developer if the node falls behind a certain number of blocks. The following is an implementation of this concept using ethers.js subscriptions, this program will leave a notification in the console if the node falls more than 100 blocks behind. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToSync() { try { // Log the initial sync status on connection handleConnected(); // Re-check the sync status on every new block provider.on("block", handleSync); // React to connection errors provider.on("error", handleError); } catch (error) { console.error(`Error: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs a message when the connection is ready function handleConnected() { console.log("Subscribed to sync status updates."); } // Event listener that compares the current and highest blocks async function handleSync() { const status = await provider.send("eth_syncing", []); // eth_syncing returns false when the node is already in sync if (status === false) { console.log("The node is in sync with the network."); return; } const currentBlock = Number(status.currentBlock); const highestBlock = Number(status.highestBlock); if (!Number.isNaN(currentBlock) && !Number.isNaN(highestBlock)) { const blocksBehind = highestBlock - currentBlock; console.log(`The node is ${blocksBehind} blocks behind the network.`); if (blocksBehind > 1000) { alert(`The node is ${blocksBehind} blocks behind the network. Please check your connection.`); } } } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToSync(); ``` This code monitors the sync status over a WebSocket connection using a `WebSocketProvider`. It listens for new blocks with `provider.on("block", ...)` and queries the current sync status on each block with `provider.send("eth_syncing", [])`. The code defines three event listener functions that are attached to the provider: `handleConnected`, `handleSync`, and `handleError`. The `handleConnected` function is called when the connection is ready, and it logs a message. The `handleSync` function is called on every new block, reads the `currentBlock` and the `highestBlock` fields from the `eth_syncing` result, and then compares them. If the node is more than 1,000 blocks behind, the user will receive an alert. The `handleError` function is called when an error occurs, and it logs an error message. Finally, the code calls the `subscribeToSync` function, which attaches the event listeners. When a new block is received, the `handleSync` function is called to query the sync status and log it to the console. # eth_syncing | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-syncing openapi/arbitrum_node_api/chain_info/eth_syncing.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method that returns an object with information about the current synchronization status of the node, or false if the node is fully synced. Arbitrum API method that returns an object with information about the current synchronization status of the node, or `false` if the node is fully synced. This method can track the progress of a node's synchronization with the Arbitrum blockchain. The returned object contains data such as the starting block, current block, and highest block of the node, allowing developers to monitor and estimate the time remaining for the node to sync fully. ## Parameters * `none` ## Response * `result` — the boolean value `false` when not syncing or a JSON object when syncing: * `startingBlock` — the block number where the synchronization process started, encoded as hexadecimal. * `currentBlock` — the block number that the node has currently processed, same as `eth_blockNumber`, encoded as hexadecimal. * `highestBlock` — the block number of the latest block in the blockchain known to the node. ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const syncStatus = async () => { const status = await provider.send("eth_syncing", []); console.log(status); }; syncStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" print (web3.eth.syncing) ``` ## Use case The `eth_syncing` method on Arbitrum can be useful for developers building applications that interact with the Arbitrum blockchain. For example, you may want to provide feedback to your application's user about the status of the Arbitrum node the application is connecting to. Here is a code example using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function syncstatus() { try { // Query the sync status const syncing = await provider.send("eth_syncing", []); if (syncing) { console.log(`Node is syncing. Current block: ${syncing.currentBlock}. Highest block: ${syncing.highestBlock}`); } else { console.log('Node is fully synced.'); } } catch (error) { console.error(error); } }; syncstatus() ``` In this example, the program creates a new `ethers.js` provider and connects to the node. Then, it calls `eth_syncing` to get the current synchronization status of the node. If the node is fully synced, the function logs a message to the console indicating that the node is fully synced. If the node is still syncing, the function logs the current block number and the highest block number known to the node. # debug_traceTransaction with stylusTracer Source: https://docs.chainstack.com/reference/arbitrum-tracetransaction Arbitrum API method that provides a native tracer called stylusTracer for debugging Stylus contracts. Chainstack Chainstack reference. Arbitrum API method that provides a native tracer called `stylusTracer` for debugging Stylus contracts. This method returns detailed execution traces of transactions involving Stylus contracts by capturing metadata for each HostIO (calls the WasmVM makes to read and write data in the EVM). Combined with the contract's source code, this gives developers complete visibility into transaction execution for debugging purposes. > [!NOTE] > > **Available on customized Dedicated Nodes only** > > Custom JavaScript tracers are available as customized solutions on the [Enterprise plan](https://chainstack.com/pricing/) on [Dedicated Nodes](/docs/dedicated-node). ## Parameters * `hash` — the hash identifying a transaction. * `object` — (optional) an object identifying the type of tracer and its configuration: * `stylusTracer` — tracer that captures metadata for each HostIO executed during a Stylus transaction. ## JSON-RPC example ```shell theme={"system"} curl https://arbitrum-mainnet.core.chainstack.com/d44bf5f1b27688507d0ccd50bf263e83 \ -X POST \ -H "Content-Type: application/json" \ --data '{"method":"debug_traceTransaction","params":["0x3c9329b765d88c03d18f87dc1f2b5138c89d9a415d5c73dc7827b87aca176c8e", {"tracer": "stylusTracer"}], "id":1,"jsonrpc":"2.0"}' ``` ## Response types ### `stylusTracer` response * `object` — the `stylusTracer` traces object containing an array of HostIO metadata: * `name` — name of the executed HostIO. * `args` — arguments of the HostIO encoded as hex. * `outs` — outputs of the HostIO encoded as hex. * `startInk` — amount of Ink before executing the HostIO. * `endInk` — amount of Ink after executing the HostIO. * `address` — for \*call HostIOs, the address of the called contract. * `steps` — for \*call HostIOs, the steps performed by the called contract. ## Use case A key use case for the `debug_traceTransaction` method with `stylusTracer` is debugging failed Stylus contract transactions. The tracer provides detailed information about each HostIO executed during the transaction, allowing developers to: 1. Track the exact sequence of operations 2. Monitor Ink consumption 3. Identify where failures occurred 4. Analyze interactions with other contracts This data can be used with the contract source code to understand transaction behavior and diagnose issues. # Transactions info | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-transactions-rpc-methods Arbitrum transaction info JSON-RPC methods — retrieve transaction data, receipts, and status for monitoring and analyzing on-chain activity. Retrieving transaction information from the Arbitrum blockchain can be done by using the following methods: > [card] [eth\_getTransactionByHash](/reference/arbitrum-gettransactionbyhash) > [card] [eth\_getTransactionReceipt](/reference/arbitrum-gettransactionreceipt) > [card] [eth\_getTransactionByBlockHashAndIndex](/reference/arbitrum-gettransactionbyblockhashandindex) > [card] [eth\_getTransactionByBlockNumberAndIndex](/reference/arbitrum-gettransactionbyblocknumberandindex) > [card] [eth\_simulateV1](/reference/arbitrum-simulatev1) # eth_uninstallFilter | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-uninstallfilter openapi/arbitrum_node_api/filter_handling/eth_uninstallFilter.json POST /b566089e915b02b08492b7a324eb82bd Arbitrum API method used to remove a filter previously created using one of the following methods: eth_newFilter eth_newBlockFilter. Arbitrum API method used to remove a filter previously created using one of the following methods: * [eth\_newFilter](/reference/arbitrum-newfilter) * [eth\_newBlockFilter](/reference/arbitrum-newblockfilter) Upon successful execution, the filter is removed and will no longer emit events. This method can be useful for optimizing resource usage when a developer no longer needs to monitor certain events or blocks. It can also be used to clean up unused filters in order to reduce clutter in the client-side filter list. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/arbitrum-newfilter) > * [eth\_newBlockFilter](/reference/arbitrum-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `string` — the filter ID that you want to uninstall ## Response * `boolean` — a boolean value indicating whether the filter was successfully uninstalled. `true` if successfully removed, and `false` if not. ## `eth_uninstallFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/arbitrum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function removeFilter(filter) { try { const removed = await provider.send('eth_uninstallFilter', [filter]); console.log(removed); // Log whether the filter is removed } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' removeFilter(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def remove_filter(filter_id): try: removed = web3.eth.uninstall_filter(filter_id) return removed except Exception as e: print(e) filter_id = '0x0' print(remove_filter(filter_id)) ``` ## Use case One use case for `eth_uninstallFilter` is to optimize resource usage in a DApp. When a DApp needs to monitor events on the blockchain, it can create a filter using [eth\_newFilter](/reference/arbitrum-newfilter) or [eth\_newBlockFilter](/reference/arbitrum-newblockfilter) to listen for specific events or blocks. However, once the DApp no longer needs to monitor these events or blocks, it can use `eth_uninstallFilter` to stop watching for them. By doing so, the DApp can reduce the number of active filters and free up resources, such as network bandwidth and computational power, that would otherwise be used to maintain the filter. For example, let's say that a DApp is monitoring incoming blocks, logs batches of 50 blocks, and then uses `uninstallFilter` to remove the filter using. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const BLOCKS_TO_FETCH = 50; const BLOCK_FETCH_INTERVAL_MS = 200; async function getNewBlocks() { const filterId = await provider.send("eth_newBlockFilter", []); let blocks = []; while (blocks.length < BLOCKS_TO_FETCH) { const changes = await provider.send("eth_getFilterChanges", [filterId]); //console.log(changes) // show the changes if (changes.length === 0) { await new Promise(resolve => setTimeout(resolve, BLOCK_FETCH_INTERVAL_MS)); // wait for new blocks } else { blocks = blocks.concat(changes); } } const removeFilter = await provider.send("eth_uninstallFilter", [filterId]); // clean up filter if (removeFilter) { console.log(`We got 50 blocks, filter ${filterId} was removed.`) return blocks } } async function main() { const fiftyBlocks = await getNewBlocks(); console.log(fiftyBlocks) } main() ``` This code fetches the latest 50 blocks using a filter and creates an array. The `BLOCKS_TO_FETCH` constant is set to 50, which represents the number of blocks to fetch. The `BLOCK_FETCH_INTERVAL_MS` constant is set to 200, which represents the time interval (in milliseconds) to wait for new blocks to arrive. The `getNewBlocks` function is defined to fetch the blocks. This function uses a loop to retrieve new blocks until it has fetched the desired number of blocks. The loop waits for new blocks to arrive for a certain amount of time before trying again. Inside the `getNewBlocks` function, the [eth\_newBlockFilter](/reference/arbitrum-newblockfilter) JSON-RPC method is called to create a new filter to watch for new blocks. The filter ID is returned and stored in the `filterId` variable. An array stores the blocks fetched. Inside the loop, the [eth\_getFilterChanges](/reference/arbitrum-getfilterchanges) JSON-RPC method is called to retrieve any new blocks that have arrived since the filter was created. If there are no new blocks, the code waits a certain amount of time before trying again. If there are new blocks, they are stored in the `blocks` array. Once the `blocks` array has reached the `BLOCKS_TO_FETCH` limit, the filter is uninstalled using the `eth_uninstallFilter` JSON-RPC method. The `removeFilter` variable is set to true if the filter is successfully removed. If the filter is removed successfully, a message is logged to the console indicating that `BLOCKS_TO_FETCH` blocks have been fetched and the filter was removed. The blocks array is returned as the result of the `getNewBlocks` function. The main function is defined to call the `getNewBlocks` function and store the result in the `fiftyBlocks` variable. The `fiftyBlocks` variable is then logged to the console. # Subscriptions | Arbitrum Source: https://docs.chainstack.com/reference/arbitrum-web3js-subscriptions-methods Arbitrum web3.js subscription methods — subscribe to new block headers, contract events, and other real-time blockchain updates via WebSocket. > [!NOTE] > > Note that all subscriptions require a WebSocket connection. ## Native EVM subscriptions Developers can use the following native methods to subscribe to events: > [card] [eth\_subscribe('newHeads')](/reference/arbitrum-native-subscribe-newheads) > [card] [eth\_subscribe('logs')](/reference/arbitrum-native-subscribe-logs) The subscriptions created can be cleared using the following method: > [card] [eth\_unsubscribe](/reference/arbitrum-native-unsubscribe) > [!NOTE] > > ### Arbitrum does not have pending transactions > > Note that [Arbitrum does not have pending transactions](https://support.arbitrum.io/hc/en-us/articles/4411923413531-Pending-transaction) as transactions on Arbitrum are instantly approved or declined by the sequencer. ## web3.js subscriptions The [web3.js](https://web3js.readthedocs.io/en/v1.2.11/getting-started.html) library also offers subscription methods. web3.js subscriptions are the successors of the deprecated `web3.eth.filter` methods and provide a way for developers to receive real-time updates about events on the blockchain without constantly polling the blockchain for new data. Subscriptions can be created using one of the following methods: > [card] [subscribe('newBlockHeaders')](/reference/arbitrum-subscribenewblockheaders) > [card] [subscribe('logs')](/reference/arbitrum-subscribelogs) > [card] [subscribe('syncing')](/reference/arbitrum-subscribesyncing) The subscriptions created can be cleared using the following method: > [card] [clearSubscriptions](/reference/arbitrum-clearsubscriptions) # Accounts info | Arc Source: https://docs.chainstack.com/reference/arc-accounts-info-rpc-methods Arc account information JSON-RPC methods — retrieve balances, nonces, storage values, contract bytecode, and Merkle proofs for addresses on Arc. Retrieving account information from Arc can be done using the following methods: > [card] [eth\_getAccount](/reference/arc-eth-getaccount) > [card] [eth\_getBalance](/reference/arc-eth-getbalance) > [card] [eth\_getCode](/reference/arc-eth-getcode) > [card] [eth\_getProof](/reference/arc-eth-getproof) > [card] [eth\_getStorageAt](/reference/arc-eth-getstorageat) > [card] [eth\_getTransactionCount](/reference/arc-eth-gettransactioncount) # Blocks info | Arc Source: https://docs.chainstack.com/reference/arc-blocks-info-rpc-methods Arc block JSON-RPC methods — retrieve blocks by number or hash, block headers, receipts, and transaction counts on Arc. Retrieving block data from Arc can be done using the following methods: > [card] [eth\_blockNumber](/reference/arc-eth-blocknumber) > [card] [eth\_getBlockByHash](/reference/arc-eth-getblockbyhash) > [card] [eth\_getBlockByNumber](/reference/arc-eth-getblockbynumber) > [card] [eth\_getBlockReceipts](/reference/arc-eth-getblockreceipts) > [card] [eth\_getBlockTransactionCountByHash](/reference/arc-eth-getblocktransactioncountbyhash) > [card] [eth\_getBlockTransactionCountByNumber](/reference/arc-eth-getblocktransactioncountbynumber) > [card] [eth\_getHeaderByHash](/reference/arc-eth-getheaderbyhash) > [card] [eth\_getHeaderByNumber](/reference/arc-eth-getheaderbynumber) > [card] [eth\_getUncleByBlockHashAndIndex](/reference/arc-eth-getunclebyblockhashandindex) > [card] [eth\_getUncleByBlockNumberAndIndex](/reference/arc-eth-getunclebyblocknumberandindex) > [card] [eth\_getUncleCountByBlockHash](/reference/arc-eth-getunclecountbyblockhash) > [card] [eth\_getUncleCountByBlockNumber](/reference/arc-eth-getunclecountbyblocknumber) # Chain info | Arc Source: https://docs.chainstack.com/reference/arc-chain-info-rpc-methods Arc chain JSON-RPC methods — read the chain ID, network version, sync status, and the namespaces the node exposes on Arc. Retrieving chain-level information from Arc can be done using the following methods: > [card] [eth\_accounts](/reference/arc-eth-accounts) > [card] [eth\_chainId](/reference/arc-eth-chainid) > [card] [eth\_hashrate](/reference/arc-eth-hashrate) > [card] [eth\_protocolVersion](/reference/arc-eth-protocolversion) > [card] [eth\_syncing](/reference/arc-eth-syncing) > [card] [rpc\_modules](/reference/arc-rpc-modules) # Client info | Arc Source: https://docs.chainstack.com/reference/arc-client-info-rpc-methods Arc client JSON-RPC methods — read the client version, protocol version, and peer information on Arc. Retrieving client information from an Arc node can be done using the following methods: > [card] [net\_listening](/reference/arc-net-listening) > [card] [net\_peerCount](/reference/arc-net-peercount) > [card] [net\_version](/reference/arc-net-version) > [card] [web3\_clientVersion](/reference/arc-web3-clientversion) > [card] [web3\_sha3](/reference/arc-web3-sha3) # Debug and trace | Arc Source: https://docs.chainstack.com/reference/arc-debug-and-trace-rpc-methods Arc debug and trace JSON-RPC methods — trace transactions, blocks, and calls with the debug and Parity-style trace namespaces on Arc. Arc runs the Reth client, which exposes both the `debug_*` and the Parity-style `trace_*` namespaces: > [card] [debug\_getBadBlocks](/reference/arc-debug-getbadblocks) > [card] [debug\_getRawBlock](/reference/arc-debug-getrawblock) > [card] [debug\_getRawHeader](/reference/arc-debug-getrawheader) > [card] [debug\_getRawReceipts](/reference/arc-debug-getrawreceipts) > [card] [debug\_getRawTransaction](/reference/arc-debug-getrawtransaction) > [card] [debug\_traceBlockByHash](/reference/arc-debug-traceblockbyhash) > [card] [debug\_traceBlockByNumber](/reference/arc-debug-traceblockbynumber) > [card] [debug\_traceCall](/reference/arc-debug-tracecall) > [card] [debug\_traceTransaction](/reference/arc-debug-tracetransaction) > [card] [trace\_block](/reference/arc-trace-block) > [card] [trace\_call](/reference/arc-trace-call) > [card] [trace\_callMany](/reference/arc-trace-callmany) > [card] [trace\_filter](/reference/arc-trace-filter) > [card] [trace\_get](/reference/arc-trace-get) > [card] [trace\_rawTransaction](/reference/arc-trace-rawtransaction) > [card] [trace\_replayBlockTransactions](/reference/arc-trace-replayblocktransactions) > [card] [trace\_replayTransaction](/reference/arc-trace-replaytransaction) > [card] [trace\_transaction](/reference/arc-trace-transaction) # debug_getBadBlocks | Arc Source: https://docs.chainstack.com/reference/arc-debug-getbadblocks openapi/arc_node_api/debug_and_trace/debug_getBadBlocks.json POST / Arc API method that returns a list of invalid blocks that the node has encountered. Useful for debugging consensus or block validation issues. Arc API method that returns a list of invalid blocks that the node has encountered. Useful for debugging consensus or block validation issues. ## Parameters * `none` ## Response * `result` — an array of bad block objects that the node has encountered. Each object contains the full block data and the reason it was rejected. ## `debug_getBadBlocks` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_getBadBlocks", "params": [], "id": 1}' ``` # debug_getRawBlock | Arc Source: https://docs.chainstack.com/reference/arc-debug-getrawblock openapi/arc_node_api/debug_and_trace/debug_getRawBlock.json POST / Arc API method that returns the RLP-encoded block data for a given block number or tag. debug_getRawBlock on Arc via Chainstack. Arc API method that returns the RLP-encoded block data for a given block number or tag. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the RLP-encoded block data as a hex string. ## `debug_getRawBlock` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_getRawBlock", "params": ["latest"], "id": 1}' ``` # debug_getRawHeader | Arc Source: https://docs.chainstack.com/reference/arc-debug-getrawheader openapi/arc_node_api/debug_and_trace/debug_getRawHeader.json POST / Arc API method that returns the RLP-encoded block header for a given block number or tag. debug_getRawHeader on Arc via Chainstack. Arc API method that returns the RLP-encoded block header for a given block number or tag. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the RLP-encoded block header as a hex string. ## `debug_getRawHeader` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_getRawHeader", "params": ["latest"], "id": 1}' ``` # debug_getRawReceipts | Arc Source: https://docs.chainstack.com/reference/arc-debug-getrawreceipts openapi/arc_node_api/debug_and_trace/debug_getRawReceipts.json POST / Arc API method that returns the RLP-encoded transaction receipts for a given block. debug_getRawReceipts on Arc via Chainstack. Arc API method that returns the RLP-encoded transaction receipts for a given block. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — an array of RLP-encoded transaction receipts as hex strings. ## `debug_getRawReceipts` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_getRawReceipts", "params": ["latest"], "id": 1}' ``` # debug_getRawTransaction | Arc Source: https://docs.chainstack.com/reference/arc-debug-getrawtransaction openapi/arc_node_api/debug_and_trace/debug_getRawTransaction.json POST / Arc API method that returns the RLP-encoded transaction data for a given transaction hash. transactionHash — the hash of the transaction. Arc API method that returns the RLP-encoded transaction data for a given transaction hash. ## Parameters * `transactionHash` — the hash of the transaction ## Response * `result` — the RLP-encoded transaction data as a hex string, or `null` if the transaction is not found. ## `debug_getRawTransaction` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_getRawTransaction", "params": ["0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2"], "id": 1}' ``` # debug_traceBlockByHash | Arc Source: https://docs.chainstack.com/reference/arc-debug-traceblockbyhash openapi/arc_node_api/debug_and_trace/debug_traceBlockByHash.json POST / Arc API method that returns detailed traces for all transactions in a block specified by block hash. Chainstack Arc reference. Arc API method that returns detailed traces for all transactions in a block specified by block hash. This is useful for analyzing all contract interactions within a specific block. ## Parameters * `blockHash` — the hash of the block to trace * `tracerConfig` — (optional) tracer configuration object: * `tracer` — tracer type (e.g., `callTracer`, `prestateTracer`) * `timeout` — (optional) timeout for the trace * `tracerConfig` — (optional) tracer-specific configuration ## Response * `result` — array of trace objects, one for each transaction in the block. The format depends on the tracer used. For `callTracer`, each trace contains: * `from` — sender address * `to` — recipient address * `gas` — gas provided * `gasUsed` — gas consumed * `input` — call data * `output` — return data * `calls` — nested array of internal calls * `type` — call type (CALL, DELEGATECALL, etc.) ## `debug_traceBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByHash = async (blockHash) => { const traces = await provider.send("debug_traceBlockByHash", [ blockHash, { tracer: "callTracer" } ]); console.log(`Block contains ${traces.length} transaction traces`); for (let i = 0; i < traces.length; i++) { const trace = traces[i].result; console.log(`\nTx ${i}: ${trace.from} -> ${trace.to}`); console.log(` Type: ${trace.type}, Gas Used: ${trace.gasUsed}`); } }; traceBlockByHash("0xa86891cc8ae2e3efe67b21e24f8f852e76a0875892e9e8c9a4c77aab8e2761cc"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceBlockByHash", [ "0xa86891cc8ae2e3efe67b21e24f8f852e76a0875892e9e8c9a4c77aab8e2761cc", {"tracer": "callTracer"} ]) traces = result['result'] print(f"Block contains {len(traces)} transaction traces") for i, tx_trace in enumerate(traces): trace = tx_trace['result'] print(f"\nTx {i}: {trace['from']} -> {trace.get('to', 'Contract Creation')}") print(f" Type: {trace['type']}, Gas Used: {trace['gasUsed']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceBlockByHash", "params": ["0xa86891cc8ae2e3efe67b21e24f8f852e76a0875892e9e8c9a4c77aab8e2761cc", {"tracer": "callTracer"}], "id": 1 }' ``` # debug_traceBlockByNumber | Arc Source: https://docs.chainstack.com/reference/arc-debug-traceblockbynumber openapi/arc_node_api/debug_and_trace/debug_traceBlockByNumber.json POST / Arc API method that returns detailed traces for all transactions in a block specified by block number. Chainstack Arc reference. Arc API method that returns detailed traces for all transactions in a block specified by block number. This is useful for analyzing all contract interactions within a specific block. ## Parameters * `blockNumber` — the block number (hex) or tag (`latest`, `earliest`, `pending`) * `tracerConfig` — (optional) tracer configuration object: * `tracer` — tracer type (e.g., `callTracer`, `prestateTracer`) * `timeout` — (optional) timeout for the trace * `tracerConfig` — (optional) tracer-specific configuration ## Response * `result` — array of trace objects, one for each transaction in the block. The format depends on the tracer used. For `callTracer`, each trace contains: * `from` — sender address * `to` — recipient address * `gas` — gas provided * `gasUsed` — gas consumed * `input` — call data * `output` — return data * `calls` — nested array of internal calls * `type` — call type (CALL, DELEGATECALL, etc.) ## `debug_traceBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlock = async (blockNumber) => { const traces = await provider.send("debug_traceBlockByNumber", [ blockNumber, { tracer: "callTracer" } ]); console.log(`Block contains ${traces.length} transaction traces`); for (let i = 0; i < traces.length; i++) { const trace = traces[i].result; console.log(`\nTx ${i}: ${trace.from} -> ${trace.to}`); console.log(` Type: ${trace.type}, Gas Used: ${trace.gasUsed}`); if (trace.calls) { console.log(` Internal calls: ${trace.calls.length}`); } } }; traceBlock("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceBlockByNumber", [ "latest", {"tracer": "callTracer"} ]) traces = result['result'] print(f"Block contains {len(traces)} transaction traces") for i, tx_trace in enumerate(traces): trace = tx_trace['result'] print(f"\nTx {i}: {trace['from']} -> {trace.get('to', 'Contract Creation')}") print(f" Type: {trace['type']}, Gas Used: {trace['gasUsed']}") if 'calls' in trace: print(f" Internal calls: {len(trace['calls'])}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceBlockByNumber", "params": ["latest", {"tracer": "callTracer"}], "id": 1 }' ``` # debug_traceCall | Arc Source: https://docs.chainstack.com/reference/arc-debug-tracecall openapi/arc_node_api/debug_and_trace/debug_traceCall.json POST / Arc API method that traces the execution of a call without creating a transaction on the blockchain. Chainstack Arc reference. Arc API method that traces the execution of a call without creating a transaction on the blockchain. This is useful for debugging contract interactions before sending actual transactions. ## Parameters * `callObject` — the call object: * `from` — (optional) sender address * `to` — recipient address * `gas` — (optional) gas limit * `gasPrice` — (optional) gas price * `value` — (optional) value to send * `data` — (optional) call data * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) * `tracerConfig` — (optional) tracer configuration object: * `tracer` — tracer type (e.g., `callTracer`, `prestateTracer`) ## Response * `result` — the trace result object, format depends on the tracer used For the default tracer: * `gas` — gas used * `failed` — whether the call failed * `returnValue` — return data * `structLogs` — array of execution steps ## `debug_traceCall` code examples The following example traces a `balanceOf` call on the USDC token: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // USDC token address const USDC = "0x3600000000000000000000000000000000000000"; const TARGET_ADDRESS = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"; // Encode balanceOf(address) const iface = new ethers.Interface(["function balanceOf(address) view returns (uint256)"]); const data = iface.encodeFunctionData("balanceOf", [TARGET_ADDRESS]); const traceCall = async () => { // Using default tracer const trace = await provider.send("debug_traceCall", [ { to: USDC, data: data }, "latest", {} ]); console.log("Gas used:", trace.gas); console.log("Failed:", trace.failed); console.log("Return value:", trace.returnValue); console.log("Execution steps:", trace.structLogs.length); }; traceCall(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # USDC token address USDC = "0x3600000000000000000000000000000000000000" TARGET_ADDRESS = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294" # balanceOf(address) calldata data = "0x70a08231000000000000000000000000" + TARGET_ADDRESS[2:].lower() result = web3.provider.make_request("debug_traceCall", [ { "to": USDC, "data": data }, "latest", {} ]) trace = result['result'] print(f"Gas used: {trace['gas']}") print(f"Failed: {trace['failed']}") print(f"Return value: {trace['returnValue']}") print(f"Execution steps: {len(trace['structLogs'])}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceCall", "params": [ { "to": "0x3600000000000000000000000000000000000000", "data": "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" }, "latest", {} ], "id": 1 }' ``` # debug_traceTransaction | Arc Source: https://docs.chainstack.com/reference/arc-debug-tracetransaction openapi/arc_node_api/debug_and_trace/debug_traceTransaction.json POST / Arc API method that returns a detailed trace of a transaction's execution. Reference for debug_traceTransaction on Arc via Chainstack. Arc API method that returns a detailed trace of a transaction's execution. This is useful for debugging failed transactions or understanding contract behavior. ## Parameters * `transactionHash` — the hash of the transaction to trace * `tracerConfig` — (optional) tracer configuration object: * `tracer` — tracer type (e.g., `callTracer`, `prestateTracer`) * `timeout` — (optional) timeout for the trace ## Response * `result` — the trace result object, format depends on the tracer used ## `debug_traceTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txHash) => { const trace = await provider.send("debug_traceTransaction", [ txHash, { tracer: "callTracer" } ]); console.log(JSON.stringify(trace, null, 2)); }; traceTransaction("0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceTransaction", [ "0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2", {"tracer": "callTracer"} ]) print(result['result']) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceTransaction", "params": ["0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2", {"tracer": "callTracer"}], "id": 1 }' ``` # eth_accounts | Arc Source: https://docs.chainstack.com/reference/arc-eth-accounts openapi/arc_node_api/chain_info/eth_accounts.json POST / Arc API method that returns a list of addresses owned by the node. Since Arc nodes do not manage private keys, this always returns an empty array. Arc API method that returns a list of addresses owned by the node. Since Arc nodes do not manage private keys, this always returns an empty array. ## Parameters * `none` ## Response * `result` — an array of addresses owned by the node. On Arc, this always returns an empty array since the node does not manage accounts. ## `eth_accounts` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}' ``` # eth_blobBaseFee | Arc Source: https://docs.chainstack.com/reference/arc-eth-blobbasefee openapi/arc_node_api/gas_data/eth_blobBaseFee.json POST / Arc API method that returns the current blob base fee per gas in wei. Arc inherits this from the EIP-4844 specification. On Arc. Arc API method that returns the current blob base fee per gas in wei. Arc inherits this from the EIP-4844 specification. ## Parameters * `none` ## Response * `result` — the current blob base fee in wei, encoded as hexadecimal. ## `eth_blobBaseFee` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_blobBaseFee", "params": [], "id": 1}' ``` # eth_blockNumber | Arc Source: https://docs.chainstack.com/reference/arc-eth-blocknumber openapi/arc_node_api/blocks_info/eth_blockNumber.json POST / Arc API method that returns the latest block number of the blockchain. Arc developers can call eth_blockNumber via Chainstack. Arc API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. Arc produces blocks approximately every 0.5 seconds with sub-second finality using Simplex Consensus. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number encoded as hexadecimal. ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockNumber = async () => { const block_Number = await provider.getBlockNumber(); console.log(block_Number); }; eth_getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.block_number) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1}' ``` ## Use case Arc has sub-second block times (\~0.5 seconds), making it ideal for applications that need to track real-time payment status. A DApp can use the current block number to determine when a payment transaction has been finalized. # eth_call | Arc Source: https://docs.chainstack.com/reference/arc-eth-call openapi/arc_node_api/execute_transactions/eth_call.json POST / Arc API method that executes a new message call immediately without creating a transaction on the blockchain. Arc via Chainstack. Arc API method that executes a new message call immediately without creating a transaction on the blockchain. This is commonly used to read data from smart contracts, such as ERC-20 token balances. ## Parameters * `callObject` — the call object: * `from` — (optional) address the call is sent from * `to` — the address the call is directed to * `gas` — (optional) gas provided for the call * `gasPrice` — (optional) gas price for the call * `value` — (optional) value sent with the call * `data` — (optional) hash of the method signature and encoded parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `result` — the return value of the executed contract call ## `eth_call` code examples The following example queries the `balanceOf` function on the USDC ERC-20 token contract: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // USDC token address const USDC = "0x3600000000000000000000000000000000000000"; const getBalance = async (address) => { const abi = ["function balanceOf(address) view returns (uint256)"]; const token = new ethers.Contract(USDC, abi, provider); const balance = await token.balanceOf(address); // USDC uses 6 decimals console.log(`USDC balance: ${ethers.formatUnits(balance, 6)}`); }; getBalance("0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # USDC token address USDC = "0x3600000000000000000000000000000000000000" # balanceOf function signature: keccak256("balanceOf(address)")[:4] # Plus address padded to 32 bytes address = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294" data = "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" result = web3.eth.call({ 'to': USDC, 'data': data }) balance = int(result.hex(), 16) / 10**6 # USDC uses 6 decimals print(f"USDC balance: {balance}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_call", "params": [ { "to": "0x3600000000000000000000000000000000000000", "data": "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" }, "latest" ], "id": 1 }' ``` # eth_chainId | Arc Source: https://docs.chainstack.com/reference/arc-eth-chainid openapi/arc_node_api/chain_info/eth_chainId.json POST / Arc API method that returns the chain ID of the current network. Arc Mainnet is 5042 (0x13b2 in hexadecimal) and Arc Testnet is 5042002 (0x4cef52). Arc API method that returns the chain ID of the current network. Arc Mainnet is `5042` (`0x13b2` in hexadecimal) and Arc Testnet is `5042002` (`0x4cef52`). ## Parameters * `none` ## Response * `result` — the chain ID encoded as hexadecimal (`0x13b2` for Arc Mainnet, `0x4cef52` for Arc Testnet) ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getChainId = async () => { const network = await provider.getNetwork(); console.log(`Chain ID: ${network.chainId}`); }; getChainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.chain_id) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_chainId", "params": [], "id": 1}' ``` # eth_createAccessList | Arc Source: https://docs.chainstack.com/reference/arc-eth-createaccesslist openapi/arc_node_api/execute_transactions/eth_createAccessList.json POST / Arc API method that creates an EIP-2930 access list for a transaction. The access list identifies storage slots and addresses the transaction will access. Arc API method that creates an EIP-2930 access list for a transaction. The access list identifies storage slots and addresses the transaction will access, which can be used to optimize gas costs. ## Parameters * `transactionObject` — the transaction call object: * `from` — (optional) sender address * `to` — recipient address * `gas` — (optional) gas limit * `gasPrice` — (optional) gas price * `value` — (optional) value to send * `data` — (optional) call data * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — an object containing: * `accessList` — an array of access list entries (address and storage keys) * `gasUsed` — the gas used by the transaction with the access list applied ## `eth_createAccessList` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_createAccessList", "params": [{"to": "0x3600000000000000000000000000000000000000", "data": "0x95d89b41"}, "latest"], "id": 1}' ``` # eth_estimateGas | Arc Source: https://docs.chainstack.com/reference/arc-eth-estimategas openapi/arc_node_api/execute_transactions/eth_estimateGas.json POST / Arc API method that estimates the gas needed to execute a transaction. Arc pays gas in USDC, its native currency, denominated in wei with 18 decimals. Arc API method that estimates the gas needed to execute a transaction. Arc pays gas in USDC, which is the native currency of the chain, so costs are denominated in wei with 18 decimals exactly as on Ethereum. ## Parameters * `callObject` — the call object: * `from` — (optional) address the transaction is sent from * `to` — (optional) address the transaction is directed to * `gas` — (optional) gas provided for the transaction * `gasPrice` — (optional) gas price * `value` — (optional) value sent with the transaction * `data` — (optional) hash of the method signature and encoded parameters ## Response * `result` — the estimated gas amount encoded as hexadecimal ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimateGas = async () => { const gas = await provider.estimateGas({ from: "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294", to: "0x3600000000000000000000000000000000000000", data: "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" }); console.log(`Estimated gas: ${gas}`); }; estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) gas = web3.eth.estimate_gas({ 'from': "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294", 'to': "0x3600000000000000000000000000000000000000", 'data': "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" }) print(f"Estimated gas: {gas}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_estimateGas", "params": [{"from": "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294", "to": "0x3600000000000000000000000000000000000000", "data": "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294"}], "id": 1 }' ``` # eth_feeHistory | Arc Source: https://docs.chainstack.com/reference/arc-eth-feehistory openapi/arc_node_api/gas_data/eth_feeHistory.json POST / Arc API method that returns historical gas information for a range of blocks. Reference for eth_feeHistory on Arc via Chainstack. Arc API method that returns historical gas information for a range of blocks. This is useful for estimating appropriate gas fees based on recent network activity. ## Parameters * `blockCount` — number of blocks to return (hex or integer, max 1024) * `newestBlock` — highest block number or tag (`latest`, `pending`) * `rewardPercentiles` — (optional) array of percentiles to calculate priority fees ## Response * `result` — the fee history object: * `baseFeePerGas` — array of base fees for each block (plus next block) * `gasUsedRatio` — array of gas used ratios for each block * `oldestBlock` — the oldest block number in the range * `reward` — (optional) array of priority fee percentiles for each block ## `eth_feeHistory` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getFeeHistory = async () => { // Get fee history for last 10 blocks with 25th, 50th, 75th percentiles const feeHistory = await provider.send("eth_feeHistory", [ "0xa", "latest", [25, 50, 75] ]); console.log(`Oldest block: ${parseInt(feeHistory.oldestBlock, 16)}`); console.log(`\nBase fees (gwei):`); for (let i = 0; i < feeHistory.baseFeePerGas.length; i++) { const baseFee = ethers.formatUnits(feeHistory.baseFeePerGas[i], "gwei"); const gasRatio = feeHistory.gasUsedRatio[i] ? (feeHistory.gasUsedRatio[i] * 100).toFixed(2) : "N/A"; console.log(` Block +${i}: ${baseFee} gwei (${gasRatio}% full)`); } if (feeHistory.reward) { console.log(`\nPriority fee percentiles for latest block (gwei):`); const latest = feeHistory.reward[feeHistory.reward.length - 1]; console.log(` 25th: ${ethers.formatUnits(latest[0], "gwei")}`); console.log(` 50th: ${ethers.formatUnits(latest[1], "gwei")}`); console.log(` 75th: ${ethers.formatUnits(latest[2], "gwei")}`); } }; getFeeHistory(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Get fee history for last 10 blocks result = web3.provider.make_request("eth_feeHistory", [ "0xa", "latest", [25, 50, 75] ]) fee_history = result['result'] oldest = int(fee_history['oldestBlock'], 16) print(f"Oldest block: {oldest}") print("\nBase fees (gwei):") for i, base_fee in enumerate(fee_history['baseFeePerGas']): base_fee_gwei = web3.from_wei(int(base_fee, 16), 'gwei') gas_ratio = fee_history['gasUsedRatio'][i] * 100 if i < len(fee_history['gasUsedRatio']) else 'N/A' print(f" Block +{i}: {base_fee_gwei} gwei ({gas_ratio:.2f}% full)" if isinstance(gas_ratio, float) else f" Block +{i}: {base_fee_gwei} gwei") if fee_history.get('reward'): print("\nPriority fee percentiles for latest block (gwei):") latest = fee_history['reward'][-1] print(f" 25th: {web3.from_wei(int(latest[0], 16), 'gwei')}") print(f" 50th: {web3.from_wei(int(latest[1], 16), 'gwei')}") print(f" 75th: {web3.from_wei(int(latest[2], 16), 'gwei')}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_feeHistory", "params": ["0xa", "latest", [25, 50, 75]], "id": 1 }' ``` # eth_gasPrice | Arc Source: https://docs.chainstack.com/reference/arc-eth-gasprice openapi/arc_node_api/gas_data/eth_gasPrice.json POST / Arc API method that returns the current gas price in wei. Arc pays gas in USDC, its native currency, denominated in wei with 18 decimals. Arc API method that returns the current gas price in wei. Arc pays gas in USDC, which is the native currency of the chain, so the price is denominated in wei with 18 decimals exactly as on Ethereum. ## Parameters * `none` ## Response * `result` — the current gas price in wei encoded as hexadecimal ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getGasPrice = async () => { const feeData = await provider.getFeeData(); console.log(`Gas price: ${feeData.gasPrice} wei`); }; getGasPrice(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.gas_price) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_gasPrice", "params": [], "id": 1}' ``` # eth_getAccount | Arc Source: https://docs.chainstack.com/reference/arc-eth-getaccount openapi/arc_node_api/accounts_info/eth_getAccount.json POST / Arc API method that returns the account information for a given address. Reference for eth_getAccount on Arc via Chainstack. Arc API method that returns the account information for a given address. ## Parameters * `address` — the address of the account * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — account object with balance, nonce, codeHash, and storageRoot ## `eth_getAccount` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getAccount", [ "0x5d20879655df3c1e04ab111af0009ad650e762f0", "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getAccount", "params": ["0x5d20879655df3c1e04ab111af0009ad650e762f0", "latest"], "id": 1 }' ``` # eth_getBalance | Arc Source: https://docs.chainstack.com/reference/arc-eth-getbalance openapi/arc_node_api/accounts_info/eth_getBalance.json POST / Arc API method that returns the native USDC gas balance of an account, in wei. Arc's native currency carries 18 decimals, not the 6 of the USDC ERC-20 token. Arc API method that returns the balance of an account in wei. On Arc the native currency is USDC, which is what pays for gas, so this method returns the account's spendable gas balance. > [!WARNING] > > Arc's native currency carries **18 decimals**, not the 6 that the USDC ERC-20 token uses. Divide by 1018, or you will be out by a factor of 1012. See [Arc tooling](/docs/arc-tooling) for the library helpers. ## Parameters * `address` — the address to check the balance for * `blockParameter` — the block number in hexadecimal, or a tag: `latest`, `earliest`, `safe`, `finalized`, or `pending` ## Response * `result` — the account balance in wei, encoded as hexadecimal Divide the result by 1018 to get the balance in USDC. `ethers.formatEther` and `web3.from_wei(..., "ether")` both apply that conversion, so use them rather than the 6-decimal helpers you would reach for with the USDC ERC-20 token. ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getBalance = async (address) => { const balance = await provider.getBalance(address); console.log(`Balance: ${ethers.formatEther(balance)} USDC`); }; getBalance("0x19d7CF8eA0CE468417d1edA365a222a9dCBC7D27"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) address = Web3.to_checksum_address("0x19d7cf8ea0ce468417d1eda365a222a9dcbc7d27") balance = web3.eth.get_balance(address) print(f"Balance: {web3.from_wei(balance, 'ether')} USDC") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getBalance", "params": ["0x19d7CF8eA0CE468417d1edA365a222a9dCBC7D27", "latest"], "id": 1}' ``` > [!NOTE] > > web3.py rejects lowercase addresses with `InvalidAddress`. Wrap any address you did not get from the library itself in `Web3.to_checksum_address()`. ## Checking ERC-20 token balances `eth_getBalance` only reports the native USDC gas balance. To read an ERC-20 token balance, call the token contract's `balanceOf` function with [`eth_call`](/reference/arc-eth-call) and apply that token's own decimals. # eth_getBlockByHash | Arc Source: https://docs.chainstack.com/reference/arc-eth-getblockbyhash openapi/arc_node_api/blocks_info/eth_getBlockByHash.json POST / Arc API method that returns information about a block specified by its hash. Reference for eth_getBlockByHash on Arc via Chainstack. Arc API method that returns information about a block specified by its hash. This method can return full transaction objects or just transaction hashes depending on the second parameter. Arc blocks include additional fields not found in standard Ethereum blocks: `mainBlockGeneralGasLimit`, `sharedGasLimit`, and `timestampMillisPart` for sub-second precision. ## Parameters * `blockHash` — the hash of the block to retrieve * `fullTransactions` — if `true`, returns full transaction objects; if `false`, returns only transaction hashes ## Response * `result` — a block object, or `null` if no block was found: * `number` — the block number * `hash` — the block hash * `parentHash` — hash of the parent block * `timestamp` — block timestamp in seconds * `timestampMillisPart` — sub-second timestamp (0-999 ms), Arc-specific * `transactions` — array of transaction objects or hashes * Additional Arc fields for payment lanes gas limits ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getBlockByHash = async () => { const block = await provider.getBlock("0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9"); console.log(block); }; getBlockByHash(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block = web3.eth.get_block("0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9") print(block) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getBlockByHash", "params": ["0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9", false], "id": 1}' ``` # eth_getBlockByNumber | Arc Source: https://docs.chainstack.com/reference/arc-eth-getblockbynumber openapi/arc_node_api/blocks_info/eth_getBlockByNumber.json POST / Arc API method that returns information about a block specified by its number or tag. eth_getBlockByNumber on Arc via Chainstack. Arc API method that returns information about a block specified by its number or tag. This method can return full transaction objects or just transaction hashes depending on the second parameter. Arc blocks include additional fields not found in standard Ethereum blocks: `mainBlockGeneralGasLimit`, `sharedGasLimit`, and `timestampMillisPart` for sub-second precision. ## Parameters * `blockNumber` — the block number (hex) or tag (`latest`, `earliest`, `pending`) * `fullTransactions` — if `true`, returns full transaction objects; if `false`, returns only transaction hashes ## Response * `result` — a block object, or `null` if no block was found: * `number` — the block number * `hash` — the block hash * `parentHash` — hash of the parent block * `timestamp` — block timestamp in seconds * `timestampMillisPart` — sub-second timestamp (0-999 ms), Arc-specific * `transactions` — array of transaction objects or hashes * Additional Arc fields for payment lanes gas limits ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getBlockByNumber = async () => { const block = await provider.getBlock("latest"); console.log(block); }; getBlockByNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block = web3.eth.get_block("latest") print(block) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getBlockByNumber", "params": ["latest", false], "id": 1}' ``` # eth_getBlockReceipts | Arc Source: https://docs.chainstack.com/reference/arc-eth-getblockreceipts openapi/arc_node_api/blocks_info/eth_getBlockReceipts.json POST / Arc API method that returns all transaction receipts for a given block. Reference for eth_getBlockReceipts on Arc via Chainstack. Arc API method that returns all transaction receipts for a given block. This is more efficient than fetching receipts individually when you need all receipts from a block. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — array of receipt objects for all transactions in the block: * `transactionHash` — hash of the transaction * `transactionIndex` — index of the transaction in the block * `blockHash` — hash of the block * `blockNumber` — block number * `from` — sender address * `to` — recipient address * `cumulativeGasUsed` — total gas used in the block up to this transaction * `gasUsed` — gas used by this transaction * `contractAddress` — contract address if this was a deployment * `logs` — array of log objects * `logsBloom` — bloom filter for logs * `status` — `0x1` for success, `0x0` for failure * `effectiveGasPrice` — actual gas price paid * `type` — transaction type: `0x0` legacy, `0x1` EIP-2930, `0x2` EIP-1559, or `0x4` EIP-7702 ## `eth_getBlockReceipts` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getBlockReceipts = async (blockNumber) => { const receipts = await provider.send("eth_getBlockReceipts", [blockNumber]); console.log(`Block ${blockNumber}: ${receipts.length} transactions`); let totalGasUsed = 0n; let successCount = 0; let failCount = 0; for (const receipt of receipts) { const gasUsed = BigInt(receipt.gasUsed); totalGasUsed += gasUsed; if (receipt.status === "0x1") { successCount++; } else { failCount++; } console.log(`\nTx ${receipt.transactionIndex}: ${receipt.transactionHash}`); console.log(` Status: ${receipt.status === "0x1" ? "Success" : "Failed"}`); console.log(` Gas Used: ${gasUsed}`); console.log(` Logs: ${receipt.logs.length}`); } console.log(`\n--- Summary ---`); console.log(`Total transactions: ${receipts.length}`); console.log(`Successful: ${successCount}, Failed: ${failCount}`); console.log(`Total gas used: ${totalGasUsed}`); }; getBlockReceipts("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getBlockReceipts", ["latest"]) receipts = result['result'] print(f"Block latest: {len(receipts)} transactions") total_gas_used = 0 success_count = 0 fail_count = 0 for receipt in receipts: gas_used = int(receipt['gasUsed'], 16) total_gas_used += gas_used if receipt['status'] == "0x1": success_count += 1 else: fail_count += 1 print(f"\nTx {receipt['transactionIndex']}: {receipt['transactionHash']}") print(f" Status: {'Success' if receipt['status'] == '0x1' else 'Failed'}") print(f" Gas Used: {gas_used}") print(f" Logs: {len(receipt['logs'])}") print(f"\n--- Summary ---") print(f"Total transactions: {len(receipts)}") print(f"Successful: {success_count}, Failed: {fail_count}") print(f"Total gas used: {total_gas_used}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getBlockReceipts", "params": ["latest"], "id": 1}' ``` # eth_getBlockTransactionCountByHash | Arc Source: https://docs.chainstack.com/reference/arc-eth-getblocktransactioncountbyhash openapi/arc_node_api/blocks_info/eth_getBlockTransactionCountByHash.json POST / Arc API method that returns the number of transactions in a block specified by block hash. blockHash — the hash of the block. On Arc. Arc API method that returns the number of transactions in a block specified by block hash. ## Parameters * `blockHash` — the hash of the block ## Response * `result` — the number of transactions in the block encoded as hexadecimal ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionCount = async (blockHash) => { const count = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); console.log(`Block ${blockHash}`); console.log(`Transaction count: ${parseInt(count, 16)}`); }; getTransactionCount("0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9" result = web3.provider.make_request("eth_getBlockTransactionCountByHash", [block_hash]) count = int(result['result'], 16) print(f"Block {block_hash}") print(f"Transaction count: {count}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByHash", "params": ["0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9"], "id": 1 }' ``` # eth_getBlockTransactionCountByNumber | Arc Source: https://docs.chainstack.com/reference/arc-eth-getblocktransactioncountbynumber openapi/arc_node_api/blocks_info/eth_getBlockTransactionCountByNumber.json POST / Arc API method that returns the number of transactions in a block specified by block number. eth_getBlockTransactionCountByNumber on Arc via Chainstack. Arc API method that returns the number of transactions in a block specified by block number. ## Parameters * `blockNumber` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the number of transactions in the block encoded as hexadecimal ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionCount = async (blockNumber) => { const count = await provider.send("eth_getBlockTransactionCountByNumber", [blockNumber]); console.log(`Block ${blockNumber}: ${parseInt(count, 16)} transactions`); }; // Get transaction count for latest block getTransactionCount("latest"); // Get transaction count for a specific block getTransactionCount("0x564000"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Get transaction count for latest block result = web3.provider.make_request("eth_getBlockTransactionCountByNumber", ["latest"]) count = int(result['result'], 16) print(f"Latest block: {count} transactions") # Using web3.py method count = web3.eth.get_block_transaction_count('latest') print(f"Latest block (via web3.py): {count} transactions") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByNumber", "params": ["latest"], "id": 1}' ``` # eth_getCode | Arc Source: https://docs.chainstack.com/reference/arc-eth-getcode openapi/arc_node_api/accounts_info/eth_getCode.json POST / Arc API method that returns the bytecode at a given address. This is commonly used to verify if an address is a contract. On Arc. Arc API method that returns the bytecode at a given address. This is commonly used to verify if an address is a contract. ## Parameters * `address` — the address to get the code from * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the bytecode at the given address, or `0x` if no code exists (EOA) ## `eth_getCode` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const USDC = "0x3600000000000000000000000000000000000000"; const getCode = async () => { const code = await provider.getCode(USDC); console.log(`Is contract: ${code !== '0x'}`); }; getCode(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) USDC = "0x3600000000000000000000000000000000000000" code = web3.eth.get_code(USDC) print(f"Is contract: {code != b''}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getCode", "params": ["0x3600000000000000000000000000000000000000", "latest"], "id": 1}' ``` # eth_getFilterChanges | Arc Source: https://docs.chainstack.com/reference/arc-eth-getfilterchanges openapi/arc_node_api/filter_handling/eth_getFilterChanges.json POST / Arc API method that returns an array of logs or block/transaction hashes that occurred since the last poll. Arc via Chainstack. Arc API method that returns an array of logs or block/transaction hashes that occurred since the last poll. Used with filters created by `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. ## Parameters * `filterId` — the filter ID returned from a filter creation method ## Response * `result` — array of changes since last poll: * For log filters: array of log objects * For block filters: array of block hashes * For pending transaction filters: array of transaction hashes ## `eth_getFilterChanges` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // USDC token address const USDC = "0x3600000000000000000000000000000000000000"; const TRANSFER_TOPIC = ethers.id("Transfer(address,address,uint256)"); const pollFilter = async () => { // Create a log filter for Transfer events const filterId = await provider.send("eth_newFilter", [{ address: USDC, topics: [TRANSFER_TOPIC] }]); console.log(`Created filter: ${filterId}`); // Poll for changes every 2 seconds const interval = setInterval(async () => { const changes = await provider.send("eth_getFilterChanges", [filterId]); if (changes.length > 0) { console.log(`\nFound ${changes.length} new Transfer events:`); for (const log of changes) { console.log(` Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`); } } else { console.log("No new changes..."); } }, 2000); // Stop after 20 seconds setTimeout(async () => { clearInterval(interval); await provider.send("eth_uninstallFilter", [filterId]); console.log("\nFilter uninstalled"); }, 20000); }; pollFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 import time node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # USDC token address USDC = "0x3600000000000000000000000000000000000000" TRANSFER_TOPIC = web3.keccak(text="Transfer(address,address,uint256)").hex() # Create a log filter filter_result = web3.provider.make_request("eth_newFilter", [{ "address": USDC, "topics": [TRANSFER_TOPIC] }]) filter_id = filter_result['result'] print(f"Created filter: {filter_id}") # Poll for changes try: for _ in range(10): changes_result = web3.provider.make_request("eth_getFilterChanges", [filter_id]) changes = changes_result['result'] if changes: print(f"\nFound {len(changes)} new Transfer events:") for log in changes: print(f" Block {int(log['blockNumber'], 16)}: {log['transactionHash']}") else: print("No new changes...") time.sleep(2) finally: web3.provider.make_request("eth_uninstallFilter", [filter_id]) print("\nFilter uninstalled") ``` ```bash cURL theme={"system"} # First create a filter curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_newFilter", "params": [{"address": "0x3600000000000000000000000000000000000000"}], "id": 1 }' # Then poll for changes using the returned filter ID curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x1"], "id": 1 }' ``` # eth_getFilterLogs | Arc Source: https://docs.chainstack.com/reference/arc-eth-getfilterlogs openapi/arc_node_api/filter_handling/eth_getFilterLogs.json POST / Arc API method that returns an array of all logs matching the filter with the given ID. eth_getFilterLogs on Arc via Chainstack. Arc API method that returns an array of all logs matching the filter with the given ID. Unlike `eth_getFilterChanges`, this returns all matching logs, not just new ones since the last poll. ## Parameters * `filterId` — the filter ID returned from `eth_newFilter` ## Response * `result` — array of log objects matching the filter: * `address` — contract address that emitted the log * `topics` — array of indexed log parameters * `data` — non-indexed log parameters * `blockNumber` — block number containing the log * `blockHash` — hash of the block * `transactionHash` — hash of the transaction that emitted the log * `transactionIndex` — index of the transaction in the block * `logIndex` — position of the log in the block * `removed` — `true` if the log was removed due to a chain reorganization ## `eth_getFilterLogs` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // USDC token address const USDC = "0x3600000000000000000000000000000000000000"; const TRANSFER_TOPIC = ethers.id("Transfer(address,address,uint256)"); const getFilterLogs = async () => { // Create a log filter const filterId = await provider.send("eth_newFilter", [{ address: USDC, topics: [TRANSFER_TOPIC], fromBlock: "0x560000" }]); console.log(`Created filter: ${filterId}`); // Get all matching logs const logs = await provider.send("eth_getFilterLogs", [filterId]); console.log(`Found ${logs.length} Transfer events`); for (const log of logs.slice(0, 5)) { console.log(`\nBlock ${parseInt(log.blockNumber, 16)}:`); console.log(` Tx: ${log.transactionHash}`); console.log(` From: 0x${log.topics[1].slice(26)}`); console.log(` To: 0x${log.topics[2].slice(26)}`); } // Clean up await provider.send("eth_uninstallFilter", [filterId]); }; getFilterLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # USDC token address USDC = "0x3600000000000000000000000000000000000000" TRANSFER_TOPIC = web3.keccak(text="Transfer(address,address,uint256)").hex() # Create a log filter filter_result = web3.provider.make_request("eth_newFilter", [{ "address": USDC, "topics": [TRANSFER_TOPIC], "fromBlock": "0x560000" }]) filter_id = filter_result['result'] print(f"Created filter: {filter_id}") # Get all matching logs logs_result = web3.provider.make_request("eth_getFilterLogs", [filter_id]) logs = logs_result['result'] print(f"Found {len(logs)} Transfer events") for log in logs[:5]: print(f"\nBlock {int(log['blockNumber'], 16)}:") print(f" Tx: {log['transactionHash']}") print(f" From: 0x{log['topics'][1][26:]}") print(f" To: 0x{log['topics'][2][26:]}") # Clean up web3.provider.make_request("eth_uninstallFilter", [filter_id]) ``` ```bash cURL theme={"system"} # First create a filter curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_newFilter", "params": [{"address": "0x3600000000000000000000000000000000000000", "fromBlock": "0x560000"}], "id": 1 }' # Then get all logs using the returned filter ID curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getFilterLogs", "params": ["0x1"], "id": 1}' ``` # eth_getHeaderByHash | Arc Source: https://docs.chainstack.com/reference/arc-eth-getheaderbyhash openapi/arc_node_api/blocks_info/eth_getHeaderByHash.json POST / Arc API method that returns the block header by block hash. data — the hash of the block object — the block header object. On Arc. Arc API method that returns the block header by block hash. ## Parameters * `data` — the hash of the block ## Response * `object` — the block header object ## `eth_getHeaderByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getHeaderByHash", ["0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getHeaderByHash", "params": ["0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9"], "id": 1 }' ``` # eth_getHeaderByNumber | Arc Source: https://docs.chainstack.com/reference/arc-eth-getheaderbynumber openapi/arc_node_api/blocks_info/eth_getHeaderByNumber.json POST / Arc API method that returns the block header by block number. quantity or tag — the block number in hex format or block tag (latest, earliest, pending. Arc API method that returns the block header by block number. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — the block header object ## `eth_getHeaderByNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getHeaderByNumber", [ "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getHeaderByNumber", "params": ["0x34808ff"], "id": 1 }' ``` # eth_getLogs | Arc Source: https://docs.chainstack.com/reference/arc-eth-getlogs openapi/arc_node_api/logs_and_events/eth_getLogs.json POST / Arc API method that returns an array of logs matching a filter object. This is commonly used to track events emitted by smart contracts. Arc API method that returns an array of logs matching a filter object. This is commonly used to track events emitted by smart contracts, such as ERC-20 token transfers. ## Parameters * `filterObject` — the filter object: * `fromBlock` — (optional) block number (hex) or tag to start from * `toBlock` — (optional) block number (hex) or tag to end at * `address` — (optional) contract address or array of addresses * `topics` — (optional) array of topic filters ## Response * `result` — array of log objects: * `address` — contract address that emitted the log * `topics` — array of indexed log parameters * `data` — non-indexed log parameters * `blockNumber` — block number containing the log * `transactionHash` — hash of the transaction that emitted the log * `logIndex` — position of the log in the block ## `eth_getLogs` code examples The following example retrieves Transfer events from the USDC token: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // USDC token address const USDC = "0x3600000000000000000000000000000000000000"; // Transfer event topic const TRANSFER_TOPIC = ethers.id("Transfer(address,address,uint256)"); const getLogs = async () => { const logs = await provider.getLogs({ address: USDC, topics: [TRANSFER_TOPIC], fromBlock: "latest" }); for (const log of logs) { console.log(`Transfer in tx: ${log.transactionHash}`); } }; getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # USDC token address USDC = "0x3600000000000000000000000000000000000000" # Transfer event topic TRANSFER_TOPIC = web3.keccak(text="Transfer(address,address,uint256)").hex() logs = web3.eth.get_logs({ 'address': USDC, 'topics': [TRANSFER_TOPIC], 'fromBlock': 'latest' }) for log in logs: print(f"Transfer in tx: {log['transactionHash'].hex()}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getLogs", "params": [{ "fromBlock": "latest", "toBlock": "latest", "address": "0x3600000000000000000000000000000000000000", "topics": [] }], "id": 1 }' ``` # eth_getProof | Arc Source: https://docs.chainstack.com/reference/arc-eth-getproof openapi/arc_node_api/accounts_info/eth_getProof.json POST / Arc API method that returns the Merkle proof for an account and optionally some storage keys. Available on Arc via Chainstack. Arc API method that returns the Merkle proof for an account and optionally some storage keys. This is useful for verifying account state without trusting the node. > [!NOTE] > > On Global Nodes, this method returns a proof for the latest block only. A block number behind the tip returns `-32602` `distance to target block exceeds maximum proof window`. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). ## Parameters * `address` — the address to get the proof for * `storageKeys` — array of storage positions to prove (hex strings) * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the proof object: * `address` — the account address * `accountProof` — array of RLP-encoded Merkle Patricia trie nodes from state root to account * `balance` — the account balance * `codeHash` — the hash of the account code * `nonce` — the account nonce * `storageHash` — the hash of the storage trie root * `storageProof` — array of storage proofs, one per requested key: * `key` — the storage key * `value` — the storage value * `proof` — array of RLP-encoded trie nodes from storage root to value ## `eth_getProof` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // USDC token address const USDC = "0x3600000000000000000000000000000000000000"; const getProof = async () => { // Get proof for storage slot 0 (often totalSupply) const proof = await provider.send("eth_getProof", [ USDC, ["0x0", "0x1"], "latest" ]); console.log(`Account: ${proof.address}`); console.log(`Balance: ${proof.balance}`); console.log(`Nonce: ${parseInt(proof.nonce, 16)}`); console.log(`Code Hash: ${proof.codeHash}`); console.log(`Storage Hash: ${proof.storageHash}`); console.log(`\nAccount Proof nodes: ${proof.accountProof.length}`); for (const sp of proof.storageProof) { console.log(`\nStorage Key: ${sp.key}`); console.log(` Value: ${sp.value}`); console.log(` Proof nodes: ${sp.proof.length}`); } }; getProof(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # USDC token address USDC = "0x3600000000000000000000000000000000000000" result = web3.provider.make_request("eth_getProof", [ USDC, ["0x0", "0x1"], "latest" ]) proof = result['result'] print(f"Account: {proof['address']}") print(f"Balance: {proof['balance']}") print(f"Nonce: {int(proof['nonce'], 16)}") print(f"Code Hash: {proof['codeHash']}") print(f"Storage Hash: {proof['storageHash']}") print(f"\nAccount Proof nodes: {len(proof['accountProof'])}") for sp in proof['storageProof']: print(f"\nStorage Key: {sp['key']}") print(f" Value: {sp['value']}") print(f" Proof nodes: {len(sp['proof'])}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getProof", "params": [ "0x3600000000000000000000000000000000000000", ["0x0", "0x1"], "latest" ], "id": 1 }' ``` # eth_getRawTransactionByHash | Arc Source: https://docs.chainstack.com/reference/arc-eth-getrawtransactionbyhash openapi/arc_node_api/transaction_info/eth_getRawTransactionByHash.json POST / Arc API method that returns the raw transaction data by transaction hash. data — the hash of the transaction. Arc via Chainstack. Arc API method that returns the raw transaction data by transaction hash. ## Parameters * `data` — the hash of the transaction ## Response * `data` — the raw transaction data, or `null` when no transaction was found ## `eth_getRawTransactionByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getRawTransactionByHash", [ "0xTRANSACTION_HASH" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getRawTransactionByHash", "params": ["0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2"], "id": 1 }' ``` # eth_getStorageAt | Arc Source: https://docs.chainstack.com/reference/arc-eth-getstorageat openapi/arc_node_api/accounts_info/eth_getStorageAt.json POST / Arc API method that returns the value from a storage position at a given address. This is useful for reading raw contract storage slots. Arc API method that returns the value from a storage position at a given address. This is useful for reading raw contract storage slots. ## Parameters * `address` — the address of the contract * `storagePosition` — the position in storage (hex) * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the value at the storage position encoded as hexadecimal ## `eth_getStorageAt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // USDC token address const USDC = "0x3600000000000000000000000000000000000000"; const getStorage = async () => { // Slot 0 typically contains the total supply in ERC20 contracts const value = await provider.getStorage(USDC, 0); console.log(`Storage slot 0: ${value}`); // Read a specific slot const slot5 = await provider.send("eth_getStorageAt", [ USDC, "0x5", "latest" ]); console.log(`Storage slot 5: ${slot5}`); }; getStorage(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # USDC token address USDC = "0x3600000000000000000000000000000000000000" # Read storage slot 0 value = web3.eth.get_storage_at(USDC, 0) print(f"Storage slot 0: {value.hex()}") # Read a specific slot using raw RPC result = web3.provider.make_request("eth_getStorageAt", [ USDC, "0x5", "latest" ]) print(f"Storage slot 5: {result['result']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getStorageAt", "params": ["0x3600000000000000000000000000000000000000", "0x0", "latest"], "id": 1 }' ``` # eth_getTransactionByBlockHashAndIndex | Arc Source: https://docs.chainstack.com/reference/arc-eth-gettransactionbyblockhashandindex openapi/arc_node_api/transaction_info/eth_getTransactionByBlockHashAndIndex.json POST / Arc API method that returns information about a transaction by block hash and transaction index position. Chainstack Arc reference. Arc API method that returns information about a transaction by block hash and transaction index position. ## Parameters * `blockHash` — the hash of the block * `transactionIndex` — the transaction index position (hex) ## Response * `result` — the transaction object, or `null` if not found: * `hash` — transaction hash * `nonce` — sender's transaction count * `blockHash` — hash of the containing block * `blockNumber` — block number * `transactionIndex` — index in the block * `from` — sender address * `to` — recipient address (null for contract creation) * `value` — value transferred in wei * `gas` — gas limit * `gasPrice` — gas price * `input` — transaction data * `v`, `r`, `s` — signature values ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionByIndex = async (blockHash, index) => { const tx = await provider.send("eth_getTransactionByBlockHashAndIndex", [ blockHash, index ]); if (tx) { console.log(`Transaction at index ${parseInt(index, 16)}:`); console.log(` Hash: ${tx.hash}`); console.log(` From: ${tx.from}`); console.log(` To: ${tx.to || 'Contract Creation'}`); console.log(` Value: ${ethers.formatEther(tx.value)} ETH`); } else { console.log("Transaction not found"); } }; getTransactionByIndex( "0xa86891cc8ae2e3efe67b21e24f8f852e76a0875892e9e8c9a4c77aab8e2761cc", "0x0" ); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getTransactionByBlockHashAndIndex", [ "0xa86891cc8ae2e3efe67b21e24f8f852e76a0875892e9e8c9a4c77aab8e2761cc", "0x0" ]) tx = result['result'] if tx: print(f"Transaction at index 0:") print(f" Hash: {tx['hash']}") print(f" From: {tx['from']}") print(f" To: {tx.get('to', 'Contract Creation')}") print(f" Value: {web3.from_wei(int(tx['value'], 16), 'ether')} ETH") else: print("Transaction not found") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getTransactionByBlockHashAndIndex", "params": ["0xa86891cc8ae2e3efe67b21e24f8f852e76a0875892e9e8c9a4c77aab8e2761cc", "0x0"], "id": 1 }' ``` # eth_getTransactionByBlockNumberAndIndex | Arc Source: https://docs.chainstack.com/reference/arc-eth-gettransactionbyblocknumberandindex openapi/arc_node_api/transaction_info/eth_getTransactionByBlockNumberAndIndex.json POST / Arc API method that returns information about a transaction by block number and transaction index position. Arc via Chainstack. Arc API method that returns information about a transaction by block number and transaction index position. ## Parameters * `blockNumber` — the block number (hex) or tag (`latest`, `earliest`, `pending`) * `transactionIndex` — the transaction index position (hex) ## Response * `result` — the transaction object, or `null` if not found: * `hash` — transaction hash * `nonce` — sender's transaction count * `blockHash` — hash of the containing block * `blockNumber` — block number * `transactionIndex` — index in the block * `from` — sender address * `to` — recipient address (null for contract creation) * `value` — value transferred in wei * `gas` — gas limit * `gasPrice` — gas price * `input` — transaction data * `v`, `r`, `s` — signature values ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionByIndex = async (blockNumber, index) => { const tx = await provider.send("eth_getTransactionByBlockNumberAndIndex", [ blockNumber, index ]); if (tx) { console.log(`Transaction at block ${blockNumber}, index ${parseInt(index, 16)}:`); console.log(` Hash: ${tx.hash}`); console.log(` From: ${tx.from}`); console.log(` To: ${tx.to || 'Contract Creation'}`); console.log(` Gas: ${parseInt(tx.gas, 16)}`); } else { console.log("Transaction not found"); } }; // Get first transaction in latest block getTransactionByIndex("latest", "0x0"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getTransactionByBlockNumberAndIndex", [ "latest", "0x0" ]) tx = result['result'] if tx: print(f"Transaction at latest block, index 0:") print(f" Hash: {tx['hash']}") print(f" From: {tx['from']}") print(f" To: {tx.get('to', 'Contract Creation')}") print(f" Gas: {int(tx['gas'], 16)}") else: print("Transaction not found") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getTransactionByBlockNumberAndIndex", "params": ["latest", "0x0"], "id": 1 }' ``` # eth_getTransactionByHash | Arc Source: https://docs.chainstack.com/reference/arc-eth-gettransactionbyhash openapi/arc_node_api/transaction_info/eth_getTransactionByHash.json POST / Arc API method that returns the information about a transaction requested by transaction hash. transactionHash — the hash of the transaction to retrieve. Arc API method that returns the information about a transaction requested by transaction hash. ## Parameters * `transactionHash` — the hash of the transaction to retrieve ## Response * `result` — a transaction object, or `null` if no transaction was found: * `hash` — the transaction hash * `nonce` — the number of transactions made by the sender prior to this one * `blockHash` — hash of the block containing this transaction * `blockNumber` — block number containing this transaction * `from` — address of the sender * `to` — address of the receiver * `value` — value transferred in wei * `input` — data sent along with the transaction * `type` — transaction type: `0x0` legacy, `0x1` EIP-2930, `0x2` EIP-1559, or `0x4` EIP-7702 ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async () => { const tx = await provider.getTransaction("0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2"); console.log(tx); }; getTransaction(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) tx = web3.eth.get_transaction("0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2") print(tx) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getTransactionByHash", "params": ["0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2"], "id": 1}' ``` # eth_getTransactionCount | Arc Source: https://docs.chainstack.com/reference/arc-eth-gettransactioncount openapi/arc_node_api/accounts_info/eth_getTransactionCount.json POST / Arc API method that returns the number of transactions sent from an address (the nonce). This is used to set the correct nonce when sending transactions. Arc API method that returns the number of transactions sent from an address (the nonce). This is used to set the correct nonce when sending transactions. ## Parameters * `address` — the address to get the transaction count for * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the number of transactions sent from the address encoded as hexadecimal ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getNonce = async () => { const nonce = await provider.getTransactionCount("0x5d20879655df3c1e04ab111af0009ad650e762f0"); console.log(`Nonce: ${nonce}`); }; getNonce(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) nonce = web3.eth.get_transaction_count("0x5d20879655df3c1e04ab111af0009ad650e762f0") print(f"Nonce: {nonce}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getTransactionCount", "params": ["0x5d20879655df3c1e04ab111af0009ad650e762f0", "latest"], "id": 1}' ``` # eth_getTransactionReceipt | Arc Source: https://docs.chainstack.com/reference/arc-eth-gettransactionreceipt openapi/arc_node_api/transaction_info/eth_getTransactionReceipt.json POST / Arc API method that returns the receipt of a transaction by transaction hash. Transaction receipts contain information about the execution of a transaction. Arc API method that returns the receipt of a transaction by transaction hash. Transaction receipts contain information about the execution of a transaction. ## Parameters * `transactionHash` — the hash of the transaction to retrieve the receipt for ## Response * `result` — a receipt object, or `null` if no receipt was found: * `transactionHash` — the transaction hash * `blockHash` — hash of the block containing this transaction * `blockNumber` — block number containing this transaction * `from` — address of the sender * `to` — address of the receiver * `status` — `1` for success, `0` for failure * `gasUsed` — amount of gas used * `logs` — array of log objects * `effectiveGasPrice` — the gas price actually paid, in wei * `type` — transaction type: `0x0` legacy, `0x1` EIP-2930, `0x2` EIP-1559, or `0x4` EIP-7702 ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getReceipt = async () => { const receipt = await provider.getTransactionReceipt("0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2"); console.log(receipt); }; getReceipt(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) receipt = web3.eth.get_transaction_receipt("0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2") print(receipt) print(f"Effective gas price: {receipt['effectiveGasPrice']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getTransactionReceipt", "params": ["0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2"], "id": 1}' ``` # eth_getUncleByBlockHashAndIndex | Arc Source: https://docs.chainstack.com/reference/arc-eth-getunclebyblockhashandindex openapi/arc_node_api/blocks_info/eth_getUncleByBlockHashAndIndex.json POST / Arc API method that returns information about an uncle by block hash and uncle index position. Use it on Arc via Chainstack. Arc API method that returns information about an uncle by block hash and uncle index position. ## Parameters * `data` — the hash of the block * `quantity` — the uncle index position ## Response * `object` — a block object, or `null` when no uncle was found ## `eth_getUncleByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleByBlockHashAndIndex", ["0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9", "0x0"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleByBlockHashAndIndex", "params": ["0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9", "0x0"], "id": 1 }' ``` # eth_getUncleByBlockNumberAndIndex | Arc Source: https://docs.chainstack.com/reference/arc-eth-getunclebyblocknumberandindex openapi/arc_node_api/blocks_info/eth_getUncleByBlockNumberAndIndex.json POST / Arc API method that returns information about an uncle by block number and uncle index position. Use it on Arc via Chainstack. Arc API method that returns information about an uncle by block number and uncle index position. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `quantity` — the uncle index position ## Response * `object` — a block object, or `null` when no uncle was found ## `eth_getUncleByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleByBlockNumberAndIndex", ["0x34808ff", "0x0"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleByBlockNumberAndIndex", "params": ["0x34808ff", "0x0"], "id": 1 }' ``` # eth_getUncleCountByBlockHash | Arc Source: https://docs.chainstack.com/reference/arc-eth-getunclecountbyblockhash openapi/arc_node_api/blocks_info/eth_getUncleCountByBlockHash.json POST / Arc API method that returns the number of uncles in a block by block hash. data — the hash of the block quantity — the number of uncles in the block. Arc API method that returns the number of uncles in a block by block hash. ## Parameters * `data` — the hash of the block ## Response * `quantity` — the number of uncles in the block ## `eth_getUncleCountByBlockHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleCountByBlockHash", ["0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleCountByBlockHash", "params": ["0x6dabcf7ed4cf53e05054a4d671b19b7597740bce1042019ff31af68dde9c98a9"], "id": 1 }' ``` # eth_getUncleCountByBlockNumber | Arc Source: https://docs.chainstack.com/reference/arc-eth-getunclecountbyblocknumber openapi/arc_node_api/blocks_info/eth_getUncleCountByBlockNumber.json POST / Arc API method that returns the number of uncles in a block by block number. Arc uses Simplex Consensus and does not produce uncle blocks. Arc API method that returns the number of uncles in a block by block number. Arc uses Simplex Consensus and does not produce uncle blocks, so this always returns `0x0`. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the number of uncles in the block, encoded as hexadecimal. On Arc, this always returns `0x0` since Arc uses Simplex Consensus and has no uncle blocks. ## `eth_getUncleCountByBlockNumber` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getUncleCountByBlockNumber", "params": ["latest"], "id": 1}' ``` # eth_hashrate | Arc Source: https://docs.chainstack.com/reference/arc-eth-hashrate openapi/arc_node_api/chain_info/eth_hashrate.json POST / Arc API method that returns the number of hashes per second that the node is mining with. eth_hashrate on Arc via Chainstack. Arc API method that returns the number of hashes per second that the node is mining with. ## Parameters None ## Response * `quantity` — the number of hashes per second ## `eth_hashrate` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_hashrate", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_hashrate", "params": [], "id": 1 }' ``` # eth_maxPriorityFeePerGas | Arc Source: https://docs.chainstack.com/reference/arc-eth-maxpriorityfeepergas openapi/arc_node_api/gas_data/eth_maxPriorityFeePerGas.json POST / Arc API method that returns the current maximum priority fee per gas. This is the fee tip that goes to validators on top of the base fee. Arc API method that returns the current maximum priority fee per gas. This is the fee tip that goes to validators on top of the base fee. ## Parameters None. ## Response * `result` — the current max priority fee per gas encoded as hexadecimal (in wei) ## `eth_maxPriorityFeePerGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getMaxPriorityFee = async () => { const feeData = await provider.getFeeData(); console.log(`Max Priority Fee: ${ethers.formatUnits(feeData.maxPriorityFeePerGas, "gwei")} gwei`); // Or using raw RPC const maxPriorityFee = await provider.send("eth_maxPriorityFeePerGas", []); console.log(`Raw response: ${maxPriorityFee}`); console.log(`In gwei: ${ethers.formatUnits(maxPriorityFee, "gwei")}`); }; getMaxPriorityFee(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Get max priority fee max_priority_fee = web3.eth.max_priority_fee print(f"Max Priority Fee: {web3.from_wei(max_priority_fee, 'gwei')} gwei") # Using raw RPC result = web3.provider.make_request("eth_maxPriorityFeePerGas", []) print(f"Raw response: {result['result']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_maxPriorityFeePerGas", "params": [], "id": 1}' ``` # eth_newBlockFilter | Arc Source: https://docs.chainstack.com/reference/arc-eth-newblockfilter openapi/arc_node_api/filter_handling/eth_newBlockFilter.json POST / Arc API method that creates a filter for new block notifications. The filter can be polled with eth_getFilterChanges to get new block hashes. Arc API method that creates a filter for new block notifications. The filter can be polled with `eth_getFilterChanges` to get new block hashes. With Arc's \~0.5 second block times, this filter will receive frequent updates. ## Parameters * `none` ## Response * `result` — the filter ID ## `eth_newBlockFilter` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createBlockFilter = async () => { const filterId = await provider.send("eth_newBlockFilter", []); console.log(`Filter ID: ${filterId}`); // Poll for new blocks const newBlocks = await provider.send("eth_getFilterChanges", [filterId]); console.log(`New block hashes: ${newBlocks}`); }; createBlockFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block_filter = web3.eth.filter('latest') print(f"Filter ID: {block_filter.filter_id}") # Poll for new blocks new_blocks = block_filter.get_new_entries() print(f"New blocks: {new_blocks}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_newBlockFilter", "params": [], "id": 1}' ``` # eth_newFilter | Arc Source: https://docs.chainstack.com/reference/arc-eth-newfilter openapi/arc_node_api/filter_handling/eth_newFilter.json POST / Arc API method that creates a new filter for logs matching specified criteria. The filter can be polled with eth_getFilterChanges. Arc API method that creates a new filter for logs matching specified criteria. The filter can be polled with `eth_getFilterChanges`. ## Parameters * `filterObject` — the filter object: * `fromBlock` — (optional) block number (hex) or tag to start from * `toBlock` — (optional) block number (hex) or tag to end at * `address` — (optional) contract address or array of addresses * `topics` — (optional) array of topic filters ## Response * `result` — the filter ID ## `eth_newFilter` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const USDC = "0x3600000000000000000000000000000000000000"; const createFilter = async () => { const filterId = await provider.send("eth_newFilter", [{ address: USDC, fromBlock: "latest", toBlock: "latest" }]); console.log(`Filter ID: ${filterId}`); }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) USDC = "0x3600000000000000000000000000000000000000" filter_id = web3.eth.filter({ 'address': USDC, 'fromBlock': 'latest' }) print(f"Filter ID: {filter_id.filter_id}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_newFilter", "params": [{"address": "0x3600000000000000000000000000000000000000", "fromBlock": "latest"}], "id": 1 }' ``` # eth_protocolVersion | Arc Source: https://docs.chainstack.com/reference/arc-eth-protocolversion openapi/arc_node_api/chain_info/eth_protocolVersion.json POST / Arc API method that returns the current Ethereum protocol version. result — the current Ethereum protocol version, encoded as hexadecimal. Arc API method that returns the current Ethereum protocol version. ## Parameters * `none` ## Response * `result` — the current Ethereum protocol version, encoded as hexadecimal. ## `eth_protocolVersion` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_protocolVersion", "params": [], "id": 1}' ``` # eth_sendRawTransaction | Arc Source: https://docs.chainstack.com/reference/arc-eth-sendrawtransaction openapi/arc_node_api/execute_transactions/eth_sendRawTransaction.json POST / Arc API method that sends a signed transaction to the network. Chainstack supports eth_sendRawTransaction on Arc JSON-RPC nodes. Arc API method that sends a signed transaction to the network. Arc accepts the standard Ethereum transaction types — legacy (`0x0`), EIP-2930 (`0x1`), EIP-1559 (`0x2`), and EIP-7702 (`0x4`) — and adds none of its own. > [!NOTE] > > Arc pays gas in USDC, which is the native currency of the chain. Fees are denominated in wei with 18 decimals, exactly as on Ethereum. ## Parameters * `signedTransaction` — the signed transaction data as a hex string ## Response * `result` — the transaction hash of the submitted transaction ## `eth_sendRawTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const sendTransaction = async () => { const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); const tx = { to: "0xRecipientAddress", value: ethers.parseEther("0.01"), // USDC, 18 decimals }; const txResponse = await wallet.sendTransaction(tx); console.log(`Transaction hash: ${txResponse.hash}`); const receipt = await txResponse.wait(); console.log(`Confirmed in block: ${receipt.blockNumber}`); }; sendTransaction(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Build and sign transaction account = web3.eth.account.from_key("YOUR_PRIVATE_KEY") tx = { 'nonce': web3.eth.get_transaction_count(account.address), 'to': "0xRecipientAddress", 'value': web3.to_wei(0.01, 'ether'), # USDC, 18 decimals 'gas': 21000, 'gasPrice': web3.eth.gas_price, 'chainId': 5042 # Arc Mainnet (use 5042002 for Arc Testnet) } signed = account.sign_transaction(tx) tx_hash = web3.eth.send_raw_transaction(signed.raw_transaction) print(f"Transaction hash: {tx_hash.hex()}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": ["0x02f8"], "id": 1}' ``` # eth_sendRawTransactionSync | Arc Source: https://docs.chainstack.com/reference/arc-eth-sendrawtransactionsync openapi/arc_node_api/execute_transactions/eth_sendRawTransactionSync.json POST / Arc-specific API method that sends a signed transaction to the network and blocks until the transaction receipt is returned. On Arc. Arc-specific API method that sends a signed transaction to the network and blocks until the transaction receipt is returned. This is a synchronous version of [`eth_sendRawTransaction`](/reference/arc-eth-sendrawtransaction) — instead of returning just the transaction hash, it waits for the transaction to be included in a block and returns the full receipt. > [!NOTE] > > This method is particularly useful on Arc because of its \~500ms finality. The synchronous wait adds minimal latency while eliminating the need to poll for receipts. ## Parameters * `signedTransaction` — the signed transaction data as a hex string ## Response * `result` — the full transaction receipt object, returned once the transaction is included in a block. It carries the same fields as the response from [`eth_getTransactionReceipt`](/reference/arc-eth-gettransactionreceipt). ## `eth_sendRawTransactionSync` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const sendTransactionSync = async () => { const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); const tx = { to: "0xRecipientAddress", value: ethers.parseEther("0.01"), // USDC, 18 decimals }; // Sign the transaction const signedTx = await wallet.signTransaction(tx); // Send synchronously — blocks until receipt is returned const receipt = await provider.send("eth_sendRawTransactionSync", [signedTx]); console.log(`Confirmed in block: ${receipt.blockNumber}`); console.log(`Status: ${receipt.status}`); }; sendTransactionSync(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) account = web3.eth.account.from_key("YOUR_PRIVATE_KEY") tx = { 'nonce': web3.eth.get_transaction_count(account.address), 'to': "0xRecipientAddress", 'value': web3.to_wei(0.01, 'ether'), # USDC, 18 decimals 'gas': 21000, 'gasPrice': web3.eth.gas_price, 'chainId': 5042 # Arc Mainnet (use 5042002 for Arc Testnet) } signed = account.sign_transaction(tx) # Send synchronously — blocks until receipt is returned receipt = web3.provider.make_request( "eth_sendRawTransactionSync", [signed.raw_transaction.hex()] ) print(f"Receipt: {receipt['result']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_sendRawTransactionSync", "params": ["SIGNED_TX_DATA"], "id": 1}' ``` ## When to use Use `eth_sendRawTransactionSync` instead of `eth_sendRawTransaction` when: * You need the receipt immediately and want to avoid polling with `eth_getTransactionReceipt` * You are building synchronous request/response flows (like payment APIs) * You want to simplify transaction submission logic by getting the result in a single call For fire-and-forget scenarios or when submitting many transactions in parallel, use [`eth_sendRawTransaction`](/reference/arc-eth-sendrawtransaction) instead. # eth_simulateV1 | Arc Source: https://docs.chainstack.com/reference/arc-eth-simulatev1 openapi/arc_node_api/execute_transactions/eth_simulateV1.json POST / Arc API method that simulates transaction execution. eth_simulateV1 JSON-RPC method available on the Arc blockchain via Chainstack. Arc API method that simulates transaction execution. ## Parameters * `object` — the simulation configuration object * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — the simulation result ## Example: simulate a USDC transfer The code examples on this page simulate a transfer of 1 USDC from `0x5d20879655df3c1e04ab111af0009ad650e762f0` to `0x000000000000000000000000000000000000dEaD` through the USDC ERC-20 interface at `0x3600000000000000000000000000000000000000`. The `stateOverrides` entry sets the sender's balance to 10 USDC (`0x8ac7230489e80000` in the 18-decimal native unit) for the simulation only, so the result does not depend on the account's live balance. The simulated call returns `true` in `returnData` and two `Transfer` logs for the same movement: * The [EIP-7708](https://eips.ethereum.org/EIPS/eip-7708) native-value log emitted by the system address `0xfffffffffffffffffffffffffffffffffffffffe`, with the amount `1000000000000000000` in 18 decimals. * The ERC-20 `Transfer` event emitted by `0x3600000000000000000000000000000000000000`, with the amount `1000000` in 6 decimals. ## `eth_simulateV1` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_simulateV1", [{"blockStateCalls": [{"stateOverrides": {"0x5d20879655df3c1e04ab111af0009ad650e762f0": {"balance": "0x8ac7230489e80000"}}, "calls": [{"from": "0x5d20879655df3c1e04ab111af0009ad650e762f0", "to": "0x3600000000000000000000000000000000000000", "data": "0xa9059cbb000000000000000000000000000000000000000000000000000000000000dead00000000000000000000000000000000000000000000000000000000000f4240"}]}]}, "latest"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) sender = "0x5d20879655df3c1e04ab111af0009ad650e762f0" usdc = "0x3600000000000000000000000000000000000000" transfer_data = "0xa9059cbb000000000000000000000000000000000000000000000000000000000000dead00000000000000000000000000000000000000000000000000000000000f4240" result = web3.provider.make_request("eth_simulateV1", [ { "blockStateCalls": [ { "stateOverrides": {sender: {"balance": "0x8ac7230489e80000"}}, "calls": [{"from": sender, "to": usdc, "data": transfer_data}] } ] }, "latest" ]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_simulateV1", "params": [{"blockStateCalls": [{"stateOverrides": {"0x5d20879655df3c1e04ab111af0009ad650e762f0": {"balance": "0x8ac7230489e80000"}}, "calls": [{"from": "0x5d20879655df3c1e04ab111af0009ad650e762f0", "to": "0x3600000000000000000000000000000000000000", "data": "0xa9059cbb000000000000000000000000000000000000000000000000000000000000dead00000000000000000000000000000000000000000000000000000000000f4240"}]}]}, "latest"], "id": 1 }' ``` # eth_syncing | Arc Source: https://docs.chainstack.com/reference/arc-eth-syncing openapi/arc_node_api/chain_info/eth_syncing.json POST / Arc API method that returns the sync status of the node. Returns false if the node is fully synced, or a sync status object if syncing. Arc API method that returns the sync status of the node. Returns `false` if the node is fully synced, or a sync status object if syncing. ## Parameters * `none` ## Response * `result` — `false` if not syncing, or a sync status object: * `startingBlock` — block at which sync started * `currentBlock` — current block being synced * `highestBlock` — estimated highest block ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function checkSyncStatus() { const syncing = await provider.send("eth_syncing", []); if (syncing === false) { console.log("Node is fully synced"); } else { console.log(`Syncing: ${Number(syncing.currentBlock)}/${Number(syncing.highestBlock)}`); } } checkSyncStatus() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) syncing = web3.eth.syncing if syncing is False: print("Node is fully synced") else: print(f"Syncing: {syncing['currentBlock']}/{syncing['highestBlock']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1}' ``` # eth_uninstallFilter | Arc Source: https://docs.chainstack.com/reference/arc-eth-uninstallfilter openapi/arc_node_api/filter_handling/eth_uninstallFilter.json POST / Arc API method that uninstalls a filter created by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter. On Arc. Arc API method that uninstalls a filter created by `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. Filters should be uninstalled when no longer needed to free server resources. ## Parameters * `filterId` — the filter ID to uninstall ## Response * `result` — `true` if the filter was successfully uninstalled, `false` otherwise ## `eth_uninstallFilter` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filterLifecycle = async () => { // Create a block filter const filterId = await provider.send("eth_newBlockFilter", []); console.log(`Created block filter: ${filterId}`); // Use the filter const blocks = await provider.send("eth_getFilterChanges", [filterId]); console.log(`Got ${blocks.length} new blocks`); // Uninstall when done const uninstalled = await provider.send("eth_uninstallFilter", [filterId]); console.log(`Filter uninstalled: ${uninstalled}`); // Trying to use it again will fail try { await provider.send("eth_getFilterChanges", [filterId]); } catch (e) { console.log("Filter no longer exists (expected)"); } }; filterLifecycle(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Create a block filter filter_result = web3.provider.make_request("eth_newBlockFilter", []) filter_id = filter_result['result'] print(f"Created block filter: {filter_id}") # Use the filter changes_result = web3.provider.make_request("eth_getFilterChanges", [filter_id]) print(f"Got {len(changes_result['result'])} new blocks") # Uninstall when done uninstall_result = web3.provider.make_request("eth_uninstallFilter", [filter_id]) print(f"Filter uninstalled: {uninstall_result['result']}") ``` ```bash cURL theme={"system"} # First create a filter curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_newBlockFilter", "params": ["0x1"], "id": 1}' # Then uninstall it using the returned filter ID curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_uninstallFilter", "params": ["0x1"], "id": 1}' ``` # Execute transactions | Arc Source: https://docs.chainstack.com/reference/arc-execute-transactions-rpc-methods Arc execution JSON-RPC methods — simulate calls, estimate gas, build access lists, and broadcast signed transactions on Arc. Executing and simulating transactions on Arc can be done using the following methods: > [card] [eth\_call](/reference/arc-eth-call) > [card] [eth\_createAccessList](/reference/arc-eth-createaccesslist) > [card] [eth\_estimateGas](/reference/arc-eth-estimategas) > [card] [eth\_sendRawTransaction](/reference/arc-eth-sendrawtransaction) > [card] [eth\_sendRawTransactionSync](/reference/arc-eth-sendrawtransactionsync) > [card] [eth\_simulateV1](/reference/arc-eth-simulatev1) # Filter handling | Arc Source: https://docs.chainstack.com/reference/arc-filter-handling-rpc-methods Arc filter JSON-RPC methods — create, poll, and remove log and block filters on Arc. Working with filters on Arc can be done using the following methods: > [card] [eth\_getFilterChanges](/reference/arc-eth-getfilterchanges) > [card] [eth\_getFilterLogs](/reference/arc-eth-getfilterlogs) > [card] [eth\_newBlockFilter](/reference/arc-eth-newblockfilter) > [card] [eth\_newFilter](/reference/arc-eth-newfilter) > [card] [eth\_uninstallFilter](/reference/arc-eth-uninstallfilter) # Gas data | Arc Source: https://docs.chainstack.com/reference/arc-gas-data-rpc-methods Arc gas JSON-RPC methods — read the current gas price, priority fee, fee history, and blob base fee on Arc. Retrieving gas and fee data from Arc can be done using the following methods: > [card] [eth\_blobBaseFee](/reference/arc-eth-blobbasefee) > [card] [eth\_feeHistory](/reference/arc-eth-feehistory) > [card] [eth\_gasPrice](/reference/arc-eth-gasprice) > [card] [eth\_maxPriorityFeePerGas](/reference/arc-eth-maxpriorityfeepergas) # arc_getCertificate | Arc Source: https://docs.chainstack.com/reference/arc-getcertificate openapi/arc_node_api/arc_namespace/arc_getCertificate.json POST / Arc API method that returns the consensus commit certificate for a block height, including the round, the block hash, and the validator signatures. Use it on Arc via Chainstack. Arc API method that returns the consensus commit certificate for a block height. The certificate is the [Malachite](https://github.com/circlefin/malachite) BFT proof that the validator set committed the block, so it is what you check when you need finality evidence rather than the block contents. `arc_getCertificate` belongs to the custom `arc_*` namespace, which Arc adds on top of the standard Reth surface. > [!NOTE] > > `rpc_modules` does not list `arc` in its response, because the namespace is registered outside the standard module registry. The method works regardless — do not use `rpc_modules` to decide whether it is available. ## Parameters * `height` — the block height to get the commit certificate for, as an integer. Must be `1` or greater. ## Response * `height` — the block height the certificate commits. * `round` — the consensus round in which the block was committed. `0` means the block was committed in the first round. * `block_hash` — the hash of the committed block. * `signatures` — the validator signatures that form the commit. Each entry has an `address` and a base64-encoded `signature`. ## Error codes * `-32602` — invalid params, returned when the height is below `1`. * `-32004` — no certificate is available for that height. * `-32005` — the upstream consensus source is unreachable. ## `arc_getCertificate` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("arc_getCertificate", [55000000]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("arc_getCertificate", [55000000]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "arc_getCertificate", "params": [55000000], "id": 1 }' ``` ## Use case A cross-chain bridge or an exchange crediting deposits needs to know that a block is final, not merely present. On a BFT chain the commit certificate is that evidence: it names the validators that signed the block and the round they agreed in, so a relayer can verify the quorum itself instead of waiting out a confirmation count. # Arc API reference: getting started Source: https://docs.chainstack.com/reference/arc-getting-started Get started building on Arc Mainnet and Arc Testnet with Chainstack. Access the JSON-RPC API on Circle's USDC-native EVM layer 1, including the custom arc_* consensus methods. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). ## What is Arc? Arc is an EVM-compatible layer 1 from Circle. It pairs the [Reth](https://github.com/paradigmxyz/reth) execution client with [Malachite](https://github.com/circlefin/malachite) BFT consensus, and it uses USDC for gas instead of a volatile native token. Chainstack serves both Arc Mainnet and Arc Testnet. The two networks carry different chain IDs — `5042` and `5042002` — so a transaction signed for one is rejected by the other. > [!NOTE] > > The **Try it** panels on every method page in this section run against a public Arc Testnet endpoint. The request and response shapes are identical on mainnet; only the chain ID and the data differ. ### Key characteristics * **USDC as gas** — transaction fees are paid in USDC, so costs are predictable in dollar terms. * **Sub-second blocks** — Arc produces a block roughly every 0.5 seconds under Malachite BFT consensus, with deterministic finality rather than probabilistic confirmation. * **Standard EVM surface** — the `eth_*` methods behave as they do on any Reth node, so existing tooling works unchanged. * **Full debug and trace** — both the `debug_*` namespace and the Parity-style `trace_*` namespace are available. * **A custom `arc_*` namespace** — two methods that have no equivalent on other chains, covering the node build and the consensus commit certificate. ## Network details > [tab] Mainnet > > | Property | Value | > | - | - | > | Network | Arc Mainnet | > | Chain ID | `5042` (`0x13b2`) | > | Native gas token | USDC | > | Execution client | Reth | > | Consensus | Malachite BFT | > | Block time | \~0.5s | > | Node modes | Full and Archive | > | Explorer | [explorer.arc.io](https://explorer.arc.io) | > [tab] Testnet > > | Property | Value | > | - | - | > | Network | Arc Testnet | > | Chain ID | `5042002` (`0x4cef52`) | > | Native gas token | USDC | > | Execution client | Reth | > | Consensus | Malachite BFT | > | Block time | \~0.5s | > | Node modes | Full and Archive | > | Explorer | [explorer.testnet.arc.io](https://explorer.testnet.arc.io) | > | Faucet | [faucet.circle.com](https://faucet.circle.com) | ## Namespaces Chainstack Arc nodes expose `eth`, `debug`, `trace`, `txpool`, `net`, `web3`, and `rpc`, plus the custom `arc` namespace. > [!WARNING] > > `rpc_modules` does not list `arc`, because that namespace is registered outside the standard module registry. Both `arc_*` methods work regardless. Do not use `rpc_modules` as an inventory of what the node serves — see [Arc methods](/docs/arc-methods) for verified per-method availability. ## Two things that differ from other EVM chains **Pending transactions are not observable.** Arc rejects both `eth_newPendingTransactionFilter` and `eth_subscribe("newPendingTransactions")` with `-32001`. `txpool_status` works, but `txpool_content`, `txpool_contentFrom`, and `txpool_inspect` are not available on Global Nodes and Trader Nodes, so you can read the pool's transaction counts but not its contents. **Most historical queries bill as archive.** The standard rule is that a request 127 or more blocks behind the tip bills as archive. At roughly 0.5 seconds per block that covers only about a minute of history, against roughly 25 minutes on Ethereum. See [Request units](/docs/request-units#evm-methods-affected-by-block-age). ## Getting an endpoint 1. [Sign up with Chainstack](https://console.chainstack.com/). 2. [Deploy a node](/docs/manage-your-networks) on Arc Mainnet or Arc Testnet. Arc Mainnet and Arc Testnet offer Full and Archive on Global Nodes. 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Every method page in this section carries a **Try it** panel wired to a public Arc Testnet endpoint, so you can run any call before deploying anything. ## Where to go next > [card] [Arc namespace](/reference/arc-namespace-rpc-methods) > [card] [Method availability](/docs/arc-methods) > [card] [Debug and trace](/reference/arc-debug-and-trace-rpc-methods) > [card] [Transaction pool](/reference/arc-txpool-rpc-methods) # arc_getVersion | Arc Source: https://docs.chainstack.com/reference/arc-getversion openapi/arc_node_api/arc_namespace/arc_getVersion.json POST / Arc API method that returns the build information for the node's execution layer, including the git tag, commit hash, and cargo version. Use it on Arc via Chainstack. Arc API method that returns the build information for the node's execution layer. Use it to confirm which [arc-node](https://github.com/circlefin/arc-node) release a node is running, for example when a fix or a new method landed in a specific version. `arc_getVersion` belongs to the custom `arc_*` namespace, which Arc adds on top of the standard Reth surface. > [!NOTE] > > `rpc_modules` does not list `arc` in its response, because the namespace is registered outside the standard module registry. The method works regardless — do not use `rpc_modules` to decide whether it is available. ## Parameters None. ## Response * `git_version` — the git tag of the release, or a short commit hash for untagged builds. * `git_commit` — the full git commit hash the binary was built from. * `git_short_hash` — the abbreviated commit hash. * `cargo_version` — the cargo package version, with the short commit appended. ## `arc_getVersion` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("arc_getVersion", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("arc_getVersion", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "arc_getVersion", "params": [], "id": 1 }' ``` ## Use case Arc is young and moving quickly, so behaviour can differ between releases. When a method returns something unexpected, `arc_getVersion` tells you which build you are talking to before you start bisecting your own code. # Logs and events | Arc Source: https://docs.chainstack.com/reference/arc-logs-and-events-rpc-methods Arc log JSON-RPC methods — query contract event logs by block range, address, and topic on Arc. Retrieving logs and events from Arc can be done using the following methods: > [card] [eth\_getLogs](/reference/arc-eth-getlogs) # Arc namespace | Arc Source: https://docs.chainstack.com/reference/arc-namespace-rpc-methods Arc's custom arc_* JSON-RPC methods — read the execution layer build information and the Malachite consensus commit certificate for a block. Arc adds a custom `arc_*` namespace on top of the standard Reth surface: > [card] [arc\_getCertificate](/reference/arc-getcertificate) > [card] [arc\_getVersion](/reference/arc-getversion) # net_listening | Arc Source: https://docs.chainstack.com/reference/arc-net-listening openapi/arc_node_api/client_info/net_listening.json POST / Arc API method that returns true if the client is actively listening for network connections. Available on Arc via Chainstack. Arc API method that returns `true` if the client is actively listening for network connections. ## Parameters None. ## Response * `result` — `true` if the client is listening, `false` otherwise ## `net_listening` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const checkListening = async () => { const listening = await provider.send("net_listening", []); console.log(`Node is listening: ${listening}`); }; checkListening(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) listening = web3.net.listening print(f"Node is listening: {listening}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "net_listening", "params": [], "id": 1}' ``` # net_peerCount | Arc Source: https://docs.chainstack.com/reference/arc-net-peercount openapi/arc_node_api/client_info/net_peerCount.json POST / Arc API method that returns the number of peers currently connected to the client. Available on Arc via Chainstack JSON-RPC nodes. Arc API method that returns the number of peers currently connected to the client. ## Parameters None. ## Response * `result` — the number of connected peers encoded as hexadecimal ## `net_peerCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getPeerCount = async () => { const peerCount = await provider.send("net_peerCount", []); console.log(`Connected peers: ${parseInt(peerCount, 16)}`); }; getPeerCount(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) peer_count = web3.net.peer_count print(f"Connected peers: {peer_count}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "net_peerCount", "params": [], "id": 1}' ``` # net_version | Arc Source: https://docs.chainstack.com/reference/arc-net-version openapi/arc_node_api/client_info/net_version.json POST / Arc API method that returns the current network ID. Arc Mainnet is 5042 and Arc Testnet is 5042002. Arc API method that returns the current network ID. Arc Mainnet is `5042` and Arc Testnet is `5042002`. The network ID matches the chain ID on both networks. ## Parameters * `none` ## Response * `result` — the network ID as a decimal string (`5042` for Arc Mainnet, `5042002` for Arc Testnet) ## `net_version` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getNetworkId() { const network = await provider.getNetwork(); console.log(`Network ID: ${network.chainId}`); } getNetworkId() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.net.version) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "net_version", "params": [], "id": 1}' ``` # rpc_modules | Arc Source: https://docs.chainstack.com/reference/arc-rpc-modules openapi/arc_node_api/chain_info/rpc_modules.json POST / Arc API method that lists the JSON-RPC namespaces the node exposes and their versions. Use it on Arc via Chainstack. Arc API method that lists the JSON-RPC namespaces the node exposes, each mapped to its interface version. ## Parameters None. ## Response * `result` — an object whose keys are the enabled namespaces and whose values are version strings. On a Chainstack Arc node this covers `eth`, `debug`, `trace`, `txpool`, `net`, `web3`, and `rpc`. > [!WARNING] > > The response is not a complete inventory of what the node serves. Arc's custom `arc_*` namespace is registered outside the standard module registry, so `arc_getVersion` and `arc_getCertificate` both work even though `arc` never appears here. See [Arc methods](/docs/arc-methods) for what is actually available. ## `rpc_modules` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("rpc_modules", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("rpc_modules", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "rpc_modules", "params": [], "id": 1 }' ``` ## Use case Client libraries sometimes call `rpc_modules` at startup to decide which features to enable. On Arc that check will under-report, so treat it as a floor rather than the full picture. # trace_block | Arc Source: https://docs.chainstack.com/reference/arc-trace-block openapi/arc_node_api/debug_and_trace/trace_block.json POST / Arc API method that returns execution traces for all transactions in a block. Reference for trace_block on Arc via Chainstack. Arc API method that returns execution traces for all transactions in a block. This provides detailed call-level traces using the Parity/OpenEthereum trace format. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — array of trace objects for all transactions in the block: * `action` — the action object: * `from` — sender address * `to` — recipient address * `callType` — type of call (call, delegatecall, staticcall, etc.) * `gas` — gas provided * `input` — call data * `value` — value transferred * `blockHash` — hash of the block * `blockNumber` — block number * `result` — the result object: * `gasUsed` — gas consumed * `output` — return data * `subtraces` — number of child traces * `traceAddress` — position in the trace tree * `transactionHash` — hash of the transaction * `transactionPosition` — index of the transaction in the block * `type` — trace type (call, create, suicide, reward) ## `trace_block` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlock = async (blockNumber) => { const traces = await provider.send("trace_block", [blockNumber]); console.log(`Block has ${traces.length} traces`); // Group traces by transaction const txTraces = {}; for (const trace of traces) { const txHash = trace.transactionHash; if (!txTraces[txHash]) { txTraces[txHash] = []; } txTraces[txHash].push(trace); } console.log(`Across ${Object.keys(txTraces).length} transactions`); for (const [txHash, txTrace] of Object.entries(txTraces)) { console.log(`\nTx: ${txHash}`); for (const trace of txTrace) { const indent = " ".repeat(trace.traceAddress.length); console.log(`${indent}${trace.action.callType}: ${trace.action.from} -> ${trace.action.to}`); } } }; traceBlock("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 from collections import defaultdict node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("trace_block", ["latest"]) traces = result['result'] print(f"Block has {len(traces)} traces") # Group traces by transaction tx_traces = defaultdict(list) for trace in traces: tx_hash = trace['transactionHash'] tx_traces[tx_hash].append(trace) print(f"Across {len(tx_traces)} transactions") for tx_hash, tx_trace in tx_traces.items(): print(f"\nTx: {tx_hash}") for trace in tx_trace: indent = " " * len(trace['traceAddress']) call_type = trace['action'].get('callType', trace['type']) from_addr = trace['action']['from'] to_addr = trace['action'].get('to', 'Contract Creation') print(f"{indent}{call_type}: {from_addr} -> {to_addr}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_block", "params": ["latest"], "id": 1 }' ``` # trace_call | Arc Source: https://docs.chainstack.com/reference/arc-trace-call openapi/arc_node_api/debug_and_trace/trace_call.json POST / Arc API method that traces a call without creating a transaction. This provides detailed execution traces using the Parity/OpenEthereum trace format. Arc API method that traces a call without creating a transaction. This provides detailed execution traces using the Parity/OpenEthereum trace format. ## Parameters * `callObject` — the call object: * `from` — (optional) sender address * `to` — recipient address * `gas` — (optional) gas limit * `gasPrice` — (optional) gas price * `value` — (optional) value to send * `data` — (optional) call data * `traceTypes` — array of trace types to include: * `trace` — basic execution trace * `vmTrace` — full VM execution trace * `stateDiff` — state changes * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the trace result object: * `output` — return data from the call * `trace` — array of trace objects (if requested) * `vmTrace` — VM execution trace (if requested) * `stateDiff` — state differences (if requested) ## `trace_call` code examples The following example traces a `balanceOf` call on the USDC token: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // USDC token address const USDC = "0x3600000000000000000000000000000000000000"; const TARGET_ADDRESS = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"; // Encode balanceOf(address) const iface = new ethers.Interface(["function balanceOf(address) view returns (uint256)"]); const data = iface.encodeFunctionData("balanceOf", [TARGET_ADDRESS]); const traceCall = async () => { const result = await provider.send("trace_call", [ { to: USDC, data: data }, ["trace", "stateDiff"], "latest" ]); console.log("Output:", result.output); console.log("Traces:", result.trace.length); if (result.stateDiff) { console.log("State changes:", Object.keys(result.stateDiff).length, "addresses affected"); } }; traceCall(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # USDC token address USDC = "0x3600000000000000000000000000000000000000" TARGET_ADDRESS = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294" # balanceOf(address) calldata data = "0x70a08231000000000000000000000000" + TARGET_ADDRESS[2:].lower() result = web3.provider.make_request("trace_call", [ { "to": USDC, "data": data }, ["trace", "stateDiff"], "latest" ]) trace_result = result['result'] print(f"Output: {trace_result['output']}") print(f"Traces: {len(trace_result['trace'])}") if trace_result.get('stateDiff'): print(f"State changes: {len(trace_result['stateDiff'])} addresses affected") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_call", "params": [ { "to": "0x3600000000000000000000000000000000000000", "data": "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" }, ["trace", "stateDiff"], "latest" ], "id": 1 }' ``` # trace_callMany | Arc Source: https://docs.chainstack.com/reference/arc-trace-callmany openapi/arc_node_api/debug_and_trace/trace_callMany.json POST / Arc API method that traces multiple calls in sequence using the OpenEthereum trace format. Each call's state changes are visible to subsequent calls. Arc API method that traces multiple calls in sequence using the OpenEthereum trace format. Each call's state changes are visible to subsequent calls. ## Parameters * `calls` — an array of call bundles, where each bundle is an array of `[callObject, traceTypes]` pairs * `blockParameter` — (optional) the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — an array of trace results, one for each call. ## `trace_callMany` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "trace_callMany", "params": [[[{"to": "0x3600000000000000000000000000000000000000", "data": "0x95d89b41"}, ["trace"]]], "latest"], "id": 1}' ``` # trace_filter | Arc Source: https://docs.chainstack.com/reference/arc-trace-filter openapi/arc_node_api/debug_and_trace/trace_filter.json POST / Arc API method that returns execution traces matching specific filter criteria. Available on Arc via Chainstack JSON-RPC nodes. Arc API method that returns execution traces matching specific filter criteria. This is useful for finding all interactions with a specific contract or address across multiple blocks. ## Parameters * `filterObject` — the filter object: * `fromBlock` — (optional) starting block number (hex) * `toBlock` — (optional) ending block number (hex) * `fromAddress` — (optional) array of sender addresses to filter * `toAddress` — (optional) array of recipient addresses to filter * `after` — (optional) offset for pagination * `count` — (optional) maximum number of traces to return ## Response * `result` — array of trace objects matching the filter: * `action` — the action object: * `from` — sender address * `to` — recipient address * `callType` — type of call * `gas` — gas provided * `input` — call data * `value` — value transferred * `blockHash` — hash of the block * `blockNumber` — block number * `result` — the result object * `subtraces` — number of child traces * `traceAddress` — position in the trace tree * `transactionHash` — hash of the transaction * `transactionPosition` — index in the block * `type` — trace type ## `trace_filter` code examples The following example finds all calls to the USDC token contract: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // USDC token address const USDC = "0x3600000000000000000000000000000000000000"; const traceFilter = async () => { const currentBlock = await provider.getBlockNumber(); const fromBlock = "0x" + (currentBlock - 1000).toString(16); const toBlock = "0x" + currentBlock.toString(16); const traces = await provider.send("trace_filter", [{ fromBlock: fromBlock, toBlock: toBlock, toAddress: [USDC], count: 20 }]); console.log(`Found ${traces.length} traces to USDC`); for (const trace of traces) { console.log(`Block ${trace.blockNumber}: ${trace.action.from} -> ${trace.action.to}`); console.log(` Call type: ${trace.action.callType}`); console.log(` Tx: ${trace.transactionHash}`); } }; traceFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # USDC token address USDC = "0x3600000000000000000000000000000000000000" current_block = web3.eth.block_number from_block = hex(current_block - 1000) to_block = hex(current_block) result = web3.provider.make_request("trace_filter", [{ "fromBlock": from_block, "toBlock": to_block, "toAddress": [USDC], "count": 20 }]) traces = result['result'] print(f"Found {len(traces)} traces to USDC") for trace in traces: print(f"Block {trace['blockNumber']}: {trace['action']['from']} -> {trace['action']['to']}") print(f" Call type: {trace['action']['callType']}") print(f" Tx: {trace['transactionHash']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_filter", "params": [{ "fromBlock": "0x564000", "toBlock": "0x565000", "toAddress": ["0x3600000000000000000000000000000000000000"], "count": 20 }], "id": 1 }' ``` # trace_get | Arc Source: https://docs.chainstack.com/reference/arc-trace-get openapi/arc_node_api/debug_and_trace/trace_get.json POST / Arc API method that returns a specific trace from a transaction by its trace address. Available on Arc via Chainstack JSON-RPC nodes. Arc API method that returns a specific trace from a transaction by its trace address. The trace address is an array of indices that identifies a particular internal call within the transaction execution tree. ## Parameters * `transactionHash` — the hash of the transaction * `traceAddress` — an array of index positions describing the trace address within the transaction ## Response * `result` — the trace object at the specified address, or `null` if not found. ## `trace_get` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "trace_get", "params": ["0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2", ["0x0"]], "id": 1}' ``` # trace_rawTransaction | Arc Source: https://docs.chainstack.com/reference/arc-trace-rawtransaction openapi/arc_node_api/debug_and_trace/trace_rawTransaction.json POST / Arc API method that traces a signed transaction without broadcasting it to the network. trace_rawTransaction on Arc via Chainstack. Arc API method that traces a signed transaction without broadcasting it to the network. Returns the execution trace as if the transaction were executed at the current block. ## Parameters * `signedTransaction` — the signed transaction data as a hex string * `traceTypes` — an array of trace types to include (e.g., `trace`, `vmTrace`, `stateDiff`) ## Response * `result` — the trace result object containing the requested trace types. ## `trace_rawTransaction` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "trace_rawTransaction", "params": ["0x00", ["trace"]], "id": 1}' ``` # trace_replayBlockTransactions | Arc Source: https://docs.chainstack.com/reference/arc-trace-replayblocktransactions openapi/arc_node_api/debug_and_trace/trace_replayBlockTransactions.json POST / Arc API method that replays all transactions in a block and returns execution traces for each. Available on Arc via Chainstack. Arc API method that replays all transactions in a block and returns execution traces for each. This is useful for detailed analysis of block execution with customizable trace options. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) * `traceTypes` — array of trace types to include: * `trace` — basic execution trace * `vmTrace` — full VM execution trace * `stateDiff` — state changes ## Response * `result` — array of trace results, one per transaction: * `output` — return data from the transaction * `trace` — array of trace objects (if requested) * `vmTrace` — VM execution trace (if requested) * `stateDiff` — state differences (if requested) * `transactionHash` — hash of the transaction ## `trace_replayBlockTransactions` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const replayBlock = async (blockNumber) => { const results = await provider.send("trace_replayBlockTransactions", [ blockNumber, ["trace"] ]); console.log(`Replayed ${results.length} transactions`); for (let i = 0; i < results.length; i++) { const result = results[i]; console.log(`\nTransaction ${i}: ${result.transactionHash}`); console.log(` Output: ${result.output}`); console.log(` Traces: ${result.trace?.length || 0}`); if (result.trace && result.trace.length > 0) { for (const trace of result.trace) { const callType = trace.action.callType || trace.type; console.log(` ${callType}: ${trace.action.from} -> ${trace.action.to || 'Create'}`); } } } }; replayBlock("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("trace_replayBlockTransactions", [ "latest", ["trace"] ]) results = result['result'] print(f"Replayed {len(results)} transactions") for i, tx_result in enumerate(results): print(f"\nTransaction {i}: {tx_result['transactionHash']}") print(f" Output: {tx_result['output']}") traces = tx_result.get('trace', []) print(f" Traces: {len(traces)}") for trace in traces: call_type = trace['action'].get('callType', trace['type']) to = trace['action'].get('to', 'Create') print(f" {call_type}: {trace['action']['from']} -> {to}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_replayBlockTransactions", "params": ["latest", ["trace"]], "id": 1 }' ``` # trace_replayTransaction | Arc Source: https://docs.chainstack.com/reference/arc-trace-replaytransaction openapi/arc_node_api/debug_and_trace/trace_replayTransaction.json POST / Arc API method that replays a specific transaction and returns detailed execution traces. trace_replayTransaction on Arc via Chainstack. Arc API method that replays a specific transaction and returns detailed execution traces. This is useful for debugging transaction behavior with customizable trace options. ## Parameters * `transactionHash` — the hash of the transaction to replay * `traceTypes` — array of trace types to include: * `trace` — basic execution trace * `vmTrace` — full VM execution trace * `stateDiff` — state changes ## Response * `result` — trace result object: * `output` — return data from the transaction * `trace` — array of trace objects (if requested) * `vmTrace` — VM execution trace (if requested) * `stateDiff` — state differences (if requested) ## `trace_replayTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const replayTransaction = async (txHash) => { // Replay with all trace types const result = await provider.send("trace_replayTransaction", [ txHash, ["trace", "stateDiff"] ]); console.log("Transaction Output:", result.output); console.log("Traces:", result.trace?.length || 0); if (result.trace) { for (const trace of result.trace) { const callType = trace.action.callType || trace.type; const to = trace.action.to || "Contract Creation"; console.log(` ${callType}: ${trace.action.from} -> ${to}`); } } if (result.stateDiff) { console.log("\nState changes:"); for (const [address, diff] of Object.entries(result.stateDiff)) { console.log(` ${address}:`); if (diff.balance) { console.log(` Balance: ${diff.balance['*']?.from || 'new'} -> ${diff.balance['*']?.to || diff.balance['+']}`); } } } }; replayTransaction("0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("trace_replayTransaction", [ "0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2", ["trace", "stateDiff"] ]) trace_result = result['result'] print(f"Transaction Output: {trace_result['output']}") print(f"Traces: {len(trace_result.get('trace', []))}") for trace in trace_result.get('trace', []): call_type = trace['action'].get('callType', trace['type']) to = trace['action'].get('to', 'Contract Creation') print(f" {call_type}: {trace['action']['from']} -> {to}") if trace_result.get('stateDiff'): print("\nState changes:") for address, diff in trace_result['stateDiff'].items(): print(f" {address}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_replayTransaction", "params": ["0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2", ["trace", "stateDiff"]], "id": 1 }' ``` # trace_transaction | Arc Source: https://docs.chainstack.com/reference/arc-trace-transaction openapi/arc_node_api/debug_and_trace/trace_transaction.json POST / Arc API method that returns execution traces for a specific transaction. Reference for trace_transaction on Arc via Chainstack. Arc API method that returns execution traces for a specific transaction. This provides detailed call-level traces using the Parity/OpenEthereum trace format. ## Parameters * `transactionHash` — the hash of the transaction to trace ## Response * `result` — array of trace objects for the transaction: * `action` — the action object: * `from` — sender address * `to` — recipient address * `callType` — type of call (call, delegatecall, staticcall, etc.) * `gas` — gas provided * `input` — call data * `value` — value transferred * `blockHash` — hash of the block * `blockNumber` — block number * `result` — the result object: * `gasUsed` — gas consumed * `output` — return data * `subtraces` — number of child traces * `traceAddress` — position in the trace tree * `transactionHash` — hash of the transaction * `transactionPosition` — index of the transaction in the block * `type` — trace type (call, create, suicide, reward) ## `trace_transaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txHash) => { const traces = await provider.send("trace_transaction", [txHash]); console.log(`Transaction has ${traces.length} traces`); for (const trace of traces) { const indent = " ".repeat(trace.traceAddress.length); const callType = trace.action.callType || trace.type; const to = trace.action.to || "Contract Creation"; console.log(`${indent}[${trace.traceAddress.join(",")}] ${callType}`); console.log(`${indent} From: ${trace.action.from}`); console.log(`${indent} To: ${to}`); console.log(`${indent} Gas Used: ${trace.result?.gasUsed || "N/A"}`); if (trace.error) { console.log(`${indent} Error: ${trace.error}`); } } }; traceTransaction("0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("trace_transaction", [ "0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2" ]) traces = result['result'] print(f"Transaction has {len(traces)} traces") for trace in traces: indent = " " * len(trace['traceAddress']) call_type = trace['action'].get('callType', trace['type']) to = trace['action'].get('to', 'Contract Creation') print(f"{indent}[{','.join(map(str, trace['traceAddress']))}] {call_type}") print(f"{indent} From: {trace['action']['from']}") print(f"{indent} To: {to}") if trace.get('result'): print(f"{indent} Gas Used: {trace['result'].get('gasUsed', 'N/A')}") if trace.get('error'): print(f"{indent} Error: {trace['error']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_transaction", "params": ["0xfc48f7c70e8ffaa46b1110dc43f32e90f65434a0949edab384d066c820c57ee2"], "id": 1 }' ``` # Transaction info | Arc Source: https://docs.chainstack.com/reference/arc-transaction-info-rpc-methods Arc transaction JSON-RPC methods — retrieve transactions by hash or index, receipts, and raw transaction data on Arc. Retrieving transaction data from Arc can be done using the following methods: > [card] [eth\_getRawTransactionByHash](/reference/arc-eth-getrawtransactionbyhash) > [card] [eth\_getTransactionByBlockHashAndIndex](/reference/arc-eth-gettransactionbyblockhashandindex) > [card] [eth\_getTransactionByBlockNumberAndIndex](/reference/arc-eth-gettransactionbyblocknumberandindex) > [card] [eth\_getTransactionByHash](/reference/arc-eth-gettransactionbyhash) > [card] [eth\_getTransactionReceipt](/reference/arc-eth-gettransactionreceipt) # txpool_content | Arc Source: https://docs.chainstack.com/reference/arc-txpool-content openapi/arc_node_api/txpool/txpool_content.json POST / Arc API method that returns the contents of the transaction pool. This includes both pending and queued transactions. Arc via Chainstack. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Arc API method that returns the contents of the transaction pool. This includes both pending and queued transactions. > [!WARNING] > > `txpool_content` is not available on Arc Global Nodes and Trader Nodes — the call returns `-32601`. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ## Parameters None. ## Response * `result` — object containing pending and queued transactions: * `pending` — object mapping addresses to their pending transactions (keyed by nonce) * `queued` — object mapping addresses to their queued transactions (keyed by nonce) Each transaction object contains: * `hash` — transaction hash * `nonce` — sender's nonce * `from` — sender address * `to` — recipient address * `value` — value in wei * `gas` — gas limit * `gasPrice` — gas price * `input` — transaction data ## `txpool_content` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTxpoolContent = async () => { const content = await provider.send("txpool_content", []); const pendingAddresses = Object.keys(content.pending); const queuedAddresses = Object.keys(content.queued); console.log(`Pending transactions from ${pendingAddresses.length} addresses`); console.log(`Queued transactions from ${queuedAddresses.length} addresses`); // Show details for pending transactions for (const [address, txs] of Object.entries(content.pending)) { console.log(`\nPending from ${address}:`); for (const [nonce, tx] of Object.entries(txs)) { console.log(` Nonce ${nonce}: ${tx.hash}`); console.log(` To: ${tx.to || 'Contract Creation'}`); console.log(` Gas: ${parseInt(tx.gas, 16)}`); } } }; getTxpoolContent(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("txpool_content", []) content = result['result'] pending_addresses = list(content['pending'].keys()) queued_addresses = list(content['queued'].keys()) print(f"Pending transactions from {len(pending_addresses)} addresses") print(f"Queued transactions from {len(queued_addresses)} addresses") # Show details for pending transactions for address, txs in content['pending'].items(): print(f"\nPending from {address}:") for nonce, tx in txs.items(): print(f" Nonce {nonce}: {tx['hash']}") print(f" To: {tx.get('to', 'Contract Creation')}") print(f" Gas: {int(tx['gas'], 16)}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "txpool_content", "params": [], "id": 1}' ``` # txpool_contentFrom | Arc Source: https://docs.chainstack.com/reference/arc-txpool-contentfrom openapi/arc_node_api/txpool/txpool_contentFrom.json POST / Arc API method that returns the pending and queued transactions in the node's pool for a single sender address. Use it on Arc via Chainstack. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Arc API method that returns the transactions in the node's pool for one sender address. It is [txpool\_content](/reference/arc-txpool-content) narrowed to a single account, so the response stays small on a busy node. > [!WARNING] > > `txpool_contentFrom` is not available on Arc Global Nodes and Trader Nodes — the call returns `-32601`. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ## Parameters * `address` — the sender address to filter the pool by. ## Response * `pending` — the sender's transactions that are ready for inclusion, keyed by nonce. * `queued` — the sender's transactions held back by a nonce gap, keyed by nonce. > [!NOTE] > > Arc produces a block roughly every 0.5 seconds, so a healthy sender's transactions usually clear before you can observe them. An empty result means nothing was pending for that address at that moment. ## `txpool_contentFrom` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("txpool_contentFrom", ["0x5d20879655df3c1e04ab111af0009ad650e762f0"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("txpool_contentFrom", ["0x5d20879655df3c1e04ab111af0009ad650e762f0"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "txpool_contentFrom", "params": ["0x5d20879655df3c1e04ab111af0009ad650e762f0"], "id": 1 }' ``` ## Use case When a user reports that a transaction is not landing, this answers the question directly for their address alone. If the transaction sits in `queued` rather than `pending`, the sender has a nonce gap and every later transaction stays parked until the missing nonce is filled. # txpool_inspect | Arc Source: https://docs.chainstack.com/reference/arc-txpool-inspect openapi/arc_node_api/txpool/txpool_inspect.json POST / Arc API method that returns a human-readable summary of the transactions in the node's pool, grouped by sender and nonce. Use it on Arc via Chainstack. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Arc API method that returns a human-readable summary of the transactions waiting in the node's pool. It reports the same set as [txpool\_content](/reference/arc-txpool-content), but each transaction is collapsed to a single string instead of a full object, which makes it far cheaper to poll. > [!WARNING] > > `txpool_inspect` is not available on Arc Global Nodes and Trader Nodes — the call returns `-32601`. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ## Parameters None. ## Response * `pending` — transactions ready for inclusion, keyed by sender address and then by nonce. Each value is a string of the form `recipient: value wei + gas gas × gasPrice wei`. * `queued` — transactions held back because the sender's nonce sequence has a gap, in the same shape. > [!NOTE] > > Arc produces a block roughly every 0.5 seconds, so the pool drains quickly and both objects are frequently empty. An empty result means the node had nothing pending at that moment, not that the method failed. ## `txpool_inspect` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("txpool_inspect", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("txpool_inspect", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "txpool_inspect", "params": [], "id": 1 }' ``` ## Use case Use `txpool_inspect` when you want to know whether a sender has transactions stuck in the pool without pulling every field of every transaction. Because a nonce gap parks everything behind it in `queued`, this is the fastest way to see that a sender is blocked and which nonce is missing. # Transaction pool | Arc Source: https://docs.chainstack.com/reference/arc-txpool-rpc-methods Arc transaction pool JSON-RPC methods — inspect pending and queued transactions in the node's pool on Arc. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Inspecting the Arc transaction pool can be done using the following methods. On Global Nodes and Trader Nodes, only `txpool_status` is available — see [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). > [card] [txpool\_content](/reference/arc-txpool-content) > [card] [txpool\_contentFrom](/reference/arc-txpool-contentfrom) > [card] [txpool\_inspect](/reference/arc-txpool-inspect) > [card] [txpool\_status](/reference/arc-txpool-status) # txpool_status | Arc Source: https://docs.chainstack.com/reference/arc-txpool-status openapi/arc_node_api/txpool/txpool_status.json POST / Arc API method that returns the number of transactions currently pending or queued in the transaction pool. Arc via Chainstack. Arc API method that returns the number of transactions currently pending or queued in the transaction pool. ## Parameters * `none` ## Response * `result` — an object with: * `pending` — number of pending transactions (hex) * `queued` — number of queued transactions (hex) ## `txpool_status` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTxpoolStatus = async () => { const status = await provider.send("txpool_status", []); console.log(`Pending: ${parseInt(status.pending, 16)}`); console.log(`Queued: ${parseInt(status.queued, 16)}`); }; getTxpoolStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("txpool_status", []) status = result['result'] print(f"Pending: {int(status['pending'], 16)}") print(f"Queued: {int(status['queued'], 16)}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "txpool_status", "params": [], "id": 1}' ``` # web3_clientVersion | Arc Source: https://docs.chainstack.com/reference/arc-web3-clientversion openapi/arc_node_api/client_info/web3_clientVersion.json POST / Arc API method that returns the current client version. Arc is built on the Reth SDK. result — the current client version string. Arc API method that returns the current client version. Arc is built on the Reth SDK. ## Parameters * `none` ## Response * `result` — the current client version string ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getClientVersion() { const version = await provider.send("web3_clientVersion", []); console.log(`Client version: ${version}`); } getClientVersion() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.client_version) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "web3_clientVersion", "params": [], "id": 1}' ``` # web3_sha3 | Arc Source: https://docs.chainstack.com/reference/arc-web3-sha3 openapi/arc_node_api/client_info/web3_sha3.json POST / Arc API method that returns the Keccak-256 hash of the given data. This is the same hashing algorithm used by Ethereum for various purposes. Arc API method that returns the Keccak-256 hash of the given data. This is the same hashing algorithm used by Ethereum for various purposes. ## Parameters * `data` — the data to hash (hex encoded) ## Response * `result` — the Keccak-256 hash of the input data ## Use case The `web3_sha3` method is useful for: * Computing function selectors (first 4 bytes of keccak256 of function signature) * Computing event topics * General-purpose hashing ## `web3_sha3` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const sha3Example = async () => { // Hash hex-encoded data via RPC const hash = await provider.send("web3_sha3", ["0x68656c6c6f"]); // "hello" in hex console.log(`Hash of 'hello': ${hash}`); // Compare with ethers.js local computation const localHash = ethers.keccak256("0x68656c6c6f"); console.log(`Local hash: ${localHash}`); console.log(`Hashes match: ${hash === localHash}`); // Compute a function selector const transferSelector = await provider.send("web3_sha3", [ ethers.toUtf8Bytes("transfer(address,uint256)").reduce( (hex, byte) => hex + byte.toString(16).padStart(2, '0'), '0x' ) ]); console.log(`\nTransfer function selector: ${transferSelector.slice(0, 10)}`); }; sha3Example(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Hash hex-encoded data data = "0x68656c6c6f" # "hello" in hex result = web3.provider.make_request("web3_sha3", [data]) print(f"Hash of 'hello': {result['result']}") # Compare with local computation local_hash = web3.keccak(hexstr=data).hex() print(f"Local hash: {local_hash}") # Compute a function selector transfer_sig = "transfer(address,uint256)" selector = web3.keccak(text=transfer_sig).hex()[:10] print(f"\nTransfer function selector: {selector}") ``` ```bash cURL theme={"system"} # Hash "hello" (0x68656c6c6f in hex) curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x68656c6c6f"], "id": 1}' ``` # Accounts info | Avalanche Source: https://docs.chainstack.com/reference/avalanche-accounts-info-rpc-methods Avalanche account information JSON-RPC methods — retrieve balances, nonces, contract code, and storage data for accounts on the C-Chain network. Retrieving account information from the Avalanche blockchain can be done by using the following methods: > [card] [eth\_getTransactionCount](/reference/avalanche-gettransactioncount) > [card] [eth\_getBalance](/reference/avalanche-getbalance) > [card] [eth\_getCode](/reference/avalanche-getcode) > [card] [eth\_getStorageAt](/reference/avalanche-getstorageat) # Blocks info | Avalanche Source: https://docs.chainstack.com/reference/avalanche-blocks-rpc-methods Avalanche block information JSON-RPC methods — query block details including transactions, timestamps, height, headers, and uncle counts on C-Chain. Retrieving block information from the Avalanche blockchain can be done by using the following methods: > [card] [eth\_blockNumber](/reference/avalanche-getblocknumber) > [card] [eth\_getBlockByHash](/reference/avalanche-getblockbyhash) > [card] [eth\_getBlockByNumber](/reference/avalanche-getblockbynumber) > [card] [eth\_getBlockTransactionCountByHash](/reference/avalanche-getblocktransactioncountbyhash) > [card] [eth\_getBlockTransactionCountByNumber](/reference/avalanche-getblocktransactioncountbynumber) > [card] [eth\_newBlockFilter](/reference/avalanche-newblockfilter) This information can be used to track the state of the blockchain and for: * **Blockchain analysis** — insights into the overall activity on the network, such as the number of processed transactions and average transaction fees, can be obtained. * **Block explores** — web-based tools that provide access to information on a blockchain, including block data, transactions, and addresses, allowing users to search, view, and analyze the network's activity. * **Smart contract analysis** — the activity of specific smart contracts * **DApp development** — block information can be utilized in the development of decentralized applications. # Chain info | Avalanche Source: https://docs.chainstack.com/reference/avalanche-chain-data-rpc-methods Avalanche C-Chain data RPC methods for querying network details like chain ID, syncing status, and protocol version through Chainstack endpoints. For example, if a developer wants to build a DApp on Avalanche, they would use these methods to get information about the network they are connecting to. They could use the chain ID information to make sure they are connecting to the right network and to ensure the security of their DApp; for example, distinguishing between the main network and a test network. Retrieving chain information from the Avalanche blockchain can be done by using the following methods: > [card] [eth\_chainId](/reference/avalanche-getchainid) > [card] [eth\_syncing](/reference/avalanche-syncing) # clearSubscriptions | Avalanche Source: https://docs.chainstack.com/reference/avalanche-clearsubscriptions Use eth_clearSubscriptions on Avalanche via ethers.js to remove all active WebSocket subscriptions from your Chainstack node connection at once. The subscriptions available using the `provider.on` method are: > [card] [subscribe('newBlockHeaders')](/reference/avalanche-subscribenewblockheaders) > [card] [subscribe('pendingTransactions')](/reference/avalanche-subscribependingtransactions) > [card] [subscribe('logs')](/reference/avalanche-subscribelogs) > [card] [subscribe('syncing')](/reference/avalanche-subscribesyncing) ## Parameters `boolean` — keep the [subscribe("syncing")](/reference/avalanche-subscribesyncing) subscription if `true`. ## Response `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `clearSubscriptions` code example ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function clearSubscriptions() { try { await provider.removeAllListeners(); console.log("Subscriptions were canceled successfully"); return process.exit(1) } catch (error) { console.error("Failed to cancel subscriptions:", error); return process.exit(1) } } clearSubscriptions() ``` ## Use case A way to use the `clearSubscriptions` method is to place it in a timer to clear all of the subscriptions after a pre-determined about. The following code is an example using the [subscribe("newBlockHeaders")](/reference/avalanche-subscribenewblockheaders) method, which will be stopped and removed after one minute using `clearSubscriptions`: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToNewBlocks() { try { // Create a new subscription to the 'block' event provider.on("block", handleNewBlock); // Attach an event listener for any provider errors provider.on("error", handleError); console.log("New subscription created"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs the received block number function handleNewBlock(blockNumber) { console.log(blockNumber); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } async function clearSubscriptions() { try { await provider.removeAllListeners(); console.log("Subscriptions were canceled successfully"); return process.exit(1) } catch (error) { console.error("Failed to cancel subscriptions:", error); return process.exit(1) } } async function main() { subscribeToNewBlocks(); // Run clearSubscriptions() once after 60 seconds setTimeout(clearSubscriptions, 60000); } main() ``` The code starts by defining an async function called `subscribeToNewBlocks` that creates a new subscription to the `block` event using the `provider.on` method. This method attaches event listeners to the provider for different events, such as `block` and `error`. The code defines two event listener functions `handleNewBlock` and `handleError` that log messages to the console when these events occur. The `subscribeToNewBlocks` function is called from the `main` function, which is also defined in the code. The `main` function calls `subscribeToNewBlocks` to create a new subscription and then schedules the `clearSubscriptions` function to run once after 60 seconds using the `setTimeout` function. The `clearSubscriptions` function uses the `provider.removeAllListeners` method to cancel any existing subscriptions and log a message to the console if the cancellation was successful. # Client information | Avalanche Source: https://docs.chainstack.com/reference/avalanche-client-data-rpc-methods Avalanche client information JSON-RPC methods — check client version, network ID, chain ID, listening status, and peer count for your C-Chain node. Retrieving blockchain client information from an Avalanche node can be done by using the following methods: > [card] [web3\_clientVersion](/reference/avalanche-listening) > [card] [net\_listening](/reference/avalanche-listening) # web3_clientVersion | Avalanche Source: https://docs.chainstack.com/reference/avalanche-clientversion openapi/avalanche_node_api/client_info/web3_clientVersion.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns the client type and version running on the Avalanche node. Available on Avalanche via Chainstack. Avalanche API method that returns the client type and version running on the Avalanche node. This information can be useful to developers to verify that a node they are connecting to is compatible with their needs. ## Parameters * `none` ## Response * `string` — a string identifying the type of client, version, operating system, and language version running on the node ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const clientVersion = async () => { const version = await provider.send("web3_clientVersion"); console.log(`Client version: ${version}`); }; clientVersion(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) client_version = web3.provider.make_request('web3_clientVersion', []) print(client_version) ``` ## Use case A use case for the `web3_clientVersion` method can be to verify which client version is running to then decide which function to call. Let's say you have a DApp that needs to call a different function based on the client's version. You can use `web3_clientVersion` to build this logic. Here is an implementation of this use case using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Get client version async function getClientVersion() { const clientVersion = await provider.send("web3_clientVersion"); return clientVersion } // define a function to be run if the client version is 'v0.11.5' function runFunction1() { console.log('Running function 1...'); } // define a function to be run if the client version is 'v0.11.4' function runFunction2() { console.log('Running function 2...'); } // define an async function to retrieve the client version and run the appropriate function async function runBasedOnClientVersion() { try { const clientVersion = await getClientVersion(); console.log(`Client version: ${clientVersion}`); if (clientVersion === 'v0.11.5') { runFunction1(); } else if (clientVersion === 'v0.11.4') { runFunction2(); } // log an error if the client version is not recognized else { console.error('Unrecognized client version.'); } } catch (error) { console.error(error); } } // call the async function to retrieve the client version and run the appropriate function runBasedOnClientVersion(); ``` This code uses the ethers.js library to connect to a blockchain node specified by the `NODE_URL` variable and retrieves the client version using the `web3_clientVersion` method. It then runs different functions based on the client version by defining two functions `runFunction1` and `runFunction2`, and using an if statement to check the client version and call the appropriate function. The `getClientVersion` function uses `provider.send` to call the `web3_clientVersion` method directly and returns the result. This function is called within the `runBasedOnClientVersion` function to retrieve the client version. Overall, this code demonstrates how to retrieve the client version on a blockchain node using the ethers.js library and how to use the client version to run different functions based on the version. The code can be modified to add additional functions and version checks or to perform other actions based on the client version. # Debug and Trace | Avalanche Source: https://docs.chainstack.com/reference/avalanche-debug-trace-rpc-methods Avalanche debug and trace API methods for inspecting smart contract execution, analyzing gas usage, and tracing transactions on Chainstack nodes. Developers can access the debug and trace tools with the following methods: > [card] [debug\_traceBlockByHash](/reference/avalanche-traceblockbyhash) > [card] [debug\_traceBlockByNumber](/reference/avalanche-traceblockbynumber) > [card] [debug\_traceCall](/reference/avalanche-tracecall) > [card] [debug\_traceTransaction](/reference/avalanche-tracetransaction) ## Pre-built native tracers > [!NOTE] > > ### Custom JavaScript tracers need a Dedicated Node > > The tracers listed below are native (built-in) tracers—you select one by name. Custom JavaScript tracers, where you pass raw JavaScript as the `tracer`, are available as customized solutions on the [Enterprise plan](https://chainstack.com/pricing/) on [Dedicated Nodes](/docs/dedicated-node). Developers can customize the type of tracing using various debug and trace methods, and there are numerous pre-built tracers available to choose from. Further is a list of the pre-built tracers that can be utilized. ### `4byteTracer` This tracer looks for function signatures that are 4 bytes long during contract execution and gathers information about the method identifiers and the size of the provided data. This information can be helpful in matching a signature against the data size. ### `callTracer` This tracer extracts information about all internal calls made by a transaction, providing visibility into how contracts interact with each other. ### `prestateTracer` This tracer returns sufficient information about an account to create a local execution of the transaction. This procedure runs the given transaction again and monitors every aspect of the state that is modified or accessed during execution. ### `noopTracer` This tracer does nothing but respond with a blank object. This a no operations tracer for testing setup. # eth_estimateGas | Avalanche Source: https://docs.chainstack.com/reference/avalanche-estimategas openapi/avalanche_node_api/gas_data/eth_estimateGas.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns an estimate of gas units needed for a given transaction. eth_estimateGas on Avalanche via Chainstack. Avalanche API method that returns an estimate of gas units needed for a given transaction. It provides an estimate of the maximum amount of gas units required to complete the transaction based on the network's current state and the transaction's parameters. A client or wallet can use this estimate to determine the appropriate amount of gas to include in a transaction to ensure that the network processes it. ## Parameters * `object` — the transaction call object, same as in `eth_call`; the `from` field is optional, and the `nonce` field is omitted: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — the estimated amount of gas units needed, represented as a hexadecimal string. ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimateGas = async () => { // The transaction to estimate is a simple transfer in this example. const transactionObj = { from: "0x20b963E266767075F26e16bc1887CaA3c7c68Ed2", to: "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8", } const gasUnits = await provider.estimateGas(transactionObj); console.log(`Estimated gas units required: ${gasUnits}`); }; estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.estimate_gas({"from":"0x20b963E266767075F26e16bc1887CaA3c7c68Ed2","to":"0xbe0eb53f46cd790cd13851d5eff43d12404d33e8"}, "latest" )) ``` ## Use case A practical use case for `eth_estimateGas` is to calculate the gas cost required for a transaction that transfers a value from one Avalanche address (`fromAddress`) to another (`toAddress`). The gas cost is an important factor in transactions, as it determines the amount of fees that need to be paid by the sender to the network for processing the transaction. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function estimateGasCost(from, to, value) { try { const gasEstimate = await provider.estimateGas({ from: from, to: to, value: value }); return gasEstimate } catch (error) { console.error(`Error estimating gas cost: ${error}`); } } async function main() { const fromAddress = '0x4E75e27e5Aa74F0c7A9D4897dC10EF651f3A3995'; const toAddress = '0x81ab6Fd4A68d0aDDFAD65F9F97Bc1427dCd40C8E'; const value = '25000000000000000000'; // 25 Avax in Wei const estimatedGas= await estimateGasCost(fromAddress, toAddress, value) if (estimatedGas) { console.log(`Estimated gas cost: ${estimatedGas}`); } } main() ``` The `estimateGasCost` function wraps the call to `provider.estimateGas` in a `try-catch` block so that any errors that occur during the estimation process can be handled gracefully. If an error occurs, the error message is logged to the console. If the estimate is successful, the estimated gas cost is returned. The main function is the entry point of the code. It sets the `fromAddress`, `toAddress`, and value for the transaction, and then calls the `estimateGasCost` function to get the estimated gas cost. The estimated gas cost is logged to the console if the estimate is successful, but it is available to be used for further processing in the `estimatedGas` constant. # eth_call | Avalanche Source: https://docs.chainstack.com/reference/avalanche-ethcall openapi/avalanche_node_api/execute_transactions/eth_call.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. Avalanche API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. This can be useful for testing and debugging by simulating transfers or smart contract transactions and retrieving data from the blockchain. > [!NOTE] > > **Disclaimer** > > The interactive example on this page uses `eth_call` to call the `poolLength` method from [Trader Joe: Master Chef Joe V2 smart contract on Avalanche](https://snowtrace.io/address/0xd6a4F121CA35509aF06A0Be99093d08462f53052#code). It returns how many liquidity pools are available on the Trader Joe exchange. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `object` — the transaction call object: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters. See the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `data` — the result of executing the specified call on the Ethereum Virtual Machine (EVM), encoded as a hexadecimal string. ## `eth_call` code examples The following code snippets provide a clear demonstration of how to simulate a standard AVAX transfer between two accounts. Note that when using `eth_call` to simulate a transfer or a smart contract transaction, the sender account must have a sufficient balance to cover the associated gas fee. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const simulateTransfer = async () => { try { // Define the accounts to transfer between const fromAddress = "0xCd19A5475983D85bdb77991FA886105743a9CbE9"; const toAddress = "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"; // Define the value to transfer const value = ethers.parseEther("1"); // Build transaction object const transactionObject = { from: fromAddress, to: toAddress, gasLimit: ethers.toQuantity(21000), value: value } // Use eth_call to simulate the transfer on the latest block const result = await provider.call(transactionObject); console.log(`Result: ${result}`); } catch (error) { console.error(`Error: ${error.message}`); } } simulateTransfer(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the accounts to transfer between from_address = web3.to_checksum_address('0xAd80f9fcc2a315aBB6007F6fd6cCEC74C1168b9a') to_address = web3.to_checksum_address('0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a') # Define the value to transfer (in Ether) value = web3.to_wei(1, 'ether') # Build the transaction object transaction = { 'from': from_address, 'to': to_address, 'gas': 21000, # 21000 is the standard for a regular transfer 'value': value, 'data': '', } # Use eth_call to simulate the transfer result = web3.eth.call(transaction, 'latest') print(result) ``` Executing this code will produce a return value of `0x`, indicating a successful transaction. This implies that the transaction will be executed without any errors when the code is used to send the transaction. ## Use case A common use case for `eth_call` is calling smart contract functions. For example, interact with an ERC-20 token smart contract deployed on the Avalanche network and retrieve the balance of a specific account. The following code uses the ethers.js library to call the `balanceOf` function of the [AAVE token smart contract](https://snowtrace.io/token/0x63a72806098bd3d9520cc43356dd78afe5d386d9#code) on the Avalanche network. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); // Contract address and ABI for the token const CONTRACT_ADDRESS = "0x63a72806098Bd3D9520cC43356dD78afe5D386D9"; const ABI = [ { "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "balance", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" } ]; // Asynchronously fetches the balance of an account for the given address async function getAccountBalance(address) { try { // Create a new contract instance const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider); // Call the balanceOf function to retrieve the account balance in wei using eth_call const balanceInWei = await contract.balanceOf(address); // Convert the balance from wei to ether const balanceInEth = ethers.formatUnits(balanceInWei, "ether"); // Log the account balance in ether to the console console.log(`The account balance for address ${address} is: ${balanceInEth} AAVE`); } catch (error) { console.error("Error occurred while fetching account balance:", error); } } // Call the getAccountBalance function with a sample account address getAccountBalance("0x78bbcab7902f14fb84563aea2b8ae3f7d5c8c5c4"); ``` The `getAccountBalance` function is called with the address of the account whose balance we want to retrieve. This function creates a new contract instance, calls the `balanceOf` function of the smart contract with the provided address, and retrieves the account balance in wei. The balance is then converted to ether using the `formatUnits` function of the `ethers` object. Finally, the account balance is printed to the console for easy viewing. # Executing transactions | Avalanche Source: https://docs.chainstack.com/reference/avalanche-execution-rpc-methods Avalanche transaction execution JSON-RPC methods — call smart contracts, sign transactions, send raw transactions, and estimate gas on the C-Chain. Executing and simulating transactions on the Avalanche blockchain can be done by using the following methods: > [card] [eth\_call](/reference/avalanche-ethcall) > [card] [eth\_sendRawTransaction](/reference/avalanche-sendrawtransaction) # Filter handling | Avalanche Source: https://docs.chainstack.com/reference/avalanche-filters-rpc-methods Avalanche filter JSON-RPC methods — create event and block filters, poll for changes, retrieve filter logs, and manage subscriptions on the C-Chain. Filters are a mechanism for clients to receive notifications of changes to the Avalanche blockchain. The methods described in this section allow developers to retrieve the changes that happened since the filter was last polled or allow them to delete it. Handling filters can be done by using the following methods: > [card] [eth\_getFilterChanges](/reference/avalanche-getfilterchanges) > [card] [eth\_uninstallFilter](/reference/avalanche-uninstallfilter) # Gas data | Avalanche Source: https://docs.chainstack.com/reference/avalanche-gas-data-rpc-methods Avalanche gas data JSON-RPC methods — query current gas prices and estimate gas costs for transactions to build cost-effective dapps on the C-Chain. Retrieving gas information from the Avalanche blockchain can be done by using the following methods: > [card] [eth\_estimateGas](/reference/avalanche-estimategas) > [card] [eth\_gasPrice](/reference/gasprice) # eth_getBalance | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getbalance openapi/avalanche_node_api/accounts_info/eth_getBalance.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns the balance of a specific Avalanche account in Wei, the smallest unit of ether. Avalanche via Chainstack. Avalanche API method that returns the balance of a specific Avalanche account in [Wei](https://www.investopedia.com/terms/w/wei.asp), the smallest unit of ether. This method allows developers to retrieve the current balance of an Avalanche account for various purposes, such as checking available funds or displaying balance information. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to check the balance of * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — the integer value of the current balance in Wei ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getBalance = async (address, block) => { const balance = await provider.send("eth_getBalance", [address, block]); console.log(balance); }; getBalance("0x38545c4b331D8BFb3bee94C62D77a6735b5eF8c0", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" address = web3.to_checksum_address("0x38545c4b331D8BFb3bee94C62D77a6735b5eF8c0") balance = web3.eth.get_balance(address, "latest") print(balance) ``` ## Use case One practical use case for `eth_getBalance` is to check the balance of an account for a program that scans the balance periodically and fills up the account when the balance drops below a certain value. Here's an example of how you can achieve this in ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const accountAddress = '0xB18614D1e3A3B67A6E0c83Be98AC04157b674083'; const minimumBalance = 10000000000000000000n; // 10 Avax in wei async function checkBalance(address) { const balance = await provider.getBalance(address, 'latest'); return balance; } async function fillUp(balance) { const minimumConverted = ethers.formatEther(minimumBalance); if (balance < minimumBalance) { console.log(`The balance of the account ${accountAddress} is below ${minimumConverted} AVAX.`); console.log(`Sending more funds...`) // Call another function to fill up your account } else { console.log(`The balance of the account ${accountAddress} is above ${minimumConverted} AVAX.`); console.log(`No need to send more funds.`) } } async function main() { const balance = await checkBalance(accountAddress); const balanceInMatic = ethers.formatEther(balance); console.log(`The balance of the account ${accountAddress} is ${balanceInMatic} AVAX. \n`); await fillUp(balance); } main(); ``` The code is a simple script that checks the balance of an account on the Avalanche network and takes action if the balance falls below a specified minimum. The `accountAddress` variable holds the address for which you want to check the balance. The `minimumBalance` variable holds the minimum balance in Wei you want to check against. The `checkBalance` function takes an address as an argument and returns the balance of the account in Wei. The `fillUp` function takes the balance as an argument, and converts the minimum balance from Wei to AVAX using the `formatEther` method from ethers.js, and checks if the balance is below the minimum balance. > [!NOTE] > > Note that the Wei value is converted only to display it, as it is more accurate to work with the Wei value. If the balance is below the minimum, it logs a message to the console indicating that more funds need to be sent and calls another function to fill up the account. If the balance is above the minimum, it logs a message indicating that no additional funds need to be sent. The main function calls the `checkBalance` and `fillUp` functions to convert the balance from Wei to AVAX using the `formatEther` method, and log the balance and the result of the check to the console. In summary, this script allows you to easily check the balance of an account on the Avalanche network and take action if the balance falls below a specified minimum. # eth_getBlockByHash | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getblockbyhash openapi/avalanche_node_api/blocks_info/eth_getBlockByHash.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns information about the block matching the given block hash. Available on Avalanche via Chainstack. Avalanche API method that returns information about the block matching the given block hash. This method can be useful for analyzing a block and its transactions. ## Parameters * `hash` — the hash of the block * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — the hash of the previous block that was used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ethereum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ethereum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. Avalanche C-Chain uses Snowman consensus protocol and this value is not relevant. * `extradata` — the extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — the array of uncle hashes. ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByHash = async () => { const blockByNumber = await provider.send("eth_getBlockByHash", ["0xcd97eae4d21a74d2f42251153e80ccb6b9520278f10603b1acb8f2f4d727a87c",false]); console.log(blockByNumber); }; eth_getBlockByHash(); ``` ```python web3.py theme={"system"} # Avalanche is a Proof of Authority (POA) chain. Web3.py requires the use of the POA compatibility middleware. from web3 import Web3 from web3.middleware import geth_poa_middleware node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Inject the POA compatibility middleware to the innermost layer. web3.middleware_onion.inject(geth_poa_middleware, layer=0) print(web3.eth.get_block("0xcd97eae4d21a74d2f42251153e80ccb6b9520278f10603b1acb8f2f4d727a87c", False)) ``` ## Use case The `eth_getBlockByHash` is excellent for analytics purposes. For example, it can be used to quickly find out the hashes of transactions that create new smart contracts. The `transactions` field, an array of all the transactions within the block, is returned in the response. By iterating through this array and examining each transaction's `to` field, a user can determine which transactions in the block are creating new smart contracts. The following example uses the web3.js library to create a function to extract this data: The previous example shows how the `eth_getBlockByHash` method might be used to know which transactions create a smart contract. When a new smart contract is created, the `to` field will be `null`. Running this function on a block hash with the transactions detail field set to `true` will inspect each transaction's `to` field. If the field is `null`, the transaction creates a smart contract, printing the transaction hash, which can then be used for further analysis. # eth_getBlockByNumber | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getblockbynumber openapi/avalanche_node_api/blocks_info/eth_getBlockByNumber.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns information about the block matching the given block number. Use it on Avalanche via Chainstack. Avalanche API method that returns information about the block matching the given block number. This method can retrieve a wide range of information about the block, such as its hash, timestamp, difficulty, and gas limit. You can also retrieve transaction objects included in the block if needed. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — a block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ethereum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ethereum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. Avalanche C-Chain uses Snowman consensus protocol and this value is not relevant. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — array of transaction objects. See [eth\_getTransactionByHash](/reference/avalanche-gettransactionbyhash) for the exact shape. * `uncles` — array of uncle hashes. ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByNumber = async () => { const blockByNumber = await provider.send("eth_getBlockByNumber", ["latest",false]); console.log(blockByNumber); }; eth_getBlockByNumber(); ``` ```python web3.py theme={"system"} # Avalanche is a Proof of Authority (POA) chain. Web3.py requires the use of the POA compatibility middleware. from web3 import Web3 from web3.middleware import geth_poa_middleware node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Inject the POA compatibility middleware to the innermost layer. web3.middleware_onion.inject(geth_poa_middleware, layer=0) print(web3.eth.get_block("latest", False)) ``` ## Use case The `eth_getBlockByNumber` method can determine whether a block you are analyzing has been included in the main chain or was forked due to a 'reorg'. A block hash is generated through a cryptographic hash function, which takes the block's data, including transactions and other information, and the hash of the parent block (the previous block) and outputs a unique, fixed-length digest. This hash represents the current block within the blockchain. The script compares the parent hash of the following block with the hash under inspection. If the two values match, it can be concluded that the block was included in the main chain. Conversely, if the values do not match, it indicates that the block was not used to generate the next block's hash and has therefore been excluded from the main chain, resulting in a forked block. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT" const provider = new ethers.JsonRpcProvider(NODE_URL) // If the hash of the block to inspect is === to the parent hash of the next block, then this is not a forked block. async function isForked(block) { const nextBlock = blockToInspect + 1 console.log(`Block to inspect: ${block}`) console.log(`Next block: ${nextBlock} \n`) const blockToInspectHash = (await provider.getBlock(block, false)).hash console.log(`Block hash of the block you are inspecting: ${blockToInspectHash} \n`) const nextBlockParentHash = (await provider.getBlock(nextBlock, false)).parentHash console.log(`Parent hash of the next block: ${nextBlockParentHash} \n`) if (String(nextBlockParentHash) == String(nextBlockParentHash)) { console.log(`You are inspecting the correct block`) return false } else { console.log(`You are inspecting a forked block!`) return true } } const blockToInspect = 27425409 isForked(blockToInspect) ``` In this case, the `eth_getBlockByNumber` is used to retrieve the hash of a block you want to analyze and the parent hash of the next block. # eth_blockNumber | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getblocknumber openapi/avalanche_node_api/blocks_info/eth_blockNumber.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns the latest block number of the blockchain. Available on Avalanche via Chainstack JSON-RPC nodes. Avalanche API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0x69B5B` means that the block is the 432,987th block in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const eth_getBlockNumber = async () => { const block_Number = await provider.getBlockNumber(); console.log(block_Number); }; eth_getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.block_number) ``` ## Use case A possible use case for the `eth_blockNumber` method in Avalanche is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. The following code uses the web3.js library and defines a function called `getCurrentBlockNumber`. When called, this function returns the latest block number from the network, and you can use it in your DApp. You can then use your application's `getCurrentBlockNumber` function to get the current block number whenever you need it. In this example, we use it to calculate on which block a special offer to a specific user will expire. We retrieve the block when we issue the offer to the user and add `50400`, which is approximately how many Ethereum blocks are produced weekly. Now, in the `offerExpires` constant, we have the last block when the user can exercise the special price. Use the `getCurrentBlockNumber` function to compare it to `offerExpires` to know if the offer is still valid. # eth_getBlockTransactionCountByHash | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getblocktransactioncountbyhash openapi/avalanche_node_api/blocks_info/eth_getBlockTransactionCountByHash.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. Avalanche API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. ## Parameters * `hash` — the block hash of the requested block. ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockHash) => { const count = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); console.log(count); }; getTransactionsCount('0x49b96784d08c346d56ace5b1ebfee608479ec3bd51e7d11b3d955578efde648c'); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block_transaction_count("0x49b96784d08c346d56ace5b1ebfee608479ec3bd51e7d11b3d955578efde648c")) ``` ## Use case `eth_getBlockTransactionCountByHash` is a useful tool for analyzing transaction volume on the Avalanche blockchain. On average, a new block is generated on the Avalanche mainnet every 2 seconds, resulting in approximately 1,700 blocks per hour. Using the ethers.js library, one can inspect the past 1,700 blocks starting from the latest block, retrieve the hash of each block, and use `eth_getBlockTransactionCountByHash` to find the number of transactions in each block. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function retrieveTransactionsCount() { // Get the current block number const currentBlockNumber = await provider.getBlockNumber(); // Calculate the block number from one hour ago const oneHourAgoBlockNumber = currentBlockNumber - 1700; // 1700 blocks/hr on average on the Avalanche mainnet // Initialize a variable to store the total number of transactions let totalTransactionCount = 0; // Loop through all blocks from the current block number to the block number from one hour ago for (let block = currentBlockNumber; block > oneHourAgoBlockNumber; block--) { // Get the hash of each block const getBlockHash = await provider.getBlock(block, false); const blockHash = getBlockHash.hash // Get the number of transactions in this block using each hash const transactionCount = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); // Add the number of transactions in this block to the total totalTransactionCount += Number(transactionCount); console.log(`Block #${block} has ${Number(transactionCount)} transactions.`); } console.log(`The total number of transactions in the previous hour is: ${totalTransactionCount}`); } retrieveTransactionsCount(); ``` This example connects to an Avalanche node using the ethers.js library. The `retrieveTransactionsCount` function gets the current block number, calculates the block number from one hour ago, and uses a for loop to iterate through all blocks in that range. The function then retrieves the hash of each block and the number of transactions in each block and adds it to a total transaction count. The final result, the total number of transactions in the previous hour, is logged to the console. This is a good example of how you can combine different methods. # eth_getBlockTransactionCountByNumber | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getblocktransactioncountbynumber openapi/avalanche_node_api/blocks_info/eth_getBlockTransactionCountByNumber.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns the number of transactions in a block specified by block number or tag. Avalanche via Chainstack. Avalanche API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockId) => { const count = await provider.send("eth_getBlockTransactionCountByNumber", [blockId]); console.log(count); }; getTransactionsCount('latest') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.get_block_transaction_count(32732976)) # A hex value starting with "0x" is accepted as well. ``` ## Use case `eth_getBlockTransactionCountByNumber` can be used to analyze how many transactions are included on the Avalanche blockchain in a certain period. On average, a new block is generated on the Avalanche mainnet every 2 seconds, resulting in approximately 1,700 blocks per hour. Using the ethers.js library, one can inspect the past 1,700 blocks starting from the latest block and use `eth_getBlockTransactionCountByNumber` to find the number of transactions in each block to sum them. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); async function retrieveTransactionCounts() { // Get the current block number const currentBlockNumber = await provider.getBlockNumber(); // Calculate the block number from one hour ago const oneHourAgoBlockNumber = currentBlockNumber - 1700; // 1700/blocks/hr on average on the Avalanche mainnet // Initialize a variable to store the total number of transactions let totalTransactionCount = 0; // Loop through all blocks from the current block number to the block number from one hour ago for (let block = currentBlockNumber; block > oneHourAgoBlockNumber; block--) { // Get the number of transactions in this block const transactionCount = await provider.send("eth_getBlockTransactionCountByNumber", [block]); // Make the necessery conversions from hex to decimal const stringCount = BigInt(transactionCount).toString(); const decimalCount = Number(stringCount) // Add the number of transactions in this block to the total totalTransactionCount += decimalCount; const stringBlock = BigInt(block).toString(); const decimalBlock = Number(stringBlock) console.log(`Block ${decimalBlock} has ${decimalCount} transactions.`); } console.log(`The total number of transactions in the previous hour is: ${totalTransactionCount}`); } retrieveTransactionCounts(); ``` Using `eth_getBlockTransactionCountByNumber` is a bit more straightforward compared to the [example shown using `eth_getBlockTransactionCountByHash`](/reference/avalanche-getblocktransactioncountbyhash) for this same use case, you don't need to extract the block hash; instead, you can query the transaction count directly. This example is made using the ethers.js library, and you can notice that it requires more parsing compared to web3.js, and the results are given as hexadecimal. # eth_chainId | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getchainid openapi/avalanche_node_api/chain_info/eth_chainId.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns the current chain ID. eth_chainId JSON-RPC method available on the Avalanche blockchain via Chainstack. Avalanche API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and generally verify if a network is the desired one. It was introduced in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). ## Parameters * `none` ## Response * `quantity` — EIP-155 Chain ID ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const chainId = async () => { // This will return the value in Hex const chainId = await provider.send("eth_chainId"); console.log(`Hex Chain ID: ${chainId}`); }; chainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.chain_id) ``` ## Use case One possible use case for the `eth_chainId` method in Avalanche is to determine which chain a user is connected to when using a DApp with the MetaMask browser extension. This can be useful to provide a customized user experience based on the specific chain that the user is interacting with. Here is an example of how the `eth_chainId` method might be used in this context with MetaMask. Keep in mind that the Chain ID is returned as a hexadecimal value: ```js JavaScript theme={"system"} // Check which network is selected on MetaMask async function checkChain() { // Declare the desired chain ID to match const desiredChainId = '0xA86A'; // Avalanche mainnet chain ID // Retrieve the current Chain ID selected by the user ethereum.request({ method: 'eth_chainId' }).then(chainId => { // compare the actual chain ID it to the declared chain ID if (chainId !== desiredChainId) { console.log(`You are currently on the wrong network. Please switch to the mainnet.`); // Call the promtSwitch function promtSwitch() } else { console.log("This is the correct network") } }) } // Prompt user to switch to a determined network based on the chain ID async function promtSwitch() { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0xA86A' // chainId must be in HEX with 0x in front }], }); } ``` In this case, the `checkChain` function is called to get the current chain ID using the Ethereum object provided by MetaMask. The chain ID is then checked to determine if the user is connected to the Avalanche mainnet and calls the `promtSwitch` function to prompt the user to switch chain in case the chain ID returned does not match the `desiredChainId` constant. # eth_getCode | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getcode openapi/avalanche_node_api/accounts_info/eth_getCode.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that retrieves the compiled bytecode of a smart contract providing its address as a parameter. Avalanche via Chainstack. Avalanche API method that retrieves the compiled bytecode of a smart contract providing its address as a parameter. This method returns a hexadecimal string representing the smart contract's bytecode. Developers can use this bytecode to verify if a smart contract is legitimate and to ensure that it performs the intended functions. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response `data` — the compiled bytecode of a smart contract. Returns `0x` if the address is not associated with a smart contract. ## `eth_getCode` code examples The following example retrieves the compiled bytecode from the [SushiSwapRouter smart contract](https://snowtrace.io/address/0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506#code). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getCode = async (address, block) => { const code = await provider.send("eth_getCode", [address, block]); console.log(code); }; getCode("0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" code = web3.eth.get_code("0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", "latest") print(web3.to_hex(code)) # Convert the Bytes result into HEX. ``` ## Use case A practical use case for the `eth_getcode` method is verifying whether an address is associated with a smart contract. This can be particularly useful for blockchain explorers, auditors, and DApp developers, who must ensure that the code they interact with is legitimate. Here is an example using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function verifyContractAddress(address) { try { const bytecode = await provider.getCode(address); return bytecode !== '0x'; } catch (error) { console.error(`Error: ${error.message}`); return false; } } async function main() { const addressToVerify = "0xf26f52d5985F6391E541A8d638e1EDaa522Ae56C" const isContract = await verifyContractAddress(addressToVerify); if (isContract) { console.log(`📑The address ${addressToVerify} is a smart contract.`); } else { console.log(`🚨The address ${addressToVerify} is not a smart contract.`); } } main(); ``` The `verifyContractAddress` function takes an `address` as a parameter and returns a `boolean` value indicating whether the address is associated with a smart contract. The function works by using the `provider.getCode` method to retrieve the bytecode of the contract associated with the given address. If the returned bytecode is not equal to `0x`, then the function assumes that the address is associated with a smart contract and returns `true`. If an error occurs while calling `provider.getCode`, the function returns `false` and logs the error message to the console. The `main` function is responsible for calling `verifyContractAddress` with a specific address to verify and log the results to the console. In this example, `main` is set up to verify the address `0xc094fd7ef80be094f3d98e227a487490ab1f9b9c`. After calling `verifyContractAddress`, `main` logs either a success message indicating that the address is associated with a smart contract or a warning message indicating that it is not. Overall, this code provides a useful utility for verifying the legitimacy of smart contract addresses on the Avalanche blockchain. It can be useful for auditors, blockchain explorers, and DApp developers who want to ensure that they are interacting with trustworthy code on the blockchain. # eth_getFilterChanges | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getfilterchanges openapi/avalanche_node_api/filter_handling/eth_getFilterChanges.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Avalanche API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Filters can be created using one of the following methods: * [eth\_newFilter](/reference/avalanche-newfilter) * [eth\_newPendingTransactionFilter](/reference/avalanche-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/avalanche-newblockfilter) After creating a filter using one of the available methods, the resulting filter ID can be used to fetch changes by calling the `eth_getFilterChanges` method. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/avalanche-newfilter) > * [eth\_newPendingTransactionFilter](/reference/avalanche-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/avalanche-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `string` — the filter ID of the filter for which you want to retrieve the changes ## Response * `array` — an array that represents the changes that have occurred on the blockchain since the last time the filter was polled: * For filters created with `eth_newBlockFilter`: * `blockHash` — the hashes of the new blocks since the last time the filter was polled. * For filters created with `eth_newPendingTransactionFilter`: * `transactionHash` — the hashes identifying new pending transactions since the last time the filter was polled. * For filters created with `eth_newFilter`, the following event logs: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getFilterChanges` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/avalanche-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getFilterChanges(filter) { try { const changes = await provider.send('eth_getFilterChanges', [filter]); console.log(changes); // Log the with the new data } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' getFilterChanges(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_filtered_events(filter_id): try: filter_changes = web3.eth.get_filter_changes(filter_id) print(filter_changes) # the array of events that match the filter except Exception as e: print(e) filter_id = '0x0' get_filtered_events(filter_id) ``` ## Use case The `eth_getFilterChanges` method can be used to periodically poll a filter to retrieve new data. In the following Python program, using the web3.py library creates a filter using [eth\_newBlockFilter](/reference/avalanche-newblockfilter) to poll for new blocks, and uses `eth_getFilterChanges` to poll the filter and retrieve new blocks. Here is the implementation in Python: ```python main.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def create_block_filter(): try: # Create a new block filter block_filter = web3.eth.filter('latest') # Split the string at the space character parts = str(block_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(f'Error creating block filter: {e}') def get_filtered_events(filter_id): try: filter_changes = web3.eth.getFilterChanges(filter_id) return filter_changes # the array of events that match the filter except Exception as e: print(f'Error getting filter changes: {e}') def main(): # Create a block filter block_filter_id = create_block_filter() if block_filter_id is not None: print(f'New block filter ID: {block_filter_id}') else: print('Error creating block filter') return # Loop forever and poll the filter every 10 seconds while True: print('Polling for new blocks...') new_blocks = get_filtered_events(block_filter_id) if new_blocks is not None: print(new_blocks) else: print('Error getting filter changes') time.sleep(10) if __name__ == '__main__': main() ``` Here is the explanation of the code: The `create_block_filter` function creates a new block filter with the `web3.eth.filter('latest')` method, which matches against the latest block. The resulting filter object is converted to a string, split into parts using the `split` method and the filter ID is extracted from the second part using array indexing. The filter ID is then returned by the function. The `get_filtered_events` uses the `eth_getFilterChanges` method to retrieve the new blocks. The function takes a single parameter, `filter_id`, which is a string that represents the ID of the filter to be polled, the filter created by the `create_block_filter` function in this case. If the filter ID is valid, `get_filtered_events` calls `web3.eth.getFilterChanges(filter_id)` to retrieve the array of events that match the filter criteria. This array is then returned by the function, which is then printed to the console in the `main` function. The `main` function encapsulates the program logic, calling `create_block_filter` to create a new block filter and print the resulting filter ID to the console. The function then enters an infinite loop, calling `get_filtered_events` every 10 seconds to retrieve the changes to the blockchain that match the filter criteria. If the changes are not `None`, they are printed to the console. An error message is printed to the console if an error occurs while creating the block filter or retrieving the filter changes. # eth_gasPrice | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getgasprice openapi/avalanche_node_api/gas_data/eth_gasPrice.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns the current gas base fee of the network. Reference for eth_gasPrice on Avalanche via Chainstack. Avalanche API method that returns the current gas base fee of the network. The gas price is the quantity of the native token the transaction's sender must pay per unit of gas consumed. The value returned is in Wei. ## Parameters * `none` ## Response * `quantity` — the integer value of the current gas base fee, returned in Wei ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getGasPrice() { const baseFee = await provider.send("eth_gasPrice"); console.log(`The base gas fee is: ${baseFee} Wei`); } getGasPrice() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.gas_price) ``` ## Use case You can use `eth_gasPrice` to calculate the total gas value to send with a transaction based on the base and priority fee system. This concept was implemented with [EIP-1559](https://metamask.io/1559/) in Ethereum's London hard fork. EIP-1559 aimed to solve the problem of network congestion by implementing a dynamic fee market mechanism, which adjusts the fee required to process a transaction based on network demand. with the current system, the total gas price is composed of a **base fee** determined by the network's load and a **priority fee** added by the user. > [!NOTE] > > **Note** > > The `eth_gasPrice` method returns the base fee. The following script calculates the total fee to send with a transaction based on the base fee and an arbitrary priority fee that the user can choose. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getGas(priorityFee) { const priorityFeeWei = ethers.parseUnits(String(priorityFee), 'gwei'); const baseFee = (await provider.getFeeData()).gasPrice; const totalFee = priorityFeeWei + baseFee return [totalFee, baseFee] } async function main() { // The priority fee is constant at 2 nAVAX, the user can change it. const PRIORITY_FEE = 2; const [totalFee, baseFee] = await getGas(PRIORITY_FEE) const baseFeeGwei = ethers.formatUnits(baseFee, 'gwei') console.log(`The priority fee: ${PRIORITY_FEE} nAVAX.`) console.log(`The Avalanche base fee at this moment is: ${Number(baseFeeGwei).toFixed(2)} nAVAX.`) const gweiFee = ethers.formatUnits(totalFee, 'gwei') console.log(`The total gas fee should be at least: ${Number(gweiFee).toFixed(2)} nAVAX.`) } main() ``` The `getGas` function calculates the total fee required to process a transaction. The function takes in a `priorityFee` as input representing the fee a user is willing to pay to prioritize their transaction processing. The function first converts the `priorityFee` from Gwei to Wei (the smallest unit of ether) using `ethers.parseUnits`. Then, it uses `provider.getFeeData` and reads the `gasPrice` field to retrieve the current base fee set by the network. The function finally adds the priority fee to the base fee and returns the total fee along with the base fee as an array. The `main` function is the entry point of the code. It first sets a constant `PRIORITY_FEE` to 2 Gwei. Then calls the `getGas` function with `PRIORITY_FEE` as an argument and destructures the returned array into two variables `totalFee` and `baseFee`. The code then converts `baseFee` and `totalFee` from Wei to nAVAX using `ethers.formatUnits` for better readability, and logs the results to the console. The main function logs the priority fee, the base fee at that moment, and the minimum recommended total fee that should be paid to process a transaction. # eth_getLogs | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getlogs openapi/avalanche_node_api/logs_and_events/eth_getLogs.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns an array of all logs that match a given filter object. Available on Avalanche via Chainstack JSON-RPC nodes. Avalanche API method that returns an array of all logs that match a given filter object. Logs are records of smart contract-generated events on the network and contain valuable information about transactions and smart contract interactions. Some common use cases for `eth_getLogs` include monitoring smart contract events, tracking token transfers, and analyzing blockchain data. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block > * `earliest` — the earliest available or genesis block > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `array` — an array of log objects that match the specified filter or an empty array if there have been no new events since the last poll: * `address` — the contract address from which the event originated * `topics` — an array of 32-byte data fields containing indexed event parameters * `data` — the non-indexed data that was emitted along with the event * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getLogs` code examples The following examples retrieve the logs of the Transfer event from the [WAVAX](https://snowtrace.io/token/0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7#code) smart contract on Avalanche. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getLogs = async () => { const latestBlock = await provider.getBlockNumber(); const filter = { fromBlock: latestBlock - 10, toBlock: 'latest', address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const logs = await provider.getLogs(filter); console.log(logs); }; getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) latest_block = web3.eth.block_number filter = { 'fromBlock': latest_block - 10, 'toBlock': 'latest', 'address': '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } logs = web3.eth.get_logs(filter) print(logs) ``` > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about the `eth_getLogs` method. ## Block range limitations While `eth_getLogs` is a powerful tool, it's crucial to understand its limitations, particularly when working with different EVM-compatible chains, as these networks often have different constraints. The `eth_getLogs` method allows you to select a range of blocks to get events from and is important to exercise proper management. `eth_getLogs` block range is capped by your [subscription plan](/docs/limits#evm-range-limits). On high-activity chains or contracts, query smaller ranges and paginate to avoid oversized responses or timeouts. ## Use case One use case for `eth_getLogs` is to retrieve the transfer events for a specific Token ID from an ERC-721 smart contract. ERC-721 is a token standard for non-fungible tokens (NFTs), and each token transfer is recorded as an event on the blockchain. By retrieving these events, developers can track the movement of NFTs and build applications that interact with them. The following program uses `eth_getLogs` and ethers.js to track the transfers of the token number '4776' from the [Cloudheadz](https://snowtrace.io/token/0xe8dba81d5b9f605d3c53c67adea91d295606ce74) collection on Avalanche. This will retrieve the logs and display the address that initiated the transfer and the block number where it occurred. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTokenTransferLogs(contractAddress, tokenId) { try { const tokenIdHex = ethers.toBeHex(tokenId); const tokenIdTopic = ethers.zeroPadValue(tokenIdHex, 32); const filter = { fromBlock: 'earliest', toBlock: 'latest', address: contractAddress, topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', null, null, tokenIdTopic] }; const logs = await provider.getLogs(filter); const transferLogs = logs.map((log) => { const topics = log.topics; const fromAddress = topics[1]; const formattedFromAddress = `0x${fromAddress.slice(26)}`; if (!ethers.isAddress(formattedFromAddress)) { throw new Error(`${formattedFromAddress} is an invalid address`); } const blockNumber = log.blockNumber; // Display a summary console.log(`Address ${formattedFromAddress} transferred the token ${tokenId} in block ${blockNumber}.`); return { fromAddress: formattedFromAddress, tokenId: tokenId, blockNumber: blockNumber }; }); return transferLogs; } catch (error) { throw new Error(`Error getting transfer logs: ${error.message}`); } } async function main() { const contractAddress = '0xE8dBa81D5B9f605D3C53c67AdEa91D295606cE74'; const tokenId = '4776'; try { const transferLogs = await getTokenTransferLogs(contractAddress, tokenId); console.log(transferLogs); } catch (error) { console.error(error.message); } } main(); ``` This code defines an async function called `getTokenTransferLogs` which takes a `contractAddress` and a `tokenId` as input parameters. Within the function, the `tokenId` is converted to hexadecimal format and then used to create a topic for a filter to get past logs for a specific token transfer. The `provider.getLogs` function is an API that returns logs that match a certain filter. Once the logs have been retrieved, the function loops through each log and extracts the relevant information, such as the sender's address and the block number. It also checks whether the sender's address is a valid address; this is in case the parsing logic fails. For each valid transfer log, the function logs a summary to the console and creates a transfer log object that includes the `fromAddress`, `tokenId`, and `blockNumber` properties. The function returns an array of transfer log objects, and an exception is thrown if there are any errors. The `main` function calls `getTokenTransferLogs` with a specific `contractAddress` and `tokenId`, and then logs the array of transfer log objects to the console. If there are any errors, an error message is logged to the console instead. # eth_getStorageAt | Avalanche Source: https://docs.chainstack.com/reference/avalanche-getstorageat openapi/avalanche_node_api/accounts_info/eth_getStorageAt.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns the data stored at a specific storage slot within a smart contract. Chainstack Avalanche reference. Avalanche API method that returns the data stored at a specific storage slot within a smart contract. It can help developers to read a smart contract's internal state, like user data or balances, to inform decisions and build more advanced applications that interact with smart contracts on the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity` — the index of the storage position to retrieve the data from * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response `data` — the data stored in the storage slot. Returned as a hexadecimal string. ## `eth_getStorageAt` code examples The following example returns the storage value at slot `0` from the [Chainlink VRFCoordinatorV2](https://snowtrace.io/address/0xd5D517aBE5cF79B7e95eC98dB0f0277788aFF634#readContract) on the Avalanche mainnet. In this case, this is the address owning the smart contract. This method can retrieve the different storage values in time by querying different blocks in the past. Note that to query a state in the past, you need an [archive node](https://chainstack.com/evm-nodes-a-dive-into-the-full-vs-archive-mode). > [!NOTE] > > curl needs a `HEX String` starting with `0x` to identify the block if you want to use a hex integer block number as a parameter.\ > For example, block number `14000000` will be `0xD59F80`. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getstorage = async (address, slot, block) => { const storage = await provider.send("eth_getStorageAt", [address, slot, block]); console.log(storage); }; getstorage("0xd5D517aBE5cF79B7e95eC98dB0f0277788aFF634", "0", "latest" ) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" storage = web3.eth.get_storage_at("0xd5D517aBE5cF79B7e95eC98dB0f0277788aFF634", 0, "latest") print(web3.to_hex(storage)) ``` ## Use case One use case for `eth_getStorageAt` is analyzing how the value stored at a specific storage slot of a smart contract changes over time. For example, a developer may want to track the change in a specific variable of a smart contract, such as the total supply of a token, and perform some analysis on it. Here's an example implementation of this use case using ethers.js. The following program analyzes the [Chainlink Token](https://snowtrace.io/token/0x5947bb275c521040051d82396192181b413227a3#code) smart contract on the Avalanche chain to track how the total supply changes over time. The storage slot matches the position of the variable in the smart contract; the following code snippet shows the top of the Chainlink Token smart contract where the variables used in this program are declared. ```sol ERC20.sol theme={"system"} contract ERC20 is Context, IERC20, IERC20Metadata { mapping (address => uint256) private _balances; // Storage slot 0x0 mapping (address => mapping (address => uint256)) private _allowances; // Storage slot 0x1 uint256 private _totalSupply;// Storage slot 0x2 string private _name;// Storage slot 0x3 string private _symbol;// Storage slot 0x4 . . . ``` Here's an example implementation of this use case using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // You need an archive node is going more than 127 blocks in the past. async function getStorageValueOverTime() { // The address of the Chainlink Token smart contract const linkAddress = '0x5947BB275c521040051D82396192181b413227A3'; // The index of the storage slots where the total supply and name data are stored const totalSupplySlot = 2; const nameSlot = 3; // The range of blocks to analyze const startBlock = 16000000; const endBlock = await provider.getBlockNumber(); for (let blockNumber = startBlock; blockNumber <= endBlock; blockNumber++) { // Retrieve the name and total supply from the smart contract storage at each block const [name, supply] = await Promise.all([ provider.getStorage(linkAddress, nameSlot, blockNumber), provider.getStorage(linkAddress, totalSupplySlot, blockNumber) ]); // Decode the name and total supply from hexadecimal format to their original values const decodedName = ethers.toUtf8String(name); const decodedSupply = BigInt(supply); // Convert the total supply from wei to ether and round to 4 decimal places const convertedSupply = ethers.formatEther(decodedSupply); const adjustedSupply = Number(convertedSupply).toFixed(4); // Output the block number, name, and total supply for each block console.log(`Block ${blockNumber}: Name = ${decodedName}, Total supply = ${adjustedSupply}`); } } async function main() { try { await getStorageValueOverTime(); } catch (error) { console.error(error); } } main(); ``` The bulk of the program is the `getStorageValueOverTime` function. The `getStorageValueOverTime` function retrieves and outputs the name and total supply of the Chainlink Token smart contract at each block height between a specified start block and the current block height. The function begins by declaring constants for the Chainlink Token contract address, the storage slot indices for the name and total supply data, the starting block height, and the current block height. Next, the function enters a `for` loop that iterates over every block between the start block and the current block height. Within the loop, the function retrieves the name and total supply from the smart contract storage at the current block height using the `provider.getStorage` method. The `Promise.all` method is used to retrieve the name and total supply in parallel for improved efficiency. The `ethers.toUtf8String` method is then used to convert the name from hexadecimal format to its original string value. The `BigInt` constructor is used to convert the total supply from hexadecimal format to a `bigint` value, which can be used for mathematical operations. The `ethers.formatEther` method is then used to convert the total supply from wei to ether. Finally, the `Number` and `toFixed` methods are used to round the total supply to four decimal places and output the block number, name, and total supply for each block; note that this is for readability, and you should use the raw Wei data to make calculations. Overall, the `getStorageValueOverTime` function provides a way to monitor changes in the state of a smart contract over time, which can be useful for understanding the contract's behavior and performance. # Avalanche API reference: C-Chain JSON-RPC on Chainstack Source: https://docs.chainstack.com/reference/avalanche-getting-started Start building on Avalanche C-Chain with EVM compatibility. Deploy RPC nodes, use JSON-RPC API, and create smart contracts on proof-of-stake blockchain. ## What is the Avalanche protocol? Avalanche is a blockchain protocol that is designed to be EVM-compatible, meaning that it supports the same programming languages and smart contract standards as Ethereum. This allows developers to easily port their existing Ethereum-based applications to the Avalanche network without having to learn new languages or rewrite their code from scratch. Find useful Avalanche tools in the [Avalanche tooling](/docs/avalanche-tooling) section. ### Avalanche C-chain The C-Chain, which stands for Contract Chain, is the default smart contract network on the Avalanche platform. It is a highly scalable and decentralized smart contract platform that allows developers to create and deploy custom smart contracts using EVM-compatible tools. The C-Chain operates on a proof-of-stake consensus mechanism, which ensures fast transaction finality and high throughput. ## What is the Avalanche API The Avalanche API allows developers to communicate with the Avalanche blockchain to build applications. To read data from and send transactions to the Avalanche blockchain, an application must connect to an Avalanche RPC node. When communicating with an Avalanche RPC node, the Avalanche client implements a JSON-RPC specification, a communication protocol allowing one to make remote calls and execute them as if they were made locally. ## How to start using the Avalanche API To use the Avalanche API, you need access to an Avalanche RPC node. Follow these steps to sign up on Chainstack, deploy an Avalanche RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the Avalanche blockchain and use the Avalanche API to build. > [!NOTE] > > **See also** > > [Build better with Avalanche](https://chainstack.com/build-better-with-avalanche/) # eth_getTransactionByBlockHashAndIndex | Avalanche Source: https://docs.chainstack.com/reference/avalanche-gettransactionbyblockhashandindex openapi/avalanche_node_api/transactions_info/eth_getTransactionByBlockHashAndIndex.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. Avalanche API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Avalanche blockchain. ## Parameters * `hash` — the hash of the block * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal ## Response * `object` — a transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blockHash, index) => { const transaction = await provider.send("eth_getTransactionByBlockHashAndIndex", [blockHash, index]); console.log(transaction); } getTransaction("0xe07e904936fae48b44fa09fe4410837d9b592f5561026c0827dc93238d87bb0a", "0x2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block("0xe07e904936fae48b44fa09fe4410837d9b592f5561026c0827dc93238d87bb0a", 2)) ``` ## Use case The `eth_getTransactionByBlockHashAndIndex` can be used to retrieve transaction details from a block. In the following example the `getSendersInBlock` checks how many transactions exist in a block, then receives the sender of each (`from` field) and displays it. > [!NOTE] > > Note that there are simpler ways to get this information, and this is only a practical example of how to use `eth_getTransactionByBlockHashAndIndex`, if the number of transactions in the block is already known, the first step can be skipped. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getSendersInBlock(blockHash) { const block = await provider.getBlock(blockHash); const transactions = block.transactions; console.log(`Block ${blockHash} contains ${transactions.length} transactions`); for (let i = 0; i < transactions.length; i++) { const transaction = await provider.getTransaction(transactions[i]); console.log(`Sender of transaction at index ${i}:`, transaction.from); } } getSendersInBlock("0xe07e904936fae48b44fa09fe4410837d9b592f5561026c0827dc93238d87bb0a"); ``` The number of transactions in the block is determined using `transactions.length` and the total number of transactions logged to the console. A `for` loop is then used to retrieve information about each transaction in the block. For each iteration of the loop, the code calls the `provider.getTransaction` method with the transaction hash at the current index to retrieve information about that transaction. The `from` property of the returned transaction object is logged to the console to show the sender of the transaction. # eth_getTransactionByBlockNumberAndIndex | Avalanche Source: https://docs.chainstack.com/reference/avalanche-gettransactionbyblocknumberandindex openapi/avalanche_node_api/transactions_info/eth_getTransactionByBlockNumberAndIndex.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. Avalanche API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Avalanche blockchain. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal. ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blocNumber, index) => { const transaction = await provider.send("eth_getTransactionByBlockNumberAndIndex", [blocNumber, index]); console.log(transaction); } getTransaction("latest", "0x1"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block(27425957, 2)) # Hex encoded parameters starting with "0x" are accepted as well. ``` ## Use case The `eth_getTransactionByBlockNumberAndIndex` can be used to retrieve transaction details from a block. In the following example the `getTypeInBlock` function checks how many transactions exist in a block, then retrieves the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of each (`type` field), and displays it. > [!NOTE] > > Note that there are simpler ways to get this information, and this is only a practical example of how to use `eth_getTransactionByBlockNumberAndIndex`, if the number of transactions in the block is already known, the first step can be skipped. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTypeInBlock() { const latestBblock = await provider.getBlockNumber(); const block = await provider.getBlock(latestBblock, true); const transactions = block.prefetchedTransactions; console.log(`The latest block is ${latestBblock} and contains ${transactions.length} transactions`); for (let i = 0; i < transactions.length; i++) { const transaction = transactions[i]; console.log(`The type of transaction at index ${i}:`, transaction.type); } } getTypeInBlock(); ``` The number of transactions in the block is determined using `transactions.length` and the total number of transactions logged to the console. A `for` loop is then used to retrieve information about each transaction in the block. The block is fetched with `provider.getBlock(latestBblock, true)`, which prefetches the full transaction objects so they are available through `block.prefetchedTransactions`. For each iteration of the loop, the code reads the transaction at the current index, and the `type` property of the transaction object is logged to the console to show the type of the transaction. ## Try the `eth_getTransactionByBlockNumberAndIndex` RPC method yourself # eth_getTransactionByHash | Avalanche Source: https://docs.chainstack.com/reference/avalanche-gettransactionbyhash openapi/avalanche_node_api/transactions_info/eth_getTransactionByHash.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns the information about a transaction from the transaction hash. Use it on Avalanche via Chainstack. Avalanche API method that returns the information about a transaction from the transaction hash. It is used to retrieve information about a specific transaction by its hash. The method returns an object with details about the transaction, such as the sender, recipient, gas used, transaction value, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction., usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet; it essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. This field is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (transactionHash) => { const transaction = await provider.send("eth_getTransactionByHash", [transactionHash]); console.log(transaction); }; getTransaction("0x1920819ae895661e825fc034706ff66a638b3a0f0d2d6e5262781a36de836769") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction("0x1920819ae895661e825fc034706ff66a638b3a0f0d2d6e5262781a36de836769")) ``` ## Use case One use case for `eth_getTransactionByHash` is to inspect a specific transaction and display information about it, such as the address that was sent, the block it was included in, and the number of transactions the sender sent so far (nonce). This information can be useful for monitoring and analyzing transactions. Here's an example of how you can achieve this with ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTransaction(transactionHash) { // Get the transaction using the provided transaction hash const transaction = await provider.getTransaction(transactionHash) // Store the desired parameters in constants const sender = transaction.from; const block = transaction.blockNumber const transactionsCount = transaction.nonce // Log the transaction information to the console console.log(`Transaction ${transactionHash} was sent from address ${sender}`); console.log(`It was included in block ${block}`); console.log(`The sender has sent ${transactionsCount} transactions so far\n`); } getTransaction("0x1920819ae895661e825fc034706ff66a638b3a0f0d2d6e5262781a36de836769") ``` In this example, the program starts by connecting to a node endpoint. Then, call the `eth_getTransactionByHash` method and pass in the transaction hash as an argument. The method returns a Promise that resolves to a transaction object, which is used to retrieve the sender address, the block number the transaction was included in, and the address nonce. The last step is to log the information to the console. > [!NOTE] > > Note that the desired parameters are stored in constants, so you can use them for further processing. # eth_getTransactionCount | Avalanche Source: https://docs.chainstack.com/reference/avalanche-gettransactioncount openapi/avalanche_node_api/accounts_info/eth_getTransactionCount.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns the number of transactions sent from an address at the selected block. Avalanche via Chainstack. Avalanche API method that returns the number of transactions sent from an address at the selected block. This value is also called `nonce`; it is an important piece of information, especially to ensure that a transaction is not sent twice. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`— the address to retrieve the transaction count. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — an integer value identifying the number of transactions sent from an address at the specified block. ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getNonce = async (address, blockId) => { const nonce = await provider.send("eth_getTransactionCount", [address, blockId]); console.log(nonce); }; getNonce("0x38545c4b331D8BFb3bee94C62D77a6735b5eF8c0", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_count("0x38545c4b331D8BFb3bee94C62D77a6735b5eF8c0", "latest")) ``` ## Use case One of the most common use cases for `eth_getTransactionCount`is to create the transaction object built in a script designed to send a transaction. The nonce field is required, and it is retrieved using the `eth_getTransactionCount` method. The following code shows how to build a signed raw transaction using ethers.js. > [!WARNING] > > **Security notice** > > You need your private key to sign the transaction; never share your private key with anyone. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Initialize the wallet with your private key and connect it to the provider const PRIVATE_KEY = "PRIVATE_KEY"; const wallet = new ethers.Wallet(PRIVATE_KEY, provider); // Validate input parameters and return an error message if invalid const validateInputs = (fromAddress, toAddress) => { if (!ethers.isAddress(fromAddress)) { throw new Error(`Invalid fromAddress: ${fromAddress}`); } if (!ethers.isAddress(toAddress)) { throw new Error(`Invalid toAddress: ${toAddress}`); } }; // Async function to create a raw transaction async function createRawTransaction(fromAddress, toAddress, value) { validateInputs(fromAddress, toAddress); const nonce = await provider.getTransactionCount(fromAddress); const gasPrice = (await provider.getFeeData()).gasPrice; const gasLimit = await provider.estimateGas({ from: fromAddress, to: toAddress, }); // Build the transaction object const transactionObject = { to: toAddress, gasPrice: gasPrice, gasLimit: gasLimit, nonce: nonce, value: value, }; // Sign the transaction using the wallet and return it as a raw transaction const rawTransaction = await wallet.signTransaction(transactionObject); return rawTransaction; } async function main() { const rawTransaction = await createRawTransaction('0x38545c4b331D8BFb3bee94C62D77a6735b5eF8c0', "0xe341b2f448eb190495ed4a89c01f20078b26b0f6" , "100000000000000") console.log(`Raw transaction: ${rawTransaction}`) } main() ``` In summary, the code creates a valid raw transaction that can be broadcasted to the network. During the process, the script retrieves important values such as the `nonce`, `gasPrice`, and `gasLimit`, builds a transaction object and signs it using a private key. First, the `createRawTransaction` function calls `validateInputs` to ensure that the `fromAddress` and `toAddress` parameters are valid addresses. If either of these addresses is invalid, the function throws an error with a descriptive message. Next, the function makes a call to `provider.getTransactionCount` with the `fromAddress` as a parameter. This method returns the number of transactions sent from the `fromAddress`, and is used as the `nonce` for the transaction. The function retrieves the `gasPrice` and `gasLimit` for the transaction using `provider.getFeeData` and `provider.estimateGas`, respectively. The [eth\_gasPrice](/reference/gasprice) is the amount of gas that the transaction sender is willing to pay per unit of gas consumed by the transaction, while the [eth\_estimateGas](/reference/avalanche-estimategas) is an estimate of the maximum amount of gas that the transaction will consume. With the `nonce`, `gasPrice`, and `gasLimit` values, the function builds a transaction object, which includes the `toAddress`, `gasPrice`, `gasLimit`, `nonce`, and `value` of the transaction. The value is the amount transferred from the `fromAddress` to the `toAddress`. The transaction object is then signed by the wallet using the private key, returning a `rawTransaction`, which can be broadcasted to the network to execute the transaction using [eth\_sendRawTransaction](/reference/avalanche-sendrawtransaction). # eth_getTransactionReceipt | Avalanche Source: https://docs.chainstack.com/reference/avalanche-gettransactionreceipt openapi/avalanche_node_api/transactions_info/eth_getTransactionReceipt.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that retrieves the receipt of a transaction from a transaction hash. eth_getTransactionReceipt on Avalanche via Chainstack. > [!NOTE] > > **No response in the interactive API** > > The response to the call might be too big for the web app to handle, so if you are not getting it here, copy the CURL example and run in your terminal—this will work. Avalanche API method that retrieves the receipt of a transaction from a transaction hash. It can be used to verify the success or failure of a transaction, check the execution status of a smart contract, retrieve logs, check if a transaction deployed a smart contract, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `Transaction receipt` — the receipt object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the address of the contract created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects generated by this transaction, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionReceipt = async (transactionHash) => { const receipt = await provider.send("eth_getTransactionReceipt", [transactionHash]); console.log(receipt); }; getTransactionReceipt('0x1920819ae895661e825fc034706ff66a638b3a0f0d2d6e5262781a36de836769') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_receipt("0x1920819ae895661e825fc034706ff66a638b3a0f0d2d6e5262781a36de836769")) ``` ## Use case A practical use case for `eth_getTransactionReceipt` is to verify a transaction's status and whether it was successful or not. This can be especially useful when deploying a contract, as you can use the receipt to confirm that the contract was deployed successfully and has a valid contract address. Here's an example in ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function verifyTransaction(transactionHash) { // Get the transaction receipt const receipt = await provider.getTransactionReceipt(transactionHash); // Check if the receipt is not yet available if (!receipt) { console.log(`Transaction ${transactionHash} is still pending...`); return; } // Check if the transaction was successful if (receipt.status) { console.log(`✅The transaction was successful! \nHash: ${transactionHash}`); // Check if the transaction deployed a smart contract if (receipt.contractAddress != null) { console.log(`📑The smart contract was deployed at address: ${receipt.contractAddress}`); } else { console.log(`❗This transaction did not deploy a smart contract`); } } else { console.log(`💥The transaction failed. \nHash: ${transactionHash}`); } } verifyTransaction('0x1920819ae895661e825fc034706ff66a638b3a0f0d2d6e5262781a36de836769') ``` This script holds an async function called `verifyTransaction` that takes a transaction hash as input. The function uses the ethers.js library to retrieve the transaction receipt of the specified transaction using the `eth_getTransactionReceipt` method. The function first checks if the transaction receipt is not yet available, and if so, it logs a message to the console indicating that the transaction is still pending. If the receipt is available, the function checks the status property of the receipt to determine if the transaction was successful. If the transaction was successful, the function checks if a smart contract was deployed as part of the transaction. The function logs the smart contract's address to the console if a smart contract is deployed. If not, the function logs a message indicating that no smart contract was deployed. If the transaction was not successful, the function logs a message indicating that the transaction failed, along with the transaction hash. # net_listening | Avalanche Source: https://docs.chainstack.com/reference/avalanche-listening openapi/avalanche_node_api/client_info/net_listening.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns a boolean value indicating whether the client is currently listening for network connections or not. Avalanche API method that returns a boolean value indicating whether the client is currently listening for network connections or not. The `net_listening` method can be useful in many scenarios where it is important to monitor the status of an Avalanche client's network connection to ensure that it is working properly. ## Parameters * `none` ## Response * `boolean` — a boolean value that indicates whether or not a node is currently actively seeking peer connections. `true` if the client is actively listening for connections, `false` if not. ## `net_listening` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const isListening = async () => { const listen = await provider.send("net_listening"); console.log(listen); } isListening() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) is_listening = web3.provider.make_request('net_listening', []) print(is_listening) ``` ## Use case A method like `net_listening` can be used to monitor the network status of the Avalanche node a developer is connecting to. Imagine a blockchain explorer where users can search for transactions, addresses, and other information on the Avalanche blockchain. In order to provide accurate information to its users, the website needs to be able to monitor the current status of its connection. Here is an example of this implementation using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Create a function to monitor the node status and display information to users async function monitorNetworkStatus() { try { // Check if the client is currently listening for network connections const isListening = await provider.send("net_listening"); // Get the number of peers currently connected to the client const peerCount = Number(await provider.send("net_peerCount")); // Log the network status information to the console console.log(`Network status: ${isListening ? 'Listening' : 'Not listening'} | Peers: ${peerCount}`); } catch (error) { // Handle any errors that might occur console.error(`Error monitoring network status: ${error}`); } } // Call the monitorNetworkStatus function at regular intervals to continuously monitor the network status setInterval(monitorNetworkStatus, 5000); // Monitor every 5 seconds ``` The `monitorNetworkStatus` function defined in this code uses the `net_listening` method to check whether the Avalanche client is currently listening for network connections, and the net\_peerCount method to get the number of peers currently connected to the client. The function logs this information to the console, which could be displayed to the user in a user-friendly format on the blockchain explorer website. By calling this function at regular intervals using the `setInterval` function, the website can continuously monitor the node's status and provide users with up-to-date information about the overall health of the node. # Logs and events | Avalanche Source: https://docs.chainstack.com/reference/avalanche-logs-rpc-methods Avalanche C-Chain event log methods for querying smart contract events, applying log filters, and tracking on-chain activity via Chainstack endpoints. Retrieving logs information from the Avalanche blockchain can be done by using the following methods: > [card] [eth\_getLogs](/reference/avalanche-getlogs) > [card] [eth\_newFilter](/reference/avalanche-newfilter) > [!NOTE] > > Find logs limitations in [Understanding eth\_getLogs limitations](/docs/understanding-eth-getlogs-limitations) # eth_subscribe ("logs") | Avalanche Source: https://docs.chainstack.com/reference/avalanche-native-subscribe-logs Subscribe to real-time Avalanche event logs via WebSocket. Receive notifications whenever new logs matching your specified filter criteria are emitted. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) the event filter options: * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about topics and event logs. ## Response * `subscription` — the subscription ID. * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_subscribe("logs")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_subscribe", "params": ["logs", {"address":"0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewLogs() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['logs', {"address":"0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewLogs(); ``` This will generate a continuous stream of data displaying new event logs matching the filter. Use [eth\_unsubscribe](/reference/avalanche-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("logs")` method on Avalanche is used to subscribe to the logs generated by smart contracts on the blockchain. These logs contain information about events that have occurred on the blockchain, such as details of a smart contract function and outputs of transactions. A practical and real-world scenario for using the `eth_subscribe("logs")` method could be in the context of a decentralized exchange (DEX). In the context of a DEX, trades are executed using smart contracts that run on the Avalanche blockchain. When a trade is executed on a DEX, a smart contract is called to process the transaction. This smart contract generates a log that contains information about the trade, such as the amount of cryptocurrency being traded, the parties involved, and so on. By using the `eth_subscribe("logs")` method, a user or application can subscribe to the logs generated by the DEX's smart contracts. This can be useful for tracking trades in real time, monitoring the performance of the DEX, and generating reports on trading activity. For example, a trader might use the `eth_subscribe("logs")` method to monitor the logs generated by a DEX in order to identify trends in trading activity. They might look for patterns in the volume and price of trades, which could help them make more informed trading decisions. Similarly, an analytics platform might use the `eth_subscribe("logs")` method to collect data on trading activity across multiple DEXs. By analyzing this data, they could generate insights into the performance of different DEXs, which could be useful for investors and traders looking to make informed decisions about where to trade. # eth_subscribe ("newHeads") | Avalanche Source: https://docs.chainstack.com/reference/avalanche-native-subscribe-newheads Subscribe to new Avalanche block headers via WebSocket. Receive real-time notifications whenever a new block is added to the Avalanche C-Chain. Note that because the Avalanche mempool is [only accessible to validators](/docs/mempool-configuration), this method will only return transactions that were submitted through the same node you are connected to. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `newHeads` in this case. ## Response * `subscription` — the subscription ID. * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Avalanche nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Avalanche blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Avalanche network since the inception of the network. It measures the overall security and integrity of the Avalanche network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/avalanche-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `eth_subscribe("newHeads")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewBlocks() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewBlocks(); ``` This will generate a continuous stream of data displaying new block headers as they are added to the chain. Use [eth\_unsubscribe](/reference/avalanche-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newHeads")` method can be useful in a variety of real-world scenarios, such as: * Real-time monitoring of blockchain activity. Companies or individuals who rely on the Avalanche network for their business can use `eth_subscribe("newHeads")` to receive real-time notifications about new blocks being added to the chain. This can help them keep track of important transactions, such as high-value trades, in real time and respond quickly to any issues that may arise. * Decentralized Finance (DeFi) applications. DeFi applications, such as lending platforms, can use `eth_subscribe("newHeads")` to monitor the blockchain for new blocks that might contain important data, such as changes in interest rates or new lending opportunities. * Smart contract automation. Developers can use `eth_subscribe("newHeads")` to trigger automated smart contract actions whenever a new block is added to the chain. For example, a smart contract could automatically execute an action order whenever a new block is added that meets certain criteria. # eth_subscribe ("newPendingTransactions") | Avalanche Source: https://docs.chainstack.com/reference/avalanche-native-subscribe-newpendingtransactions Subscribe to pending Avalanche transactions via WebSocket. Receive real-time notifications when new unconfirmed transactions enter the mempool. Note that because the Avalanche mempool is [only accessible to validators](/docs/mempool-configuration), this method will only return transactions that were submitted through the same node you are connected to. ## Parameters * To subscribe to transaction hashes only —`newPendingTransactions`. * To subscribe to full transaction bodies — `"newPendingTransactions", true` ## Response * `subscription` — the subscription ID. * `string `— the hash of a pending transaction. ## `eth_subscribe("newPendingTransactions")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newPendingTransactions", true]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewPendingTransactions() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newPendingTransactions', true], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewPendingTransactions(); ``` This will generate a continuous stream of data displaying new pending transactions as they are added to the mempool. Use [eth\_unsubscribe](/reference/avalanche-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newPendingTransactions")` method on Avalanche is used to subscribe to the network and receive real-time updates on all new pending transactions on the network. This can be useful in a variety of real-world scenarios, including: * Trading applications. A trading application can use this method to keep track of any new transactions that are being made on the network. This can be particularly useful for high-frequency trading, where speed is crucial. By subscribing to this method, the trading application can receive real-time updates on new trades, allowing it to quickly respond and execute trades. * Monitoring tools. Monitoring tools can use this method to keep track of any suspicious activity on the network. By subscribing to this method, monitoring tools can receive real-time updates on any new pending transactions and quickly take action if necessary. * Payment processors. Payment processors can use the `eth_subscribe("newPendingTransactions")` method to monitor incoming transactions from their customers. This can be particularly useful for businesses that rely on blockchain technology to process payments, as it allows them to track incoming payments in real time. By subscribing to this method, payment processors can receive instant notifications when new payments are received, which can help streamline their payment processing workflows. # eth_unsubscribe | Avalanche Source: https://docs.chainstack.com/reference/avalanche-native-unsubscribe Cancel any active Avalanche WebSocket subscription with eth_unsubscribe. Pass the subscription ID to stop receiving real-time event notifications. When a client subscribes to a particular event, the Avalanche node will send updates as soon as they occur. However, in some cases, the client may no longer be interested in receiving updates for a particular event. The `eth_unsubscribe` method can be used to cancel the following subscriptions: > [card] [eth\_subscribe('newHeads')](/reference/avalanche-native-subscribe-newheads) > [card] [eth\_subscribe('newPendingTransactions')](/reference/avalanche-native-subscribe-newpendingtransactions) > [card] [eth\_subscribe('logs')](/reference/avalanche-native-subscribe-logs) ## Parameters * `string` — the ID of the subscription that you want to cancel ## Response * `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `eth_unsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_unsubscribe", "params": ["0x7529626735859f32962b10b19ee2e3eb"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function removeSubscription() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_unsubscribe', params: ['0x9de3135dd3dd6c6b99bd104061a5af4f'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } removeSubscription(); ``` This will remove the subscription matching the ID passed as a parameter. # eth_newBlockFilter | Avalanche Source: https://docs.chainstack.com/reference/avalanche-newblockfilter openapi/avalanche_node_api/blocks_info/eth_newBlockFilter.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that creates a filter that watches for new blocks on the blockchain. eth_newBlockFilter on Avalanche via Chainstack. Avalanche API method that creates a filter that watches for new blocks on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/avalanche-getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter. The filters created are stored on the blockchain client instance. The filter fill is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/avalanche-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/avalanche-uninstallfilter) to remove the filter ## `eth_newBlockFilter` code examples > [!NOTE] > > **web3.eth.filterdeprecation** > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/avalanche-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newBlockFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') return blocks_filter except Exception as e: print(e) blocks = get_new_blocks() filter_id = blocks.filter_id print(filter_id) ``` ## Use case One use case for `eth_newBlockFilter` in a simple DApp is to listen for new blocks and update the user interface with the latest block information. When the DApp starts, it creates a new filter using `eth_newBlockFilter` to listen for new blocks. When a new block is added to the blockchain, the filter is triggered, and the DApp retrieves the latest block information using a Web3 library. Here is an implementation of this use case using web3.py: ```python main.py theme={"system"} import time from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Set up a new blocks filter def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) def watch_for_blocks(new_filter): # Wait for new blocks to arrive while True: # Retrieve the changes that match the filter changes = web3.eth.getFilterChanges(new_filter) # If there are no changes, wait for new blocks if len(changes) == 0: time.sleep(1) # Wait for 1 second continue # Process the new blocks for change in changes: block = web3.eth.getBlock(change) print('New block:', block) def main(): # Create a new filter to watch for new blocks new_filter = get_new_blocks() print(new_filter) # Watch for new blocks watch_for_blocks(new_filter) if __name__ == '__main__': main() ``` The code is divided into three main functions: `get_new_blocks`, `watch_for_blocks`, and `main`. Let's take a closer look at each of them: `get_new_blocks` is responsible for setting up a new filter to watch for new blocks. The try block sets up the filter by calling the `eth.filter` method of the web3.py library and passing it the string `latest`. The `split` method is then used to split the resulting string at each space character, and the second part of the resulting list is extracted as the filter ID. The filter ID is returned from the function. > [!NOTE] > > The `eth.filter` method of the web3.py library does not only return the filter ID but it returns a sentence saying `Filter for 0x3c3f5a5e637d29c033e4e7a3f7e870cd`; this is the reason for the string manipulation within the `get_new_blocks` function. `watch_for_blocks` is the function that watches for new blocks. It does this by first entering an infinite loop while `True`. Within this loop, the [eth\_getFilterChanges](/reference/avalanche-getfilterchanges) method is used to retrieve any changes that match the filter ID passed to the function. If there are no changes, the code waits for 1 second using the `time.sleep` method and then continues with the loop. If there are changes, the new blocks are processed by looping through them and calling the `eth.getBlock` method of the web3 library to retrieve the details of each block. Finally, the details of the new blocks are printed on the console. `main` is the function that ties everything together. It first calls `get_new_blocks` to set up a new filter to watch for new blocks, and then passes the resulting filter ID to `watch_for_blocks` to start watching for new blocks. The filter ID is also printed to the console to confirm that the filter has been set up correctly. # eth_newFilter | Avalanche Source: https://docs.chainstack.com/reference/avalanche-newfilter openapi/avalanche_node_api/logs_and_events/eth_newFilter.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that generates a filter object based on the filter parameters. Available on Avalanche via Chainstack JSON-RPC nodes. Avalanche API method that generates a filter object based on the filter parameters. It returns a filter ID, which can be used to retrieve the results of the filter using the [eth\_getFilterChanges](/reference/avalanche-getfilterchanges) method. By creating a filter for specific events, developers can receive notifications when those events occur and use them to trigger actions in their applications. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block > * `earliest` — the earliest available or genesis block > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `result` — a hexadecimal string representing the ID of the newly created filter > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/avalanche-getfilterchanges) to retrieve updated logs * [eth\_uninstallFilter](/reference/avalanche-uninstallfilter) to remove the filter ## `eth_newFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/avalanche-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const createFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) filter = { 'toBlock': 'latest', 'address': '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } def create_logs_filter(filter_object): try: logs_filter = web3.eth.filter(filter_object) return logs_filter except Exception as e: print(e) logs_filter = create_logs_filter(filter) filter_id = logs_filter.filter_id print(f'New filter ID: {filter_id}') ``` ## Use case You can use `eth_newFilter` to create a filter for a specific action on a smart contract, for example, to monitor the transfer transactions from the [Wrapped ETH token](https://snowtrace.io/token/0x49d5c2bdffac6ce2bfdb6640f4f80f226bc10bab). The idea is to create a filter using the `eth_newFilter` method to monitor an ERC-20 smart contract, WETH in this case. The following filter parameters are selected: ```javascript filter_params theme={"system"} const filter = { // Poll the latest block available toBlock: 'latest', // WETH smart contract address address: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB', // Signature of the Transfer event. topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], }; ``` The program is then polling the filter every 2 seconds to check for any new changes using the `eth_getFilterChanges` method. Here is the implementation using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], }; const createAndPollFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(`The filter ID is: ${filterId}`); // the filter ID returned by eth_newFilter // Poll the filter every 30 seconds setInterval(async () => { const filterChanges = await provider.send('eth_getFilterChanges', [filterId]); if (filterChanges.length === 0) { console.log('No new logs since the last poll.'); } else { console.log(filterChanges); // the filter changes returned by eth_getFilterChanges } }, 2000); // 2 seconds in milliseconds return filterId; } catch (error) { console.log(error); } }; createAndPollFilter(); ``` Here's a breakdown of how the code works: The `createAndPollFilter` function is an asynchronous function that creates and polls a filter for the WETH smart contract on Avalanche. The `eth_newFilter` method creates a new filter with the specified parameters. In this case, it passes in the filter object as the filter parameter. The method returns the ID of the newly created filter, which is then logged into the console. The `setInterval` method is used to poll the filter every 2 seconds. It takes a function as the first argument and an interval in milliseconds as the second argument. The function passed to `setInterval` is an asynchronous function that calls `eth_getFilterChanges` with the filter ID returned by `eth_newFilter`. This method returns an array of all the changes to the filter since the last time it was polled. If there are no changes, it logs "No new logs since the last poll." to the console. If there are changes, it logs the filter changes to the console. The `createAndPollFilter` function returns the filter ID, and if there are any errors thrown while creating or polling the filter, the catch block logs the error to the console. # eth_newPendingTransactionFilter | Avalanche Source: https://docs.chainstack.com/reference/avalanche-newpendingtransactionfilter openapi/avalanche_node_api/transactions_info/eth_newPendingTransactionFilter.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Reference docs for the eth_newPendingTransactionFilter JSON-RPC method on the Avalanche blockchain, available via Chainstack JSON-RPC nodes. Note that because the Avalanche mempool is [only accessible to validators](/docs/mempool-configuration), this method will only return transactions that were submitted through the same node you are connected to. Avalanche API method that creates a filter that listens for new pending transactions on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/avalanche-getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter ## Code examples > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter fill is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/avalanche-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/avalanche-uninstallfilter) to remove the filter ## `eth_newPendingTransactionFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/avalanche-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newPendingTransactionFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_pending_transactions(): try: blocks_filter = web3.eth.filter('pending') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) new_filter = get_new_pending_transactions() print(new_filter) ``` ## Use case One way to use the `eth_newPendingTransactionFilter` method is to listen for new pending transactions at predefined intervals and extract specific data from them. For instance, a decentralized application might check for pending transactions every second and identify those that transfer a value greater than a certain amount of the AVAX token. This could be useful for monitoring high-value transactions or detecting potential fraud or security threats in real time. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Create a filter using eth_newPendingTransactionFilter const createFilter = async () => { try { const filterId = await provider.send('eth_newPendingTransactionFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; // Use the filter to extract the value from each transaction async function getValue(filter) { try { // Retrieve the list of new pending transactions const transactions = await provider.send('eth_getFilterChanges', [filter]); // Loop through the list of transactions and process each one for (const hash of transactions) { const receipt = await provider.send("eth_getTransactionByHash", [hash]); // Check that the receipt is not null and has a non-null value field if (receipt && receipt.value != null) { const value = receipt.value; const decimalValue = BigInt(value).toString(); const convertedValue = ethers.utils.formatEther(decimalValue); // Check if the transferred value is greater than or equal to 100 AVAX if (convertedValue >= 100) { console.log(`This transaction is sending more than 100 AVAX`); console.log(`Transaction Hash: ${hash}`); console.log(`Value transferred: ${convertedValue} \n`); } } } } catch (error) { console.error(error); // Handle errors that may occur } } // Main program setting an interval to call the `getValue` function at regular intervals async function main() { const filterId = await createFilter(); setInterval(getValue, 1000, filterId); } main() ``` This code sets up an Ethereum filter to listen for new pending transactions and extract specific data from them. The code consists of three functions: `createFilter`, `getValue`, and `main`. Here's an overview of how each function works: The `createFilter` function sets up a new filter using the `eth_newPendingTransactionFilter` method. It returns the ID of the filter, which can be used to retrieve data from the filter later. The `getValue` function retrieves the list of new pending transactions using the [eth\_getFilterChanges](/reference/avalanche-getfilterchanges) method and loops through each transaction. For each transaction, it retrieves the transaction data using the [eth\_getTransactionByHash](/reference/avalanche-gettransactionbyhash) method and checks if the transaction value is greater than or equal to 100 AVAX. If the value is above this threshold, the function logs information about the transaction to the console. The `main` function sets up the program by first calling the `createFilter` function to create a new filter and retrieve its ID. It then sets an interval to call the `getValue` function every 1 second, passing the filter ID as an argument to retrieve the latest transaction data at regular intervals. # eth_sendRawTransaction | Avalanche Source: https://docs.chainstack.com/reference/avalanche-sendrawtransaction openapi/avalanche_node_api/execute_transactions/eth_sendRawTransaction.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that allows submitting a signed transaction to the network. eth_sendRawTransaction on Avalanche via Chainstack. Avalanche API method that allows submitting a signed transaction to the network. Once a transaction is signed, you can use the `eth_sendRawTransaction` method to submit the signed transaction to the Avalanche network for processing. > [!WARNING] > > Note that the interactive example in this page will not work, due to the fact that `eth_sendRawTransaction` is used to modify the state of the blockchain, it is not possible to duplicate the same request. ## Parameters * `data` — the signed transaction. The serialized transaction data, which includes the transaction metadata such as the nonce, gas price, gas limit, recipient address, and data payload, as well as the digital signature generated using the private key associated with the address that is sending the transaction. Typically, signed with a library such as web3.py, web3.js, or ethers.js using the sender's private key. ## Response * `result` — the unique hash identifying the transaction or the zero hash if the transaction is not available yet. The zero hash is a special value that represents an invalid or null hash. ## `eth_sendRawTransaction` code examples The following examples demonstrate how to use Web3 libraries to make an AVAX transfer on the Fuji testnet. > [!NOTE] > > **Additional libraries requirement** > > Note that web3.js requires installing two extra libraries: > > * [ethereumjs-tx](https://www.npmjs.com/package/@ethereumjs/tx) > * [ethereumjs-common](https://www.npmjs.com/package/@ethereumjs/common) ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function sendAvax(value) { // Define the sender and receiver addresses, and the private key const sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a'; const receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114'; const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Define the gas limit const gasLimit = await provider.estimateGas({ from: sender, to: receiver, value: ethers.parseEther(value), }); // Get the transaction count for the sender address const nonce = await provider.getTransactionCount(sender); // Define the transaction object const transactionObject = { to: receiver, gasPrice: ethers.parseUnits('50', 'gwei'), gasLimit: gasLimit.toString(), nonce: nonce, value: ethers.parseEther(value), }; // Sign the transaction using the private key const wallet = new ethers.Wallet(privateKey, provider); console.log(`Sending transaction...`) const transaction = await wallet.sendTransaction(transactionObject); console.log('Transaction hash:', transaction.hash); } sendAvax("2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" # Define the sender and receiver addresses, and the private key sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a' receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114' private_key = '1111111111111111111111111111111111111111111111111111111111111111' # Estimate gas limit gas_limit = web3.eth.estimate_gas({ 'from': sender, 'to': receiver }) # Build the transaction object transaction = { 'nonce': web3.eth.get_transaction_count(sender), 'to': receiver, 'value': web3.to_wei(1, 'ether'), # value to send 'gas': gas_limit, 'gasPrice': web3.eth.gas_price, 'chainId': web3.eth.chain_id } # Sign the transaction signed_tx = web3.eth.account.sign_transaction(transaction, private_key) print(f'Signed transaction: {signed_tx}') # Send the transaction print('Sending transaction...') tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) print(f'Transaction hash: {web3.toHex(tx_hash)}') ``` > [!NOTE] > > Note that you will need to edit the `sender`, `receiver`, and `privateKey` fields to be able to use this code. ## Use case One potential use case for `eth_sendRawTransaction` is to automatically swap AVAX for a token when its price reaches a certain level. `eth_sendRawTransaction` will allow specifying custom transaction parameters, such as gas price or gas limit, to optimize the speed and cost of the transaction. For instance, you could build a program monitoring the price of a specific token and execute a `swapExactETHForTokens` transaction when the token reaches a certain price level, it could use `eth_sendRawTransaction` to submit a raw transaction with a custom gas price and limit that are optimized for the current network conditions. The following code shows how to call the `swapExactETHForTokens` function from the SushiSwap exchange on Avalanche using ethers.js: > [!NOTE] > > Note that this code only shows how to execute the `swapExactETHForTokens` function on [SushiSwap on Avalanche mainnet](https://snowtrace.io/address/0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506#code), and not how to track the prices. You need to build a separate part for this. This particular example shows how to swap AVAX for AAVE tokens. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // ABI for the swapExactETHForTokens function const sushiRouterABI = [ { "inputs":[ { "internalType":"uint256", "name":"amountOutMin", "type":"uint256" }, { "internalType":"address[]", "name":"path", "type":"address[]" }, { "internalType":"address", "name":"to", "type":"address" }, { "internalType":"uint256", "name":"deadline", "type":"uint256" } ], "name":"swapExactETHForTokens", "outputs":[ { "internalType":"uint256[]", "name":"amounts", "type":"uint256[]" } ], "stateMutability":"payable", "type":"function" } ] async function swapETHForToken(value, token, privateKey) { // Create a new wallet using the private key const wallet = new ethers.Wallet(privateKey, provider); // Get the SushiSwap Router contract and create a contract instance const sushiRouterAddress = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; // SushiSwap Router address on Avalanche Mainnet const sushiRouterContract = new ethers.Contract(sushiRouterAddress, sushiRouterABI, wallet); // Prepare the swap parameters const tokenToBuy = token; const amountOfETH = ethers.utils.parseEther(value); // Convert the input value to Wei const minAmountOfTokens = ethers.utils.parseUnits('0'); // Set the minimum output token amount to 0, this means that you will always receive something. const deadline = Math.floor(Date.now() / 1000) + 60 * 10; // Set the deadline to 10 minutes from now // Define the token path for the swap const path = ['0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7', tokenToBuy]; // WAVAX to token to buy // Parameters for the transaction object const nonce = await wallet.getTransactionCount(); const gasPrice = await provider.getGasPrice(); const transactionData = sushiRouterContract.interface.encodeFunctionData('swapExactETHForTokens', [ minAmountOfTokens, path, wallet.address, deadline ]); // Calculate the gas limit based on the parameters const gasLimit = await provider.estimateGas({to: sushiRouterAddress, value: amountOfETH, data: transactionData}); // Get the chain ID for replay-protected (EIP-155) transactions const chainId = await provider.getNetwork().then(network => network.chainId); // Build the transaction object const transactionObject = { to: sushiRouterAddress, value: amountOfETH, nonce: nonce, gasPrice: gasPrice, gasLimit: gasLimit, data: transactionData, chainId: chainId }; // Sign and send the transaction console.log(`Swapping tokens...`); const signedTx = await wallet.signTransaction(transactionObject); const transaction = await provider.sendTransaction(signedTx); console.log(`Transaction sent; transaction hash: ${transaction.hash}\n`); // Wait for the transaction to be confirmed console.log(`Validating transaction...`); const receipt = await transaction.wait(); return receipt } async function main() { try { // Set the swap parameters and private key const quantity = '0.2'; // Amount of AVAX to swap const addressToBuy = '0x63a72806098Bd3D9520cC43356dD78afe5D386D9'; // Address of the token to buy (AAVE in this case) const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Replace with your actual private key // Execute the swap and check if successful const receipt = await swapETHForToken(quantity, addressToBuy, privateKey); if (receipt.status === 1) { console.log(`The swap was successful!`); console.log(`Transaction receipt`, receipt); } else { console.log(`The swap failed with status ${receipt.status}.`); } } catch (error) { console.error(`An error occurred during the swap: ${error.message}`); } } main(); ``` > [!WARNING] > > **Security notice** > > You need your private key to sign the transaction; never share your private key with anyone. > > On a side note, the private key in this case must be imported without '0x' at the beginning of the string. In the `swapETHForToken` function, a new wallet is created using the provided `privateKey`. Then, the instance of the SushiSwap Router contract is obtained using its address and the ABI (Application Binary Interface) for the `swapExactETHForTokens` function. This function swaps a specified amount of AVAX for a token at the current market price. The `tokenToBuy`, `amountOfETH`, `minAmountOfTokens`, and `deadline` variables are used to prepare the swap parameters. The `path` variable defines the input and output token path for the swap. The `nonce`, `gasPrice`, and the `transactionData` variables define the parameters for the transaction object and encode the `swapExactETHForTokens` function call with the specified parameters. The `gasLimit` variable is calculated based on the parameters, and the `chainId` variable is obtained for replay-protected (EIP-155) transactions. Then, the `transactionObject` variable is built, including the `to` address, the `value`, the `nonce`, the `gasPrice`, the `gasLimit`, the `data`, and the `chainId`. Finally, the transaction is signed and sent, and the receipt is returned. If the status of the receipt is equal to 1, the swap was successful, and the receipt object is logged to the console. If the status is not equal to 1, the swap failed, and an error message is logged to the console. # subscribe ("logs") | Avalanche Source: https://docs.chainstack.com/reference/avalanche-subscribelogs Reference for the subscribe ("logs") JSON-RPC method on the Avalanche blockchain via Chainstack nodes. Parameters, response, and examples included. Subscription equivalent to [eth\_newFilter](/reference/avalanche-newfilter). `subscribe("logs")` allows developers to subscribe to real-time updates about new event logs on the Avalanche blockchain; the application will receive notifications whenever new logs matching the filter are emitted. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) The subscription options: * `fromBlock` — an integer that specifies the starting block number from which the logs should be fetched. `null` by default. * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `subscribe("logs")` code example > [!NOTE] > > Note that log subscriptions require a WebSocket connection. Use the [`ethers.WebSocketProvider`](https://docs.ethers.org/v6/api/providers/#WebSocketProvider) `provider.on` method to attach a listener to the log filter: * The listener fires for each new log matching the filter. * `provider.websocket.on("error", ...)` activates if an error is detected during the subscription. * `provider.websocket.on("open", ...)` activates after the connection is successfully established. * `provider.off(filter)` removes the listener and stops receiving updates. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); // Only log transfer events from the WETH smart contract. const logsFilter = { address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } async function subscribeToLogs() { try { // React to connection and error events on the underlying WebSocket provider.websocket.on('open', handleConnected); provider.websocket.on('error', handleError); // Create a new subscription to the logs matching the filter provider.on(logsFilter, handleLogs); } catch (error) { console.error(`Error subscribing to new logs: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs a message when the subscription is connected function handleConnected() { console.log(`New subscription connected`); } // Event listener that logs the filtered events async function handleLogs(logs) { console.log(logs) } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToLogs(); ``` ## Use case A practical use case for `subscribe("logs")` is a DApp that continuously listens for new specific event logs and isolates certain fields for analytics purposes. This is useful, for example, to only track events about a specific ERC-721 token. The following is an implementation of this concept using ethers.js subscriptions; this program will only track the `Transfer` event from the [FunnelHeads collection](https://snowtrace.io/token/0x5378d068001d7e53d6c0a7ef539fbe213b5eb075#code) smart contract. The `Tranfer` event looks like this in the smart contract: ```sol IERC721.sol theme={"system"} event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); ``` The following code will log all of the new `Transfer` events and extract the block number. This can be useful to analyze when tokens are moved. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); // Only log Tranfer events from the Avalanche FunnelHeads smart contract. const logsFilter = { address: '0x5378d068001D7E53D6c0a7ef539fbE213B5eB075', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } async function subscribeToLogs() { try { // React to connection and error events on the underlying WebSocket provider.websocket.on('open', handleConnected); provider.websocket.on('error', handleError); // Create a new subscription to the logs matching the filter provider.on(logsFilter, handleLogs); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } // Use this function to unsuscribe and exit the program function unsubscribe() { provider.off(logsFilter); console.log("Successfully unsubscribed!"); return process.exit(1) } /* Fallback functions to react to the different events */ // Event listener that logs a message when the subscription is connected function handleConnected() { console.log(`New subscription connected`); } // Event listener that logs the filtered events async function handleLogs(logs) { const block = logs.blockNumber console.log(`A FunnelHead was transferred in this block: ${block}`) } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToLogs(); ``` This code creates a new subscription to the logs matching the filter using the `provider.on` method. This method attaches a listener that fires every time a new log matching the filter is emitted. The code defines three event listener functions: `handleConnected`, `handleLogs`, and `handleError`. The `handleConnected` function is called when the WebSocket connection opens, and it logs a message. The `handleLogs` function is called when a new log matching the filter is emitted, then extracts the block number field and logs it to the console. The `handleError` function is called when an error occurs, and it logs an error message. The code includes the `unsubscribe` function that can be implemented in the logic to unsubscribe and exit the program when a condition is met. Finally, the code calls the `subscribeToLogs` function, which creates the subscription and attaches the event listeners. When a new event log is received, the `handleLogs` function is called to extract the data and log it to the console. # subscribe ("newBlockHeaders") | Avalanche Source: https://docs.chainstack.com/reference/avalanche-subscribenewblockheaders Reference docs for the subscribe ("newBlockHeaders") JSON-RPC method on the Avalanche blockchain, available via Chainstack JSON-RPC nodes. The ethers.js subscription equivalent to [eth\_newBlockFilter](/reference/avalanche-newblockfilter). `subscribe("newBlockHeaders")` allows developers to subscribe to real-time updates about new block headers on the Avalanche blockchain; the application will receive notifications whenever a new block is added to the blockchain. The notification will include information about the new block, such as its block number, hash, and timestamp. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `newBlockHeaders` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Avalanche nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Avalanche blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Avalanche network since the inception of the network. It measures the overall security and integrity of the Avalanche network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `subscribe("newBlockHeaders")` code example > [!NOTE] > > Note that ethers.js subscriptions require a WebSocket connection. Use the [provider event listeners](https://docs.ethers.org/v6/api/providers/#Provider-on) to react to subscription events on the `WebSocketProvider`: * `provider.on("block", listener)` — activates for each new block header received. * `provider.on("error", listener)` — activates if an error is detected during the subscription. * `provider.off("block", listener)` — removes the listener and stops the subscription. * `provider.destroy()` — closes the WebSocket connection. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToNewBlocks() { try { // Subscribe to the 'block' event for new block headers provider.on("block", handleNewBlock); // React to any provider errors during the subscription provider.on("error", handleError); console.log("New subscription to block headers"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that fetches and logs the received block header data async function handleNewBlock(blockNumber) { const blockHeader = await provider.getBlock(blockNumber); console.log(blockHeader); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToNewBlocks(); ``` ## Use case A practical use case for `subscribe("newBlockHeaders")` is a DApp that continuously listens for new block headers, then isolates the block number and `baseFeePerGas` for analytics purposes. The following is an implementation of this concept using ethers.js subscriptions: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); let blockCount = 0; async function subscribeToNewBlocks() { try { // Subscribe to the 'block' event for new block headers provider.on("block", handleNewBlock); // React to any provider errors during the subscription provider.on("error", handleError); console.log("New subscription to block headers"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } function unsubscribeFromNewBlocks() { // Remove the listener and close the WebSocket connection provider.off("block", handleNewBlock); provider.destroy(); console.log("Successfully unsubscribed!"); return process.exit(1); } /* Fallback functions to react to the different events */ // Event listener that extracts block number and base fee per gas from the received block header data async function handleNewBlock(blockNumber) { const blockHeader = await provider.getBlock(blockNumber); const baseFeePerGas = blockHeader.baseFeePerGas; const baseFeeGwei = ethers.formatUnits(baseFeePerGas, "gwei"); blockCount++; console.log(`Block number: ${blockNumber} \n Base Fee per Gas: ${baseFeeGwei} Gwei \n`); if (blockCount === 100) { unsubscribeFromNewBlocks(); } } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToNewBlocks(); ``` This code creates a new subscription to new block headers using the `provider.on("block", listener)` method on a `WebSocketProvider`. This method registers a listener that is called with the block number every time a new block is added. The code defines two event listener functions that are attached to the provider: `handleNewBlock` and `handleError`. The `handleNewBlock` function is called when a new block header is received, and it fetches the full block with `provider.getBlock` and then extracts the block number and base fee per gas from the block header data using the `ethers.formatUnits` method. The `handleError` function is called when an error occurs, and it logs an error message. This code includes an `unsubscribeFromNewBlocks` function which removes the listener with `provider.off`, closes the WebSocket connection with `provider.destroy`, and quits the program using `return process.exit(1)` after it received 100 new blocks. This is to demonstrate its use. Finally, the code calls the `subscribeToNewBlocks` function, which creates the subscription and attaches the event listeners. When a new block header is received, the `handleNewBlock` function is called to extract the block data and log it to the console. # subscribe ("pendingTransactions") | Avalanche Source: https://docs.chainstack.com/reference/avalanche-subscribependingtransactions Reference docs for the subscribe ("pendingTransactions") JSON-RPC method on the Avalanche blockchain, available via Chainstack JSON-RPC nodes. Note that because the Avalanche mempool is [only accessible to validators](/docs/mempool-configuration), this method will only return transactions that were submitted through the same node you are connected to. The ethers.js subscription equivalent to [eth\_newPendingTransactionFilter](/reference/avalanche-newpendingtransactionfilter). `subscribe("pendingTransactions")` allows developers to subscribe to real-time updates about pending transactions on the Avalanche blockchain; the application will receive notifications whenever a pending transaction appears on the blockchain. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `pendingTransactions` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `string` — the hash identifying the pending transaction. ## `subscribe("pendingTransactions")` code example > [!NOTE] > > Note that ethers.js subscriptions require a WebSocket connection. Use the `provider.on` method with the `"pending"` event to attach a listener to the WebSocket provider: * `"pending"` — fires for each new pending transaction hash received by the connected node. * `"error"` — fires if an error is detected on the provider connection. * `provider.removeAllListeners` — removes the listeners so the program can stop receiving updates. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToPendingTransactions() { try { // Subscribe to the 'pending' event to receive new pending transaction hashes provider.on("pending", handleNewPending); // Attach an error listener to the provider connection provider.on("error", handleError); console.log("New subscription: pending"); } catch (error) { console.error(`Error subscribing to pending transactions: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs the received pending transaction hashes function handleNewPending(transactionHash) { console.log(transactionHash); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToPendingTransactions(); ``` ## Use case A practical use case for `subscribe("pendingTransactions")` is a DApp that continuously listens for new pending transactions, then isolates the `from`, `to`, and `value` fields for analytics purposes. This is useful, for example, to only track transactions that move at least a certain amount of AVAX. The following is an implementation of this concept using ethers.js subscriptions: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeTopendingTransactions() { try { // Subscribe to the 'pending' event to receive new pending transaction hashes provider.on("pending", handleNewPending); // Attach an error listener to the provider connection provider.on("error", handleError); console.log("New subscription: pending"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } // Use this function to unsubscribe and exit the program function unsubscribe() { provider.removeAllListeners(); console.log("Successfully unsubscribed!"); return process.exit(1); } /* Fallback functions to react to the different events */ // Event listener that logs the received pending transactions and extracts from, to, and value fields async function handleNewPending(transactionHash) { try { const transaction = await provider.getTransaction(transactionHash); if (!transaction) return; // The transaction may not be available yet const from = transaction.from; const to = transaction.to; const value = transaction.value; if (value >= 100000000000000000000n) { // 100 AVAX in wei console.log(`----- New pending transaction ------`); console.log(`From: ${from}`) console.log(`To: ${to}`) console.log(`Value: ${Number(ethers.formatEther(value)).toFixed(2)} AVAX\n`) } } catch (error) { console.error(`Error getting transaction: ${error}`); } } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeTopendingTransactions(); ``` This code creates a new subscription to the `pending` event using the `provider.on` method on a `WebSocketProvider`. This method attaches a listener that the provider calls every time a new pending transaction hash arrives. The code defines two event listener functions that are attached to the provider: `handleNewPending` and `handleError`. The `handleNewPending` function is called when a new pending transaction is received; it runs the [eth\_getTransactionByHash](/reference/avalanche-gettransactionbyhash) method through `provider.getTransaction` and extracts the `from`, `to`, and `value` fields. If the value transferred is above 100 AVAX, the data is logged. The code includes the `unsubscribe` function that can be implemented in the logic to remove the listeners and exit the program when a condition is met. The `handleError` function is called when an error occurs, and it logs an error message. Finally, the code calls the `subscribeTopendingTransactions` function, which creates the subscription and attaches the event listeners. When a new pending transaction is received, the `handleNewPending` function is called to extract the data and log it to the console. # subscribe ("syncing") | Avalanche Source: https://docs.chainstack.com/reference/avalanche-subscribesyncing Monitor Avalanche node sync status in real time with web3.js. Subscribe to syncing updates to track block download progress and chain synchronization. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `logs` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `object` — the following sync object when the node is currently syncing: * `startingBlock` — the block number from which the node began syncing. * `currentBlock` — the latest block number the node has synced to. * `highestBlock` — the estimated highest block number that needs to be synced. * `pulledStates` — the number of state entries that have already been downloaded. * `knownStates` — the estimated number of state entries to be downloaded during the sync. * `boolean` — returns `False` when the node is already in sync. ## `subscribe("syncing")` code example > [!NOTE] > > Note that subscriptions require a WebSocket connection. Use a `WebSocketProvider` with ethers.js. ethers.js does not expose a dedicated `syncing` event, so use the WebSocketProvider to send the raw `eth_subscribe` request and listen for `eth_subscription` notifications on the underlying socket: * `eth_subscription` notification — fires for each new syncing event. * `error` — fires if an error is detected during the subscription. * the subscription ID — returned by `eth_subscribe` after the subscription is successfully created. * `eth_unsubscribe` — removes the subscription and returns `true` if successful. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToSync() { try { // Create a new subscription to the 'syncing' event const subscriptionId = await provider.send("eth_subscribe", ["syncing"]); handleConnected(subscriptionId); // Listen for subscription notifications on the underlying WebSocket provider.websocket.onmessage = (message) => { const payload = JSON.parse(message.data); if (payload.method === "eth_subscription" && payload.params.subscription === subscriptionId) { handleSync(payload.params.result); } }; provider.websocket.onerror = handleError; } catch (error) { console.error(`Error subscribing to sync: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs a message when there is a new syncing event function handleConnected(subscriptionId) { console.log(`New subscription: ${subscriptionId}`); } // Event listener that logs the filtered events async function handleSync(sync) { console.log(sync) } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error: ${error}`); } subscribeToSync(); ``` ## Use case A practical use case for `subscribe("syncing")` is a DApp that continuously listens for the status of a node and notifies the developer if the node falls behind a certain number of blocks. The following is an implementation of this concept using ethers.js subscriptions, this program will leave a notification in the console if the node falls more than 100 blocks behind. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToSync() { try { // Create a new subscription to the 'syncing' event const subscriptionId = await provider.send("eth_subscribe", ["syncing"]); handleConnected(subscriptionId); // Listen for subscription notifications on the underlying WebSocket provider.websocket.onmessage = (message) => { const payload = JSON.parse(message.data); if (payload.method === "eth_subscription" && payload.params.subscription === subscriptionId) { handleSync(payload.params.result); } }; provider.websocket.onerror = handleError; } catch (error) { console.error(`Error: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs a message when there is a new syncing event function handleConnected(subscriptionId) { console.log(`New subscription: ${subscriptionId}`); } // Event listener that logs the filtered events async function handleSync(sync) { let currentBlock; let highestBlock; const status = sync.status; for (const property in status) { if (property === 'CurrentBlock') { currentBlock = status[property]; } else if (property === 'HighestBlock') { highestBlock = status[property]; } } if (currentBlock !== undefined && highestBlock !== undefined) { const blocksBehind = highestBlock - currentBlock; console.log(`The node is ${blocksBehind} blocks behind the network.`); if (blocksBehind > 1000) { alert(`The node is ${blocksBehind} blocks behind the network. Please check your connection.`); } } } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToSync(); ``` This code creates a new subscription to the `syncing` event using the `eth_subscribe` request sent through the `WebSocketProvider`. This returns a subscription ID that is used to match incoming notifications on the underlying WebSocket. The code defines three event listener functions: `handleConnected`, `handleSync`, and `handleError`. The `handleConnected` function is called when the subscription is created, and it logs a message with the subscription ID. The `handleSync` function is called when a new syncing notification arrives, extracts the `CurrentBlock` and the `HighestBlock` field, and then compares them. If the node is more than 1,000 blocks behind, the user will receive an alert. The `handleError` function is called when an error occurs, and it logs an error message. Finally, the code calls the `subscribeToSync` function, which creates the subscription and attaches the event listeners. When a new event is received, the `handleSync` function is called to extract the data and log it to the console. # eth_syncing | Avalanche Source: https://docs.chainstack.com/reference/avalanche-syncing openapi/avalanche_node_api/chain_info/eth_syncing.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns an object with information about the current synchronization status of the node, or false if the node is fully synced. Avalanche API method that returns an object with information about the current synchronization status of the node, or `false` if the node is fully synced. This method can track the progress of a node's synchronization with the Avalanche blockchain. The returned object contains data such as the starting block, current block, and highest block of the node, allowing developers to monitor and estimate the time remaining for the node to sync fully. ## Parameters * `none` ## Response * `result` — the boolean value `false` when not syncing or a JSON object when syncing: * `startingBlock` — the block number where the synchronization process started, encoded as hexadecimal. * `currentBlock` — the block number that the node has currently processed, same as `eth_blockNumber`, encoded as hexadecimal. * `highestBlock` — the block number of the latest block in the blockchain known to the node. ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const syncStatus = async () => { const status = await provider.send("eth_syncing", []); console.log(status); }; syncStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" print (web3.eth.syncing) ``` ## Use case The `eth_syncing` method on Avalanche can be useful for developers building applications that interact with the Avalanche blockchain. For example, you may want to provide feedback to your application's user about the status of the Avalanche node the application is connecting to. Here is a code example using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function syncstatus() { try { // Query the sync status const syncing = await provider.send("eth_syncing", []); if (syncing) { console.log(`Node is syncing. Current block: ${syncing.currentBlock}. Highest block: ${syncing.highestBlock}`); } else { console.log('Node is fully synced.'); } } catch (error) { console.error(error); } }; syncstatus() ``` In this example, the program creates a new `ethers.js` provider and connects to the node. Then, it calls the `eth_syncing` RPC method through the provider to get the current synchronization status of the node. If the node is fully synced, the function logs a message to the console indicating that the node is fully synced. If the node is still syncing, the function logs the current block number and the highest block number known to the node. # debug_traceBlockByHash | Avalanche Source: https://docs.chainstack.com/reference/avalanche-traceblockbyhash openapi/avalanche_node_api/debug_and_trace/debug_traceBlockByHash.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that traces the execution of a block. Chainstack supports debug_traceBlockByHash on Avalanche JSON-RPC nodes. Avalanche API method that traces the execution of a block. This method can be used to debug and analyze smart contracts and transactions on the Avalanche blockchain. It provides a detailed trace of the execution of a block, including information on all the transactions and calls that interacted with the block, as well as the gas used, memory, storage operations, and other performance metrics for each operation. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#avalanche) with the debug and trace API methods enabled. ## Parameters * `hash` — the hash of the block to be traced. * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call. Encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ## `debug_traceBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByHash = async (blockHash) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer const tracer = { tracer: '4byteTracer' }; const traces = await provider.send("debug_traceBlockByHash", [blockHash, tracer]); console.log(traces); }; traceBlockByHash("0x917b447fc7b12ae37b768d0b442a6e0d4c5aef3378aca19db7f34d1ba5a9d0a2") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block_hash = "0x917b447fc7b12ae37b768d0b442a6e0d4c5aef3378aca19db7f34d1ba5a9d0a2" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer tracer = { "tracer": '4byteTracer' } block_traces = web3.provider.make_request('debug_traceBlockByHash', [block_hash, tracer]) print(block_traces) ``` ## Use case One practical use case for `debug_traceBlockByHash` with the `4byteTracer` tracer would be for auditing or analysis purposes of a blockchain. For example, if there are token contracts on the blockchain and the transfer function of these contracts is used to send tokens between users, a developer could use the `debug_traceBlockByHash` method with the `4byteTracer` tracer to trace the transactions in a given block and count how many times the transfer function was called. The following is an implementation of this logic using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function traceTransferFunction(blockHash) { try { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer const tracer = { tracer: '4byteTracer' }; const response = await provider.send("debug_traceBlockByHash", [blockHash, tracer]); let sum = 0; for (let obj of response) { for (let key in obj.result) { const signature = key.split('-')[0]; // Look for transfer function signatures if (signature === "0xa9059cbb") { const timesCalled = obj.result[key] sum += timesCalled } } } console.log(`In this block the transfer function was called ${sum} times!`) } catch (err) { console.error(`Error in traceBlockByHash: ${err.message}`); } } traceTransferFunction("0x917b447fc7b12ae37b768d0b442a6e0d4c5aef3378aca19db7f34d1ba5a9d0a2"); ``` This code uses the `provider.send` method to call the `debug_traceBlockByHash` function directly, since ethers.js does not expose it as a built-in method. This call uses the `4byteTracer` to identify the functions called in the block. Then, the `traceTransferFunction` function is defined, which takes a block hash as an argument. This function sends the `debug_traceBlockByHash` request with the provided block hash and uses a loop to iterate over the results. Inside the loop, the function checks for the `transfer` function from the ERC-20 standard with signature `0xa9059cbb` and counts how many times it was called in that block by adding up the times it was called in each trace. Finally, the function logs the number of times the transfer function was called in that block. The code also has error handling in case anything goes wrong with the tracing process. # debug_traceBlockByNumber | Avalanche Source: https://docs.chainstack.com/reference/avalanche-traceblockbynumber openapi/avalanche_node_api/debug_and_trace/debug_traceBlockByNumber.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that enables the tracing of the execution of a specific block using its number. Chainstack Avalanche reference. Avalanche API method that enables the tracing of the execution of a specific block using its number. This method can be used to troubleshoot and analyze smart contracts and transactions on the Avalanche blockchain. It provides an in-depth trace of the block execution, with details on all the interactions, such as transactions and calls, that took place. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#avalanche) with the debug and trace API methods enabled. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ## `debug_traceBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByNumber = async (block) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer const tracer = { tracer: 'callTracer' }; const traces = await provider.send("debug_traceBlockByNumber", [block, tracer]); console.log(traces); }; traceBlockByNumber("latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block = "latest" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer tracer = { "tracer": 'callTracer' } block_traces = web3.provider.make_request('debug_traceBlockByNumber', [block, tracer]) print(block_traces) ``` ## Use case One use case for `traceBlockByNumber` would be to trace a block, calculate how many transactions are in it, and then display the number of transactions and the average gas used. To accomplish this, you can use `traceBlockByNumber` to retrieve the trace of each transaction in the block and then iterate over the transactions to count the total number of transactions and calculate the total gas used. Once you have this information, you can divide the total gas used by the total number of transactions to get the average gas used. Here is an implementation of this idea using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getAverageGasUsed(block) { const tracer = { tracer: 'callTracer' }; // Call the debug_traceBlockByNumber RPC method directly const traceResult = await provider.send("debug_traceBlockByNumber", [block, tracer]); let totalGasUsed = 0n; let transactionCount = 0; for (let obj of traceResult) { const gasUsed = obj.result.gasUsed; const decimalGas = BigInt(gasUsed); totalGasUsed += decimalGas; transactionCount++; } const averageGasUsed = Number(totalGasUsed) / transactionCount; console.log(`Transaction in this block: ${transactionCount}`) console.log(`Average units of gas used per transaction: ${Math.round(averageGasUsed)}`); } getAverageGasUsed("latest"); ``` First, the code sets up an ethers.js provider connected to your Chainstack node. The `getAverageGasUsed` function then calls the `debug_traceBlockByNumber` RPC method directly with `provider.send` to trace the transactions in the specified block. Inside the function, we define a tracer object with the value of `callTracer`, which will help us get more detailed information about the block. Then, we call the `debug_traceBlockByNumber` RPC method, passing in the block number and the tracer object as parameters. This returns an array of objects containing information about each transaction in the block. We then declare two variables to keep track of the total gas used and the number of transactions in the block. We loop through each transaction in the block using a `for...of` loop and extract the `gasUsed` property of each transaction's result object. We convert this hexadecimal value to a `BigInt` using the `BigInt` constructor and add it to the `totalGasUsed` variable. We also increment the `transactionCount` variable to keep track of how many transactions there are in the block. Finally, we calculate the `averageGasUsed` by dividing the `totalGasUsed` by the `transactionCount` and displaying the result. We use the `Math.round()` method to round the result to the nearest integer for readability. When we call the function, we pass in the string `"latest"` to get the average gas used for the latest block. # debug_traceCall | Avalanche Source: https://docs.chainstack.com/reference/avalanche-tracecall openapi/avalanche_node_api/debug_and_trace/debug_traceCall.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that traces the execution of eth_call within the context of a specific block's execution. Avalanche via Chainstack. Avalanche API method that traces the execution of [eth\_call](/reference/avalanche-ethcall) within the context of a specific block's execution. This method uses the final state of the parent block as its base and allows developers to trace the execution of a particular call. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#avalanche) with the debug and trace API methods enabled. ## Parameters * `object` — the transaction call object: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/avalanche-debug-trace-rpc-methods#pre-built-native-tracers) in the debug and trace overview. You can also use additional configuration parameters. The following settings are available: * `disableStorage` — when enabled, it prevents tracing of storage changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. By default, `debug_traceTransaction` traces both memory and storage changes, but storage tracing can be particularly resource-intensive, especially for large transactions. * `disableStack` — when enabled, it skips tracing of [stack](https://en.wikipedia.org/wiki/Stack_\(abstract_data_type\)) changes made by the transaction being analyzed. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableMemory` — when `false`, it prevents tracing of memory changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableReturnData` — when `false`, it prevents the method from tracing the return data produced by the transaction being analyzed. The return data tracing can be particularly resource-intensive, requiring a significant amount of time and processing power to complete. * `timeout` — setting that allows developers to customize the method's timeout period for JavaScript-based tracing calls. The default timeout is `5s`, and you can find the values formats in the [Go documentation](https://pkg.go.dev/time#ParseDuration). > [!NOTE] > > It's worth noting that when using one of the three built-in tracers in Avalanche clients, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData`settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the [Struct/opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#structopcode-logger). ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction., usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract, if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ### `Struct/opcode logger` response * `object` — the `Struct/opcode logger` traces object: * `pc` — the current program counter of the transaction, which represents the location of the current opcode being executed. A register that keeps track of the address of the next instruction to be executed in a program. * `op` — the current [opcode](https://ethereum.org/en/developers/docs/evm/opcodes/) being executed, such as PUSH1, ADD, or SWAP1. A code that represents a specific operation or instruction that a computer or processor can understand and execute. * `gas` — the amount of gas remaining in the transaction at the current step of execution. * `gasCost` — the amount of gas consumed by the current opcode being executed. * `depth` — the current depth of the call stack for the transaction, reflecting the number of active frames representing nested function calls. * `stack` — the contents of the EVM stack at the current step of the transaction's execution. The stack is a data structure used by the EVM to manage and manipulate data during the execution of opcodes * `memory` — the contents of the memory at the current step of execution. A temporary data storage location that is available to smart contracts during execution. ## `debug_traceCall` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceCall = async () => { const call = { from: '0xbdd87538521123603205c2d14cf566851f244983', to: '0xd2d4189265b7af891524a66f650e4cd592357484', gas: '0x6DDD00', gasPrice: '0x62F762A9A', data: '0x157a5ea900000000000000000000000000000000000000000000000000000000000000fb000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000bdd87538521123603205c2d14cf566851f2449830000000000000000000000000000000000000000000000000000000000000001' } const block = '0x1A284B9' // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. const tracer = {}; const traces = await provider.send("debug_traceCall", [call, block, tracer]); console.log(traces); }; traceCall() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) call = { 'from': '0xbdd87538521123603205c2d14cf566851f244983', 'to': '0xd2d4189265b7af891524a66f650e4cd592357484', 'gas': '0x6DDD00', 'gasPrice': '0x62F762A9A', 'data': '0x157a5ea900000000000000000000000000000000000000000000000000000000000000fb000000000000000000000000000000000000000000000000000000000000012c000000000000000000000000bdd87538521123603205c2d14cf566851f2449830000000000000000000000000000000000000000000000000000000000000001' } block = '0x1A284B9' # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. tracer = { "tracer": 'prestateTracer' } tx_traces = web3.provider.make_request('debug_traceCall', [call, block, tracer]) print(tx_traces) ``` ## Use case A practical use case for the `debug_traceCall` method is to inspect the internal transactions that happened during a smart contract call. For example, a developer can examine a past transaction using the `callTracer` tracer where an account used a proxy smart contract to deploy a new instance using the `CREATE2` method. > [!NOTE] > > Read [Deploying a deterministic smart contract on Ethereum](https://chainstack.com/deploying-a-deterministic-contract-on-ethereum/) to learn more about the `CREATE2` method and how to deploy a smart contract to a predetermined address. The following code is an implementation of the logic to inspect internal transactions using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function traceCall(call, block) { try { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. // Leave empty {} for Struct/opcode logger. const tracer = { tracer: 'callTracer' }; // Trace the call const result = await provider.send("debug_traceCall", [call, block, tracer]); const calls = result.calls; console.log(`===== Internal Calls =====`); // Extract and display the desired properties from each result object using a loop for (const callObj of calls) { const from = callObj.from; const to = callObj.to; const type = callObj.type; console.log(`From: ${from}`); console.log(`To: ${to}`); console.log(`Type: ${type}\n`); } } catch (error) { console.error(`Error while tracing call: ${error}`); } } async function main() { try { const call = { from: '0x215462dc79523ac795216e1baa27586840fa9382', to: '0x0c30d3d66bc7c73a83fda929888c34dcb24fd599', gas: '0x773788', gasPrice: '0x5D21DBA00', data: '0xd7665a48000000000000000000000000b7499a92fc36e9053a4324affae59d333635d9c300000000000000000000000060781c2586d68229fde47564546784ab3faca982000000000000000000000000215462dc79523ac795216e1baa27586840fa938200000000000000000000000077e5ba3bb034290d014063edff5eaac8cd6ba33500000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000029d36b4b517f24c0000000000000000000000000008b206547cfe6f35a77ddab2d6d97260765a349ef' }; // Define the block number or block tag to trace const block = '0x1A28663'; await traceCall(call, block); } catch (error) { console.error(`Error while executing main: ${error}`); } } main(); ``` The `traceCall` function is an asynchronous function that takes two parameters: `call` and `block`. The `call` parameter is an object that contains the details of the call that needs to be traced. In this case, those are the details of a [transaction that deployed a smart contract instance using the CREATE2 method](https://snowtrace.io/tx/0xdb7ef3e36448c8a5980a2987d898fcfd59caaee519f744a28c8f88bd06259e93#internal) on the Avalanche blockchain. The block parameter is the block number or block tag to trace. The function first creates a tracer object that specifies the type of tracer to be used. In this case, it's set to `callTracer`, which means that the function will trace all the internal calls made by the smart contract during the execution of the specified call. The function then calls `provider.send` with the `debug_traceCall` method and the call, block, and tracer parameters. This call returns an object that contains information about the internal calls made by the smart contract during the execution of the specified call. The function then loops through the array of calls in the result object and extracts the `from`, `to`, and `type` properties of each call object. The `from` property is the address of the sender of the call, the `to` property is the address of the smart contract being called, and the `type` property represents the type of internal calls, such as a contract creation or an external function call. Finally, the function logs the extracted information to the console. The main function is also an asynchronous function that simply calls the `traceCall` function with the appropriate parameters. It also wraps the function call in a try-catch block to handle any errors that might occur during execution. # debug_traceTransaction | Avalanche Source: https://docs.chainstack.com/reference/avalanche-tracetransaction openapi/avalanche_node_api/debug_and_trace/debug_traceTransaction.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method that returns a transaction's traces by replaying it. Reference for debug_traceTransaction on Avalanche via Chainstack. Avalanche API method that returns a transaction's traces by replaying it. This method provides a detailed breakdown of every step in the execution of a transaction on the Avalanche blockchain, including gas usage and opcode output. What sets it apart is its ability to accurately simulate the transaction's execution path by replaying any prior transactions, making it a powerful tool for developers to identify and diagnose issues in their applications. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#avalanche) with the debug and trace API methods enabled. ## Parameters * `hash` — the hash identifying a transaction. * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/avalanche-debug-trace-rpc-methods#pre-built-native-tracers) in the debug and trace overview. You can also use additional configuration parameters. The following settings are available: * `disableStorage` — when enabled, it prevents tracing of storage changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. By default, `debug_traceTransaction` traces both memory and storage changes, but storage tracing can be particularly resource-intensive, especially for large transactions. * `disableStack` — when enabled, it skips tracing of [stack](https://en.wikipedia.org/wiki/Stack_\(abstract_data_type\)) changes made by the transaction being analyzed. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableMemory` — when `false`, it prevents tracing of memory changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableReturnData` — when `false`, it prevents the method from tracing the return data produced by the transaction being analyzed. The return data tracing can be particularly resource-intensive, requiring a significant amount of time and processing power to complete. * `timeout` — setting that allows developers to customize the method's timeout period for JavaScript-based tracing calls. The default timeout is `5s`, and you can find the values formats in the [Go documentation](https://pkg.go.dev/time#ParseDuration). > [!NOTE] > > It's worth noting that when using one of the three built-in tracers in Avalanche clients, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData`settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the [Struct/opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#structopcode-logger). ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ### `Struct/opcode logger` response * `object` — the `Struct/opcode logger` traces object: * `pc` — the current program counter of the transaction, which represents the location of the current opcode being executed. A register that keeps track of the address of the next instruction to be executed in a program. * `op` — the current [opcode](https://ethereum.org/en/developers/docs/evm/opcodes/) being executed, such as PUSH1, ADD, or SWAP1. A code that represents a specific operation or instruction that a computer or processor can understand and execute. * `gas` — the amount of gas remaining in the transaction at the current step of execution. * `gasCost` — the amount of gas consumed by the current opcode being executed. * `depth` — the current depth of the call stack for the transaction, reflecting the number of active frames representing nested function calls. * `stack` — the contents of the EVM stack at the current step of the transaction's execution. The stack is a data structure used by the EVM to manage and manipulate data during the execution of opcodes * `memory` — the contents of the memory at the current step of execution. A temporary data storage location that is available to smart contracts during execution. ## `debug_traceTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txhash) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. const tracer = {}; const traces = await provider.send("debug_traceTransaction", [txhash, tracer]); console.log(traces); }; traceTransaction("0xdb7ef3e36448c8a5980a2987d898fcfd59caaee519f744a28c8f88bd06259e93") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) tx_hash = "0xdb7ef3e36448c8a5980a2987d898fcfd59caaee519f744a28c8f88bd06259e93" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. tracer = { "tracer": '4byteTracer' } tx_traces = web3.provider.make_request('debug_traceTransaction', [tx_hash, tracer]) print(tx_traces) ``` ## Use case A potential use case for the `debug_traceTransaction` method is to inspect failed transactions on the Avalanche blockchain by using the built-in `callTracer` tracer to extract the error. By creating a script that analyzes the latest block's transactions and selectively extracts data only from the failed ones, you can effectively utilize this feature. Below is an example implementation of this logic using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Trace a failed transaction using the callTracer async function traceTransaction(txHash) { const tracer = { tracer: 'callTracer' }; const result = await provider.send("debug_traceTransaction", [txHash, tracer]); return result; } // Get all transactions from the specified block number async function getTransactionsFromBlock(blockNumber) { const block = await provider.getBlock(blockNumber, false); const txArray = block.transactions; return txArray; } // Main function that iterates through transactions and extracts data from failed ones async function main() { try { const transactions = await getTransactionsFromBlock('latest'); for (let i = 0; i < transactions.length; i++) { const txHash = transactions[i]; const tx = await provider.getTransactionReceipt(txHash); const txStatus = tx.status; // If transaction failed, extract data using traceTransaction method if (txStatus === 0) { console.log(`Transaction ${txHash} failed!`); const traces = await traceTransaction(txHash); const error = traces.error; console.log(`Transaction failed with error: ${error}`); } } } catch (error) { console.error(`Error occurred: ${error}`); } } // Call the main function main(); ``` The code is a JavaScript implementation that utilizes the ethers.js library to extract the error from failed transactions on the Avalanche blockchain. Because `debug_traceTransaction` is not exposed as a dedicated ethers.js method, the code calls it directly through `provider.send`, which allows for tracing of transactions with built-in tracers such as `callTracer`. The `traceTransaction` function in the code is responsible for using the `debug_traceTransaction` method to extract error and revert reason data from a failed transaction. The function takes in a transaction hash as a parameter and specifies the `callTracer` tracer type, and sends the `debug_traceTransaction` request. The resulting data is then returned to the caller. The `getTransactionsFromBlock` function retrieves all the transactions from a specified block number and returns an array of transaction hashes. The `main` function in the code is the main entry point, which first calls the `getTransactionsFromBlock` function to retrieve all transactions from the latest block on the Avalanche blockchain. It then iterates through the array of transactions and calls `traceTransaction` for each failed transaction, extracting the error. The extracted data is then printed to the console using `console.log`. The code also handles any errors that may occur during the execution of the code by wrapping the entire execution in a `try-catch` block. # Transactions info | Avalanche Source: https://docs.chainstack.com/reference/avalanche-transactions-rpc-methods Avalanche transaction info JSON-RPC methods — retrieve transaction data, receipts, and status for monitoring and analyzing on-chain C-Chain activity. Retrieving transaction information from the Avalanche blockchain can be done by using the following methods: > [card] [eth\_getTransactionByHash](/reference/avalanche-gettransactionbyhash) > [card] [eth\_getTransactionReceipt](/reference/avalanche-gettransactionreceipt) > [card] [eth\_getTransactionByBlockHashAndIndex](/reference/avalanche-gettransactionbyblockhashandindex) > [card] [eth\_getTransactionByBlockNumberAndIndex](/reference/avalanche-gettransactionbyblocknumberandindex) > [card] [eth\_newPendingTransactionFilter](/reference/avalanche-newpendingtransactionfilter) # eth_uninstallFilter | Avalanche Source: https://docs.chainstack.com/reference/avalanche-uninstallfilter openapi/avalanche_node_api/filter_handling/eth_uninstallFilter.json POST /ext/bc/C/rpc/05b3ad71cdf5342ca73d720e664207dd Avalanche API method used to remove a filter previously created using one of the following methods: eth_newFilter eth_newBlockFilter. Avalanche API method used to remove a filter previously created using one of the following methods: * [eth\_newFilter](/reference/avalanche-newfilter) * [eth\_newBlockFilter](/reference/avalanche-newblockfilter) Upon successful execution, the filter is removed and will no longer emit events. This method can be useful for optimizing resource usage when a developer no longer needs to monitor certain events or blocks. It can also be used to clean up unused filters in order to reduce clutter in the client-side filter list. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/avalanche-newfilter) > * [eth\_newBlockFilter](/reference/avalanche-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `string` — the filter ID that you want to uninstall ## Response * `boolean` — a boolean value indicating whether the filter was successfully uninstalled. `true` if successfully removed, and `false` if not. ## `eth_uninstallFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/avalanche-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function removeFilter(filter) { try { const removed = await provider.send('eth_uninstallFilter', [filter]); console.log(removed); // Log whether the filter is removed } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' removeFilter(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def remove_filter(filter_id): try: removed = web3.eth.uninstall_filter(filter_id) return removed except Exception as e: print(e) filter_id = '0x0' print(remove_filter(filter_id)) ``` ## Use case One use case for `eth_uninstallFilter` is to optimize resource usage in a DApp. When a DApp needs to monitor events on the blockchain, it can create a filter using [eth\_newFilter](/reference/avalanche-newfilter) or [eth\_newBlockFilter](/reference/arbitrum-newblockfilter) to listen for specific events or blocks. However, once the DApp no longer needs to monitor these events or blocks, it can use `eth_uninstallFilter` to stop watching for them. By doing so, the DApp can reduce the number of active filters and free up resources, such as network bandwidth and computational power, that would otherwise be used to maintain the filter. For example, let's say that a DApp is monitoring incoming blocks, logs batches of 50 blocks, and then uses `uninstallFilter` to remove the filter using. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const BLOCKS_TO_FETCH = 50; const BLOCK_FETCH_INTERVAL_MS = 200; async function getNewBlocks() { const filterId = await provider.send("eth_newBlockFilter", []); let blocks = []; while (blocks.length < BLOCKS_TO_FETCH) { const changes = await provider.send("eth_getFilterChanges", [filterId]); //console.log(changes) // show the changes if (changes.length === 0) { await new Promise(resolve => setTimeout(resolve, BLOCK_FETCH_INTERVAL_MS)); // wait for new blocks } else { blocks = blocks.concat(changes); } } const removeFilter = await provider.send("eth_uninstallFilter", [filterId]); // clean up filter if (removeFilter) { console.log(`We got 50 blocks, filter ${filterId} was removed.`) return blocks } } async function main() { const fiftyBlocks = await getNewBlocks(); console.log(fiftyBlocks) } main() ``` This code fetches the latest 50 blocks using a filter and creates an array. The `BLOCKS_TO_FETCH` constant is set to 50, which represents the number of blocks to fetch. The `BLOCK_FETCH_INTERVAL_MS` constant is set to 200, which represents the time interval (in milliseconds) to wait for new blocks to arrive. The `getNewBlocks` function is defined to fetch the blocks. This function uses a loop to retrieve new blocks until it has fetched the desired number of blocks. The loop waits for new blocks to arrive for a certain amount of time before trying again. Inside the `getNewBlocks` function, the [eth\_newBlockFilter](/reference/arbitrum-newblockfilter) JSON-RPC method is called to create a new filter to watch for new blocks. The filter ID is returned and stored in the `filterId` variable. An array stores the blocks fetched. Inside the loop, the [eth\_getFilterChanges](/reference/arbitrum-getfilterchanges) JSON-RPC method is called to retrieve any new blocks that have arrived since the filter was created. If there are no new blocks, the code waits a certain amount of time before trying again. If there are new blocks, they are stored in the `blocks` array. Once the `blocks` array has reached the `BLOCKS_TO_FETCH` limit, the filter is uninstalled using the `eth_uninstallFilter` JSON-RPC method. The `removeFilter` variable is set to true if the filter is successfully removed. If the filter is removed successfully, a message is logged to the console indicating that `BLOCKS_TO_FETCH` blocks have been fetched and the filter was removed. The blocks array is returned as the result of the `getNewBlocks` function. The main function is defined to call the `getNewBlocks` function and store the result in the `fiftyBlocks` variable. The `fiftyBlocks` variable is then logged to the console. # Subscriptions | Avalanche Source: https://docs.chainstack.com/reference/avalanche-web3js-subscriptions-methods Avalanche web3.js subscription methods — subscribe to new block headers, pending transactions, contract events, and other real-time C-Chain updates. > [!NOTE] > > Note that all subscriptions require a WebSocket connection. ## Native EVM subscriptions Developers can use the following native methods to subscribe to events: > [card] [eth\_subscribe('newHeads')](/reference/avalanche-native-subscribe-newheads) > [card] [eth\_subscribe('newPendingTransactions')](/reference/avalanche-native-subscribe-newpendingtransactions) > [card] [eth\_subscribe('logs')](/reference/avalanche-native-subscribe-logs) The subscriptions created can be cleared using the following method: > [card] [eth\_unsubscribe](/reference/avalanche-native-unsubscribe) ## web3.js subscriptions The [web3.js](https://web3js.readthedocs.io/en/v1.2.11/getting-started.html) library also offers subscription methods. web3.js subscriptions are the successors of the deprecated `web3.eth.filter` methods and provide a way for developers to receive real-time updates about events on the blockchain without constantly polling the blockchain for new data. Subscriptions can be created using one of the following methods: > [card] [subscribe('newBlockHeaders')](/reference/avalanche-subscribenewblockheaders) > [card] [subscribe('pendingTransactions')](/reference/avalanche-subscribependingtransactions) > [card] [subscribe('logs')](/reference/avalanche-subscribelogs) > [card] [subscribe('syncing')](/reference/avalanche-subscribesyncing) The subscriptions created can be cleared using the following method: > [card] [clearSubscriptions](/reference/avalanche-clearsubscriptions) # eth_accounts | Base Source: https://docs.chainstack.com/reference/base-accounts openapi/base_node_api/eth_accounts.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that returns a list of addresses owned by the client. eth_accounts JSON-RPC method available on the Base blockchain via Chainstack. > [!WARNING] > > **Deprecated** > > Note that this method is deprecated. This example is for historical reference. Base API method that returns a list of addresses owned by the client. For most nodes, this will return an empty array unless the node is running with unlocked accounts. ## Parameters * `none` ## Response * `result` — an array of 20-byte account addresses owned by the client. For most public nodes, this array will be empty as accounts are not stored on the node for security reasons. ## Use case The `eth_accounts` method is primarily used in development environments or private networks where accounts are stored locally on the node. In production environments with public nodes, this method typically returns an empty array as accounts should be managed client-side for security reasons. # Base API reference: JSON-RPC quickstart on Chainstack Source: https://docs.chainstack.com/reference/base-api-reference Use the Base JSON-RPC API on Chainstack to deploy nodes, send transactions, and query the Coinbase Ethereum L2 optimistic rollup. ## What is the Base protocol? Base is a Layer 2 scaling solution for the Ethereum blockchain developed by Coinbase. It aims to provide a more scalable and efficient environment for decentralized applications (dApps) by leveraging Optimistic Rollups, a type of Layer 2 scaling technology. Base inherits the security and decentralization of Ethereum while offering higher throughput and lower transaction costs. Find useful Base tools in the [Base tooling](/docs/base-tooling) section. ## What is the Base API The Base API allows developers to communicate with the Base blockchain to build applications. Chainstack Base nodes provide both execution layer API and consensus layer API. An application must connect to a Base RPC node to read data from and send transactions to the Base blockchain. When communicating with a Base RPC node, the client implements a JSON-RPC specification, a communication protocol allowing one to make remote calls and execute them as if they were made locally. ## How to start using the Base API You need access to a Base RPC node to use the Base API. Follow these steps to sign up on Chainstack, deploy a Base RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now, you can connect to the Base blockchain and use the Base API to build. > [!NOTE] > > **See also** > > [Build better with Base](https://chainstack.com/build-better-with-base/) # eth_blockNumber | Base Source: https://docs.chainstack.com/reference/base-blocknumber openapi/base_node_api/eth_blockNumber.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that returns the latest block number of the blockchain. Chainstack supports eth_blockNumber on Base JSON-RPC nodes. Base API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0xbb6e14` means that the block is block 12283412 in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to, encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. ## Use case A possible use case for the `eth_blockNumber` method in Base is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. # eth_call | Base Source: https://docs.chainstack.com/reference/base-call openapi/base_node_api/eth_call.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 The example is a call to the balanceOf method on the USDC contract. eth_call JSON-RPC method available on the Base blockchain via Chainstack. The example is a call to the `balanceOf` method on the [USDC contract.](https://basescan.org/address/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913). Base API method `eth_call` executes a smart contract function call directly and returns the result without requiring a transaction or changing the state on the blockchain. This method is widely used for reading data from smart contracts. ## Parameters * `transactionObject` — an object containing transaction parameters, including: * `to` — the address of the contract to call. * `data` — the call data, typically including the function signature and parameters. * `blockParameter` — a string specifying the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the blockchain state to use for the call. Most commonly, `"latest"` is used. ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `result` — the result of the call, encoded as a hexadecimal string. The format of the result depends on the function called. ## Use case The `eth_call` method is essential for: * DApps that need to retrieve state or compute derived values from smart contracts without making a transaction. * Wallets and interfaces that display contract information, such as token balances or the state of a decentralized finance (DeFi) contract. * Automated scripts and bots that monitor contract states or conditions on the blockchain. # eth_callMany | Base Source: https://docs.chainstack.com/reference/base-callmany openapi/base_node_api/eth_callMany.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_callMany executes a list of transaction bundles without creating transactions on the blockchain. Base via Chainstack. Base API method `eth_callMany` executes a list of transaction bundles without creating transactions on the blockchain. This method is particularly useful for simulating transactions to understand their potential outcomes. It supports simulating transactions at an intermediate state of a block, simulating multiple transactions with sequential dependencies, and overwriting block headers. This examples simulates the transfer of 100 USDC to an address in block 12279785 at transaction position 1 in the block and does the balance check of the address for the USDT, all in one call. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * **BUNDLES** (array, required): A list of bundles where each bundle contains: * **transactions** (array): An array of transaction objects with parameters similar to `eth_call`. * **blockOverride** (object, optional): Overrides for the execution context block header, including `blockNumber`, `blockHash`, `coinbase`, `timestamp`, `difficulty`, `gasLimit`, and `baseFee`. * **SIMULATION CONTEXT** (object, required): The context for the simulation, specifying the `blockNumber or Tag` and `transactionIndex`. * **STATE OVERRIDES** (object, optional): An optional mapping for state overrides, following the `eth_call` standard. * **TIMEOUT** (integer, optional): The maximum running time for the simulations in milliseconds, defaulting to 5000 milliseconds. ## Response The method returns a list of lists containing either the return value of the executed contract call or the reverted error code. * **array** - All the return/error values * **value** - The value if was successful * **error** - The error if the transaction failed ## Use case The `eth_callMany` method is essential for: * Developers who need to simulate the outcome of transactions at a specific block height without making actual state changes. * Applications that require understanding the sequential dependencies between multiple transactions. * Services that need to test the impact of block header changes on transaction execution. # eth_chainId | Base Source: https://docs.chainstack.com/reference/base-chainid openapi/base_node_api/eth_chainId.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_chainId retrieves the current chain ID, which is a unique identifier for the blockchain network the node is connected to. Base API method `eth_chainId` retrieves the current chain ID, which is a unique identifier for the blockchain network the node is connected to. The chain ID is crucial for transaction signing to prevent replay attacks across different chains. ## Parameters * `none` — This method does not require any parameters. ## Response * `result` — the current chain ID, encoded as a hexadecimal string. ## Use case The `eth_chainId` method is essential for: * Wallets and applications that sign transactions, as the chain ID is used as part of the signing process to ensure transactions are only valid on the intended chain. * Applications that interact with multiple blockchain networks and need to confirm the network they are communicating with. # web3_clientVersion | Base Source: https://docs.chainstack.com/reference/base-clientversion openapi/base_node_api/web3_clientVersion.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method web3_clientVersion retrieves the version string of the Base client the node is running. Chainstack Base reference. Base API method `web3_clientVersion` retrieves the version string of the Base client the node is running. This method is useful for identifying the client and its version, which can be important for compatibility and debugging purposes. ## Parameters This method does not require any parameters. ## Response * `result` — the version string of the Base client, which typically includes the client name, version, network, platform, and other build information. ## Use case The `web3_clientVersion` method is essential for: * Developers and system administrators needing to verify the client version for compatibility with dApps or network protocols. * Services that monitor and report on the distribution of client versions across the network. * Tools that automate interactions with Base nodes and require version checks to ensure proper operation. # eth_estimateGas | Base Source: https://docs.chainstack.com/reference/base-estimategas openapi/base_node_api/eth_estimateGas.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 The eth_estimateGas method estimates the amount of gas required to execute a transaction. Available on Base via Chainstack JSON-RPC nodes. The `eth_estimateGas` method estimates the amount of gas required to execute a transaction. This is useful for understanding how much gas might be consumed by a transaction before actually sending it. The estimation is not always accurate but provides a good approximation. ## Request To use `eth_estimateGas`, send a POST request with a JSON RPC call in the body. ## Parameters A single object containing: * **from** (`string`): (optional) The address the transaction is sent from. * **to** (`string`): The address the transaction is directed to. * **gas** (`string`): (optional) The gas limit provided by the sender. * **gasPrice** (`string`): (optional) The gas price for the transaction. * **value** (`string`): (optional) The value transferred for the transaction in Wei. * **data** (`string`): (optional) The data sent along with the transaction. ## Response The method returns the estimated gas required as a hexadecimal number. * **result** (`string`): The estimated gas required for the transaction. ## Use case The `eth_estimateGas` method is essential for: * Developers to estimate transaction costs before sending them. * Wallets and interfaces that calculate transaction fees. * Optimizing smart contract interactions by understanding gas requirements. # eth_getBalance | Base Source: https://docs.chainstack.com/reference/base-getbalance openapi/base_node_api/eth_getBalance.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getBalance retrieves the balance of an account at a given block. Available on Base via Chainstack JSON-RPC nodes. Base API method `eth_getBalance` retrieves the balance of an account at a given block. This method is essential for applications that need to display or utilize the current balance of an Ethereum address. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to get the balance for. * `block` — the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the state to consider. The default is `"latest"`. ## Response * `result` — the balance of the account in Wei, as a hexadecimal string. ## Use case The `eth_getBalance` method is essential for: * Wallets and financial applications that track and display user balances. * DApps that perform actions based on the account balance. * Services that monitor account balances for thresholds, changes, or unusual activity. # eth_getBlockByHash | Base Source: https://docs.chainstack.com/reference/base-getblockbyhash openapi/base_node_api/eth_getBlockByHash.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that retrieves a block's information by its hash. The block hash is a unique identifier for each block in the blockchain. Base API method that retrieves a block's information by its hash. The block hash is a unique identifier for each block in the blockchain, represented as a hexadecimal number. ## Parameters * `hash` — the hash of the block to retrieve, as a hexadecimal string. * `fullTransactionObjects` — a boolean indicating whether to return full transaction objects or only their hashes. If `true`, full transaction objects are returned; if `false`, only the hashes of the transactions are returned. ## Response * `result` — an object containing the block's information, including the block number, timestamp, transactions, and more. If `fullTransactionObjects` is `true`, this will include detailed information about each transaction in the block. ## Use case The `eth_getBlockByHash` method is useful for applications that need to retrieve specific blocks from the blockchain, such as block explorers, wallets, or applications that process or display block information. For example, a block explorer might use this method to display detailed information about a block and its transactions to users. # eth_getBlockByNumber | Base Source: https://docs.chainstack.com/reference/base-getblockbynumber openapi/base_node_api/eth_getBlockByNumber.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that retrieves a block's information by its number. The block number is a sequential identifier for each block in the blockchain. Base API method that retrieves a block's information by its number. The block number is a sequential identifier for each block in the blockchain. ## Parameters * `blockNumber` — the number of the block to retrieve, encoded as a hexadecimal string. For example, `0x6f5f828` represents block number 116783144. * `fullTransactionObjects` — a boolean indicating whether to return full transaction objects or only their hashes. If `true`, full transaction objects are returned; if `false`, only the hashes of the transactions are returned. ## Response * `result` — an object containing the block's information, including the block number, timestamp, transactions, and more. If `fullTransactionObjects` is `true`, this will include detailed information about each transaction in the block. ## Use case The `eth_getBlockByNumber` method is useful for applications that need to retrieve specific blocks from the blockchain based on their block number. This can include block explorers, wallets, or any application that requires detailed information about a particular block or its transactions. ## Try the `eth_getBlockByNumber` RPC method yourself API method that retrieves a block's information by its number. The block number is a sequential identifier for each block in the blockchain. ## Parameters * `blockNumber` — the number of the block to retrieve, encoded as a hexadecimal string. For example, `0x6f5f828` represents block number 116783144. * `fullTransactionObjects` — a boolean indicating whether to return full transaction objects or only their hashes. If `true`, full transaction objects are returned; if `false`, only the hashes of the transactions are returned. ## Response * `result` — an object containing the block's information, including the block number, timestamp, transactions, and more. If `fullTransactionObjects` is `true`, this will include detailed information about each transaction in the block. ## Use case The `eth_getBlockByNumber` method is useful for applications that need to retrieve specific blocks from the blockchain based on their block number. This can include block explorers, wallets, or any application that requires detailed information about a particular block or its transactions. # eth_getBlockReceipts | Base Source: https://docs.chainstack.com/reference/base-getblockreceipts openapi/base_node_api/eth_getBlockReceipts.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getBlockReceipts retrieves all transaction receipts for a specified block. eth_getBlockReceipts on Base via Chainstack. Base API method `eth_getBlockReceipts` retrieves all transaction receipts for a specified block. This method enables applications to access detailed information about every transaction within a block, including outcomes, gas used, and event logs. ## Parameters * `blockIdentifier` — a string that specifies the block number in hexadecimal format or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating which block's receipts to retrieve. For instance, using `"latest"` fetches the receipts for the most recent block. ## Response * `result` — an array of objects, each representing a transaction receipt within the specified block. Key fields in each receipt include: * `transactionHash` — the hash of the transaction. * `transactionIndex` — the transaction's index position within the block. * `blockHash` — the hash of the block containing the transaction. * `blockNumber` — the number of the block containing the transaction. * `from` — the address of the sender. * `to` — the address of the receiver (`null` for contract creation transactions). * `cumulativeGasUsed` — the total amount of gas used in the block up until this transaction. * `gasUsed` — the amount of gas used by this transaction. * `contractAddress` — the contract address created by the transaction, if any. * `logs` — an array of log objects generated by the transaction. * `status` — the status of the transaction (`0x1` for success, `0x0` for failure). ## Use case The `eth_getBlockReceipts` method is crucial for applications requiring comprehensive transactional data from a block, such as: * Analyzing gas consumption and efficiency across transactions. * Monitoring smart contract interactions and logging for decentralized applications (DApps). * Verifying transaction execution outcomes for auditing and security analysis. This method offers a granular view of block transactions, facilitating detailed analysis and monitoring of transaction outcomes, gas usage, and smart contract events on the Base blockchain. # eth_getBlockTransactionCountByHash | Base Source: https://docs.chainstack.com/reference/base-getblocktransactioncountbyhash openapi/base_node_api/eth_getBlockTransactionCountByHash.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that retrieves the number of transactions in a block, identified by its hash. eth_getBlockTransactionCountByHash on Base via Chainstack. Base API method that retrieves the number of transactions in a block, identified by its hash. This method provides a way to know how many transactions were included in a specific block without retrieving the full block data. ## Parameters * `hash` — the hash of the block for which the transaction count is being queried, as a hexadecimal string. ## Response * `result` — the number of transactions in the specified block, encoded as a hexadecimal string. This count includes all transactions that were part of the block when it was mined. ## Use case The `eth_getBlockTransactionCountByHash` method is particularly useful for applications that need to quickly assess the volume of transactions within specific blocks. For instance, analytics platforms may use this information to analyze network activity over time or to identify blocks that contain an unusually high or low number of transactions. # eth_getBlockTransactionCountByNumber | Base Source: https://docs.chainstack.com/reference/base-getblocktransactioncountbynumber openapi/base_node_api/eth_getBlockTransactionCountByNumber.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that retrieves the number of transactions in a block, identified by its number. eth_getBlockTransactionCountByNumber on Base via Chainstack. Base API method that retrieves the number of transactions in a block, identified by its number. This method allows for understanding the transaction volume within a specific block without needing to load the entire block's data. ## Parameters * `blockNumber` — the number of the block for which the transaction count is being queried, encoded as a hexadecimal string. ## Response * `result` — the number of transactions in the specified block, encoded as a hexadecimal string. This includes all transactions that were part of the block at the time it was mined. ## Use case The `eth_getBlockTransactionCountByNumber` method is useful for applications that monitor blockchain activity, such as wallets, block explorers, or analytics platforms. It can help these applications quickly gauge the level of activity within specific blocks or over a range of blocks. # eth_getCode | Base Source: https://docs.chainstack.com/reference/base-getcode openapi/base_node_api/eth_getCode.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getCode retrieves the code stored at a specific address. Chainstack supports eth_getCode on Base JSON-RPC nodes. Base API method `eth_getCode` retrieves the code stored at a specific address. This method is typically used to inspect the bytecode of smart contracts on the Base blockchain. This example retrieves the bytecode of the [USDC token](https://basescan.org/address/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) on the Base Mainnet. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to get the code from. * `block` — the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the state to consider. The default is `"latest"`. ## Response * `result` — the code from the specified address, returned as a hexadecimal string. ## Use case The `eth_getCode` method is essential for: * Developers and auditors who need to verify the deployed bytecode of smart contracts. * DApps that interact with contracts dynamically based on their code. * Tools and services that monitor and analyze contract deployments and updates. # eth_getFilterChanges | Base Source: https://docs.chainstack.com/reference/base-getfilterchanges openapi/base_node_api/eth_getFilterChanges.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getFilterChanges retrieves the changes of a filter since it was last accessed. Available on Base via Chainstack. Base API method `eth_getFilterChanges` retrieves the changes of a filter since it was last accessed. This method is crucial for applications that need to monitor specific events or blocks on the Base blockchain. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter | Base](/reference/base-newfilter) > * [eth\_newBlockFilter | Base](/reference/base-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `filterId` — the ID of the filter whose changes are being queried. This ID is returned by filter creation methods such as `eth_newBlockFilter`, or `eth_newFilter`. For this example, a random value `"0x4a2b3c"` is used. ## Response * `result` — an array of changes. For log filters, this will be an array of log entries. For block filters, it will be an array of block hashes. For pending transaction filters, it will be an array of transaction hashes. Each log entry object contains details such as the log index, transaction index, transaction hash, block hash, block number, address, data, and topics. ## Use case The `eth_getFilterChanges` method is essential for: * DApps that need to react to new events on the blockchain, such as token transfers or contract interactions. * Services that monitor the blockchain for security or compliance reasons, needing to track specific transactions or blocks. * Wallets and exchanges that require real-time information about transactions or blocks for processing or notification purposes. # eth_getFilterLogs | Base Source: https://docs.chainstack.com/reference/base-getfilterlogs openapi/base_node_api/eth_getFilterLogs.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getFilterLogs retrieves all logs matching a previously created filter. eth_getFilterLogs on Base via Chainstack. Base API method `eth_getFilterLogs` retrieves all logs matching a previously created filter. This method is essential for applications that need to fetch historical log data based on specific criteria. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterLogs` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/base-newfilter) > * [eth\_newBlockFilter](/reference/base-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `filterId` — the ID of the filter for which logs are being fetched. This ID is returned by filter creation methods such as `eth_newBlockFilter`, or `eth_newFilter`. For this example, a random value `"0x5a6b7c"` is used. ## Response * `result` — an array of log objects. Each log object contains details such as whether the log was removed (due to a chain reorganization), log index, transaction index, transaction hash, block hash, block number, address from which the log originated, data contained in the log, and topics associated with the log. ## Use case The `eth_getFilterLogs` method is essential for: * DApps that require access to historical log data for analysis or display purposes. * Services that monitor smart contract interactions over time, needing to retrieve all past events that match certain criteria. * Analytical tools that process and visualize blockchain data based on logs from specific contracts or transactions. # eth_getLogs | Base Source: https://docs.chainstack.com/reference/base-getlogs openapi/base_node_api/eth_getLogs.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getLogs retrieves logs matching specific criteria, such as block range, address, and topics. Base via Chainstack. Base API method `eth_getLogs` retrieves logs matching specific criteria, such as block range, address, and topics. This method is crucial for applications that need to fetch log data for analysis, display, or monitoring smart contract events. The example track [USDC token transfers](https://basescan.org/address/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913). ## Parameters * `filterObject` — an object containing filter options: * `fromBlock` — the starting block to search for logs. In this example, it's set to `"latest"`. * `address` — the contract address to fetch logs from. * `topics` — an array of topics to match against the logs. ## Response * `result` — an array of log objects. Each log object contains details such as whether the log was removed (due to a chain reorganization), log index, transaction index, transaction hash, block hash, block number, address from which the log originated, data contained in the log, and topics associated with the log. ## Use case The `eth_getLogs` method is essential for: * DApps that require access to historical log data for analysis or display purposes. * Services that monitor smart contract interactions over time, needing to retrieve all past events that match certain criteria. * Analytical tools that process and visualize blockchain data based on logs from specific contracts or transactions. # debug_getModifiedAccountsByHash | Base Source: https://docs.chainstack.com/reference/base-getmodifiedaccountsbyhash openapi/base_node_api/debug_getModifiedAccountsByHash.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 The debug_getModifiedAccountsByHash RPC method retrieves a list of accounts that were modified in a specific block, identified by the block's hash. The `debug_getModifiedAccountsByHash` RPC method retrieves a list of accounts that were modified in a specific block, identified by the block's hash. This method is useful for developers and analysts who need to track changes to the state of accounts within a particular block for debugging or analytical purposes. ## Request To use `debug_getModifiedAccountsByHash`, you should send a POST request with a JSON RPC call in the body. ## Parameters * **Block Hash** (`string`): The hash of the block for which to retrieve the list of modified accounts. ## Response The response will be an array of account addresses (as strings) that were modified in the specified block. * **result** (`array`): The addresses of the accounts modified in the specified block. ## Use case The `debug_getModifiedAccountsByHash` method is essential for: * Developers debugging contract interactions within specific blocks. * Analysts tracking state changes for forensic or audit purposes. * Tools and services monitoring the Base state for changes to specific accounts within a block. # debug_getModifiedAccountsByNumber | Base Source: https://docs.chainstack.com/reference/base-getmodifiedaccountsbynumber openapi/base_node_api/debug_getModifiedAccountsByNumber.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 The debug_getModifiedAccountsByNumber RPC method retrieves a list of accounts that were modified between two specified block numbers. The `debug_getModifiedAccountsByNumber` RPC method retrieves a list of accounts that were modified between two specified block numbers. This method is invaluable for developers and analysts looking to track changes to the state of accounts across specific blocks for debugging or analytical purposes. ## Request To use `debug_getModifiedAccountsByNumber`, you should send a POST request with a JSON RPC call in the body. ## Parameters * **Start Block** (`string`): The block number (in hexadecimal) from which to start tracking changes. * **End Block** (`string`): The block number (in hexadecimal) up to which to track changes. ## Response The response will be an array of account addresses (as strings) that were modified between the two specified blocks. * **result** (`array`): The addresses of the accounts modified between the specified blocks. ## Use case The `debug_getModifiedAccountsByNumber` method is essential for: * Developers debugging contract interactions over specific blocks. * Analysts tracking state changes for forensic or audit purposes. * Tools and services monitoring the Base state for changes to specific accounts. # eth_getProof | Base Source: https://docs.chainstack.com/reference/base-getproof openapi/base_node_api/eth_getProof.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getProof retrieves the account and storage values of the specified account, including the Merkle-proof. On Base. Base API method `eth_getProof` retrieves the account and storage values of the specified account, including the Merkle-proof. This method is used to verify the data without needing the entire state on the client side. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > On Global Nodes, this method returns a proof for the latest block only. A block number behind the tip returns `-32602` `distance to target block exceeds maximum proof window`. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). ## Parameters * `address` — the address of the account. * `storageKeys` — an array of storage keys to prove. * `block` — the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the state to consider. The default is `"latest"`. ## Response * `result` — an object containing the account proof, balance, code hash, nonce, storage hash, and storage proof for each requested storage key. ## Use case The `eth_getProof` method is essential for: * Applications that need to verify account or contract state without syncing the entire blockchain. * Services that provide cryptographic proofs of specific data to third parties. * Tools that audit the state of accounts or contracts at specific block heights. ## Try the `eth_getProof` RPC method yourself # eth_getRawTransactionByBlockHashAndIndex | Base Source: https://docs.chainstack.com/reference/base-getrawtransactionbyblockhashandindex openapi/base_node_api/eth_getRawTransactionByBlockHashAndIndex.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getRawTransactionByBlockHashAndIndex retrieves the raw transaction data as a hexadecimal string for a transaction in a specific block. Base API method `eth_getRawTransactionByBlockHashAndIndex` retrieves the raw transaction data as a hexadecimal string for a transaction in a specific block, identified by the block hash and the transaction's index position within that block. This method is useful for obtaining the complete transaction data, including its signature, without needing the transaction hash. ## Parameters * `blockHash` — the hash of the block containing the transaction. * `transactionIndex` — the index position of the transaction in the block, specified as a hexadecimal. The example uses `"0x1"`. ## Response * `result` — the raw transaction data as a hexadecimal string. ## Use case The `eth_getRawTransactionByBlockHashAndIndex` method is essential for: * Services that need to verify or inspect the complete transaction data and signature. * Wallets and exchanges that require the raw transaction for auditing or processing purposes. * Developers and tools that analyze transactions at a low level for security, performance, or other research purposes. # eth_getRawTransactionByBlockNumberAndIndex | Base Source: https://docs.chainstack.com/reference/base-getrawtransactionbyblocknumberandindex openapi/base_node_api/eth_getRawTransactionByBlockNumberAndIndex.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getRawTransactionByBlockNumberAndIndex retrieves the raw transaction data as a hexadecimal string for a transaction in a specific block. Base API method `eth_getRawTransactionByBlockNumberAndIndex` retrieves the raw transaction data as a hexadecimal string for a transaction in a specific block, identified by the block number and the transaction's index position within that block. This method is useful for obtaining the complete transaction data, including its signature, without needing the transaction hash. ## Parameters * `blockNumber` — the number of the block. * `transactionIndex` — the index position of the transaction in the block, specified as a hexadecimal. The example uses `"0x1"`. ## Response * `result` — the raw transaction data as a hexadecimal string. ## Use case The `eth_getRawTransactionByBlockNumberAndIndex` method is essential for: * Services that need to verify or inspect the complete transaction data and signature. * Wallets and exchanges that require the raw transaction for auditing or processing purposes. * Developers and tools that analyze transactions at a low level for security, performance, or other research purposes. # eth_getRawTransactionByHash | Base Source: https://docs.chainstack.com/reference/base-getrawtransactionbyhash openapi/base_node_api/eth_getRawTransactionByHash.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getRawTransactionByHash retrieves the raw transaction data as a hexadecimal string for a given transaction hash. Base API method `eth_getRawTransactionByHash` retrieves the raw transaction data as a hexadecimal string for a given transaction hash. This method is useful for obtaining the complete transaction data, including its signature. ## Parameters * `transactionHash` — the hash of the transaction to retrieve. ## Response * `result` — the raw transaction data as a hexadecimal string. ## Use case The `eth_getRawTransactionByHash` method is essential for: * Services that need to verify or inspect the complete transaction data and signature. * Wallets and exchanges that require the raw transaction for auditing or processing purposes. * Developers and tools that analyze transactions at a low level for security, performance, or other research purposes. # eth_getStorageAt | Base Source: https://docs.chainstack.com/reference/base-getstorageat openapi/base_node_api/eth_getStorageAt.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getStorageAt retrieves the value from a specific storage position of a contract at a given address. Base via Chainstack. Base API method `eth_getStorageAt` retrieves the value from a specific storage position of a contract at a given address. This method is useful for inspecting the state of smart contracts at specific points in their execution or at a particular block. This example makes a call to slot 7 of the [USDC contract](https://basescan.org/address/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913) on the Base Mainnet. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the contract to access. * `position` — the storage position to inspect, given as a hexadecimal value. * `block` — the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the state to consider. The default is `"latest"`. ## Response * `result` — the value from the specified storage position at the given address, returned as a hexadecimal string. ## Use case The `eth_getStorageAt` method is essential for: * Developers and auditors who need to verify the state and behavior of smart contracts by inspecting their storage. * DApps that require access to contract state that is not exposed through contract methods. * Tools and services that monitor contract state for changes, updates, or anomalies. # debug_storageRangeAt | Base Source: https://docs.chainstack.com/reference/base-getstoragerangeat openapi/base_node_api/debug_storageRangeAt.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 The debug_storageRangeAt RPC method retrieves storage entries from a contract at a specific block. Available on Base via Chainstack. > [!WARNING] > > **Use eth\_getStorageAt instead** > > Chainstack Base nodes are on Reth. Reth does not support the `debug_storageRangeAt` method. See [Using eth\_getStorageAt instead of debug\_storageRangeAt on Reth](/docs/using-eth_getstorageat-instead-of-debug_storagerangeat-on-reth). The `debug_storageRangeAt` RPC method retrieves storage entries from a contract at a specific block. This method is useful for developers and analysts who need to inspect the state of a contract's storage at a particular point in time. ## Request To use `debug_storageRangeAt`, send a POST request with a JSON RPC call in the body. ## Parameters * **Block Hash** (`string`): The hash of the block. * **Transaction Index** (`integer`): The index of the transaction in the block. * **Contract Address** (`string`): The address of the contract. * **Storage Key** (`string`): The starting point of the storage to inspect. * **Max Results** (`integer`): The maximum number of storage entries to return. ## Response The response includes storage entries within the specified range and a flag indicating if the storage snapshot is complete. * **storage** (`object`): A map of storage entries. * **complete** (`boolean`): Indicates whether the snapshot of storage is complete. ## Use case The `debug_storageRangeAt` method is essential for: * Developers debugging smart contracts by inspecting storage states at specific blocks. * Analysts conducting forensic analysis of contract interactions. * Tools and services that provide insights into contract state changes over time. # eth_getTransactionByBlockHashAndIndex | Base Source: https://docs.chainstack.com/reference/base-gettransactionbyblockhashandindex openapi/base_node_api/eth_getTransactionByBlockHashAndIndex.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Reference docs for the eth_getTransactionByBlockHashAndIndex JSON-RPC method on the Base blockchain, available via Chainstack JSON-RPC nodes. Base API method `eth_getTransactionByBlockHashAndIndex` retrieves information about a transaction by specifying the block hash and the transaction's index position within the block. This method is useful for fetching specific transactions when the block hash and index are known. ## Parameters * `blockHash` — the hash of the block. * `transactionIndex` — the index position of the transaction in the block, specified as a hexadecimal. The example uses `"0x1"`. ## Response * `result` — an object containing details about the transaction, including the block hash, block number, from address, gas used, gas price, hash, input data, nonce, to address, transaction index, value transferred, and the signature components (v, r, s). ## Use case The `eth_getTransactionByBlockHashAndIndex` method is essential for: * Applications that need to retrieve specific transactions from a known block for analysis or display. * Services that monitor blocks and transactions for security, compliance, or other operational reasons. * Tools that reconstruct transaction sequences within blocks for auditing or forensic purposes. # eth_getTransactionByBlockNumberAndIndex | Base Source: https://docs.chainstack.com/reference/base-gettransactionbyblocknumberandindex openapi/base_node_api/eth_getTransactionByBlockNumberAndIndex.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Reference docs for the eth_getTransactionByBlockNumberAndIndex JSON-RPC method on the Base blockchain, available via Chainstack JSON-RPC nodes. Base API method `eth_getTransactionByBlockNumberAndIndex` retrieves information about a transaction by specifying the block number and the transaction's index position within the block. This method is useful for fetching specific transactions when the block number and index are known. ## Parameters * `blockNumber` — the number of the block. * `transactionIndex` — the index position of the transaction in the block, specified as a hexadecimal. The example uses `"0x1"`. ## Response * `result` — an object containing details about the transaction, including the block hash, block number, from address, gas used, gas price, hash, input data, nonce, to address, transaction index, value transferred, and the signature components (v, r, s). ## Use case The `eth_getTransactionByBlockNumberAndIndex` method is essential for: * Applications that need to retrieve specific transactions from a known block for analysis or display. * Services that monitor blocks and transactions for security, compliance, or other operational reasons. * Tools that reconstruct transaction sequences within blocks for auditing or forensic purposes. # eth_getTransactionByHash | Base Source: https://docs.chainstack.com/reference/base-gettransactionbyhash openapi/base_node_api/eth_getTransactionByHash.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getTransactionByHash retrieves information about a transaction given its hash. Available on Base via Chainstack. Base API method `eth_getTransactionByHash` retrieves information about a transaction given its hash. This method is crucial for tracking transaction details and status. ## Parameters * `transactionHash` — the hash of the transaction to retrieve. ## Response * `result` — an object containing details about the transaction, including the block hash, block number, from address, gas used, gas price, hash, input data, nonce, to address, transaction index, value transferred, and the signature components (v, r, s). ## Use case The `eth_getTransactionByHash` method is essential for: * Wallets and exchanges that track transactions to update user balances and display transaction statuses. * DApps that need to verify transaction execution or retrieve transaction inputs for processing. * Services that monitor the Base network for specific transaction patterns or activities. # eth_getTransactionCount | Base Source: https://docs.chainstack.com/reference/base-gettransactioncount openapi/base_node_api/eth_getTransactionCount.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Reference for the eth_getTransactionCount JSON-RPC method on the Base blockchain via Chainstack nodes. Parameters, response, and examples included. Base API method `eth_getTransactionCount` retrieves the number of transactions sent from a specified address, often used to determine the nonce for the next transaction from that address. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to get the transaction count for. In this example. * `block` — the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the state to consider. The default is `"latest"`. ## Response * `result` — the number of transactions sent from the specified address, returned as a hexadecimal string. ## Use case The `eth_getTransactionCount` method is essential for: * Wallets and transaction services that need to calculate the nonce for new transactions. * Applications that monitor account activity, including outgoing transactions. * Tools that analyze the history and behavior of accounts on the Ethereum blockchain. # eth_getTransactionReceipt | Base Source: https://docs.chainstack.com/reference/base-gettransactionreceipt openapi/base_node_api/eth_getTransactionReceipt.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_getTransactionReceipt retrieves the receipt of a transaction by its hash. eth_getTransactionReceipt on Base via Chainstack. Base API method `eth_getTransactionReceipt` retrieves the receipt of a transaction by its hash. This receipt includes details such as the transaction's outcome (status), gas used, and logs generated by the transaction. This method is crucial for confirming transaction execution and for interacting with smart contracts. ## Parameters * `transactionHash` — the hash of the transaction for which the receipt is being requested. ## Response * `result` — an object containing the transaction receipt details, including the transaction hash, index, block hash, block number, from and to addresses, cumulative gas used, gas used by this transaction, contract address (if a contract was created), an array of logs, the logs bloom filter, and the status of the transaction (1 for success, 0 for failure). ## Use case The `eth_getTransactionReceipt` method is essential for: * DApps that need to confirm the outcome of a transaction, especially after interacting with smart contracts. * Services that monitor transactions for confirmation, gas usage, and event logging. * Tools that analyze the efficiency and effects of transactions on the Base network. # eth_getUncleByBlockHashAndIndex | Base Source: https://docs.chainstack.com/reference/base-getunclebyblockhashandindex openapi/base_node_api/eth_getUncleByBlockHashAndIndex.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that returns information about an uncle block by block hash and uncle index position. Use it on Base via Chainstack. Base API method that returns information about an uncle block by block hash and uncle index position. Note that uncles are a concept specific to proof-of-work chains and don't exist in proof-of-stake chains like Base. ## Parameters * `BLOCK HASH` - 32-byte hash of the block containing the uncle * `UNCLE INDEX POSITION` - the uncle's index position. It's a hexadecimal value representing the uncle's position in the block's uncle list. ## Response * `result` — A block object with uncle information, or null when no uncle exists at the given index. The block object includes: * `number`: The block number * `hash`: Hash of the uncle block * `parentHash`: Hash of the parent block * `nonce`: Hash of the generated proof-of-work * `sha3Uncles`: SHA3 of the uncles data in the block * `logsBloom`: The bloom filter for the logs of the block * `transactionsRoot`: The root of the transaction trie of the block * `stateRoot`: The root of the final state trie of the block * `receiptsRoot`: The root of the receipts trie of the block * `miner`: The address of the beneficiary to whom the mining rewards were given * `difficulty`: Integer of the difficulty for this block * `totalDifficulty`: Integer of the total difficulty of the chain until this block * `extraData`: The "extra data" field of this block * `size`: Integer the size of this block in bytes * `gasLimit`: The maximum gas allowed in this block * `gasUsed`: The total used gas by all transactions in this block * `timestamp`: The unix timestamp for when the block was collated ## Use case The `eth_getUncleByBlockHashAndIndex` method is primarily used in proof-of-work networks for: * Analyzing blockchain history and network behavior * Calculating mining rewards (as uncle blocks receive partial rewards) * Studying network propagation and consensus * Research and analysis of fork events Note: In Base, which is a proof-of-stake chain, this method will typically return null as there are no uncle blocks in PoS networks. # eth_getUncleByBlockNumberAndIndex | Base Source: https://docs.chainstack.com/reference/base-getunclebyblocknumberandindex openapi/base_node_api/eth_getUncleByBlockNumberAndIndex.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that returns information about an uncle block by block number and uncle index position. Chainstack Base reference. Base API method that returns information about an uncle block by block number and uncle index position. Note that uncles are a concept specific to proof-of-work chains and don't exist in proof-of-stake chains like Base. ## Parameters * `BLOCK PARAMETER` - the block number, or one of the following string values: * `"latest"` - the most recent block * `"earliest"` - the genesis block * `"pending"` - the pending block * `"safe"` - the most recent safe block * `"finalized"` - the most recent finalized block * `UNCLE INDEX POSITION` - the uncle's index position. It's a hexadecimal value representing the uncle's position in the block's uncle list. ## Response * `result` — A block object with uncle information, or null when no uncle exists at the given index. The block object includes: * `number`: The block number * `hash`: Hash of the uncle block * `parentHash`: Hash of the parent block * `nonce`: Hash of the generated proof-of-work * `sha3Uncles`: SHA3 of the uncles data in the block * `logsBloom`: The bloom filter for the logs of the block * `transactionsRoot`: The root of the transaction trie of the block * `stateRoot`: The root of the final state trie of the block * `receiptsRoot`: The root of the receipts trie of the block * `miner`: The address of the beneficiary to whom the mining rewards were given * `difficulty`: Integer of the difficulty for this block * `totalDifficulty`: Integer of the total difficulty of the chain until this block * `extraData`: The "extra data" field of this block * `size`: Integer the size of this block in bytes * `gasLimit`: The maximum gas allowed in this block * `gasUsed`: The total used gas by all transactions in this block * `timestamp`: The unix timestamp for when the block was collated ## Use case The `eth_getUncleByBlockNumberAndIndex` method is primarily used in proof-of-work networks for: * Historical analysis of uncle blocks at specific block heights * Calculating mining rewards for uncle blocks * Studying network fork behavior and block propagation * Blockchain research and analysis tools Note: In Base, which is a proof-of-stake chain, this method will typically return null as there are no uncle blocks in PoS networks. # eth_getUncleCountByBlockHash | Base Source: https://docs.chainstack.com/reference/base-getunclecountbyblockhash openapi/base_node_api/eth_getUncleCountByBlockHash.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that retrieves the number of uncles in a block, identified by its hash. Base API method that retrieves the number of uncles in a block. Base API method that retrieves the number of uncles in a block, identified by its hash. Uncles are blocks that are not part of the main chain but are still valid and have a relation to a block on the main chain. This might be useful in the future when there's a decentralized or a shared sequencer. ## Parameters * `hash` — the hash of the block for which the uncle count is being queried, as a hexadecimal string. ## Response * `result` — the number of uncles in the specified block, encoded as a hexadecimal string. This count includes all uncles that were referenced by the block when it was mined. ## Use case The `eth_getUncleCountByBlockHash` method is useful for applications that analyze blockchain security and decentralization aspects, such as block explorers or network analysis tools. It can help these applications understand the level of block competition and the rate at which uncles are included in the blockchain. # eth_getUncleCountByBlockNumber | Base Source: https://docs.chainstack.com/reference/base-getunclecountbyblocknumber openapi/base_node_api/eth_getUncleCountByBlockNumber.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that retrieves the number of uncles in a block, identified by its number. Base API method that retrieves the number of uncles in a block. Base API method that retrieves the number of uncles in a block, identified by its number. Uncles are blocks that are not part of the main blockchain but are still valid and have a relationship to a block on the main chain. This might be useful in the future when there's a decentralized or a shared sequencer. ## Parameters * `blockNumber` — the number of the block for which the uncle count is being queried, encoded as a hexadecimal string. ## Response * `result` — the number of uncles in the specified block, encoded as a hexadecimal string. This includes all uncles that were referenced by the block at the time it was sealed. ## Use case The `eth_getUncleCountByBlockNumber` method is important for applications that monitor the health and security of the blockchain, such as network analysis tools or block explorers. It provides insights into the level of competition among miners and the overall inclusiveness of the blockchain towards valid blocks that did not make it to the main chain. # net_listening | Base Source: https://docs.chainstack.com/reference/base-listening openapi/base_node_api/net_listening.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method net_listening checks if the client is actively listening for network connections. Available on Base via Chainstack. Base API method `net_listening` checks if the client is actively listening for network connections. This method is useful for diagnosing network issues or confirming the node's network status. ## Parameters This method does not require any parameters. ## Response * `result` — a boolean value indicating whether the client is actively listening for network connections. ## Use case The `net_listening` method is essential for: * Network administrators and node operators to verify the operational status of their Base client. * Developers and services that need to ensure their node is correctly configured to participate in the network. * Automated systems that monitor the health and connectivity of Base nodes. # eth_maxPriorityFeePerGas | Base Source: https://docs.chainstack.com/reference/base-maxpriorityfeepergas openapi/base_node_api/eth_maxPriorityFeePerGas.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_maxPriorityFeePerGas queries the current maximum priority fee per gas. eth_maxPriorityFeePerGas on Base via Chainstack. Base API method `eth_maxPriorityFeePerGas` queries the current maximum priority fee per gas. This method is particularly useful in periods of high network congestion to estimate the appropriate priority fee for a transaction. ## Parameters This method does not require any parameters. ## Response * `result` — the current maximum priority fee per gas, returned as a hexadecimal string. ## Use case The `eth_maxPriorityFeePerGas` method is essential for: * Wallets and transaction builders that dynamically adjust transaction fees to ensure timely inclusion in a block. * Users and services looking to estimate transaction costs more accurately during periods of network congestion. * Developers and analysts monitoring Base network fee dynamics. # eth_newBlockFilter | Base Source: https://docs.chainstack.com/reference/base-newblockfilter openapi/base_node_api/eth_newBlockFilter.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_newBlockFilter creates a new filter object that notifies subscribers when a new block is added to the blockchain. Base API method `eth_newBlockFilter` creates a new filter object that notifies subscribers when a new block is added to the blockchain. This method is particularly useful for applications that need to stay updated with the latest blocks for processing or monitoring purposes. ## Parameters * `none` — This method does not require any parameters. ## Response * `result` — the ID of the created filter. This ID is used to identify the filter in subsequent calls to methods like `eth_getFilterChanges` or `eth_getFilterLogs` to retrieve updates about new blocks. ## Use case The `eth_newBlockFilter` method is essential for: * Applications that perform actions triggered by the addition of new blocks, such as updating a user interface with the latest transactions or block information. * Services that monitor the blockchain for security or analytical purposes, needing real-time data on block additions. * Wallets and exchanges that track new blocks to confirm transactions. # eth_newFilter | Base Source: https://docs.chainstack.com/reference/base-newfilter openapi/base_node_api/eth_newFilter.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_newFilter creates a new filter object for monitoring log entries that match specified criteria. Base via Chainstack. Base API method `eth_newFilter` creates a new filter object for monitoring log entries that match specified criteria. This method is crucial for applications that need to track events or changes in smart contracts. The example tracks all transfers of the [USDC token](https://basescan.org/address/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913). Once you install the filter, track the changes with [eth\_getFilterChanges](/reference/getfilterchanges). ## Parameters * `filterObject` — an object containing filter options: * `fromBlock` — (optional) the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"` from where to start filtering. Default is `"latest"`. * `address` — (optional) an address or a list of addresses to only get logs from specific contracts. * `topics` — (optional) an array of topics to filter for log entries. Each event signature generated by the EVM logging mechanism has a unique topic. ## Response * `result` — the ID of the created filter. This ID is used to identify and manage the filter with other filter methods like `eth_getFilterChanges` or `eth_getFilterLogs`. ## Use case The `eth_newFilter` method is essential for: * DApps that need to react to specific events emitted by smart contracts, such as token transfers or contract state changes. * Wallets and services that provide users with notifications based on contract interactions. * Developers and analysts monitoring specific contracts or topics for changes or patterns. # eth_newPendingTransactionFilter | Base Source: https://docs.chainstack.com/reference/base-newpendingtransactionfilter openapi/base_node_api/eth_newPendingTransactionFilter.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that creates a filter in the node to notify when new pending transactions are received. Chainstack Base reference. Base API method that creates a filter in the node to notify when new pending transactions are received. To check if the state has changed, call eth\_getFilterChanges with the filter ID returned by this method. > [!WARNING] > > On Base, this method returns an empty array by design — the mempool is [private to the sequencer](/docs/mempool-configuration), so there are no public pending transactions to surface. For a real-time transaction stream, use [Flashblocks](/docs/flashblocks-on-base) instead. ## Parameters * `none` ## Response * `result` — a string representing the filter ID. This ID should be used in subsequent calls to eth\_getFilterChanges to retrieve new pending transactions. ## Use case The `eth_newPendingTransactionFilter` method is useful for applications that need to monitor the mempool for new transactions. Common use cases include: * Building transaction monitoring tools * Creating real-time transaction tracking interfaces * Implementing MEV (Maximal Extractable Value) strategies * Monitoring specific addresses for pending transactions On Base, these patterns are not served by `eth_newPendingTransactionFilter` — the mempool is private to the sequencer, so the filter returns no pending transactions, and the `eth_subscribe("newPendingTransactions")` subscription stays empty too. The `pending` block tag returns pre-confirmed [Flashblock](/docs/flashblocks-on-base) state, not mempool contents, so a populated pending block does not mean this filter should return data. To stream transactions as the sequencer pre-confirms them, subscribe to [`eth_subscribe newFlashblockTransactions`](/reference/base-subscribe-newflashblocktransactions). Note: After creating the filter, you need to poll `eth_getFilterChanges` periodically to get updates. Filters timeout if they're not used for a period of time, so applications should be prepared to recreate them if necessary. # eth_sendRawTransaction | Base Source: https://docs.chainstack.com/reference/base-sendrawtransaction openapi/base_node_api/eth_sendRawTransaction.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_sendRawTransaction allows for the submission of a pre-signed transaction to the Base network for broadcast. On Base. Base API method `eth_sendRawTransaction` allows for the submission of a pre-signed transaction to the Base network for broadcast. This method is crucial for executing transactions from externally owned accounts without relying on Base node wallets for signing. ## Parameters * `signedTransactionData` — the signed transaction data in hexadecimal format. This data includes the transaction details such as nonce, gas price, gas limit, to address, value, data, and the signature. ## Response * `result` — the hash of the successfully submitted transaction. ## Use case The `eth_sendRawTransaction` method is essential for: * Wallets and other applications that sign transactions offline or in a secure environment and then submit them to the network. * Services that batch or automate transaction submissions. * Users and developers requiring control over the transaction signing process for security reasons. # eth_sendRawTransactionSync | Base Source: https://docs.chainstack.com/reference/base-sendrawtransactionsync openapi/base_node_api/eth_sendRawTransactionSync.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Reference docs for the eth_sendRawTransactionSync JSON-RPC method on the Base blockchain, available via Chainstack JSON-RPC nodes. Base API method `eth_sendRawTransactionSync` allows for the submission of a pre-signed transaction to the Base network and waits synchronously for the transaction receipt or a configurable timeout. This method reduces transaction submission latency by combining transaction submission and receipt retrieval into a single RPC call, based on [EIP-7966](https://eips.ethereum.org/EIPS/eip-7966). ## Parameters * `signedTransactionData` — the signed transaction data in hexadecimal format. This data includes the transaction details such as nonce, gas price, gas limit, to address, value, data, and the signature. * `timeout` (optional) — maximum wait time in milliseconds, passed as a number (not a string). If not provided, uses the node's default timeout (typically 2 seconds). Must be a positive integer up to the node-configured maximum of `6000` ms. Values above the maximum are rejected with `time out too long, timeout: ms, max: 6000 ms`. ## Response * `result` — the complete transaction receipt object containing transaction hash, block information, gas usage, contract address (if applicable), logs, and status. ## Use case The `eth_sendRawTransactionSync` method is particularly useful for: * High-frequency applications requiring immediate transaction confirmation feedback. * Layer 2 solutions with fast block times where rapid transaction throughput is critical. * DApps and wallets seeking to improve user experience with faster transaction confirmations. * Trading applications where transaction latency directly impacts performance. * Applications requiring stronger delivery guarantees than standard asynchronous transaction submission. # web3_sha3 | Base Source: https://docs.chainstack.com/reference/base-sha3 openapi/base_node_api/web3_sha3.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method web3_sha3 computes the Keccak-256 hash of the given data. web3_sha3 JSON-RPC method available on the Base blockchain via Chainstack. Base API method `web3_sha3` computes the Keccak-256 hash of the given data. This method is a utility function often used in Base for generating hashes of arbitrary data, which is a common requirement for various cryptographic operations. This example computes Keccak-256 for the value `Chainstack`. `Chainstack` in hex: `0x436861696E737461636B`. ## Parameters * `data` — the data to hash, typically provided as a hexadecimal string. ## Response * `result` — the Keccak-256 hash of the input data, returned as a hexadecimal string. ## Use case The `web3_sha3` method is essential for: * Generating identifiers or hashes for on-chain data storage or referencing. * Creating cryptographic proofs that require a hash of data. * Developers needing to perform off-chain hash calculations that match the Base network's hashing algorithm. # eth_subscribe newFlashblocks | Base Source: https://docs.chainstack.com/reference/base-subscribe-newflashblocks Subscribe to Base Flashblock streams via WebSocket. Receive pre-confirmed block state from the sequencer approximately every 200ms as standard block objects. > [!WARNING] > > Base's [Denim upgrade](https://docs.base.org/upgrades/denim/overview) replaces Flashblocks with canonical 200ms blocks. Denim activated on Base Sepolia on Oct 21, 2026, at 18:00 (UTC), so Flashblocks subscriptions and pending state are no longer available on Base Sepolia. Base Mainnet serves Flashblocks until Denim activates there in November 2026, which is Base's current target. To move your integration to `newHeads`, `logs`, and regular RPC calls, see Base's [migration guide](https://docs.base.org/upgrades/denim/migrate-from-flashblocks). Base API method that streams the accumulated pre-confirmed block state approximately every 200ms. Each notification delivers a standard Ethereum block object representing the current Flashblock snapshot—including all transactions pre-confirmed so far. This subscription type is exclusive to Flashblocks-enabled endpoints. See [Flashblocks on Base](/docs/flashblocks-on-base) for background on how Flashblocks work. ## Parameters * `string` — the subscription type, `newFlashblocks` in this case. ## Response * `subscription` — the subscription ID. Each notification delivers a block object with the following fields: * `hash` — the block hash. Set to `0x0000...0000` because the block is still being formed (pre-confirmed). * `parentHash` — hash of the parent block. * `sha3Uncles` — hash of the list of uncles included in the block. * `miner` — the address of the fee recipient (coinbase), typically `0x4200000000000000000000000000000000000011`. * `stateRoot` — root of the state trie. Set to `0x0000...0000` because the block is not yet finalized. * `transactionsRoot` — root of the transaction trie of the block. * `receiptsRoot` — root of the receipts trie of the block. * `logsBloom` — bloom filter for the logs in this Flashblock. * `difficulty` — always `0x0` on Base. * `number` — the block number, encoded as hexadecimal. * `gasLimit` — maximum gas allowed in this block (hex). * `gasUsed` — cumulative gas used up to and including this Flashblock (hex). * `timestamp` — Unix timestamp of block creation (hex). * `extraData` — arbitrary data field set by the sequencer. * `mixHash` — the previous RANDAO value. * `nonce` — always `0x0000000000000000` on Base. * `baseFeePerGas` — EIP-1559 base fee per gas (hex). * `withdrawalsRoot` — Merkle root of withdrawals. * `blobGasUsed` — cumulative blob gas used, EIP-4844 (hex). * `excessBlobGas` — excess blob gas (hex). * `parentBeaconBlockRoot` — parent beacon block root. * `uncles` — always empty on Base. * `transactions` — array of full transaction objects included in this Flashblock so far. Each object includes standard transaction fields (`type`, `from`, `to`, `value`, `input`, `hash`, etc.). * `withdrawals` — always empty on Base L2. > [!NOTE] > > The `hash` and `stateRoot` fields are zeroed because the block is still being assembled. Once the full 2-second block seals, these values are computed and finalized. See [Flashblocks on Base](/docs/flashblocks-on-base#quick-flashblock-vs-full-block-data-comparison) for a comparison. ## `eth_subscribe("newFlashblocks")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newFlashblocks"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewFlashblocks() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newFlashblocks'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewFlashblocks(); ``` This will generate a continuous stream of block objects as new Flashblocks are produced by the sequencer (\~every 200ms). > [!WARNING] > > Each notification can be large (hundreds of KB) because it includes the full `transactions` array. If your handler performs heavy processing per event, throttle or debounce it to avoid blocking the WebSocket connection. ### Example notification ```json theme={"system"} { "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x6d98f3e9368562605c063467030f8c6ab47ff606da890b879316a14f9841b3a7", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "miner": "0x4200000000000000000000000000000000000011", "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "transactionsRoot": "0xf671d9e31238c090e32742144d05b5230b06b80af0a8611cbdac89cb79eae5b6", "receiptsRoot": "0x739875b7b5439ff233106142f0a74ca134ec51e4d4e61bed0884cbd60c83c7ac", "logsBloom": "0x00000000...", "difficulty": "0x0", "number": "0x2a13e81", "gasLimit": "0x17d78400", "gasUsed": "0x3f923ca", "timestamp": "0x69ccd9e5", "extraData": "0x01000000640000000500000000004c4b40", "mixHash": "0x54389ec106a631ccd44c4b3b59a5b01e1647d039ae6cf7893fb8286d451fdceb", "nonce": "0x0000000000000000", "baseFeePerGas": "0x4c4b40", "withdrawalsRoot": "0x7d152b4dc9c65e113c7498ce4c255d9d6740185dbd81dbcb6d7b8a4ac72ba13a", "blobGasUsed": "0xc82ab0", "excessBlobGas": "0x0", "parentBeaconBlockRoot": "0x77c576c8b150f4cc43c9df655988983a309b63b0339ca27a752f2811ad5cae53", "uncles": [], "transactions": ["...full transaction objects..."], "withdrawals": [] } ``` Use [eth\_unsubscribe | Base](/reference/base-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newFlashblocks")` method is useful when you need the complete pre-confirmed block state rather than individual transactions or logs: * Block building and state reconstruction. Applications that need to reconstruct the full pending block state—such as custom block explorers or analytics platforms—can consume the Flashblock stream directly instead of polling `eth_getBlockByNumber` with `pending`. * MEV strategy development. Searchers can observe the sequencer's transaction ordering in real time across each \~200ms Flashblock, gaining insight into how the pending block is being constructed. * Infrastructure monitoring. Node operators and RPC providers can use the Flashblock stream to monitor sequencer health, block production cadence, and gas usage patterns with sub-second granularity. # eth_subscribe newFlashblockTransactions | Base Source: https://docs.chainstack.com/reference/base-subscribe-newflashblocktransactions Base API method that streams individual transactions as they are pre-confirmed into Flashblocks, delivering events approximately every 200ms. > [!WARNING] > > Base's [Denim upgrade](https://docs.base.org/upgrades/denim/overview) replaces Flashblocks with canonical 200ms blocks. Denim activated on Base Sepolia on Oct 21, 2026, at 18:00 (UTC), so Flashblocks subscriptions and pending state are no longer available on Base Sepolia. Base Mainnet serves Flashblocks until Denim activates there in November 2026, which is Base's current target. To move your integration to `newHeads`, `logs`, and regular RPC calls, see Base's [migration guide](https://docs.base.org/upgrades/denim/migrate-from-flashblocks). Base API method that streams individual transactions as they are pre-confirmed into a Flashblock. Events arrive approximately every 200ms—one item per WebSocket message. This subscription type is exclusive to Flashblocks-enabled endpoints. See [Flashblocks on Base](/docs/flashblocks-on-base) for background on how Flashblocks work. ## Parameters * `string` — the subscription type, `newFlashblockTransactions` in this case. * `boolean` — (optional) when `true`, includes complete transaction objects and associated logs in each notification. Defaults to `false` (minimal data only). ## Response * `subscription` — the subscription ID. Each notification delivers pre-confirmed transaction data. When `full` is `false` (default), the `result` is the transaction hash as a hex string. When `full` is `true`, the `result` is a complete transaction object with the following fields: * `type` — the transaction type (`0x0` Legacy, `0x1` Access List, `0x2` EIP-1559, `0x7e` Deposit). * `chainId` — the chain ID (hex). * `nonce` — the sender's nonce (hex). * `from` — the sender address. * `to` — the recipient address. * `value` — the ether value transferred (hex). * `input` — the call data. * `gas` — the gas limit (hex). * `maxFeePerGas` — the maximum fee per gas, EIP-1559 (hex). * `maxPriorityFeePerGas` — the maximum priority fee per gas, EIP-1559 (hex). * `gasPrice` — the effective gas price (hex). * `hash` — the transaction hash. * `blockHash` — `null` because the transaction is pre-confirmed (not yet in a finalized block). * `blockNumber` — the pending block number (hex). * `transactionIndex` — the transaction's index position in the block (hex). * `accessList` — the EIP-2930 access list (if applicable). * `r`, `s`, `v`, `yParity` — signature values. * `logs` — array of event logs emitted by the transaction. Each log includes `address`, `topics`, `data`, `blockHash`, `blockNumber`, `blockTimestamp`, `transactionHash`, `transactionIndex`, `logIndex`, and `removed`. ## `eth_subscribe("newFlashblockTransactions")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ### Minimal data (default) ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newFlashblockTransactions"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToFlashblockTransactions() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newFlashblockTransactions'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToFlashblockTransactions(); ``` ### Full transaction objects and logs ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newFlashblockTransactions",true]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToFlashblockTransactionsFull() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newFlashblockTransactions', true], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToFlashblockTransactionsFull(); ``` This will generate a continuous stream of data displaying pre-confirmed transactions as they are included in Flashblocks. > [!NOTE] > > If your handler performs heavy processing per event, throttle or debounce it to avoid blocking the WebSocket connection. Use [eth\_unsubscribe | Base](/reference/base-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newFlashblockTransactions")` method is useful in scenarios where sub-second transaction visibility matters: * Latency-sensitive trading. DEX aggregators and MEV searchers can observe individual transactions as soon as the Base sequencer pre-confirms them—up to \~1.8 seconds before the full block seals—enabling faster reaction to on-chain events. * Real-time transaction monitoring. Wallet providers and block explorers can show users their transaction status within \~200ms of submission instead of waiting for the next 2-second block, significantly improving perceived confirmation speed. * Event-driven automation. Applications that trigger downstream actions on specific transactions—such as bridge relayers or liquidation bots—can act on pre-confirmed data instead of polling `eth_getBlockByNumber` with `pending`. # eth_subscribe pendingLogs | Base Source: https://docs.chainstack.com/reference/base-subscribe-pendinglogs Base API method that streams filtered event logs from pre-confirmed Flashblock transactions, delivering events approximately every 200ms. > [!WARNING] > > Base's [Denim upgrade](https://docs.base.org/upgrades/denim/overview) replaces Flashblocks with canonical 200ms blocks. Denim activated on Base Sepolia on Oct 21, 2026, at 18:00 (UTC), so Flashblocks subscriptions and pending state are no longer available on Base Sepolia. Base Mainnet serves Flashblocks until Denim activates there in November 2026, which is Base's current target. To move your integration to `newHeads`, `logs`, and regular RPC calls, see Base's [migration guide](https://docs.base.org/upgrades/denim/migrate-from-flashblocks). Base API method that streams event logs from pre-confirmed transactions matching an optional filter. Events arrive approximately every 200ms—one item per WebSocket message. This subscription type is exclusive to Flashblocks-enabled endpoints. See [Flashblocks on Base](/docs/flashblocks-on-base) for background on how Flashblocks work. ## Parameters * `string` — the subscription type, `pendingLogs` in this case. * `object` — (optional) the log filter options: * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. Uses the same topic filter format as [eth\_getLogs](/reference/base-getlogs). > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about topics and event logs. ## Response * `subscription` — the subscription ID. * `object` — a log object matching the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. Set to `0x0000...0000` for pre-confirmed events. * `blocktimestamp` — the Unix timestamp of the block (hex). * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_subscribe("pendingLogs")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` In the following examples, we subscribe to USDC transfer events on Base. The address `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` is the Base USDC contract and `0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef` is the `Transfer(address,address,uint256)` event signature. ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["pendingLogs",{"address":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToPendingLogs() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['pendingLogs', { address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToPendingLogs(); ``` This will generate a continuous stream of data displaying event logs from pre-confirmed transactions matching the filter. Use [eth\_unsubscribe | Base](/reference/base-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("pendingLogs")` method is useful in scenarios where you need to react to specific smart contract events with sub-second latency: * DeFi monitoring. Track swap events on DEXs like Uniswap or Aerodrome as soon as they are pre-confirmed, giving you \~1.8 seconds of advance notice compared to waiting for the full 2-second block to seal. This is valuable for arbitrage detection, liquidity monitoring, and price feed updates. * Real-time token transfer tracking. Monitor ERC-20 transfers (such as USDC or WETH) for specific addresses or contracts as they happen, enabling faster notifications for wallets, payment processors, and compliance tools. * Smart contract event indexing. Event indexers can begin processing logs from pre-confirmed transactions instead of waiting for finalized blocks, reducing the end-to-end latency of on-chain data pipelines. # eth_syncing | Base Source: https://docs.chainstack.com/reference/base-syncing openapi/base_node_api/eth_syncing.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_syncing checks if the node is currently syncing with the blockchain. It provides details about the sync progress if the node is syncing. Base API method `eth_syncing` checks if the node is currently syncing with the blockchain. It provides details about the sync progress if the node is syncing. ## Parameters * `none` — This method does not require any parameters. ## Response * `result` — an object containing syncing details if the node is syncing; otherwise, `false`. The object includes: * `startingBlock` — the block number where the sync started. * `currentBlock` — the block number where the node is currently synced to. * `highestBlock` — the highest block number known in the chain that the node is syncing towards. If the node is not syncing, the result will be `false`. ## Use case The `eth_syncing` method is useful for: * Monitoring tools and services that track the sync status of Base nodes. * Developers and users who want to check if a node is fully synced before making transactions or deploying contracts. # trace_block | Base Source: https://docs.chainstack.com/reference/base-trace-block openapi/base_node_api/trace_block.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method trace_block provides a comprehensive trace of all transactions included in a specific block. Chainstack Base reference. Base API method `trace_block` provides a comprehensive trace of all transactions included in a specific block. This method is crucial for developers, auditors, and blockchain analysts who need to understand the detailed execution flow of every transaction within a block, including contract calls, state changes, and event emissions. It offers a holistic view of the block's impact on the Ethereum Virtual Machine (EVM) and the blockchain state. ## Parameters * `blockNumber` or `blockHash` — The number or hash of the block you wish to trace, specified as a hexadecimal string or a decimal number. This identifies the specific block whose transactions you want to analyze in detail. ## Response * `result` — An array containing the trace data for each transaction in the specified block. Each element in the array represents the trace of a single transaction, detailing every operation executed, such as calls to other contracts, value transfers, and the execution outcomes. ## Use case The `trace_block` method is invaluable for conducting in-depth block analyses. Developers can use it to debug and optimize the interactions of multiple transactions within a block, auditors can employ it to ensure the security and integrity of all transactions in a block, and analysts might use it to investigate block-wide patterns or detect anomalies. This method provides a comprehensive overview of how a block's transactions interact with the blockchain, which is crucial for understanding its overall effects and ensuring its correctness. # trace_call | Base Source: https://docs.chainstack.com/reference/base-trace-call openapi/base_node_api/trace_call.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method trace_call allows for the execution of a call transaction and returns a number of possible traces for it. On Base. Base API method `trace_call` allows for the execution of a call transaction and returns a number of possible traces for it. This method is useful for simulating transactions without broadcasting them to the network, allowing developers to test and debug their transactions in a controlled environment. ## Parameters * `transaction` — The transaction to be executed, specified as an object. This includes fields such as `from`, `to`, `gas`, `gasPrice`, `value`, and `data`. * `traceTypes` — An array of strings specifying the types of traces to return. Common trace types include `"vmTrace"`, `"trace"`, and `"stateDiff"`. * `blockNumber` — The block number against which the transaction should be executed. This can be a specific block number, `"earliest"`, `"latest"`, or `"pending"`. ## Response * `result` — Depending on the requested `traceTypes`, the result may include various forms of trace data such as VM operation trace, state changes, and executed transaction trace. This data is crucial for understanding how a transaction interacts with the EVM and alters the blockchain state. ## Use case A possible use case for the `trace_call` method in Base is for smart contract developers who need to debug complex interactions with the Ethereum Virtual Machine (EVM). By simulating transactions, developers can identify and fix issues before deploying contracts or sending transactions on the live network. Additionally, it can be used to estimate gas usage more accurately or to verify that a transaction will behave as expected without actually making a state change on the blockchain. # trace_callMany method | Base Source: https://docs.chainstack.com/reference/base-trace-callmany openapi/base_node_api/trace_callMany_method.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method trace_callMany allows for the execution of multiple call transactions and returns their trace data. Base via Chainstack. Base API method `trace_callMany` allows for the execution of multiple call transactions and returns their trace data. This method is particularly useful for developers who wish to simulate the execution of several transactions in sequence without broadcasting them to the network, enabling a comprehensive analysis of how a series of transactions would interact with the Ethereum Virtual Machine (EVM) and affect the blockchain state. ## Parameters * `transactions` — An array of objects, where each object represents a transaction to be executed. Each transaction object includes fields such as `from`, `to`, `gas`, `gasPrice`, `value`, and `data`. * `traceTypes` — An array of strings specifying the types of traces to return for each transaction. Common trace types include `"vmTrace"`, `"trace"`, and `"stateDiff"`. * `blockNumber` — The block number against which all the transactions should be executed. This can be a specific block number, `"earliest"`, `"latest"`, or `"pending"`. ## Response * `result` — An array containing the trace data for each executed transaction, as specified by the `traceTypes`. This data provides detailed insights into the execution path of each transaction, including VM operations, state changes, and other relevant information that helps in understanding the impact of the transactions on the blockchain state. ## Use case The `trace_callMany` method is invaluable for developers and analysts who need to understand the cumulative effect of multiple transactions on the blockchain state. This could be used for testing complex interactions between smart contracts, debugging transaction sequences for decentralized applications (DApps), or estimating the gas usage for a series of transactions. It provides a sandbox environment for safely simulating transactions, allowing for thorough testing and analysis without the risk of consuming gas or affecting the live blockchain. # trace_get | Base Source: https://docs.chainstack.com/reference/base-trace-get openapi/base_node_api/trace_get.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method trace_get retrieves a specific trace from a transaction by its index. Available on Base via Chainstack JSON-RPC nodes. Base API method `trace_get` retrieves a specific trace from a transaction by its index. This method is particularly useful for developers, auditors, and analysts who are interested in examining a particular step or operation within a transaction's execution path. It allows for a focused analysis on a segment of the transaction's trace, providing detailed information about a single operation, including calls to other contracts, state changes, and execution outcomes. ## Parameters * `transactionHash` — The hash of the transaction from which you want to retrieve a trace, specified as a hexadecimal string. * `traceIndex` — An array of integers specifying the index of the trace to retrieve. The index reflects the order of operations within the transaction's execution path. ## Response * `result` — The trace data for the specified index within the transaction. This includes detailed information about the operation, such as the operation type, call data, value transferred, and the result of the operation. This focused trace can provide insights into specific actions taken by the transaction, including interactions with smart contracts and changes to the blockchain state. ## Use case The `trace_get` method is invaluable for detailed transaction analysis, especially when investigating complex transactions that involve multiple steps or interactions with various contracts. Developers can use it to debug specific operations within a transaction, auditors can employ it to verify the correctness and security of particular steps, and analysts might use it to study the behavior of contracts and transactions on a granular level. # trace_replayBlockTransactions | Base Source: https://docs.chainstack.com/reference/base-trace-replayblocktransactions openapi/base_node_api/trace_replayBlockTransactions.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method trace_replayBlockTransactions replays transactions in a specified block individually, returning the trace data for each transaction. Base API method `trace_replayBlockTransactions` replays transactions in a specified block individually, returning the trace data for each transaction. This method is invaluable for developers, auditors, and analysts who wish to inspect the execution path of transactions within a block, including calls to other contracts, state changes, and execution outcomes without affecting the live blockchain. ## Parameters * `blockHash` or `blockNumber` — The hash or number of the block whose transactions you want to replay. This can be specified as a hexadecimal string for the block hash or a hexadecimal or decimal number for the block number. * `traceTypes` — An array of strings specifying the types of traces to return for each transaction. Common trace types include `"vmTrace"`, `"trace"`, and `"stateDiff"`. ## Response * `result` — An array containing the trace data for each transaction in the specified block. Each item in the array represents the trace of a single transaction, detailing calls to contracts, value transfers, and other relevant state changes that occurred during its execution. ## Use case The `trace_replayBlockTransactions` method is particularly useful for conducting detailed analyses of block transactions. Developers can use it to debug contract interactions within a specific block, auditors can use it to verify the correctness and security of transactions, and analysts might use it to study transaction patterns or detect anomalies. This method provides a granular view of transaction execution, which is essential for understanding complex interactions on the blockchain. # trace_replayTransaction | Base Source: https://docs.chainstack.com/reference/base-trace-replaytransaction openapi/base_node_api/trace_replayTransaction.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method trace_replayTransaction replays a single transaction using the trace module, returning detailed trace data about its execution. Base API method `trace_replayTransaction` replays a single transaction using the trace module, returning detailed trace data about its execution. This method is crucial for developers, auditors, and analysts who need to understand the precise execution flow of a transaction, including calls made to other contracts, state changes, and the execution outcome. ## Parameters * `transactionHash` — The hash of the transaction you want to replay, specified as a hexadecimal string. * `traceTypes` — An array of strings specifying the types of traces to return. Common trace types include `"vmTrace"`, `"trace"`, and `"stateDiff"`. ## Response * `result` — The trace data for the specified transaction. Depending on the `traceTypes` requested, this may include a detailed execution trace (`vmTrace`), a step-by-step list of executed operations (`trace`), or a summary of state changes (`stateDiff`). ## Use case The `trace_replayTransaction` method is invaluable for a detailed analysis of individual transactions. It can be used by developers to debug smart contract interactions, by auditors to verify transaction integrity and security, and by analysts to understand the effects of a transaction on the blockchain state. This method provides a deep dive into the transaction's execution, offering insights that are not readily available through standard transaction receipts. # debug_traceBlockByHash | Base Source: https://docs.chainstack.com/reference/base-traceblockbyhash openapi/base_node_api/debug_traceBlockByHash.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 The debug_traceBlockByHash RPC method is used to retrieve detailed execution traces for all transactions included in a block, identified by the block's hash. The `debug_traceBlockByHash` RPC method is used to retrieve detailed execution traces for all transactions included in a block, identified by the block's hash. This method is particularly useful for developers and analysts who need to deeply understand transaction execution and state changes within a specific block. ## Request To use `debug_traceBlockByHash`, send a POST request with a JSON RPC call in the body. ## Parameters * **Block Hash** (`string`): The hash of the block for which to retrieve the execution traces. ## Response The response includes detailed execution traces for all transactions in the specified block. * **result** (`array`): An array of execution traces for each transaction in the block. Each trace provides comprehensive details about the execution, including calls, state changes, and gas usage. ## Use case The `debug_traceBlockByHash` method is essential for: * Developers debugging complex interactions within a block. * Analysts conducting forensic analysis of block transactions. * Tools and services that provide insights into the Ethereum Virtual Machine (EVM) execution and state changes. # debug_traceBlockByNumber | Base Source: https://docs.chainstack.com/reference/base-traceblockbynumber openapi/base_node_api/debug_traceBlockByNumber.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Reference for the debug_traceBlockByNumber JSON-RPC method on the Base blockchain via Chainstack nodes. Parameters, response, and examples included. The `debug_traceBlockByNumber` RPC method is used to retrieve detailed execution traces for all transactions included in a block, identified by the block's number. This method is particularly useful for developers and analysts who need to deeply understand transaction execution and state changes within a specific block. ## Request To use `debug_traceBlockByNumber`, send a POST request with a JSON RPC call in the body. ## Parameters * **Block Number** (`string`): The number of the block for which to retrieve the execution traces, in hexadecimal format. ## Response The response includes detailed execution traces for all transactions in the specified block. * **result** (`array`): An array of execution traces for each transaction in the block. Each trace provides comprehensive details about the execution, including calls, state changes, and gas usage. ## Use case The `debug_traceBlockByNumber` method is essential for: * Developers debugging complex interactions within a block. * Analysts conducting forensic analysis of block transactions. * Tools and services that provide insights into the Ethereum Virtual Machine (EVM) execution and state changes. # debug_traceCall | Base Source: https://docs.chainstack.com/reference/base-tracecall openapi/base_node_api/debug_traceCall.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Executes a call and retrieves a detailed execution trace of the call, similar to a transaction but without creating a transaction on the blockchain. Executes a call and retrieves a detailed execution trace of the call, similar to a transaction but without creating a transaction on the blockchain. This method is crucial for developers and analysts who need to understand the execution flow of a call, including all the operations (opcodes) executed, gas usage, and the call's outcome. ## Request To use `debug_traceCall`, send a POST request with a JSON RPC call in the body. ## Parameters * **from** (`string`): The address the call is sent from. * **to** (`string`): The address the call is directed to. * **gasPrice** (`string`): The gas price for the call. * **data** (`string`): The data sent along with the call. ## Response The response includes a detailed execution trace of the call, providing insights into each step of the call's execution. * **result** (`object`): The detailed execution trace, including: * **structLogs** (`array`): An array of execution steps (opcodes) taken by the call. * **gas** (`integer`): The gas provided by the call. * **returnValue** (`string`): The return value of the call, if any. * **gasUsed** (`integer`): The total gas used by the call. * **failed** (`boolean`): Indicates whether the call failed. ## Use case The `debug_traceCall` method is essential for: * Developers debugging smart contract interactions. * Analysts conducting forensic analysis of calls. * Tools and services that provide insights into Ethereum Virtual Machine (EVM) execution. # debug_traceCallMany | Base Source: https://docs.chainstack.com/reference/base-tracecallmany openapi/base_node_api/debug_traceCallMany.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Executes a list of bundles (a bundle is a collection of transactions), without creating transactions on the blockchain. Returns their traces. Executes a list of bundles (a bundle is a collection of transactions), without creating transactions on the blockchain. Returns their traces. This method is akin to `eth_callMany` but provides detailed execution traces for each call, making it invaluable for debugging complex interactions involving multiple transactions. ## Request To use `debug_traceCallMany`, send a POST request with a JSON RPC call in the body. ## Parameters * **transactions** (`array`): An array of transaction objects to be executed. Each transaction object includes: * **from** (`string`): The address the transaction is sent from (optional). * **to** (`string`): The address the transaction is directed to. * **gas** (`string`): The gas limit for the transaction. * **gasPrice** (`string`): The gas price for the transaction. * **data** (`string`): The data sent along with the transaction. * **blockOverride** (`object`): Optional overrides for the execution context block header, including: * **blockNumber** (`string`): Block number for the execution context. * **Simulation Context** (`object`): The context for the simulation, including block number and transaction index. * **State Overrides** (`object`): Optional state overrides for the execution context. ## Response The response includes detailed execution traces for each call in the bundles, providing insights into each step of the transactions' execution. * **result** (`array`): An array of execution traces for each call, including: * **structLogs** (`array`): An array of execution steps (opcodes) taken by the call. * **gas** (`integer`): The gas provided by the call. * **returnValue** (`string`): The return value of the call, if any. * **gasUsed** (`integer`): The total gas used by the call. * **failed** (`boolean`): Indicates whether the call failed. ## Use case The `debug_traceCallMany` method is essential for: * Developers debugging complex interactions involving multiple transactions. * Analysts conducting forensic analysis of transaction sequences. * Tools and services that provide insights into Ethereum Virtual Machine (EVM) execution across multiple calls. # debug_traceTransaction | Base Source: https://docs.chainstack.com/reference/base-tracetransaction openapi/base_node_api/debug_traceTransaction.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 The debug_traceTransaction RPC method retrieves a detailed execution trace of a transaction, identified by the transaction's hash. The `debug_traceTransaction` RPC method retrieves a detailed execution trace of a transaction, identified by the transaction's hash. This method is crucial for developers and analysts who need to understand the execution flow of a transaction, including all the operations (opcodes) executed, gas usage, and the transaction's outcome. ## Request To use `debug_traceTransaction`, send a POST request with a JSON RPC call in the body. ## Parameters * **Transaction Hash** (`string`): The hash of the transaction to trace. ## Response The response includes a detailed execution trace of the transaction, providing insights into each step of the transaction's execution. * **result** (`object`): The detailed execution trace, including: * **structLogs** (`array`): An array of execution steps (opcodes) taken by the transaction. * **gas** (`integer`): The gas provided by the transaction. * **returnValue** (`string`): The return value of the transaction, if any. * **gasUsed** (`integer`): The total gas used by the transaction. * **failed** (`boolean`): Indicates whether the transaction failed. ## Use case The `debug_traceTransaction` method is essential for: * Developers debugging smart contract interactions. * Analysts conducting forensic analysis of transactions. * Tools and services that provide insights into Ethereum Virtual Machine (EVM) execution. # eth_uninstallFilter | Base Source: https://docs.chainstack.com/reference/base-uninstallfilter openapi/base_node_api/eth_uninstallFilter.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_uninstallFilter removes a previously installed filter, stopping further notifications. Chainstack Base reference. Base API method `eth_uninstallFilter` removes a previously installed filter, stopping further notifications. This method is crucial for managing resources and avoiding unnecessary data processing from filters that are no longer needed. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter | Base](/reference/base-newfilter) > * [eth\_newBlockFilter | Base](/reference/base-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `filterId` — the ID of the filter to be uninstalled. This ID is returned by filter creation methods such as `eth_newBlockFilter`, or `eth_newFilter`. For this example, a random value `"0x1a2b3c"` is used. ## Response * `result` — a boolean indicating the success of the filter removal. `true` if the filter was successfully uninstalled; otherwise, `false`. ## Use case The `eth_uninstallFilter` method is essential for: * Applications that dynamically create and remove filters based on user interaction or specific conditions to manage system resources efficiently. * Services that need to clean up filters to prevent processing outdated or irrelevant data. * Developers who are testing or debugging Base filters and need to reset their filter setup. # eth_unsubscribe | Base Source: https://docs.chainstack.com/reference/base-unsubscribe Cancel any active Base WebSocket subscription with eth_unsubscribe. Pass the subscription ID to stop receiving real-time blockchain event notifications. When a client subscribes to a particular event, the Base node will send updates as soon as they occur. However, in some cases, the client may no longer be interested in receiving updates for a particular event. The `eth_unsubscribe` method can be used to cancel the following subscriptions: > [card] [eth\_subscribe('newFlashblockTransactions')](/reference/base-subscribe-newflashblocktransactions) > [card] [eth\_subscribe('pendingLogs')](/reference/base-subscribe-pendinglogs) > [card] [eth\_subscribe('newFlashblocks')](/reference/base-subscribe-newflashblocks) ## Parameters * `string` — the ID of the subscription that you want to cancel. ## Response * `boolean` — the boolean value indicating if the subscription was removed successfully. `true` if removed successfully, `false` if not. ## `eth_unsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"eth_unsubscribe","params":["0x7529626735859f32962b10b19ee2e3eb"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function removeSubscription() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_unsubscribe', params: ['0x9de3135dd3dd6c6b99bd104061a5af4f'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } removeSubscription(); ``` This will remove the subscription matching the ID passed as a parameter. # Beacon Chain API reference: Ethereum consensus layer Source: https://docs.chainstack.com/reference/beacon-chain Ethereum Beacon Chain consensus layer API — query validators, attestations, block proposals, the fork choice algorithm, and rewards and penalties. This section provides interactive API examples on how to query the Beacon Chain through Chainstack nodes. ## How to start using the Beacon Chain API When you deploy a node with Chainstack, you get both the execution layer node endpoint and the consensus layer (aka Beacon Chain) node endpoint. Follow these steps to sign up on Chainstack, deploy an Ethereum RPC node, and find your endpoint credentials (both execution layer and the Beacon Chain): **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the Ethereum blockchain and use the Beacon Chain API to build. > [!NOTE] > > For a detailed description of all the available Beacon Chain API specifications, see [Ethereum Beacon Chain API](https://ethereum.github.io/beacon-APIs/). # Beacon Chain configuration info Source: https://docs.chainstack.com/reference/beacon-chain-configuration This section delves into the details of retrieving the Beacon Chain configuration:. Beacon Chain configuration info on Chainstack via Chainstack. This section delves into the details of retrieving the Beacon Chain configuration: > [card] [Get genesis data](/reference/ethereum-beacon-genesis) > [card] [Get fork version by state ID](/reference/getforkinformation) > [card] [Get fork schedule](/reference/getconfigforkschedule) > [card] [Node configuration](/reference/getconfigspec) > [card] [Get deposit contract](/reference/getconfigdepositcontract) Each subsection is equipped with interactive API examples, allowing you to directly engage with the Beacon Chain's configuration through the Chainstack node endpoints. Whether you're a developer, a researcher, or simply an enthusiast, this section offers real-world examples to getting the Beacon Chain's configuration data. # Beacon Chain events Source: https://docs.chainstack.com/reference/beacon-chain-events Subscribe to Beacon Chain event streams via Server-Sent Events. Get real-time head updates, block notifications, and attestation data from the consensus layer. > [card] [Subscribe to events](/reference/subscribetobeaconevents) For developers and network participants, this is a valuable tool for monitoring the chain's progress and ensuring their systems are synchronized with the latest state of the network. # Beacon Chain validators info Source: https://docs.chainstack.com/reference/beacon-chain-node Retrieve all the necessary information related to the Ethereum validators:. Beacon Chain validators info on Chainstack via Chainstack. Retrieve all the necessary information related to the Ethereum validators: > [card] [Get voluntary exits](/reference/getbeaconpoolvoluntaryexits) > [card] [Get proposer slashings from the beacon pool](/reference/getbeaconpoolproposerslashings) > [card] [Get attester slashings](/reference/getbeaconpoolattesterslashings) > [card] [Get attestations from the beacon pool](/reference/getbeaconpoolattestationsbyslotandcommitteeindex) > [card] [Validator balances by state ID](/reference/getvalidatorbalancesbystateidandvalidatorid) > [card] [Validator information by state and ID or public key](/reference/getvalidatorbystateidandindex) > [card] [Validators information from state ID](/reference/getvalidatorinformation) > [card] [Get proposer duties](/reference/getproposerduties) > [card] [Produce unsigned block](/reference/produceblock) > [card] [Produce an unsigned blinded block](/reference/produceblindedblock) > [card] [Get attestation data](/reference/getattestationdata) > [card] [Get attestations of the beacon block by block\\\_id](/reference/getbeaconblockattestationsbyblockid) # Beacon Chain state Source: https://docs.chainstack.com/reference/beacon-chain-state Query the Ethereum Beacon Chain state — retrieve validator sets, committee assignments, epoch data, and finality checkpoints from the consensus layer. > [card] [Block root by block\_id](/reference/getbeaconblockrootbyblockid) > [card] [Get block by block\_id](/reference/getbeaconblocksbyblockid) > [card] [Beacon headers by block\_id](/reference/getbeaconheadersbyblockid) > [card] [Beacon headers by slot](/reference/getbeaconheadersbyslotandparentroot) > [card] [Get sync committees by state and epoch](/reference/getsynccommitteesbystateidandepoch) > [card] [Get committees by state, epoch, index, and slot](/reference/getcommitteesbystateidepochindexandslot) > [card] [Finality checkpoints for a given state](/reference/getfinalitycheckpoints) > [card] [Root of a given state](/reference/getstateroot) # Bitcoin API reference Source: https://docs.chainstack.com/reference/bitcoin-api-reference This page provides an overview of the available methods on Chainstack Bitcoin nodes using the Bitcoin RPC API. The methods are divided into three categories: > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). * Immediately accessible methods * Methods requiring further customization * Unavailable methods on Chainstack Bitcoin nodes This guide will help you better understand and utilize the capabilities of Chainstack Bitcoin nodes within the context of the Bitcoin RPC API. ## Chainstack Bitcoin API table The table features an **Availability** column, which outlines the method's accessibility on a Bitcoin node deployed via Chainstack. The availability is classified into three levels: * **Available** — the corresponding method is immediately accessible on Chainstack Global, Trader, and Unlimited Bitcoin nodes. * **Customization required** — the corresponding method can be enabled on a dedicated Chainstack Bitcoin node with additional customization. * **Not available** — the corresponding method **cannot** be activated on any Chainstack Bitcoin node. This classification assists users in understanding the availability of each method within Chainstack Bitcoin nodes. > [!NOTE] > > Easlily interact with the Bitcoin API using the [Bitcoin API Postman collection](/reference/bitcoin-rpc-methods-postman-collection). ## Dedicated Bitcoin nodes In case you need access to methods requiring customization, you can request a Dedicated Node directly from the Chainstack console UI, and a Chainstack Support representative will contact you shortly. ### Blockchain RPC methods Blockchain RPC methods allow developers to interact with and gather information about the Bitcoin blockchain, such as querying transactions and network data. | RPC method | Availability | Remarks | | - | - | - | | `getbestblockhash` | Available | | | `getblock` | Available | | | `getblockchaininfo` | Available | | | `getblockfilter` | Customization required | | | `getblockhash` | Available | | | `getblockheader` | Available | | | `getblockstats` | Available | | | `getchaintips` | Available | | | `getchaintxstats` | Available | | | `getdifficulty` | Available | | | `getmempoolancestors` | Available | | | `getmempooldescendants` | Available | | | `getmempoolentry` | Available | | | `getmempoolinfo` | Available | | | `getrawmempool` | Available | | | `gettxoutsetinfo` | Available | | | `gettxout` | Available | | | `verifychain` | Available | | | `gettxoutproof` | Available | This function operates intermittently by default, only functioning when there is an unspent output in the UTXO for the transaction. To ensure consistent performance, maintain a transaction index using the `-txindex` command line option or manually specify the block containing the transaction (by `blockhash`). | | `preciousblock` | Available | Treats a block as if it were received before others with the same work. | | `pruneblockchain` | Not available | | | `savemempool` | Available | It will fail until the previous dump is fully loaded. | | `scantxoutset` | Available | This method may be removed or changed in future releases. | | `verifytxoutproof` | Available | Requires the hex-encoded proof generated by `gettxoutproof` method. | ### Control PRC methods Control RPC methods in the Bitcoin API allow users to manage and configure their Bitcoin Core node, such as returning general information about the node, or the total uptime. | RPC method | Availability | Remarks | | - | - | - | | `uptime` | Available | | | `getmemoryinfo` | Available | | | `getrpcinfo` | Available | | | `help` | Available | | | `logging` | Available | | | `stop` | Not available | | ### Generating RPC methods Generating RPC methods in the Bitcoin API are primarily used for mining and testing purposes. These methods enable developers to create new blocks, generate and manage addresses, and perform CPU mining for testing and development environments. | RPC method | Availability | Remarks | | - | - | - | | `generateblock` | Not available | | | `generatetoaddress` | Not available | Deprecated after Bitcoin Core v0.19 | | `generatetodescriptor` | Not available | | ### Mining RPC methods Mining RPC methods provide developers with access to mining-related data, such as network difficulty, mining statistics, and block template creation. | RPC Method | Availability | Remarks | | - | - | - | | `getblocktemplate` | Available | | | `getmininginfo` | Available | | | `getnetworkhashps` | Available | | | `prioritisetransaction` | Available | | | `submitblock` | Not available | | | `submitheader` | Not available | | ### Network RPC methods Network RPC methods in the Bitcoin API are used to manage and gather information about the node's connection to the Bitcoin network. These methods provide developers with tools to control and monitor the node's interactions with peers. | RPC method | Availability | Remarks | | - | - | - | | `getpeerinfo` | Available | | | `getnetworkinfo` | Available | | | `getconnectioncount` | Available | | | `getnettotals` | Available | | | `listbanned` | Not available | not available on Global or Trader Nodes | | `ping` | Not available | not available on Global or Trader Nodes | | `addnode` | Not available | | | `disconnectnode` | Not available | | | `getnodeaddresses` | Available | | | `setnetworkactive` | Not available | not available on Global or Trader Nodes | | `setban` | Not available | | ### Raw transactions RPC methods Raw transactions RPC methods in the Bitcoin API are used to create, inspect, sign, and broadcast custom transactions on the Bitcoin network. These methods provide developers with control over transaction creation and processing. | RPC method | Availability | Remarks | | - | - | - | | `analyzepsbt` | Available | | | `createpsbt` | Available | | | `combinepsbt` | Available | | | `createrawtransaction` | Available | | | `combinerawtransaction` | Available | | | `decodepsbt` | Available | | | `decoderawtransaction` | Available | | | `decodescript` | Available | | | `finalizepsbt` | Available | | | `fundrawtransaction` | Customization required | Require wallet support | | `getrawtransaction` | Available | | | `sendrawtransaction` | Available | | | `signrawtransactionwithkey` | Available | | | `testmempoolaccept` | Available | | ### Util RPC methods Util RPC methods in the Bitcoin API are used for various utility tasks, simplifying certain operations and calculations when working with Bitcoin transactions and blockchain data. | RPC method | Availability | Remarks | | - | - | - | | `createmultisig` | Available | | | `deriveaddresses` | Available | | | `estimatesmartfee` | Available | | | `getindexinfo` | Customization required | | | `signmessagewithprivkey` | Available | | | `validateaddress` | Available | | | `verifymessage` | Available | | ### Wallet RPC methods Wallet RPC methods in the Bitcoin API are used to interact with and manage a Bitcoin Core built-in wallet functionality. By leveraging wallet RPC methods, developers can create and manage Bitcoin wallets programmatically, enabling the implementation of various wallet-related features and applications. > [!NOTE] > > Note that the wallet module is **disabled** by default on Chainstack Global Nodes. | RPC method | Availability | Remarks | | - | - | - | | `createwallet` | Customization required | | | `listwalletdir` | Customization required | | | `loadwallet` | Customization required | | | `listtransactions` | Customization required | | | `listwallets` | Customization required | | | `abandontransaction` | Customization required | | | `abortrescan` | Customization required | | | `bumpfee` | Customization required | | | `dumpprivkey` | Customization required | | | `getaddressesbylabel` | Customization required | | | `getbalance` | Customization required | | | `getbalances` | Customization required | | | `getnewaddress` | Customization required | | | `getrawchangeaddress` | Customization required | | | `getreceivedbyaddress` | Customization required | | | `getreceivedbylabel` | Customization required | | | `gettransaction` | Customization required | | | `importaddress` | Customization required | | | `importdescriptors` | Customization required | | | `importmulti` | Customization required | | | `importprivkey` | Customization required | | | `importprunedfunds` | Customization required | | | `importpubkey` | Customization required | | | `importwallet` | Customization required | | | `keypoolrefill` | Customization required | | | `listaddressgroupings` | Customization required | | | `listlabels` | Customization required | | | `listlockunspent` | Customization required | | | `listreceivedbyaddress` | Customization required | | | `listsinceblock` | Customization required | | | `listtransactions` | Customization required | | | `listunspent` | Customization required | | | `listwalletdir` | Customization required | | | `listwallets` | Customization required | | | `lockunspent` | Customization required | | | `loadwallet` | Customization required | | | `psbtbumpfee` | Customization required | | | `removeprunedfunds` | Customization required | | | `rescanblockchain` | Customization required | | | `send` | Customization required | | | `sendmany` | Customization required | | | `sendtoaddress` | Customization required | | | `sethdseed` | Customization required | | | `setlabel` | Customization required | | | `settxfee` | Customization required | | | `setwalletflag` | Customization required | | | `signmessage` | Customization required | | | `signrawtransactionwithwallet` | Customization required | | | `unloadwallet` | Customization required | | | `upgradewallet` | Customization required | | | `walletcreatefundedpsbt` | Customization required | | | `walletlock` | Customization required | | | `walletpassphrase` | Customization required | | | `walletpassphrasechange` | Customization required | | | `walletprocesspsbt` | Customization required | | ### ZMQ RPC methods ZMQ RPC methods in the Bitcoin API related to the ZeroMQ (ZMQ) notification system. | RPC method | Availability | | - | - | | `getzmqnotifications` | Customization required | ### Signer RPC methods Signer RPC methods can be used to interact with external signers such as hardware wallets. | RPC method | Availability | | - | - | | `enumeratesigners` | Customization required | # decoderawtransaction | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-decoderawtransaction openapi/bitcoin_node_api/decoderawtransaction.json POST /e125e5e68e66f2939f3f120e6f888b3f The decoderawtransaction method is used to decode a raw transaction encoded in hex format. decoderawtransaction on Bitcoin via Chainstack. The `decoderawtransaction` method is used to decode a raw transaction encoded in hex format. It provides a detailed view of the transaction's components, such as its inputs, outputs, version, and lock time. ## Parameters * `hexString` (required): The raw transaction in hex format. ## Response * `result` — an object containing detailed information about the decoded transaction, including inputs, outputs, the transaction's version, and lock time. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `decoderawtransaction` method is crucial for developers and analysts who need to inspect the details of a raw transaction without broadcasting it to the network. It's used for debugging, transaction analysis, and educational purposes. # decodescript | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-decodescript openapi/bitcoin_node_api/decodescript.json POST /e125e5e68e66f2939f3f120e6f888b3f The decodescript method is used to decode a hex-encoded script, providing insights into its structure and purpose. Bitcoin via Chainstack. The `decodescript` method is used to decode a hex-encoded script, providing insights into its structure and purpose. This includes identifying the script type, associated addresses, and whether it is a segwit script. ## Parameters * `scriptHex` (required): The hex-encoded script to decode. ## Response * `result` — an object containing detailed information about the decoded script, including its type, related addresses, and segwit status. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `decodescript` method is invaluable for developers and analysts working with Bitcoin scripts, providing a way to inspect and understand the functionality of arbitrary scripts without executing them. # estimatesmartfee | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-estimatesmartfee openapi/bitcoin_node_api/estimatesmartfee.json POST /e125e5e68e66f2939f3f120e6f888b3f The estimatesmartfee method estimates the fee per kilobyte required for a transaction to be confirmed within a certain number of blocks. The `estimatesmartfee` method estimates the fee per kilobyte required for a transaction to be confirmed within a certain number of blocks. This is useful for dynamically adjusting transaction fees based on current network conditions. ## Parameters * `conf_target` (required): The number of blocks within which the transaction should be confirmed. ## Response * `result` — an object containing the estimated fee rate (`feerate`) in BTC per kilobyte and the target number of blocks (`blocks`) for confirmation. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `estimatesmartfee` method is crucial for wallet applications and services that need to adjust transaction fees dynamically. It helps ensure transactions are confirmed in a timely manner without overpaying. # getbestblockhash | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getbestblockhash openapi/bitcoin_node_api/getbestblockhash.json POST /e125e5e68e66f2939f3f120e6f888b3f The getbestblockhash method returns the hash of the best block in the most-work fully-validated chain. Chainstack Bitcoin reference. The `getbestblockhash` method returns the hash of the best block in the most-work fully-validated chain. The best block is the tip of the blockchain, representing the most recent block that has been validated and added to the chain. ## Parameters * `none` ## Response * `result` — a hex-encoded string representing the hash of the best block in the longest blockchain. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `getbestblockhash` method is useful for applications that need to know the current state of the blockchain. For example, a wallet application may use this method to determine the latest block to scan for transactions relevant to the wallet's addresses. # getblock | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getblock openapi/bitcoin_node_api/getblock.json POST /e125e5e68e66f2939f3f120e6f888b3f The getblock method retrieves a block from the blockchain by its hash or height. Available on Bitcoin via Chainstack JSON-RPC nodes. The `getblock` method retrieves a block from the blockchain by its hash or height. If the verbosity parameter is omitted, the method returns a serialized block in hex format. If the verbosity parameter is set to 1, the method returns an Object with information about the block. If the verbosity parameter is set to 2, the method returns an Object with information about the block and information about each transaction. ## Parameters * `blockhash` (required) — a string representing the hash of the block to retrieve. * `verbosity` (optional, default=1) — an integer specifying the level of detail to include in the response: * `0` — returns a serialized block in hex format. * `1` — returns an Object with information about the block. * `2` — returns an Object with information about the block and information about each transaction. ## Response * `result` — the requested block data, either as a serialized block in hex format, or as a JSON object with the following fields: * `hash` — the block hash. * `confirmations` — the number of confirmations. * `size` — the block size in bytes. * `strippedsize` — the block size excluding witness data. * `weight` — the block weight. * `height` — the block height or index. * `version` — the block version. * `versionHex` — the block version formatted in hexadecimal. * `merkleroot` — the merkle root. * `tx` — the transaction ids (verbosity=1) or full transaction details (verbosity=2). * `time` — the block time in seconds since epoch (Jan 1 1970 GMT). * `mediantime` — the median block time in seconds since epoch (Jan 1 1970 GMT). * `nonce` — the nonce. * `bits` — the bits. * `difficulty` — the difficulty. * `chainwork` — the expected number of hashes required to produce the current chain # getblockchaininfo | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getblockchaininfo openapi/bitcoin_node_api/getblockchaininfo.json POST /e125e5e68e66f2939f3f120e6f888b3f The getblockchaininfo method returns an object containing various state info regarding blockchain processing. Bitcoin via Chainstack. The `getblockchaininfo` method returns an object containing various state info regarding blockchain processing. ## Parameters * `none` ## Response * `result` — an object containing various state info with the following fields: * `chain` — current network name (main, test, regtest) * `blocks` — the height of the most-work fully-validated chain. The genesis block has height 0 * `headers` — the current number of headers we have validated * `bestblockhash` — the hash of the currently best block * `difficulty` — the current difficulty * `mediantime` — median time for the current best block * `verificationprogress` — estimate of verification progress \[0..1] * `initialblockdownload` — (debug information) estimate of whether this node is in Initial Block Download mode * `chainwork` — total amount of work in active chain, in hexadecimal * `size_on_disk` — the estimated size of the block and undo files on disk * `pruned` — if the blocks are subject to pruning * `pruneheight` — lowest-height complete block stored (only present if pruning is enabled) * `automatic_pruning` — whether automatic pruning is enabled (only present if pruning is enabled) * `prune_target_size` — the target size used by pruning (only present if automatic pruning is enabled) * `softforks` — status of softforks * `bip9_softforks` — status of BIP9 softforks in progress * `warnings` — any network and blockchain warnings * `error` — an object containing an error message if an error occurred, otherwise `null` * `id` — an integer representing the ID of the request, used to match requests with responses ## Use case The `getblockchaininfo` method is useful for retrieving various state information about the blockchain. This information can be used for monitoring the health and progress of the blockchain, as well as for making decisions in blockchain-based applications. For example, a mining pool might use the `difficulty` field to adjust the pool's target share difficulty. # getblockfilter | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getblockfilter openapi/bitcoin_node_api/getblockfilter.json POST /e125e5e68e66f2939f3f120e6f888b3f The getblockfilter method retrieves a BIP 157 content filter for a particular block. Available on Bitcoin via Chainstack JSON-RPC nodes. The `getblockfilter` method retrieves a BIP 157 content filter for a particular block. > [!NOTE] > > **Customization required** > > This method requires node [node customization](/docs/features-availability-across-subscription-plans) and will not work out of the box. Talk to us & we'll implement it. ## Parameters * `blockhash` (required) — the hash of the block * `filtertype` (required) — the type of filter to retrieve, currently only `basic` is supported ## Response * `result` — an object containing the block filter with the following fields: * `filter` — the hex-encoded filter data * `header` — the hex-encoded filter header * `error` — an object containing an error message if an error occurred, otherwise `null` * `id` — an integer representing the ID of the request, used to match requests with responses ## Use case Block filters are used to efficiently determine if a block may contain transactions relevant to a particular wallet or light client without downloading the entire block. The `getblockfilter` method allows retrieving these filters for a specific block. For example, a light wallet might use `getblockfilter` to check if it needs to download a particular block to search for wallet transactions, reducing bandwidth and processing requirements compared to downloading every block. # getblockhash | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getblockhash openapi/bitcoin_node_api/getblockhash.json POST /e125e5e68e66f2939f3f120e6f888b3f The getblockhash method returns the hash of the block at the given height in the blockchain. getblockhash on Bitcoin via Chainstack. The `getblockhash` method returns the hash of the block at the given height in the blockchain. ## Parameters * `height` (required) — the height of the block whose hash should be returned ## Response * `result` — a string representing the hash of the block at the given height * `error` — an object containing an error message if an error occurred, otherwise `null` * `id` — an integer representing the ID of the request, used to match requests with responses ## Use case The `getblockhash` method is useful for retrieving the hash of a block at a specific height in the blockchain. This can be used in conjunction with other methods like `getblock` to retrieve detailed information about a particular block. For example, a block explorer application might use `getblockhash` to get the hash of the latest block, and then use that hash with `getblock` to retrieve and display the contents of the latest block. # getblockheader | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getblockheader openapi/bitcoin_node_api/getblockheader.json POST /e125e5e68e66f2939f3f120e6f888b3f The getblockheader method retrieves detailed information about a block header given its hash. Available on Bitcoin via Chainstack. The `getblockheader` method retrieves detailed information about a block header given its hash. This includes the block's hash, the number of confirmations, its height in the blockchain, version, Merkle root, block time, nonce, difficulty, total work done in the chain up to this block, and the hashes of the previous and next blocks if available. ## Parameters * `blockhash` (required): The hash of the block header to retrieve. ## Response * `result` — an object containing detailed information about the block header, such as its hash, confirmations, height, version, Merkle root, time, nonce, difficulty, total work, number of transactions, and previous and next block hashes. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getblockheader` method is essential for applications and services that need to verify the blockchain's integrity or analyze its properties. It provides critical information about individual blocks without the need to download the entire block, making it useful for lightweight clients and block explorers. # getblockstats | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getblockstats openapi/bitcoin_node_api/getblockstats.json POST /e125e5e68e66f2939f3f120e6f888b3f The getblockstats method computes per block statistics based on the specified block hash and the array of stats to include. On Bitcoin. The `getblockstats` method computes per block statistics based on the specified block hash and the array of stats to include. It returns an object containing various statistics for the specified block. ## Parameters * `blockhash` (required) — the hash of the block for which statistics are to be computed. * `stats` (optional) — an array of statistics to include in the response. Possible values include: * `avgfee` * `avgfeerate` * `avgtxsize` * `blockhash` * `feerate_percentiles` * `height` * `ins` * `maxfee` * `maxfeerate` * `maxtxsize` * `medianfee` * `mediantime` * `mediantxsize` * `minfee` * `minfeerate` * `mintxsize` * `outs` * `subsidy` * `swtotal_size` * `swtotal_weight` * `swtxs` * `time` * `total_out` * `total_size` * `total_weight` * `totalfee` * `txs` * `utxo_increase` * `utxo_size_inc` ## Response * `result` — an object containing the computed block statistics based on the specified parameters. * `error` — an object containing an error message if an error occurred. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `getblockstats` method is useful for analyzing and extracting detailed statistics about individual blocks in the blockchain. These statistics can provide valuable insights into block characteristics and transaction data, aiding in blockchain analysis and monitoring. # getblocktemplate | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getblocktemplate openapi/bitcoin_node_api/getblocktemplate.json POST /e125e5e68e66f2939f3f120e6f888b3f The getblocktemplate method is used by miners to request a block template for mining new blocks. Use it on Bitcoin via Chainstack. The `getblocktemplate` method is used by miners to request a block template for mining new blocks. It can be customized with various parameters to specify the desired mining capabilities and rules. ## Parameters * `mode` (optional): Specifies the mode of operation. Can be 'template' for a block template or 'proposal' for submitting a block proposal. This field is required if parameters are specified. * `capabilities` (optional): A list of strings specifying the capabilities the miner supports. This can include 'coinbasetxn', 'workid', 'coinbase/append', etc. * `rules` (optional): A list of strings specifying the rules the miner will support. For example, 'segwit' indicates support for Segregated Witness. ## Response * `result` — an object containing the block template or proposal for mining. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getblocktemplate` method is crucial for miners who need to get information about the current state of the network to mine new blocks. It allows miners to get a template for creating a new block to be added to the blockchain. # getchaintips | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getchaintips openapi/bitcoin_node_api/getchaintips.json POST /e125e5e68e66f2939f3f120e6f888b3f The getchaintips method provides information about the current chain tips, which are the endpoints of the active blockchains in the network. The `getchaintips` method provides information about the current chain tips, which are the endpoints of the active blockchains in the network. ## Parameters * None ## Response * `result` — an array containing information about the current chain tips, including details such as block hash, block height, branch length, and whether the tip is valid. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `getchaintips` method is useful for understanding the current state of the blockchain network, including the various chain tips that may exist due to forks or reorganizations. This information can be valuable for monitoring the network's health and stability. # getchaintxstats | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getchaintxstats openapi/bitcoin_node_api/getchaintxstats.json POST /e125e5e68e66f2939f3f120e6f888b3f The getchaintxstats method computes statistics about the total number and rate of transactions in the blockchain up to a specified block height or hash. The `getchaintxstats` method computes statistics about the total number and rate of transactions in the blockchain up to a specified block height or hash. ## Parameters * `nblocks` (required) — the time frame in blocks to which to compute the statistics. * `blockhash` (required) — the block hash up to which to compute the statistics. ## Response * `result` — an object containing statistics about the total number and rate of transactions in the chain up to the specified block. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `getchaintxstats` method is useful for analyzing the transaction activity and growth of the blockchain up to a specific block. It provides valuable insights into the transaction volume and rate within the chain, aiding in blockchain analysis and monitoring. # getconnectioncount | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getconnectioncount openapi/bitcoin_node_api/getconnectioncount.json POST /e125e5e68e66f2939f3f120e6f888b3f The getconnectioncount method returns the number of connections the node has to other nodes in the network. Bitcoin via Chainstack. The `getconnectioncount` method returns the number of connections the node has to other nodes in the network. This can be useful for monitoring the node's connectivity and network health. ## Parameters * None ## Response * `result` — an integer representing the current number of connections to other nodes. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getconnectioncount` method is particularly useful for node operators and network administrators to monitor the node's network connectivity. A healthy node should have multiple connections to ensure redundancy and robustness in the network. # getdifficulty | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getdifficulty openapi/bitcoin_node_api/getdifficulty.json POST /e125e5e68e66f2939f3f120e6f888b3f The getdifficulty method retrieves the current network difficulty, which is a measure of how difficult it is to find a new block on the blockchain. The `getdifficulty` method retrieves the current network difficulty, which is a measure of how difficult it is to find a new block on the blockchain. ## Parameters * None ## Response * `result` — a number representing the current network difficulty. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `getdifficulty` method is commonly used to monitor the network's mining difficulty level. Miners and network participants can use this information to gauge the competitiveness of mining on the network. # getmemoryinfo | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getmemoryinfo openapi/bitcoin_node_api/getmemoryinfo.json POST /e125e5e68e66f2939f3f120e6f888b3f The getmemoryinfo method provides detailed information about the memory usage of the node. getmemoryinfo on Bitcoin via Chainstack. The `getmemoryinfo` method provides detailed information about the memory usage of the node. It can return data in two formats: a summary view (`stats`) or a detailed XML format (`mallocinfo`). ## Parameters * `mode` (optional): Specifies the format of the memory information. Can be either `stats` for a summary view or `mallocinfo` for detailed XML format. The default is `stats`. ## Response * `result` — an object containing memory information in the requested format. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getmemoryinfo` method is useful for monitoring the memory usage of the node, helping in diagnosing memory-related issues or for general performance monitoring. # getmempoolancestors | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getmempoolancestors openapi/bitcoin_node_api/getmempoolancestors.json POST /e125e5e68e66f2939f3f120e6f888b3f The getmempoolancestors method retrieves all in-mempool ancestors for a specific transaction in the mempool. Bitcoin via Chainstack. The `getmempoolancestors` method retrieves all in-mempool ancestors for a specific transaction in the mempool. > [!NOTE] > > **Get an unconfirmed transaction hash** > > For the example to work, get an unconfirmed transaction hash from [mempool.space](https://mempool.space/). > [!NOTE] > > **Empty result** > > If there is no pending transaction in the mempool that is an ancestor to the one you are querying, you will get an empty response. ## Parameters * `txid` (required) — the transaction ID for which to retrieve in-mempool ancestors. ## Response * `result` — an array containing all in-mempool ancestor transactions for the specified transaction. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `getmempoolancestors` method is useful for exploring the ancestry of a transaction in the mempool. It provides insights into the chain of transactions that are waiting to be included in a block. # getmempooldescendants | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getmempooldescendants openapi/bitcoin_node_api/getmempooldescendants.json POST /e125e5e68e66f2939f3f120e6f888b3f The getmempooldescendants method retrieves all in-mempool descendants for a specific transaction in the mempool. Bitcoin via Chainstack. The `getmempooldescendants` method retrieves all in-mempool descendants for a specific transaction in the mempool. > [!NOTE] > > **Get an unconfirmed transaction hash** > > For the example to work, get an unconfirmed transaction hash from [mempool.space](https://mempool.space/). > [!NOTE] > > **Empty result** > > If there is no pending transaction in the mempool that is an ancestor to the one you are querying, you will get an empty response. ## Parameters * `txid` (required) — the transaction ID for which to retrieve in-mempool descendants. ## Response * `result` — an array containing all in-mempool descendant transactions for the specified transaction. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `getmempooldescendants` method is useful for exploring the descendants of a transaction in the mempool. It provides insights into the chain of transactions that depend on the specified transaction. # getmempoolentry | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getmempoolentry openapi/bitcoin_node_api/getmempoolentry.json POST /e125e5e68e66f2939f3f120e6f888b3f The getmempoolentry method provides detailed information about a specific transaction in the mempool. Chainstack Bitcoin reference. The `getmempoolentry` method provides detailed information about a specific transaction in the mempool. ## Parameters * `txid` (required) — the transaction ID for which to retrieve detailed information. ## Response * `result` — an object containing detailed information about the specified transaction in the mempool. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `getmempoolentry` method is useful for obtaining specific details about a transaction that is currently in the mempool. This information can include transaction data, fee details, and other relevant information. # getmempoolinfo | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getmempoolinfo openapi/bitcoin_node_api/getmempoolinfo.json POST /e125e5e68e66f2939f3f120e6f888b3f The getmempoolinfo method provides information about the current state of the transaction mempool, including the number of transactions and related details. The `getmempoolinfo` method provides information about the current state of the transaction mempool, including the number of transactions and related details. ## Parameters * None ## Response * `result` — an object containing information about the transaction mempool, such as the number of transactions, size, and other relevant details. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `getmempoolinfo` method is useful for monitoring the status and contents of the transaction mempool. It can help in understanding the volume of pending transactions and the overall state of the network. # getmininginfo | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getmininginfo openapi/bitcoin_node_api/getmininginfo.json POST /e125e5e68e66f2939f3f120e6f888b3f The getmininginfo method provides a variety of mining-related information from the node, such as the current number of blocks, the difficulty. The `getmininginfo` method provides a variety of mining-related information from the node, such as the current number of blocks, the difficulty, the estimated network hash rate, and other important data for miners. ## Parameters * None ## Response * `result` — an object containing various pieces of mining-related information: * `blocks` — the current number of blocks processed by the node. * `difficulty` — the current difficulty target being used by the node. * `networkhashps` — the estimated network hashes per second based on the last n blocks. * `pooledtx` — the number of transactions in the mempool. * `chain` — the current blockchain (e.g., "main", "test", "regtest"). * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getmininginfo` method is crucial for miners and mining pool operators to understand the current state of the network. It provides essential data for making decisions about mining difficulty adjustments and estimating the network's total hash rate. # getnettotals | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getnettotals openapi/bitcoin_node_api/getnettotals.json POST /e125e5e68e66f2939f3f120e6f888b3f The getnettotals method provides an overview of the node's network traffic, including the total amount of data sent and received. On Bitcoin. The `getnettotals` method provides an overview of the node's network traffic, including the total amount of data sent and received. This can be useful for monitoring the node's bandwidth usage. ## Parameters * None ## Response * `result` — an object containing network traffic statistics, such as total bytes sent and received. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getnettotals` method is useful for node operators and network administrators to monitor the bandwidth usage of their node. It helps in understanding the node's contribution to the network and planning for bandwidth capacity. # getnetworkhashps | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getnetworkhashps openapi/bitcoin_node_api/getnetworkhashps.json POST /e125e5e68e66f2939f3f120e6f888b3f The getnetworkhashps method estimates the total hashing power of the network based on the last n blocks. Chainstack Bitcoin reference. The `getnetworkhashps` method estimates the total hashing power of the network based on the last n blocks. It can provide insights into the current or historical network difficulty and security. ## Parameters * `n` (optional): The number of blocks to calculate the average hash rate over. The default is 120 blocks. * `height` (optional): The block height at which to calculate the hash rate. The default is -1, which calculates the hash rate at the current height. ## Response * `result` — a number representing the estimated hashes per second for the network based on the specified number of blocks. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getnetworkhashps` method is useful for understanding the overall security and difficulty of the blockchain network. It helps users and analysts gauge the amount of computational power dedicated to mining and maintaining the network. # getnetworkinfo | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getnetworkinfo openapi/bitcoin_node_api/getnetworkinfo.json POST /e125e5e68e66f2939f3f120e6f888b3f The getnetworkinfo method provides comprehensive information about the node's network connections, including the version, protocol, connected peers. The `getnetworkinfo` method provides comprehensive information about the node's network connections, including the version, protocol, connected peers, and network capabilities. ## Parameters * None ## Response * `result` — an object containing detailed information about the node's network connections, such as the network version, protocol version, number of connections, and available network services. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getnetworkinfo` method is essential for diagnosing network issues, understanding the node's connectivity within the network, and assessing the node's capabilities. It's particularly useful for node operators and developers looking to monitor network health and performance. # getnodeaddresses | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getnodeaddresses openapi/bitcoin_node_api/getnodeaddresses.json POST /e125e5e68e66f2939f3f120e6f888b3f The getnodeaddresses method retrieves known addresses of network peers. Call getnodeaddresses on Bitcoin through Chainstack nodes. The `getnodeaddresses` method retrieves known addresses of network peers. This can be particularly useful for new nodes looking to find peers to connect to for initial blockchain synchronization. ## Parameters * `count` (optional): The number of addresses to return. Specifying 0 will return all known addresses. ## Response * `result` — an array of objects, each representing a known peer address. Each object includes the address, port, services offered by the peer, and the last time the address was seen. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getnodeaddresses` method is useful for bootstrapping or maintaining a healthy list of peers, ensuring a node has multiple avenues for connecting and synchronizing with the network. # getpeerinfo | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getpeerinfo openapi/bitcoin_node_api/getpeerinfo.json POST /e125e5e68e66f2939f3f120e6f888b3f The getpeerinfo method provides detailed information about all peers currently connected to the node. Chainstack Bitcoin reference. The `getpeerinfo` method provides detailed information about all peers currently connected to the node. This includes data such as their IP address, connection type, version, and more. ## Parameters * None ## Response * `result` — an array of objects, each representing a connected peer and containing detailed information about that peer. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getpeerinfo` method is useful for network diagnostics and monitoring, allowing node operators to view detailed information about all peers connected to their node. This can help in assessing the health and diversity of the network connections. # getrawmempool | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getrawmempool openapi/bitcoin_node_api/getrawmempool.json POST /e125e5e68e66f2939f3f120e6f888b3f The getrawmempool method retrieves all transaction IDs currently in the mempool, providing a list of pending transactions. On Bitcoin. The `getrawmempool` method retrieves all transaction IDs currently in the mempool, providing a list of pending transactions. ## Parameters * None ## Response * `result` — an array containing all transaction IDs in the mempool. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `getrawmempool` method is useful for obtaining a snapshot of all pending transactions in the mempool. It can be used to analyze the current state of the network and pending transaction activity. # getrawtransaction | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getrawtransaction openapi/bitcoin_node_api/getrawtransaction.json POST /e125e5e68e66f2939f3f120e6f888b3f The getrawtransaction method retrieves a transaction by its ID. It can return either the hex-encoded transaction data or a detailed. The `getrawtransaction` method retrieves a transaction by its ID. It can return either the hex-encoded transaction data or a detailed, decoded transaction object. ## Parameters * `txid` (required): The transaction ID. * `verbose` (optional): A boolean indicating whether to return a detailed, decoded transaction object instead of the hex-encoded data. Defaults to false. ## Response * `result` — depending on the `verbose` parameter, this is either a string containing the hex-encoded transaction or an object containing the decoded transaction details. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getrawtransaction` method is essential for retrieving transaction data for analysis, debugging, or verification purposes. It allows users to access detailed information about a transaction, including its inputs, outputs, and metadata. # getrpcinfo | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-getrpcinfo openapi/bitcoin_node_api/getrpcinfo.json POST /e125e5e68e66f2939f3f120e6f888b3f The getrpcinfo method returns detailed information about the RPC server, including the currently active commands and the total duration of those commands. The `getrpcinfo` method returns detailed information about the RPC server, including the currently active commands and the total duration of those commands. ## Parameters * None ## Response * `result` — an object containing information about the RPC server, such as active commands and their total duration. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `getrpcinfo` method is useful for monitoring and debugging the RPC server by providing insights into the currently active RPC commands and their execution time. This can help in identifying performance bottlenecks or understanding the server's behavior under load. # gettxout | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-gettxout openapi/bitcoin_node_api/gettxout.json POST /e125e5e68e66f2939f3f120e6f888b3f The gettxout method retrieves information about a specific transaction output in the blockchain. Use it on Bitcoin via Chainstack. The `gettxout` method retrieves information about a specific transaction output in the blockchain. ## Parameters * `transaction ID` (required): The ID of the transaction for which to retrieve the output information. ## Response * `result` — an object containing details about the specified transaction output. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `gettxout` method is useful for querying specific details about a transaction output, such as its value, script, and more. # gettxoutproof | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-gettxoutproof openapi/bitcoin_node_api/gettxoutproof.json POST /e125e5e68e66f2939f3f120e6f888b3f Reference for the gettxoutproof JSON-RPC method on the Bitcoin blockchain via Chainstack nodes. Parameters, response, and examples included. The `gettxoutproof` method provides a merkle branch proof of the inclusion of one or more transactions in a block, proving that a transaction is indeed part of a block without needing the entire block data. ## Parameters * `transaction ID(s)` (required): An array of one or more transaction IDs for which the proof of inclusion is requested. ## Response * `result` — a string representing the merkle branch proof in hexadecimal format. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `gettxoutproof` method is particularly useful for lightweight clients and for applications that need to verify transactions without downloading the entire blockchain. It provides a way to confirm transaction inclusion efficiently. # gettxoutsetinfo | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-gettxoutsetinfo openapi/bitcoin_node_api/gettxoutsetinfo.json POST /e125e5e68e66f2939f3f120e6f888b3f Reference for the gettxoutsetinfo JSON-RPC method on the Bitcoin blockchain via Chainstack nodes. Parameters, response, and examples included. The `gettxoutsetinfo` method provides statistics about the unspent transaction output set, including the total number of unspent outputs and the total value of all unspent outputs. > [!NOTE] > > **Takes a while to complete** > > This method may take a couple of minutes to complete, so you are better off using the`curl`example in your terminal. ## Parameters * None ## Response * `result` — an object containing statistics about the unspent transaction output set, such as the total number of unspent outputs and the total value. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request, used to match requests with responses. ## Use case The `gettxoutsetinfo` method is useful for understanding the current state of the unspent transaction outputs in the blockchain. It provides valuable insights into the distribution and value of unspent outputs. # preciousblock | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-preciousblock openapi/bitcoin_node_api/preciousblock.json POST /e125e5e68e66f2939f3f120e6f888b3f Reference for the preciousblock JSON-RPC method on the Bitcoin blockchain via Chainstack nodes. Parameters, response, and examples included. The `preciousblock` method allows a node to mark a specific block as "precious," meaning the block is treated as if it were received earlier than other blocks with the same amount of work. This can influence the node's choice of the active chain, potentially causing a reorganization to prefer the chain that includes the precious block. ## Parameters * `block hash` (required): The hash of the block to be marked as precious. ## Response * `result` — always `null`, indicating that the block has been successfully marked as precious. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `preciousblock` method is useful in scenarios where a blockchain network participant wants to influence the chain selection process in favor of a particular branch that includes the specified block. This can be relevant in testing, or in situations where network conditions lead to competing chains. # prioritisetransaction | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-prioritisetransaction openapi/bitcoin_node_api/prioritisetransaction.json POST /e125e5e68e66f2939f3f120e6f888b3f Reference for the prioritisetransaction JSON-RPC method on the Bitcoin blockchain via Chainstack nodes. Parameters, response, and examples included. The `prioritisetransaction` method is used to temporarily increase the priority of a transaction in the mempool, making it more likely to be included in a block. This can be useful for transactions that need to be confirmed more quickly. ## Parameters * `txid` (required): The transaction ID of the transaction to prioritise. * `priority delta` (required): The amount by which to increase the transaction's priority. This is a relative increase, not an absolute number. * `fee delta` (required): The amount in satoshis by which to increase the transaction's fee. This can help the transaction get included in a block faster. ## Response * `result` — a boolean indicating whether the transaction priority was successfully increased. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `prioritisetransaction` method is particularly useful in scenarios where a transaction has been submitted with a low fee, and the sender wishes to increase its chance of being included in the next block without resubmitting the transaction with a higher fee. # Bitcoin API Postman collection Source: https://docs.chainstack.com/reference/bitcoin-rpc-methods-postman-collection Here you will have access to a Postman collection to test the Bitcoin API methods using your Bitcoin endpoint deployed with Chainstack. ## What is Postman? [Postman](https://www.postman.com/) is a popular collaboration platform for developers that simplifies the process of building, testing, and documenting APIs. It allows developers to send HTTP requests and receive responses in a user-friendly interface, making it easy to debug and test APIs. This collection allows you to test Bitcoin RPC methods using your Chainstack node conveniently. ## Get a Chainstack Bitcoin endpoint Follow these steps to sign up on Chainstack, deploy a Bitcoin RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Test Bitcoin RPC methods in Postman You can test your Bitcoin endpoint with the following Postman collection. ### Run the collection **Step 1** Click **Run in Postman**. [ ![Run in Postman](https://mintcdn.com/chainstack/4kCLdVG8vhROt3j-/images/reference/button.svg?fit=max\&auto=format\&n=4kCLdVG8vhROt3j-\&q=85\&s=417854aa7ae0a1a626a668cf789d0923)](https://app.getpostman.com/run-collection/26848262-51843be6-aec9-4b04-b0df-46eeb979de1c?action=collection%2Ffork\&collection-url=entityId%3D26848262-51843be6-aec9-4b04-b0df-46eeb979de1c%26entityType%3Dcollection%26workspaceId%3Df1ba12a1-51de-4de6-8a9e-97b26ce685e0) **Step 2** Click **Fork collection** and follow the steps to set it up in your environment. **Step 3** After the collection is set up, select a method to test, for example, `getbestblockhash`. **Step 4** Hover over `{{YOUR_CHAINSTACK_ENDPOINT}}` and click **Add new variable**. **Step 5** Add your Chainstack node endpoint to the **Value** field, and select the collection as the **Scope**. Now your Chainstack endpoint is available for every method. Edit the **Body** parameters if you need and send the request using the **Send** button. # uptime | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-uptime openapi/bitcoin_node_api/uptime.json POST /e125e5e68e66f2939f3f120e6f888b3f Reference for the uptime JSON-RPC method on the Bitcoin blockchain via Chainstack nodes. Parameters, response, and examples included. The `uptime` method returns the total uptime of the server in seconds, providing a simple way to monitor how long the server has been running without interruption. ## Parameters * None ## Response * `result` — an integer representing the server's uptime in seconds. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `uptime` method is useful for monitoring and management purposes, allowing administrators and users to determine how long the server has been operational. This can be particularly useful for assessing system stability and performance over time. # validateaddress | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-validateaddress openapi/bitcoin_node_api/validateaddress.json POST /e125e5e68e66f2939f3f120e6f888b3f The validateaddress method checks if a given Bitcoin address is valid and provides detailed information about it. Bitcoin via Chainstack. The `validateaddress` method checks if a given Bitcoin address is valid and provides detailed information about it. This includes whether the address is valid, its scriptPubKey, if it belongs to the wallet, and more. ## Parameters * `address` (required): The Bitcoin address to validate. ## Response * `result` — an object containing various details about the address, such as its validity, ownership, script type, associated public key, and more. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `validateaddress` method is useful for verifying the validity of a Bitcoin address before sending transactions. It can also provide wallet-related information for addresses, aiding in wallet management and transaction preparation. # verifychain | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-verifychain openapi/bitcoin_node_api/verifychain.json POST /e125e5e68e66f2939f3f120e6f888b3f The verifychain method checks the integrity of the blockchain database on the node. Available on Bitcoin via Chainstack JSON-RPC nodes. The `verifychain` method checks the integrity of the blockchain database on the node. ## Parameters * `check level` (optional): An integer between 0 and 4 that determines the thoroughness of the verification process. The default level is 3. ## Response * `result` — a boolean indicating whether the blockchain is valid. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `verifychain` method is useful for node operators and developers who want to ensure the integrity and consistency of their blockchain data. It can be used as a diagnostic tool to detect issues within the blockchain database. # verifytxoutproof | Bitcoin Source: https://docs.chainstack.com/reference/bitcoin-verifytxoutproof openapi/bitcoin_node_api/verifytxoutproof.json POST /e125e5e68e66f2939f3f120e6f888b3f The verifytxoutproof method validates a proof of inclusion for a transaction in a block, returning the transaction IDs if the proof is valid. The `verifytxoutproof` method validates a proof of inclusion for a transaction in a block, returning the transaction IDs if the proof is valid. > [!NOTE] > > **Get thegettxoutprooffirst** > > Get the `gettxoutproof` first and then use it to verify the proof for this example to work ## Parameters * `proof` (required): The proof of inclusion for a transaction in a block, typically a merkle branch in hexadecimal format. ## Response * `result` — an array of transaction IDs that were included in the proof if it is valid. * `error` — an object containing an error message if an error occurred, otherwise `null`. * `id` — an integer representing the ID of the request. ## Use case The `verifytxoutproof` method is useful for verifying that a particular transaction was included in a block without needing the entire block data. This is particularly useful for lightweight clients and applications that need to verify transaction inclusion efficiently. # Overview Source: https://docs.chainstack.com/reference/blockchain-apis Reference for the Overview JSON-RPC method on the Chainstack blockchain via Chainstack nodes. Parameters, response, and examples included. ## About blockchain APIs A blockchain API is a set of programming interfaces that allow developers to access the functionality of a blockchain from within their applications. It enables external programs to interact with the blockchain, read or write data, and execute smart contracts. A blockchain API allows developers to build applications that can read or write data to the blockchain or that can execute smart contracts (which are self-executing contracts in which the terms of the agreement between buyer and seller are directly written into lines of code). > [!NOTE] > > A developer can use a blockchain API to build a decentralized exchange, a block explorer, or a supply chain management platform. Blockchain APIs are an essential part of the blockchain ecosystem, enabling developers to build and integrate new applications with existing ones. They allow different software to communicate with the blockchain and with each other. ## About this reference ### Node API A collection of articles that explain how the most popular API methods work on the following networks: > [card] [Ethereum](/reference/ethereum-getting-started) > [card] [Ethereum Beacon Chain](/reference/beacon-chain) > [card] [Polygon](/reference/polygon-getting-started) > [card] [BNB Chain](/reference/getting-started-bnb-chain) > [card] [Base](/reference/base-api-reference) > [card] [Avalanche](/reference/avalanche-getting-started) > [card] [Arbitrum](/reference/arbitrum-getting-started) > [card] [zkSync Era](/reference/getting-started-zksync) > [card] [Optimism](/reference/optimism-api-reference) > [card] [Solana](/reference/solana-getting-started) > [card] [Ronin](/reference/getting-started-ronin) > [card] [Gnosis Chain](/reference/gnosis-getting-started) > [card] [Cronos](/reference/getting-started-cronos) > [card] [Fantom](/reference/getting-started-fantom) > [card] [Bitcoin](/reference/bitcoin-api-reference) > [card] [Starknet](/reference/getting-started-starknet) > [card] [Arc](/reference/arc-getting-started) Each method has a detailed explanation, a real use case, and an interactive window to try it out. ### Chainstack platform API The Chainstack platform API allows you to manage: * Your organization * Your projects * Your networks * Your nodes * Your identities To start with the API, you must [create an API key](/reference/platform-api-getting-started#create-api-key). For all available API operations, see [API reference](/reference/platform-api-getting-started). ## Getting started Follow these steps to sign up on Chainstack, deploy a node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Make your first request Chainstack's infrastructure allows you to access and retrieve data from a blockchain using JSON-RPC and the command line. To make manual requests, it's recommended to use tools such as [curl](https://curl.se/) or [Postman](https://www.postman.com/) and send POST requests via JSON-RPC. The following is an example of a curl request that interacts with a Chainstack node's JSON-RPC API. ```curl cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H 'Content-Type: application/json' \ --data '{"method":"eth_blockNumber", "jsonrpc":"2.0", "params":[],"id":1}' ``` The request consists of the following: * `curl -X POST` which is used to initiate a POST request to the specified URL, a Chainstack node in this case; `CHAINSTACK_NODE_URL`. * `-H 'Content-Type: application/json'` specifies that the request's content type is in JSON format. * `--data '{"method":"eth_blockNumber", "jsonrpc":"2.0", "params":[],"id":1}'` is the request's payload, which contains the JSON-RPC request data. The payload of the request contains the following fields: * `method` — defines the method that is being called, in this case, [eth\_blockNumber](/reference/bnb-blocknumber). * `jsonrpc` — defines the version of the JSON-RPC protocol in use; it is set to `2.0`. * `params` — is used to pass any additional parameters required by the method; in this case, it is empty. * `id` — is used to identify the request. It is set to `1` in this case, and you can use `id` to identify the request the response belongs to. This request is asking for the latest block number in the blockchain. The method `eth_blockNumber` will return the number of the most recent block in the chain. Response example: ```shell Shell theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x24655cf" } ``` > [!NOTE] > > Find different ways to interact and retrieve data from the blockchain in the [Web3 libraries and tools guide](/reference/web3-libraries). ### Password-protected access For the blockchain API requests, you can also use basic authentication: * HTTPS endpoint: `https://ethereum-mainnet.core.chainstack.com` * WSS endpoint: `wss://ethereum-mainnet.core.chainstack.com/ws` * Username: `YOUR_USER_NAME` * Password: `YOUR_PASSWORD` > [!NOTE] > > You can find your [username and password credentials](/docs/manage-your-node#view-node-access-and-credentials) in the Chainstack console. For password-protected access, you include the username and password in your `curl` command like so: ```curl cURL theme={"system"} curl -X POST \ -u YOUR_USER_NAME:YOUR_PASSWORD \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}' \ https://ethereum-mainnet.core.chainstack.com ``` In this command, `-u YOUR_USER_NAME:YOUR_PASSWORD` includes your username and password for basic authentication. Please replace `YOUR_PASSWORD` with your actual password. # blockSubscribe | Solana Source: https://docs.chainstack.com/reference/blocksubscribe-solana Subscribe to real-time Solana block updates with blockSubscribe. Receive WebSocket notifications whenever new blocks are confirmed on the network. > [!WARNING] > > **Transaction version 1:** Solana mainnet now produces [version 1 transactions](https://solana.com/upgrades/larger-transaction-sizes). Pass `maxSupportedTransactionVersion: 1` to receive blocks—at a lower value the subscription keeps delivering notifications, but every one of them carries `block: null` and `err: {"UnsupportedTransactionVersion": 1}` instead of block data. > [!WARNING] > > **Important:** The `blockSubscribe` method is marked as unstable and has known limitations with busy programs. Common issues include connection errors (1006, 1009) and skipped data. For production use and comprehensive block tracking, especially with high-traffic programs like Raydium or pump.fun, [Geyser is the recommended alternative](https://github.com/anza-xyz/agave/issues/3879#issuecomment-2519700883). Learn more about [Geyser in our documentation](/docs/yellowstone-grpc-geyser-plugin). ## Parameters * `filter` - An object specifying the type of information to receive: * `mentionsAccountOrProgram` - A public key as a base-58 encoded string. Returns notifications only when the specified account or program ID is part of the transaction. * `slot` - Only return notifications for blocks starting from this slot. * `` - (optional) The level of commitment desired when querying state. Default is "finalized". * `maxSupportedTransactionVersion` - (optional) An integer in the `0`–`255` range declaring the highest transaction version your client can parse. Set it to `1` to receive [version 1 transactions](https://solana.com/upgrades/larger-transaction-sizes), which Solana mainnet now produces. At a lower value, or with the field omitted, each notification arrives with `block: null` and an `err` of `{"UnsupportedTransactionVersion": }`. Pass the integer `1`, not the string `"1"`—a string fails the subscription request with error `-32602`. Subscriptions that set `transactionDetails` to `signatures` or `none` return no transaction bodies and are unaffected. ## Response * `result` - An integer subscription id (needed to unsubscribe) * `subscription` - An object containing block information: * `slot` - The slot number of this block * `blockhash` - The hash of this block as a base-58 encoded string * `previousBlockhash` - The blockhash of this block's parent as a base-58 encoded string * `parentSlot` - The slot number of this block's parent * `transactions` - An array of transaction information: * `transaction` - Transaction object, either in JSON format or base-58 encoded binary data if "encoding" parameter is set to "base58" * `meta` - Transaction status metadata object: * `err` - Error if transaction failed, null if transaction succeeded * `status` - Transaction status, either `{Ok: null}` or `{Err: }` * `fee` - Fee this transaction was charged, as u64 integer * `preBalances` - Array of u64 account balances from before the transaction was processed * `postBalances` - Array of u64 account balances after the transaction was processed * `rewards` - Block reward details (if requested): * `pubkey` - The public key of the account that received the reward, as a base-58 encoded string * `lamports` - The number of lamports rewarded * `postBalance` - The new balance of the account in lamports * `rewardType` - The type of reward: "fee", "rent", "voting", "staking" * `blockTime` - Estimated production time, as Unix timestamp (seconds since the Unix epoch) * `blockHeight` - The number of blocks beneath this block ## `blockSubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat, all theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":"1","method":"blockSubscribe","params":["all",{"encoding":"json","maxSupportedTransactionVersion":1,"transactionDetails":"full","rewards":false}]} ``` ```shell wscat, program id theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":"1","method":"blockSubscribe","params":[{"mentionsAccountOrProgram":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"commitment":"confirmed","encoding":"base64","showRewards":false,"transactionDetails":"full","maxSupportedTransactionVersion":1}]} ``` Examples: * wscat, all — generates a stream of all blocks with full transaction details in the blocks * wscat, program id — generates a stream only of the transactions that involve the [pump.fun program id](https://solscan.io/account/6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P). Use [blockUnsubscribe | Solana](/reference/blockunsubscribe-solana) to remove the subscription. ## Use case The `blockSubscribe` method in Solana is used to subscribe to the network and receive real-time updates on new blocks added to the chain. This can be useful in various real-world scenarios, including: * Decentralized applications (dApps): dApps can use this method to stay synchronized with the latest state of the blockchain. By receiving notifications about new blocks, they can update their internal state and provide users with the most current information. * Block explorers: Block explorer services can utilize this method to receive instant notifications about new blocks, allowing them to update their displays in real-time and provide users with the most up-to-date blockchain information. * Trading bots: Automated trading systems can use the `blockSubscribe` method to receive timely information about new transactions and blocks. This can help in making quick trading decisions based on the latest blockchain activity. * Wallet services: Wallet providers can use this method to monitor the blockchain for transactions involving their users' accounts, enabling them to provide instant notifications and balance updates. * Analytics platforms: Blockchain analytics services can use this subscription to gather real-time data about network activity, transaction volumes, and other metrics to provide insights into the Solana ecosystem. By subscribing to the `blockSubscribe` method, applications can receive instant updates about new blocks, allowing them to react quickly to changes on the Solana blockchain and provide a more responsive user experience. # blockUnsubscribe | Solana Source: https://docs.chainstack.com/reference/blockunsubscribe-solana Cancel a Solana block subscription with blockUnsubscribe. Stop receiving WebSocket notifications for new blocks added to the Solana blockchain. See also [blockSubscribe | Solana](/reference/blocksubscribe-solana). ## Parameters * `integer` - The ID of the subscription that you want to cancel. ## Response * `boolean` - A boolean value indicating if the subscription was removed successfully. `true` if removed successfully, `false` if not. ## `blockUnsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection. You can use a WebSocket client library for your programming language of choice to interact with Solana's WebSocket API. ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"blockUnsubscribe","params":[0]} ``` These examples demonstrate how to use the `blockUnsubscribe` method to remove a subscription matching the ID passed as a parameter. Replace `'YOUR_SOLANA_WEBSOCKET_ENDPOINT'` with your actual Solana WebSocket endpoint URL, and use the correct subscription ID in the `params` array. # eth_blockNumber | BNB Chain Source: https://docs.chainstack.com/reference/bnb-blocknumber openapi/bnb_node_api/eth_blockNumber.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns the latest block number of the blockchain. Call eth_blockNumber on BNB Chain through Chainstack nodes. BNB API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0x69B5B` means that the block is the 432,987th block in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to and encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. > [!NOTE] > > Use the [Chainstack EVM Swiss Knife to convert hexadecimal to decimal](https://web3tools.chainstacklabs.com/hexadecimal-decimal). ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockNumber = async () => { const block_Number = await provider.getBlockNumber(); console.log(block_Number); }; eth_getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.block_number) ``` ## Use case A possible use case for the `eth_blockNumber` method in BNB is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. # web3_clientVersion | BNB Chain Source: https://docs.chainstack.com/reference/bnb-clientversion openapi/bnb_node_api/web3_clientVersion.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns the client type and version running on the BNB node. Available on BNB Chain via Chainstack JSON-RPC nodes. BNB API method that returns the client type and version running on the BNB node. This information can be useful to developers to verify that a node they are connecting to is compatible with their needs. ## Parameters * `none` ## Response * `string` — a string identifying the type of client, version, operating system, and language version running on the node ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const clientVersion = async () => { const version = await provider.send("web3_clientVersion"); console.log(`Client version: ${version}`); }; clientVersion(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) client_version = web3.provider.make_request('web3_clientVersion', []) print(client_version) ``` ## Use case A use case for the `web3_clientVersion` method can be to verify which client is running to then decide which method to run. Let's say you have a DApp that needs to retrieve all of the transaction receipts in a block; the Erigon client has a method for this, `eth_getBlockReceipts`, but with Geth, this method is only available starting from V1.13.0. You can use the `web3_clientVersion` method in your logic to identify which client you are using to see if you have the method available. > [!NOTE] > > Read [Expanding your blockchain horizons: The eth\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) to learn how to build a program to emulate `eth_getBlockReceipts` on any EVM-compatible chain. # Custom JS tracer | BNB Chain Source: https://docs.chainstack.com/reference/bnb-customtracer Reference for the Custom JS tracer JSON-RPC method on the BNB Chain blockchain via Chainstack nodes. Parameters, response, and examples included. > [!NOTE] > > **Available on customized Dedicated Nodes only** > > Custom JavaScript tracers are available as customized solutions on the [Enterprise plan](https://chainstack.com/pricing/) on [Dedicated Nodes](/docs/dedicated-node). In the context of BNB transactions, custom JavaScript tracing is a method used to filter and extract particular data from the comprehensive data produced during a transaction's execution inside the Ethereum Virtual Machine (EVM). Custom JavaScript tracers enable customers to concentrate on specific data points pertinent to their needs rather than reviewing the entire transaction trail, which might be a massive quantity of data. > [!NOTE] > > [Pre-built tracers](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) are also available, but allow for less fine tuning. These JavaScript-written tracers can be constructed using a number of different methods to specify how they interact with transaction data. Users can design unique tracers that meet their needs by giving a JavaScript expression and the tracer option when using one of the tracing methods. ## Developing a custom JavaScript tracer Transaction traces provide complete information about the EVM status during a transaction's execution, but often users are only interested in a subset of this data. JavaScript trace filters can be used to isolate the required information. > [!NOTE] > > Custom JavaScript tracing is available with any of the tracing methods. When specifying the tracer option in one of the tracing methods, JavaScript-based tracing is enabled. The tracer should resolve to an object with result and fault methods. Four additional methods exist: setup, step, enter, and exit. * Setup — invoked once at the beginning of the tracer construction. It accepts a single argument, `config`, which is used to pass options to the tracer. * Step — a function called for each step of the EVM or when an error occurs. It takes two arguments, `log` and `db`, and is used to trace the specified transaction. * Result — a function that returns a JSON-serializable value to the RPC caller. It takes two arguments, `ctx` and `db`. * Fault — a function invoked when an error occurs during opcode execution, which was not reported in step. It takes the same arguments as step: `log` and `db`. * Enter and exit — invoked on stepping in and out of an internal call, respectively. They are used for `CALL` variants, `CREATE` variants, and transfers implied by a `SELFDESTRUCT`. > [!NOTE] > > Find more details in the [Geth documentation](https://geth.ethereum.org/docs/developers/evm-tracing/custom-tracer#custom-javascript-tracing). ## Custom JavaScript tracer example Here you can find a summary of the steps to create a custom JavaScript tracer and code examples: 1. Choose the appropriate RPC method. Depending on what you want to trace, select the appropriate RPC method, such as `debug_traceTransaction()` for individual transactions. 2. Define the tracer object. Create a JavaScript object that defines the behavior of the custom tracer. This object should include methods like setup, step, result, fault, enter, and exit. Each of these methods will determine how the tracer interacts with the transaction data during its execution. The following tracer logs the gas used at each step during the execution of a transaction: ```javascript tracer theme={"system"} { gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {} } ``` 3. Pass the tracer object to the RPC method. Convert the tracer object to a string and pass it as an argument to the chosen RPC method. The tracer object should be defined within single quotes to ensure it is interpreted as a JavaScript expression. For example, using the `debug_traceTransaction()` method with the custom tracer defined above would look like this: ```javascript debug_traceTransaction theme={"system"} debug.traceTransaction('0x1e60e4b78c1097d01226dca6dad808f1de05efd705670a28ae8d57171c91d4aa', { tracer: '{gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {}}' }); ``` ### Run the custom tracer with curl To run a custom JavaScript tracer using curl, you need to make an HTTP POST request to your Ethereum client with the appropriate JSON-RPC payload. Create a JSON object that includes the JSON-RPC version, method, and tracer object (converted to a string).For example, using the previous tracer example for `the debug_traceTransaction` method: ```shell JSON object theme={"system"} { "jsonrpc": "2.0", "id": 1, "method": "debug_traceTransaction", "params": [ "0x19b65f065ba8ebe235f0180bfcbc62ec0e7a043073f9f11f46c5775e898ac763", { "tracer": "{gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {}}" } ] } ``` The full curl request will look like the following: ```shell Custom tracer request theme={"system"} curl --location 'YOUR_CHAINSTACK_NODE_URL' \ --header 'Content-Type: application/json' \ --data '{"jsonrpc":"2.0","id":1,"method":"debug_traceTransaction","params":["0x1e60e4b78c1097d01226dca6dad808f1de05efd705670a28ae8d57171c91d4aa",{"tracer":"{gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {}}" }]}' ``` # eth_estimateGas | BNB Chain Source: https://docs.chainstack.com/reference/bnb-estimategas openapi/bnb_node_api/eth_estimateGas.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns an estimate of gas units needed for a given transaction. Available on BNB Chain via Chainstack JSON-RPC nodes. BNB API method that returns an estimate of gas units needed for a given transaction. It estimates the maximum amount of gas units required to complete the transaction based on the network's current state and the transaction's parameters. A client or wallet can use this estimate to determine the appropriate amount of gas to include in a transaction to ensure the network processes it. ## Parameters * `object` — the transaction call object, same as in `eth_call`; the `from` field is optional, and the `nonce` field is omitted: * `from` — (optional) the address string used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — the estimated amount of gas units needed, represented as a hexadecimal string. ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimateGas = async () => { // The transaction to estimate is a simple transfer in this example. const transactionObj = { from: "0x14E520976c8791d2bBDC01eCBCeB328b5de44Fb9", to: "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8", } const gasUnits = await provider.estimateGas(transactionObj); console.log(`Estimated gas units required: ${gasUnits}`); }; estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.estimate_gas({"from":"0x14E520976c8791d2bBDC01eCBCeB328b5de44Fb9","to":"0xbe0eb53f46cd790cd13851d5eff43d12404d33e8"}, "latest" )) ``` ## Use case A practical use case for `eth_estimateGas` is to calculate the gas cost required for a transaction that transfers a value from one Ethereum address (`fromAddress`) to another (`toAddress`). The gas cost is an important factor in transactions, as it determines the amount of fees that need to be paid by the sender to the network for processing the transaction. # eth_call | BNB Chain Source: https://docs.chainstack.com/reference/bnb-ethcall openapi/bnb_node_api/eth_call.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. BNB API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. This can be useful for testing and debugging by simulating transfers or smart contract transactions and retrieving data from the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > **Disclaimer** > > The interactive example in this page uses `eth_call` to call the `getOwner()` method from the [Cake Token smart contract on BNB Chain](https://bscscan.com/address/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82#code). It returns the address of the owner of the smart contract, in this case, `0x73feaa1ee314f8c655e354234017be2193c9e24e` ## Parameters * `object` — the transaction call object: * `from` — (optional) the address string used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters. See the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` —the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized` —the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending` —the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `data` — the result of executing the specified call on the Ethereum Virtual Machine (EVM), encoded as a hexadecimal string. ## `eth_call` code examples The following code snippets clearly demonstrate how to simulate a standard ether transfer between two accounts. Note that when using `eth_call` to simulate a transfer or a smart contract transaction, the sender account must have a sufficient balance to cover the associated gas fee. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const simulateTransfer = async () => { try { // Define the accounts to transfer between const fromAddress = "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5"; const toAddress = "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"; // Define the value to transfer const value = ethers.parseEther("1"); // Build transaction object const transactionObject = { from: fromAddress, to: toAddress, gasLimit: ethers.toQuantity(21000), value: value } // Use eth_call to simulate the transfer on the latest block const result = await provider.call(transactionObject); console.log(`Result: ${result}`); } catch (error) { console.error(`Error: ${error.message}`); } } simulateTransfer(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the accounts to transfer between from_address = web3.to_checksum_address('0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5') to_address = web3.to_checksum_address('0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a') # Define the value to transfer (in Ether) value = web3.to_wei(1, 'ether') # Build the transaction object transaction = { 'from': from_address, 'to': to_address, 'gas': 21000, # 21000 is the standard for a regular transfer 'value': value, 'data': '', } # Use eth_call to simulate the transfer result = web3.eth.call(transaction, 'latest') print(result) ``` Executing this code will produce a return value of `0x`, indicating a successful transaction. This implies that the transaction will be executed without errors when the code is used to send the transaction. ## Use case A common use case for `eth_call` is calling smart contract functions. For example, interact with an ERC-20 token smart contract deployed on the BNB network and retrieve the balance of a specific account. # eth_getBalance | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getbalance openapi/bnb_node_api/eth_getBalance.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns the balance of a specific Ethereum account in Wei, the smallest unit of ether. BNB Chain via Chainstack. BNB API method that returns the balance of a specific Ethereum account in [Wei](https://www.investopedia.com/terms/w/wei.asp), the smallest unit of ether. This method allows developers to retrieve the current balance of a BNB account for various purposes, such as checking available funds or displaying balance information. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to check the balance of. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block * `pending`— the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — the integer value of the current balance in Wei ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const getBalance = async (address, block) => { const balance = await provider.send("eth_getBalance", [address, block]); console.log(balance); }; getBalance("0x8894E0a0c962CB723c1976a4421c95949bE2D4E3", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" address = web3.to_checksum_address("0x8894E0a0c962CB723c1976a4421c95949bE2D4E3") balance = web3.eth.get_balance(address, "latest") print(balance) ``` ## Use case One practical use case for `eth_getBalance` is to check the balance of an account for a program that scans the balance periodically and fills up the account when the balance drops below a certain value. # eth_getBlockByHash | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getblockbyhash openapi/bnb_node_api/eth_getBlockByHash.json POST /35848e183f3e3303c8cfeacbea831cab BNB Chain API method that returns information about the block matching the given block hash. Available on BNB Chain via Chainstack. BNB Chain API method that returns information about the block matching the given block hash. This method can be useful for analyzing a block and its transactions. ## Parameters * `hash` — the hash of the block * `boolean` — if `true`, it returns the details of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block is pending. * `parenthash` — the previous block's hash used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block is pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the block logs, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot`— the root of the block's receipts trie. This is a 32-byte hash of the root node of the receipts trie for all transactions in the block. It is used to verify the integrity of the receipt data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the network since the inception of the network. It measures the overall security and integrity of the network. * `extradata` — the extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/bnb-gettransactionbyhash) for the exact shape. * `uncles` — the array of uncle hashes. ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByHash = async () => { const blockByNumber = await provider.send("eth_getBlockByHash", ["0x06edbf31479cd7452a1d579ec7990a631068b670de89c06822ceaef58f3729d4",false]); console.log(blockByNumber); }; eth_getBlockByHash(); ``` ```python web3.py theme={"system"} # BNB is a Proof of Authority (POA) chain. Web3.py requires the use of the POA compatibility middleware. from web3 import Web3 from web3.middleware import geth_poa_middleware node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Inject the POA compatibility middleware to the innermost layer. web3.middleware_onion.inject(geth_poa_middleware, layer=0) print(web3.eth.get_block("0x06edbf31479cd7452a1d579ec7990a631068b670de89c06822ceaef58f3729d4", False)) ``` ## Use case The `eth_getBlockByHash` is excellent for analytics purposes. For example, it can be used to quickly find out the hashes of transactions that create new smart contracts. The `transactions` field, an array of all the transactions within the block, is returned in the response. By iterating through this array and examining each transaction's `to` field, a user can determine which transactions in the block are creating new smart contracts. # eth_getBlockByNumber | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getblockbynumber openapi/bnb_node_api/eth_getBlockByNumber.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns information about the block matching the given block number. eth_getBlockByNumber on BNB Chain via Chainstack. BNB API method that returns information about the block matching the given block number. This method can retrieve a wide range of information about the block, such as its hash, timestamp, difficulty, and gas limit. You can also retrieve transaction objects included in the block if needed. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). * `boolean` — if `true`, it returns the details of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — a block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block is pending. * `parenthash`— the previous block's hash used to generate the current block, also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block is pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the block logs, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot`— the root of the block's receipts trie. This is a 32-byte hash of the root node of the receipts trie for all transactions in the block. It is used to verify the integrity of the receipt data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. BNB Smart Chain uses Proof of Staked Authority (PoSA) consensus and this value is not relevant. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/bnb-gettransactionbyhash) for the exact shape. * `uncles` — array of uncle hashes. ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByNumber = async () => { const blockByNumber = await provider.send("eth_getBlockByNumber", ["latest",false]); console.log(blockByNumber); }; eth_getBlockByNumber(); ``` ```python web3.py theme={"system"} # BNB is a Proof of Authority (POA) chain. Web3.py requires the use of the POA compatibility middleware. from web3 import Web3 from web3.middleware import geth_poa_middleware node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Inject the POA compatibility middleware to the innermost layer. web3.middleware_onion.inject(geth_poa_middleware, layer=0) print(web3.eth.get_block("latest", False)) ``` ## Use case The `eth_getBlockByNumber` method can determine whether a block you are analyzing has been included in the main chain or was forked due to a 'reorg'. A block hash is generated through a cryptographic hash function. This function takes the block's data, including transactions and other information, and the hash of the parent block (the previous block) and outputs a unique, fixed-length digest. This hash represents the current block within the blockchain. # eth_getBlockReceipts | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getblockreceipts openapi/bnb_node_api/eth_getBlockReceipts.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that retrieves all transaction receipts for a given block. Reference for eth_getBlockReceipts on BNB Chain via Chainstack. BNB API method that retrieves all transaction receipts for a given block. Transaction receipts contain information about a transaction's execution status and can be useful for monitoring the status of transfers or contract execution on the blockchain. > [!NOTE] > > This method is available on both the Geth and Erigon clients. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require burning at least 1/3 of the staked BNB. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `result` — an array of objects with the following fields: * `Transaction receipt` — the object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the address of the contract created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects generated by this transaction, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getBlockReceipts` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockReceipts = async () => { const BlockReceipts = await provider.send("eth_getBlockReceipts", ["latest"]); console.log(BlockReceipts); }; eth_getBlockReceipts(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block_receipts = web3.make_request('eth_getBlockReceipts', ['latest']) print(block_receipts) ``` ## Use case The `eth_getBlockReceipts` allows you to retrieve the receipts for all transactions in a specific block. A practical use case can be to retrieve all of the logs emitted from the transactions in a block. The method returns an array of transaction receipts, each containing information about the transaction and any logs generated as a result of the transaction execution so that the logs can be isolated. > [!NOTE] > > Read [Uncovering the power of eth\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) to learn more about transactions receipts and how to use the `eth_getBlockReceipts` method. # eth_getBlockTransactionCountByHash | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getblocktransactioncountbyhash openapi/bnb_node_api/eth_getBlockTransactionCountByHash.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. BNB API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. ## Parameters * `hash` — the block hash of the requested block. ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockHash) => { const count = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); console.log(count); }; getTransactionsCount('0xc6dad3e4111ed170b899744f77d3fe2d87f3cc65b4e9bb1028293b7eff40d63e'); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block_transaction_count("0xc6dad3e4111ed170b899744f77d3fe2d87f3cc65b4e9bb1028293b7eff40d63e")) ``` ## Use case `eth_getBlockTransactionCountByHash` is a useful tool for analyzing transaction volume on the BNB blockchain. On average, a new block is generated on the BNB mainnet every 3 seconds, resulting in approximately 1,200 blocks per hour. Using a Web3 library, one can inspect the past 1,200 blocks starting from the latest block, retrieve the hash of each block, and use `eth_getBlockTransactionCountByHash` to find the number of transactions in each block. # eth_getBlockTransactionCountByNumber | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getblocktransactioncountbynumber openapi/bnb_node_api/eth_getBlockTransactionCountByNumber.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. BNB API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` —the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized` —the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending` —the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockId) => { const count = await provider.send("eth_getBlockTransactionCountByNumber", [blockId]); console.log(count); }; getTransactionsCount('latest') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.get_block_transaction_count(37278959)) # A hex value starting with "0x" is accepted as well. ``` ## Use case `eth_getBlockTransactionCountByNumber` can be used to analyze how many transactions are included on the BNB blockchain in a certain period. For instance, on average, a new block is generated on the BNB mainnet every 3 seconds, resulting in approximately 1,200 blocks per hour. Using a Web3 library, one can inspect the past 1,00 blocks starting from the latest block and use `eth_getBlockTransactionCountByNumber` to find the number of transactions in each block to sum them. # eth_chainId | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getchainid openapi/bnb_node_api/eth_chainId.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and verify whether a network is the desired one. BNB API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and verify whether a network is the desired one. It was introduced in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). ## Parameters * `none` ## Response * `quantity` — EIP-155 Chain ID > [!NOTE] > > Use the [Chainstack EVM Knife](https://web3tools.chainstacklabs.com/hexadecimal-decimal) to convert the Chain ID from Hexadecimal to Decimal. ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const chainId = async () => { // This will return the value in Hex const chainId = await provider.send("eth_chainId"); console.log(`Hex Chain ID: ${chainId}`); }; chainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.chain_id) ``` ## Use case One possible use case for the `eth_chainId` method in Ethereum is to determine which chain a user is connected to when using a DApp with the MetaMask browser extension. This can be useful to provide a customized user experience based on the specific chain that the user is interacting with. # eth_getCode | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getcode openapi/bnb_node_api/eth_getCode.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that retrieves the compiled bytecode of a smart contract, providing its address as a parameter. BNB Chain via Chainstack. BNB API method that retrieves the compiled bytecode of a smart contract, providing its address as a parameter. This method returns a hexadecimal string representing the smart contract's bytecode. Developers can use this bytecode to verify whether a smart contract is legitimate and ensure that it performs its intended functions. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response `data` — the compiled bytecode of a smart contract. Returns `0x` if the address is not associated with a smart contract. ## `eth_getCode` code examples The following example retrieves the compiled bytecode from the [CAKE token smart contract](https://bscscan.com/token/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getCode = async (address, block) => { const code = await provider.send("eth_getCode", [address, block]); console.log(code); }; getCode("0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" code = web3.eth.get_code("0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", "latest") print(web3.to_hex(code)) # Convert the Bytes result into HEX. ``` ## Use case A practical use case for the `eth_getcode` method is verifying whether an address is associated with a smart contract. This can be particularly useful for blockchain explorers, auditors, and DApp developers, who must ensure that the account they interact with is legitimate. # eth_getFilterChanges | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getfilterchanges openapi/bnb_node_api/eth_getFilterChanges.json POST /35848e183f3e3303c8cfeacbea831cab BNB API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. BNB API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Filters can be created using one of the following methods: * [eth\_newFilter](/reference/bnb-newfilter) * [eth\_newPendingTransactionFilter](/reference/bnb-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/bnb-newblockfilter) After creating a filter using one of the available methods, the resulting filter ID can be used to fetch changes by calling the `eth_getFilterChanges` method. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/bnb-newfilter) > * [eth\_newPendingTransactionFilter](/reference/bnb-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/bnb-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `string` — the filter ID of the filter for which you want to retrieve the changes ## Response * `array` — an array that represents the changes that have occurred on the blockchain since the last time the filter was polled: * For filters created with `eth_newBlockFilter`: * `blockHash` — the hashes of the new blocks since the last time the filter was polled. * For filters created with `eth_newPendingTransactionFilter`: * `transactionHash` — the hashes identifying new pending transactions since the last time the filter was polled. * For filters created with `eth_newFilter`, the following event logs: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getFilterChanges` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getFilterChanges(filter) { try { const changes = await provider.send('eth_getFilterChanges', [filter]); console.log(changes); // Log the with the new data } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' getFilterChanges(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_filtered_events(filter_id): try: filter_changes = web3.eth.get_filter_changes(filter_id) print(filter_changes) # the array of events that match the filter except Exception as e: print(e) filter_id = '0x0' get_filtered_events(filter_id) ``` ## Use case The `eth_getFilterChanges` method can poll a filter to periodically retrieve new data. You can, for example, create a blocks filter using eth\_newBlockFilter and periodically poll it to get new blocks. # eth_gasPrice | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getgasprice openapi/bnb_node_api/eth_gasPrice.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns the current gas base fee of the network. Chainstack supports eth_gasPrice on BNB Chain JSON-RPC nodes. BNB API method that returns the current gas base fee of the network. The gas price is the quantity of the native token the transaction's sender must pay per unit of gas consumed. The value returned is in Wei. ## Parameters * `none` ## Response * `quantity` — the integer value of the current gas base fee, returned in Wei ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(node_url); async function getGasPrice() { const baseFee = await provider.send("eth_gasPrice"); console.log(`The base gas fee is: ${baseFee} Wei`); } getGasPrice() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.gas_price) ``` ## Use case You can use `eth_gasPrice` to calculate the total gas value to send with a transaction based on the base and priority fee system. Ethereum's London hard fork implemented This concept with EIP-1559. EIP-1559 aimed to solve the problem of network congestion by implementing a dynamic fee market mechanism, which adjusts the fee required to process a transaction based on network demand. With the current system, the total gas price comprises a **base fee** determined by the network's load and a **priority fee** added by the user. > [!NOTE] > > **Note** > > The `eth_gasPrice` method returns the base fee. # eth_getLogs | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getlogs openapi/bnb_node_api/eth_getLogs.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns an array of all logs that match a given filter object. Available on BNB Chain via Chainstack JSON-RPC nodes. BNB API method that returns an array of all logs that match a given filter object. Logs are records of smart contract-generated events on the network and contain valuable information about transactions and smart contract interactions. Some common use cases for `eth_getLogs` include monitoring smart contract events, tracking token transfers, and analyzing blockchain data. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. > * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. > * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. > * `earliest` — the earliest available or genesis block. > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `array` — an array of log objects that match the specified filter or an empty array if there have been no new events since the last poll: * `address` — the contract address from which the event originated * `topics` — an array of 32-byte data fields containing indexed event parameters * `data` — the non-indexed data that was emitted along with the event * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the event index within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getLogs` code examples The following examples retrieve the logs of the Transfer event from the [CAKE token](https://bscscan.com/token/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82) smart contract on Ethereum. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getLogs = async () => { const latestBlock = await provider.getBlockNumber(); const filter = { fromBlock: latestBlock - 10, toBlock: 'latest', address: '0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const logs = await provider.getLogs(filter); console.log(logs); }; getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) latest_block = web3.eth.block_number filter = { 'fromBlock': latest_block - 10, 'toBlock': 'latest', 'address': '0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } logs = web3.eth.get_logs(filter) print(logs) ``` > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about the `eth_getLogs` method. ## Block range limitations While `eth_getLogs` is a powerful tool, it's crucial to understand its limitations, particularly when working with different EVM-compatible chains, as these networks often have different constraints. The `eth_getLogs` method allows you to select a range of blocks from which to get events and is important for exercising proper management. `eth_getLogs` block range is capped by your [subscription plan](/docs/limits#evm-range-limits). On high-activity chains or contracts, query smaller ranges and paginate to avoid oversized responses or timeouts. ## Use case One use case for `eth_getLogs` is to retrieve the transfer events for a specific Token ID from an ERC-721 smart contract. ERC-721 is a token standard for non-fungible tokens (NFTs), and each token transfer is recorded as an event on the blockchain. By retrieving these events, developers can track the movement of NFTs and build applications that interact with them. # eth_getProof | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getproof openapi/bnb_node_api/eth_getProof.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns a Merkle proof for a specific account, contract storage, or both for a given block. BNB Chain via Chainstack. BNB API method that returns a Merkle proof for a specific account, contract storage, or both for a given block. Merkle proofs enable users to verify the existence and authenticity of data within a Merkle trie, a data structure that helps optimize and secure data retrieval in blockchains. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > On Global Nodes, this method returns proofs only for blocks less than 127 behind the tip. An older block returns `-32602` `distance to target block exceeds maximum proof window`. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). ## Parameters * `address` — Ethereum address of the account for which the proof is requested. * `array` — the array of 32-byte storage keys that need to be proven and included. See [eth\_getStorageAt](/reference/ethereum-getstorageat). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `balance` — represents the account balance. Refer to [eth\_getBalance](/reference/ethereum-getbalance). * `codeHash` — indicates the code hash associated with the account. For a simple account without any code, the value will be `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. * `nonce` — refers to the account's nonce. See [eth\_getTransactionCount](/reference/ethereum-gettransactioncount). * `storageHash` — represents the SHA3 hash of the StorageRoot. All storage data will provide a Merkle proof that starts with this `rootHash`. This hash serves as the starting point for obtaining a Merkle proof for all storage entries linked to the account. * `accountProof` — an array of RLP-serialized Merkle trie nodes, starting with the `stateRoot` node and following the path of the SHA3 (address) as the key, which can be used to verify the existence and authenticity of data within the trie. * `storageProof` — contains the requested storage entries in an array, with each entry being an object that includes the following properties: * `key` — the requested storage key * `value`— the value associated with the storage key * `proof` — an array of RLP-serialized Merkle trie nodes, starting with the `storageHash` node and following the path of the SHA3 (key), which can be used to verify the existence and authenticity of data within the trie. ## `eth_getProof` code examples The following examples retrieve the Merkle proof for the USDC contract. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getProof() { // Get the storage slot hash (value = keccak256(storage address + slot index)) const storageSlotHash = '0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c'; // Define the contract address for USDT const contractAddress = '0x8965349fb649A33a30cbFDa057D8eC2C48AbE2A2'; // Get the Merkle proof of USDT's smart contract using a custom JSON-RPC call const proof = await provider.send('eth_getProof', [contractAddress, [storageSlotHash], 'latest']); const block = await provider.send("eth_getBlockByNumber", ["latest",false]); // The state root is crucial for Merkle verification const stateRoot = block.stateRoot; console.log('state root:', stateRoot); console.log(proof); } ``` ```python web3.py theme={"system"} from web3 import Web3 httpUrl = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(httpUrl)) proof = web3.eth.get_proof('0x8965349fb649A33a30cbFDa057D8eC2C48AbE2A2', ["0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c"], "latest") print(proof) ``` > [!NOTE] > > Read [Deep dive into Merkle proofs and eth\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) to learn more about the Merkle trie and how to use `eth_getProof`. ## Use case A use case for the `eth_getProof` method can be when a decentralized application (DApp) interacts with a smart contract on the Ethereum blockchain. The DApp needs to verify the state of the contract's storage variables without downloading the entire blockchain data. By using `eth_getProof`, the DApp can request Merkle proof for specific storage slots in the contract. This proof can then be used to cryptographically verify the authenticity and accuracy of the storage data, ensuring the DApp is working with the correct contract state. # eth_getStorageAt | BNB Chain Source: https://docs.chainstack.com/reference/bnb-getstorageat openapi/bnb_node_api/eth_getStorageAt.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns the data stored at a specific storage slot within a smart contract. Use it on BNB Chain via Chainstack. BNB API method that returns the data stored at a specific storage slot within a smart contract. It can help developers read a smart contract's internal state, like user data or balances, to inform decisions and build more advanced applications that interact with smart contracts on the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity` — the index of the storage position to retrieve the data from * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response `data` — the data stored in the storage slot. Returned as a hexadecimal string. ## `eth_getStorageAt` code examples The following example returns the storage value at slot `0` from the [CAKE Token on the BNB mainnet](https://bscscan.com/token/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82). In this case, this is the address owning the smart contract. This method can retrieve the different storage values in time by querying different blocks in the past. Note that to query a state in the past, you need an [archive node](https://chainstack.com/evm-nodes-a-dive-into-the-full-vs-archive-mode). > [!NOTE] > > cURL needs a `HEX String` starting with `0x` to identify the block if you want to use a hex integer block number as a parameter.\ > For example, block number `14000000` will be `0xD59F80`. Use the [Chainstack EVM Knife](https://web3tools.chainstacklabs.com/hexadecimal-decimal) to make those conversions. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getstorage = async (address, slot, block) => { const storage = await provider.send("eth_getStorageAt", [address, slot, block]); console.log(storage); }; getstorage("0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", "0", "latest" ) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" storage = web3.eth.get_storage_at("0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", 0, "latest") print(web3.to_hex(storage)) ``` ## Use case One use case for `eth_getStorageAt` is analyzing how the value stored at a specific storage slot of a smart contract changes over time. For example, a developer may want to track the change in a specific variable of a smart contract, such as the total supply of a token, and perform some analysis on it. # trace_get | BNB Chain Source: https://docs.chainstack.com/reference/bnb-gettrace openapi/bnb_node_api/trace_get.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method trace_get retrieves a specific trace from a transaction by its index. Available on BNB Chain via Chainstack JSON-RPC nodes. BNB API method `trace_get` retrieves a specific trace from a transaction by its index. This method is useful for developers, auditors, and analysts examining a particular step or operation within a transaction's execution path. It allows for a focused analysis of a segment of the transaction's trace, providing detailed information about a single operation, including calls to other contracts, state changes, and execution outcomes. ## Parameters * `transactionHash` — The transaction hash from which you want to retrieve a trace, specified as a hexadecimal string. * `traceIndex` — An array of integers specifying the trace index to retrieve. The index reflects the order of operations within the transaction's execution path. ## Response * `result` — The trace data for the specified index within the transaction. This includes detailed information about the operation, such as the operation type, call data, value transferred, and the result of the operation. This focused trace can provide insights into specific actions taken by the transaction, including interactions with smart contracts and changes to the blockchain state. ## Use case The \`trace\_getThe method is invaluable for detailed transaction analysis, especially when investigating complex transactions involving multiple steps or interactions with various contracts. Developers can use it to debug specific operations within a transaction, auditors can employ it to verify the correctness and security of particular steps, and analysts might use it to study the behavior of contracts and transactions on a granular level. # eth_getTransactionByBlockHashAndIndex | BNB Chain Source: https://docs.chainstack.com/reference/bnb-gettransactionbyblockhashandindex openapi/bnb_node_api/eth_getTransactionByBlockHashAndIndex.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. BNB API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the BNB blockchain. ## Parameters * `hash` — the hash of the block * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal ## Response * `object` — a transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the transaction's sender is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the transaction sender is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender's wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) with which the transaction plans to interact. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blockHash, index) => { const transaction = await provider.send("eth_getTransactionByBlockHashAndIndex", [blockHash, index]); console.log(transaction); } getTransaction("0x7a2d5056c1aa293e94b19dd6cb4e2893442338586c7c736855490e02c7e627fe", "0x3D"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block("0x7a2d5056c1aa293e94b19dd6cb4e2893442338586c7c736855490e02c7e627fe", 22)) ``` ## Use case The `eth_getTransactionByBlockHashAndIndex` can be used to retrieve transaction details from a block. # eth_getTransactionByBlockNumberAndIndex | BNB Chain Source: https://docs.chainstack.com/reference/bnb-gettransactionbyblocknumberandindex openapi/bnb_node_api/eth_getTransactionByBlockNumberAndIndex.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. BNB API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the BNB blockchain. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked BNB. * `earliest` — the earliest available or genesis block * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal. ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the transaction's sender is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the transaction sender is willing to pay per gas unit. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender's wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blocNumber, index) => { const transaction = await provider.send("eth_getTransactionByBlockNumberAndIndex", [blocNumber, index]); console.log(transaction); } getTransaction("latest", "0x1"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block(37286315, 22)) # Hex encoded parameters starting with "0x" are accepted as well. ``` ## Use case The `eth_getTransactionByBlockNumberAndIndex` can be used to retrieve transaction details from a block; for example, an analytics tool systematically scans each block for transactions by sequentially incrementing the block number and cycling through the transactions within each block using the transaction index. For each transaction, it retrieves detailed information such as the value transferred, the gas price, the sender and receiver addresses, and the input data, which can contain calls to smart contracts. # eth_getTransactionByHash | BNB Chain Source: https://docs.chainstack.com/reference/bnb-gettransactionbyhash openapi/bnb_node_api/eth_getTransactionByHash.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns information about a transaction from its hash. It is used to retrieve information about a specific transaction by its hash. BNB API method that returns information about a transaction from its hash. It is used to retrieve information about a specific transaction by its hash. The method returns an object with details about the transaction, such as the sender, recipient, gas used, transaction value, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the transaction's sender is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the transaction sender is willing to pay per gas unit. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction., usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet; it essentially identifies how many transactions an account has made. It is used to ensure each transaction is executed only once. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. This field is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (transactionHash) => { const transaction = await provider.send("eth_getTransactionByHash", [transactionHash]); console.log(transaction); }; getTransaction("0x4cdc1e231274ffd15a30384eab7f45c9e42cd5aaa79d874f1cd23c52dfabe4b8") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction("0x4cdc1e231274ffd15a30384eab7f45c9e42cd5aaa79d874f1cd23c52dfabe4b8")) ``` ## Use case One use case for `eth_getTransactionByHash` is to inspect a specific transaction and display information about it, such as the address sent, the block it was included in, and the number of transactions the sender sent so far (nonce). This information can be useful for monitoring and analyzing transactions. # eth_getTransactionCount | BNB Chain Source: https://docs.chainstack.com/reference/bnb-gettransactioncount openapi/bnb_node_api/eth_getTransactionCount.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns the number of transactions sent from an address at the selected block. Chainstack BNB Chain reference. BNB API method that returns the number of transactions sent from an address at the selected block. This value is also called `nonce`; it is an important piece of information, especially to ensure that a transaction is not sent twice. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`— the address to retrieve the transaction count. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — an integer value identifying the number of transactions sent from an address at the specified block. ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getNonce = async (address, blockId) => { const nonce = await provider.send("eth_getTransactionCount", [address, blockId]); console.log(nonce); }; getNonce("0x2D4C407BBe49438ED859fe965b140dcF1aaB71a9", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_count("0x2D4C407BBe49438ED859fe965b140dcF1aaB71a9", "latest")) ``` ## Use case One of the most common use cases for `eth_getTransactionCount`is to create the transaction object built in a script designed to send a transaction. The nonce field is required, and it is retrieved using the `eth_getTransactionCount` method. # eth_getTransactionReceipt | BNB Chain Source: https://docs.chainstack.com/reference/bnb-gettransactionreceipt openapi/bnb_node_api/eth_getTransactionReceipt.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that retrieves a transaction's receipt from a transaction hash. It can be used to verify a transaction's success or failure. > [!NOTE] > > **No response in the interactive API** > > The response to the call might be too big for the web app to handle, so if you are not getting it here, copy the CURL example and run in your terminal—this will work. BNB API method that retrieves a transaction's receipt from a transaction hash. It can be used to verify a transaction's success or failure, check a smart contract's execution status, retrieve logs, check if a transaction deployed a smart contract and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `Transaction receipt` — the receipt object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the contract address created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects this transaction generates, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionReceipt = async (transactionHash) => { const receipt = await provider.send("eth_getTransactionReceipt", [transactionHash]); console.log(receipt); }; getTransactionReceipt('0x4cdc1e231274ffd15a30384eab7f45c9e42cd5aaa79d874f1cd23c52dfabe4b8') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_receipt("0x4cdc1e231274ffd15a30384eab7f45c9e42cd5aaa79d874f1cd23c52dfabe4b8")) ``` ## Use case A practical use case for `eth_getTransactionReceipt` is to verify a transaction's status and whether it was successful. This can be especially useful when deploying a contract, as you can use the receipt to confirm that the contract was deployed successfully and has a valid contract address. # eth_maxPriorityFeePerGas | BNB Chain Source: https://docs.chainstack.com/reference/bnb-maxpriorityfeepergas openapi/bnb_node_api/eth_maxPriorityFeePerGas.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns an estimate of the current priority fee per gas. This fee is the tip that users may pay miners,post-merge. BNB API method that returns an estimate of the current priority fee per gas. This fee is the tip that users may pay miners,post-merge, to validators on top of the base fee to incentivize the inclusion of their transactions in a block. This method helps users adjust their transaction fees in a dynamic market, ensuring transactions are processed promptly without overpaying. ## Parameters * `none` ## Response * `quantity` — the estimated priority fee per gas needed, represented as a hexadecimal string. ## `eth_maxPriorityFeePerGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimatePriorityFee = async () => { const priorityFeePerGas = await provider.getFeeData(); console.log(`Estimated priority fee per gas: ${priorityFeePerGas.maxPriorityFeePerGas}`); }; estimatePriorityFee(); ``` ```python web3.py theme={"system"} from web3 import Web3 NODE_URL = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(NODE_URL)) eth_max_priority_fee_per_gas = web3.provider.make_request('eth_maxPriorityFeePerGas', []) print(eth_max_priority_fee_per_gas) ``` ## Use Case A practical use case for `eth_maxPriorityFeePerGas` using `ethers.js` is to calculate transaction fees dynamically for applications and wallets to accurately estimate the cost of transactions, especially during periods of high network congestion. This method ensures transactions are confirmed promptly without overpaying. By fetching the current priority fee (`maxPriorityFeePerGas`) and the maximum fee per gas (`maxFeePerGas`), applications can dynamically calculate the total transaction fee required for prompt processing under the current network conditions. # net_listening | BNB Chain Source: https://docs.chainstack.com/reference/bnb-netlistening openapi/bnb_node_api/net_listening.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns a boolean value indicating whether the client is currently listening for network connections or not. On BNB Chain. BNB API method that returns a boolean value indicating whether the client is currently listening for network connections or not. The `net_listening` method can be useful in many scenarios where it is important to monitor the status of an Ethereum client's network connection to ensure it works properly. ## Parameters * `none` ## Response * `boolean` — a boolean value that indicates whether a node is actively seeking peer connections. `true` if the client is actively listening for connections, `false` if not. ## `net_listening` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const isListening = async () => { const listen = await provider.send("net_listening"); console.log(listen); } isListening() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) is_listening = web3.provider.make_request('net_listening', []) print(is_listening) ``` ## Use case A method like `net_listening` can be used to monitor the network status of the BNB node a developer connects to. Imagine a blockchain explorer where users can search for transactions, addresses, and other information on the Ethereum blockchain. To provide accurate information to its users, the website needs to be able to monitor the current status of its connection. # eth_newBlockFilter | BNB Chain Source: https://docs.chainstack.com/reference/bnb-newblockfilter openapi/bnb_node_api/eth_newBlockFilter.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that creates a filter that watches for new blocks on the blockchain. eth_newBlockFilter on BNB Chain via Chainstack. BNB API method that creates a filter that watches for new blocks on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/bnb-getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real-time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter. The filters created are stored on the blockchain client instance. The filter fill is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/bnb-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/bnb-uninstallfilter) to remove the filter ## `eth_newBlockFilter` code examples > [!NOTE] > > **web3.eth.filterdeprecation** > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newBlockFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') return blocks_filter except Exception as e: print(e) blocks = get_new_blocks() filter_id = blocks.filter_id print(filter_id) ``` ## Use case One use case for `eth_newBlockFilter` in a simple DApp is to listen for new blocks and update the user interface with the latest block information. When the DApp starts, it creates a new filter using `eth_newBlockFilter` to listen for new blocks. When a new block is added to the blockchain, the filter is triggered, and the DApp retrieves the latest block information using a Web3 library. # eth_newFilter | BNB Chain Source: https://docs.chainstack.com/reference/bnb-newfilter openapi/bnb_node_api/eth_newFilter.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that generates a filter object based on the filter parameters. Available on BNB Chain via Chainstack JSON-RPC nodes. BNB API method that generates a filter object based on the filter parameters. It returns a filter ID, which can be used to retrieve the filter results using the [eth\_getFilterChanges](/reference/bnb-getfilterchanges) method. By creating a filter for specific events, developers can receive notifications when those events occur and use them to trigger actions in their applications. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. > * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. > * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. > * `earliest` — the earliest available or genesis block. > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `result` — a hexadecimal string representing the ID of the newly created filter > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/bnb-getfilterchanges) to retrieve updated logs * [eth\_uninstallFilter](/reference/bnb-uninstallfilter) to remove the filter ## `eth_newFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const createFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) filter = { 'toBlock': 'latest', 'address': '0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } def create_logs_filter(filter_object): try: logs_filter = web3.eth.filter(filter_object) return logs_filter except Exception as e: print(e) logs_filter = create_logs_filter(filter) filter_id = logs_filter.filter_id print(f'New filter ID: {filter_id}') ``` ## Use case You can use `eth_newFilter` to create a filter for a specific action on a smart contract, for example, to monitor the transfer transactions from the [Wrapped BNB token](https://bscscan.com/token/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c). The idea is to create a filter using the `eth_newFilter` method to monitor an ERC-20 smart contract, WBNB in this case. # eth_newPendingTransactionFilter | BNB Chain Source: https://docs.chainstack.com/reference/bnb-newpendingtransactionfilter openapi/bnb_node_api/eth_newPendingTransactionFilter.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that creates a filter that listens for new pending transactions on the blockchain. Use it on BNB Chain via Chainstack. BNB API method that creates a filter that listens for new pending transactions on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/bnb-getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real-time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter ## Code examples > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/bnb-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/bnb-uninstallfilter) to remove the filter ## `eth_newPendingTransactionFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newPendingTransactionFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_pending_transactions(): try: blocks_filter = web3.eth.filter('pending') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) new_filter = get_new_pending_transactions() print(new_filter) ``` ## Use case One way to use the `eth_newPendingTransactionFilter` method is to listen for new pending transactions at predefined intervals and extract specific data from them. For instance, a decentralized application might check for pending transactions every second and identify those that transfer a value greater than a certain amount of the ETH token. This could be useful for real-time monitoring high-value transactions or detecting potential fraud or security threats. # net_peerCount | BNB Chain Source: https://docs.chainstack.com/reference/bnb-peercount openapi/bnb_node_api/net_peerCount.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns the number of peers connected to the node. BNB Chain developers can call net_peerCount via Chainstack. BNB API method that returns the number of peers connected to the node. This method can be useful for developers who want to monitor their node's connectivity and ensure that it is functioning as expected. By checking the number of connected peers, developers can verify that their node receives updates and is properly synchronized with the network. ## Parameters * `none` ## Response * `quantity` — an integer value indicating the current number of peers connected to the node ## `net_peerCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const peersCount = async () => { const peers = await provider.send("net_peerCount"); console.log(`Peers connected: ${peers}`); } peersCount() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) peers = web3.provider.make_request('net_peerCount', []) print(peers) ``` ## Use case One practical use case for the `net_peerCount` method is to monitor the number of peers connected to a node and take some action if the number drops below a certain threshold. For example, a developer might want to send a notification to check the node if the peer count is low for more than 10 minutes. # trace_replayBlockTransactions Source: https://docs.chainstack.com/reference/bnb-replayblocktransactions openapi/bnb_node_api/trace_replayBlockTransactions.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method trace_replayBlockTransactions replays transactions in a specified block individually, returning the trace data for each transaction. BNB API method `trace_replayBlockTransactions` replays transactions in a specified block individually, returning the trace data for each transaction. This method is invaluable for developers, auditors, and analysts who wish to inspect the execution path of transactions within a block, including calls to other contracts, state changes, and execution outcomes, without affecting the live blockchain. ## Parameters * `blockHash` or `blockNumber` — The hash or number of the block whose transactions you want to replay, specified as: * `Hexadecimal` — for the block hash. * `Hexadecimal` or `decimal number` — for the block number. * `traceTypes` — An array of strings specifying the types of traces to return for each transaction, with options including: * `vmTrace` — virtual machine trace. * `trace` — standard trace. * `stateDiff` — state difference. ## Response * `result` — An array containing the trace data for each transaction in the specified block. Each item in the array represents the trace of a single transaction, detailing calls to contracts, value transfers, and other relevant state changes that occurred during its execution. ## Use case The `trace_replayBlockTransactions` method is particularly useful for conducting detailed analyses of block transactions. Developers can use it to debug contract interactions within a specific block, auditors can use it to verify the correctness and security of transactions, and analysts might use it to study transaction patterns or detect anomalies. This method provides a granular view of transaction execution, which is essential for understanding complex interactions on the blockchain. # trace_replayTransaction | BNB Chain Source: https://docs.chainstack.com/reference/bnb-replaytransaction openapi/bnb_node_api/trace_replayTransaction.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method trace_replayTransaction replays a single transaction using the trace module, returning detailed trace data about its execution. BNB API method `trace_replayTransaction` replays a single transaction using the trace module, returning detailed trace data about its execution. This method is crucial for developers, auditors, and analysts who need to understand the precise execution flow of a transaction, including calls made to other contracts, state changes, and the execution outcome. ## Parameters * `transactionHash` — the hash of the transaction you want to replay, specified as a hexadecimal string. * `traceTypes` — an array of strings specifying the types of traces to return, with options including: * `vmTrace` — virtual machine trace. * `trace` — standard execution trace. * `stateDiff` — state difference trace. ## Response * `result` — The trace data for the specified transaction. Depending on the `traceTypes` requested, this may include a detailed execution trace (`vmTrace`), a step-by-step list of executed operations (`trace`), or a summary of state changes (`stateDiff`). ## Use case The `trace_replayTransaction` method is invaluable for a detailed analysis of individual transactions. It can be used by developers to debug smart contract interactions, by auditors to verify transaction integrity and security, and by analysts to understand the effects of a transaction on the blockchain state. This method provides a deep dive into the transaction's execution, offering insights not readily available through standard transaction receipts. # eth_sendRawTransaction | BNB Chain Source: https://docs.chainstack.com/reference/bnb-sendrawtransaction openapi/bnb_node_api/eth_sendRawTransaction.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that allows submitting a signed transaction to the network. Reference for eth_sendRawTransaction on BNB Chain via Chainstack. BNB API method that allows submitting a signed transaction to the network. Once a transaction is signed, you can use the `eth_sendRawTransaction` method to submit the signed transaction to the Ethereum network for processing. > [!WARNING] > > Note that the interactive example in this page will not work, due to the fact that `eth_sendRawTransaction` is used to modify the state of the blockchain, it is not possible to duplicate the same request. ## Parameters * `data`—the signed transaction. The serialized transaction data includes transaction metadata such as the nonce, gas price, gas limit, recipient address, and data payload, as well as the digital signature generated using the private key associated with the address that is sending the transaction. Typically, the transaction is signed with a library such as web3.py, web3.js, or ethers.js using the sender's private key. ## Response * `result` — the unique hash identifying the transaction or the zero hash if the transaction is unavailable. The zero hash is a special value representing an invalid or null hash. ## `eth_sendRawTransaction` code examples The following examples demonstrate using Web3 libraries to transfer an ETH on the testnet. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function sendEth(value) { // Define the sender and receiver addresses, and the private key const sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a'; const receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114'; const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Define the gas limit const gasLimit = await provider.estimateGas({ from: sender, to: receiver, value: ethers.parseEther(value), }); // Get the transaction count for the sender address const nonce = await provider.getTransactionCount(sender); // Define the transaction object const transactionObject = { to: receiver, gasPrice: ethers.parseUnits('50', 'gwei'), gasLimit: gasLimit.toString(), nonce: nonce, value: ethers.parseEther(value), }; // Sign the transaction using the private key const wallet = new ethers.Wallet(privateKey, provider); console.log(`Sending transaction...`) const transaction = await wallet.sendTransaction(transactionObject); console.log('Transaction hash:', transaction.hash); } sendEth("2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the sender and receiver addresses, and the private key sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a' receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114' private_key = '1111111111111111111111111111111111111111111111111111111111111111' # Estimate gas limit gas_limit = web3.eth.estimate_gas({ 'from': sender, 'to': receiver }) # Build the transaction object transaction = { 'nonce': web3.eth.get_transaction_count(sender), 'to': receiver, 'value': web3.to_wei(1, 'ether'), # value to send 'gas': gas_limit, 'gasPrice': web3.eth.gas_price, 'chainId': web3.eth.chain_id } # Sign the transaction signed_tx = web3.eth.account.sign_transaction(transaction, private_key) print(f'Signed transaction: {signed_tx}') # Send the transaction print('Sending transaction...') tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) print(f'Transaction hash: {web3.toHex(tx_hash)}') ``` > [!NOTE] > > Note that you will need to edit the `amountToSend`, `recipientAddress`, and `secretKey` fields to be able to use this code. ## Use case One potential use case for `eth_sendRawTransaction` is to automatically swap BNB for a token when its price reaches a certain level. `eth_sendRawTransaction` will allow custom transaction parameters, such as gas price or gas limit, to be specified to optimize the speed and cost of the transaction. For instance, you could build a program monitoring the price of a specific token and execute a `swapExactETHForTokens` transaction when the token reaches a certain price level, it could use `eth_sendRawTransaction` to submit a raw transaction with a custom gas price and limit optimized for the current network conditions. # eth_syncing | BNB Chain Source: https://docs.chainstack.com/reference/bnb-syncing openapi/bnb_node_api/eth_syncing.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns an object with information about the current synchronization status of the node, or false if the node is fully synced. BNB API method that returns an object with information about the current synchronization status of the node, or `false` if the node is fully synced. This method can track the progress of a node's synchronization with the Ethereum blockchain. The returned object contains data such as the starting block, current block, and highest block of the node, allowing developers to monitor and estimate the time remaining for the node to sync fully. ## Parameters * `none` ## Response * `result` — the boolean value `false` when not syncing or a JSON object when syncing: * `startingBlock` — the block number where the synchronization process started, encoded as hexadecimal. * `currentBlock` — the block number that the node has currently processed, same as `eth_blockNumber`, encoded as hexadecimal. * `highestBlock` — the block number of the latest block in the blockchain known to the node. ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const syncStatus = async () => { const status = await provider.send("eth_syncing", []); console.log(status); }; syncStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" print (web3.eth.syncing) ``` ## Use case The `eth_syncing` method on Ethereum can be useful for developers building applications that interact with the Ethereum blockchain. For example, you may want to provide feedback to your application's user about the status of the Ethereum node the application is connecting to. # trace_call | BNB Chain Source: https://docs.chainstack.com/reference/bnb-trace-call openapi/bnb_node_api/trace_call.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method trace_call allows for executing a call transaction and returns several possible traces for it. BNB Chain via Chainstack. BNB API method `trace_call` allows for executing a call transaction and returns several possible traces for it. This method simulates transactions without broadcasting them to the network, allowing developers to test and debug their transactions in a controlled environment. ## Parameters * `transaction` — the transaction to be executed, specified as an object with fields including: * `from` — the address sending the transaction. * `to` — the address of the transaction recipient. * `gas` — the amount of gas allocated for the transaction. * `gasPrice` — the price per unit of gas. * `value` — the amount of ether to send. * `data` — the data to be sent with the transaction. * `traceTypes` — an array of strings specifying the types of traces to return, with options including: * `vmTrace` — virtual machine trace. * `trace` — standard execution trace. * `stateDiff` — state difference trace. * `blockNumber` — the block number against which the transaction should be executed, with values: * `specific block number` — execute against the specified block. * `earliest` — the earliest block (usually genesis). * `latest` — the most recent block. * `pending` — the current pending state and transactions. ## Response * `result` — Depending on the requested `traceTypes`, the result may include various forms of trace data such as VM operation trace, state changes, and executed transaction trace. This data is crucial for understanding how a transaction interacts with the EVM and alters the blockchain state. ## Use case A possible use case for the `trace_call` method in BNB is for smart contract developers who must debug complex interactions with the Ethereum Virtual Machine (EVM). Developers can identify and fix issues by simulating transactions before deploying contracts or sending transactions on the live network. It can also be used to estimate gas usage more accurately or verify that a transaction will behave as expected without actually making a state change on the blockchain. # trace_block | BNB Chain Source: https://docs.chainstack.com/reference/bnb-traceblock openapi/bnb_node_api/trace_block.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns traces for all the transactions within a specific block. Available on BNB Chain via Chainstack JSON-RPC nodes. BNB API method that returns traces for all the transactions within a specific block. Developers can use the `trace_block` method to gain insight into the behavior of smart contracts within a block, analyze gas usage, and optimize their contracts accordingly. This method is only available on an Erigon instance. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. ## Response * `result` — an object containing the traces of all of the transactions in the block: * `action` — an object that describes the action taken by the transaction: * `from` — the address of the sender who initiated the transaction. * `callType` — the type of call, `call` or `delegatecall`, two ways to invoke a function in a smart contract. `call` creates a new environment for the function to work in, so changes made in that function won't affect the environment where the function was called. `delegatecall` doesn't create a new environment. Instead, it runs the function within the environment of the caller, so changes made in that function will affect the caller's environment. * `gas` — the units of gas included in the transaction by the sender. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is null. * `value` — the value of the native token transferred along with the transaction, in Wei. * `blockHash` — the hash of the block in which the transaction was included. * `blockNumber` — the number of the block in which the transaction was included. * `error` — a string that indicates whether the transaction was successful or not. `null` if successful, `Reverted` if not. * `result` — an object that contains additional data about the execution of the transaction: * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `output` — the return value of the call, encoded as a hexadecimal string. * `subtraces` — the number of sub-traces created during execution. When a transaction is executed on the EVM, it may trigger additional sub-executions, such as when a smart contract calls another smart contract or when an external account is accessed. * `traceAddress` — an array that indicates the position of the transaction in the trace. * `transactionHash` — the hash of the transaction. * `transactionPosition` — the position of the transaction in the block. * `type` — the type of action taken by the transaction, `call` or `create`. `call` is the most common type of trace and occurs when a smart contract invokes another contract's function. `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain. ## `trace_block` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlock = async (block) => { const traces = await provider.send("trace_block", [block]); console.log(traces); }; traceBlock("latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block = "latest" traces = web3.provider.make_request('trace_block', [block]) print(traces) ``` ## Use case A practical use case for the `trace_block` method on BNB Smart Chain can be to analyze the most recent block on the network. For example, developers can gain valuable insights into the block's behavior by examining the amount of gas supplied by all the transactions and the actual amount of gas utilized. This can be the first step to developing a chain explorer. # debug_traceBlockByHash | BNB Chain Source: https://docs.chainstack.com/reference/bnb-traceblockbyhash openapi/bnb_node_api/debug_traceBlockByHash.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that traces the execution of a block. This method can be used to debug and analyze smart contracts and transactions on the BNB blockchain. BNB API method that traces the execution of a block. This method can be used to debug and analyze smart contracts and transactions on the BNB blockchain. It provides a detailed trace of the execution of a block, including information on all the transactions and calls that interacted with the block, as well as the gas used, memory, storage operations, and other performance metrics for each operation. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `hash` — the hash of the block to be traced. * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call. Encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ## `debug_traceBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByHash = async (blockHash) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer const tracer = { tracer: '4byteTracer' }; const traces = await provider.send("debug_traceBlockByHash", [blockHash, tracer]); console.log(traces); }; traceBlockByHash("0x0e77e2cf5a6999f616b56f1378b6c7b127b5a4e20cb2e12611d808af0979dacf") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block_hash = "0x0e77e2cf5a6999f616b56f1378b6c7b127b5a4e20cb2e12611d808af0979dacf" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer tracer = { "tracer": '4byteTracer' } block_traces = web3.provider.make_request('debug_traceBlockByHash', [block_hash, tracer]) print(block_traces) ``` ## Use case One practical use case for `debug_traceBlockByHash` with the `4byteTracer` tracer would be for auditing or analysis purposes of a blockchain. For example, if there are token contracts on the blockchain and the transfer function of these contracts is used to send tokens between users, a developer could use the `debug_traceBlockByHash` method with the `4byteTracer` tracer to trace the transactions in a given block and count how many times the transfer function was called. # debug_traceBlockByNumber | BNB Chain Source: https://docs.chainstack.com/reference/bnb-traceblockbynumber openapi/bnb_node_api/debug_traceBlockByNumber.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that enables the tracing of the execution of a specific block using its number. Use it on BNB Chain via Chainstack. BNB API method that enables the tracing of the execution of a specific block using its number. This method can be used to troubleshoot and analyze smart contracts and transactions on the BNB blockchain. It provides an in-depth trace of the block execution, with details on all the interactions, such as transactions and calls, that took place. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ## `debug_traceBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByNumber = async (block) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer const tracer = { tracer: 'callTracer' }; const traces = await provider.send("debug_traceBlockByNumber", [block, tracer]); console.log(traces); }; traceBlockByNumber("latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block = "latest" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer tracer = { "tracer": 'callTracer' } block_traces = web3.provider.make_request('debug_traceBlockByNumber', [block, tracer]) print(block_traces) ``` ## Use case One use case for `traceBlockByNumber` would be to trace a block, calculate how many transactions are in it, and then display the number of transactions and the average gas used. To accomplish this, you can use `traceBlockByNumber` to retrieve the trace of each transaction in the block and then iterate over the transactions to count the total number of transactions and calculate the total gas used. Once you have this information, you can divide the total gas used by the total number of transactions to get the average gas used. # debug_traceCall | BNB Chain Source: https://docs.chainstack.com/reference/bnb-tracecall openapi/bnb_node_api/debug_traceCall.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that traces the execution of eth_call within the context of a specific block's execution. BNB Chain via Chainstack. BNB API method that traces the execution of [eth\_call](/reference/bnb-ethcall) within the context of a specific block's execution. This method uses the final state of the parent block as its base and allows developers to trace the execution of a particular call. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `object` — the transaction call object: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) in the debug and trace overview. You can also use additional configuration parameters. The following settings are available: * `disableStorage` — when enabled, it prevents storage changes from being traced by the transaction being analyzed, which can reduce the resource requirements of the analysis. By default, `debug_traceTransaction` traces both memory and storage changes, but storage tracing can be particularly resource-intensive, especially for large transactions. * `disableStack` — when enabled, it skips tracing of [stack](https://en.wikipedia.org/wiki/Stack_\(abstract_data_type\)) changes made by the transaction being analyzed. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableMemory` — when `false`, it prevents tracing of memory changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. This can help optimize performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableMemory`. * `enableReturnData` — when `false`, it prevents the method from tracing the return data produced by the analyzed transaction. The return data tracing can be particularly resource-intensive, requiring significant time and processing power to complete. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableReturnData`. * `timeout` — setting that allows developers to customize the method's timeout period for JavaScript-based tracing calls. The default timeout is `5s`, and you can find the values formats in the [Go documentation](https://pkg.go.dev/time#ParseDuration). > [!NOTE] > > It's worth noting that when using one of the three built-in tracers in Ethereum clients, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData`settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the [Struct/opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#structopcode-logger). ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction., usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract, if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ### `Struct/opcode logger` response * `object` — the `Struct/opcode logger` traces object: * `pc` — the current program counter of the transaction, representing the location of the current opcode being executed. A register that keeps track of the address of the next instruction to be executed in a program. * `op` — the current [opcode](https://ethereum.org/en/developers/docs/evm/opcodes/) being executed, such as PUSH1, ADD, or SWAP1. A code that represents a specific operation or instruction that a computer or processor can understand and execute. * `gas` — the amount of gas remaining in the transaction at the current step of execution. * `gasCost` — the amount of gas consumed by the current opcode being executed. * `depth` — the current depth of the call stack for the transaction, reflecting the number of active frames representing nested function calls. * `stack` — the contents of the EVM stack at the current step of the transaction's execution. The stack is a data structure used by the EVM to manage and manipulate data during the execution of opcodes * `memory` — the contents of the memory at the current step of execution. A temporary data storage location that is available to smart contracts during execution. ## `debug_traceCall` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const traceCall = async () => { const call = { from: '0x75793540CFe0F7e4643EcEf704FFaD5FB0574240', to: '0xc2edad668740f1aa35e4d8f227fb8e17dca888cd', gas: '0x1E9EF', gasPrice: '0x72AC5BA54E', data: '0x441a3e70000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000c6c3eca729cb9e' } const block = 'latest' // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. const tracer = {}; const traces = await provider.send("debug_traceCall", [call, block, tracer]); console.log(traces); }; traceCall() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) call = { 'from': '0x75793540CFe0F7e4643EcEf704FFaD5FB0574240', 'to': '0xc2edad668740f1aa35e4d8f227fb8e17dca888cd', 'gas': '0x1E9EF', 'gasPrice': '0x72AC5BA54E', 'data': '0x441a3e70000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000c6c3eca729cb9e' } block = 'latest' # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. tracer = { "tracer": 'prestateTracer' } tx_traces = web3.provider.make_request('debug_traceCall', [call, block, tracer]) print(tx_traces) ``` ## Use case A practical use case for the `debug_traceCall` method is to inspect the internal transactions during a smart contract call. For example, a developer can examine a past transaction using the `callTracer` tracer where an account used a proxy smart contract to deploy a new instance using the `CREATE2` method. > [!NOTE] > > Read [Deploying a deterministic smart contract on Ethereum](https://chainstack.com/deploying-a-deterministic-contract-on-ethereum/) to learn more about the `CREATE2` method and how to deploy a smart contract to a predetermined address. # trace_callMany | BNB Chain Source: https://docs.chainstack.com/reference/bnb-tracecallmany openapi/bnb_node_api/trace_callMany.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method trace_callMany allows for multiple call transactions and returns their trace data. Use it on BNB Chain via Chainstack. BNB API method `trace_callMany` allows for multiple call transactions and returns their trace data. This method is particularly useful for developers who wish to simulate the execution of several transactions in sequence without broadcasting them to the network, enabling a comprehensive analysis of how a series of transactions would interact with the Ethereum Virtual Machine (EVM) and affect the blockchain state. ## Parameters * `transactions` — an array of objects, each representing a transaction to be executed. Each transaction object includes fields such as `from`, `to`, `gas`, `gasPrice`, `value`, and `data`. * `traceTypes` — an array of strings specifying the types of traces to return for each transaction, with options including: * `vmTrace` — virtual machine trace. * `trace` — standard execution trace. * `stateDiff` — state difference trace. * `blockNumber` — the block number against which all the transactions should be executed, with values: * `specific block number` — execute against the specified block. * `earliest` — the earliest block (usually genesis). * `latest` — the most recent block. * `pending` — the current pending state and transactions. ## Response * `result` — An array containing the trace data for each executed transaction, as specified by the `traceTypes`. This data provides detailed insights into the execution path of each transaction, including VM operations, state changes, and other relevant information that helps understand the transactions' impact on the blockchain state. ## Use case The `trace_callMany`The method is invaluable for developers and analysts who must understand the cumulative effect of multiple transactions on the blockchain state. This could be used to test complex interactions between smart contracts, debug transaction sequences for decentralized applications (DApps), or estimate the gas usage for a series of transactions. It provides a sandbox environment for safely simulating transactions, allowing for thorough testing and analysis without the risk of consuming gas or affecting the live blockchain. # debug_traceTransaction | BNB Chain Source: https://docs.chainstack.com/reference/bnb-tracetransaction openapi/bnb_node_api/debug_traceTransaction.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that returns a transaction's traces by replaying it. Reference for debug_traceTransaction on BNB Chain via Chainstack. BNB API method that returns a transaction's traces by replaying it. This method provides a detailed breakdown of every step in executing a transaction on the Ethereum blockchain, including gas usage and opcode output. Its ability to accurately simulate the transaction's execution path by replaying any prior transactions makes it a powerful tool for developers to identify and diagnose application issues. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `hash` — the hash identifying a transaction. * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) in the debug and trace overview. You can also use additional configuration parameters. The following settings are available: * `disableStorage` — when enabled, it prevents tracing of storage changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. By default, `debug_traceTransaction` traces both memory and storage changes, but storage tracing can be particularly resource-intensive, especially for large transactions. * `disableStack` — when enabled, it skips tracing of [stack](https://en.wikipedia.org/wiki/Stack_\(abstract_data_type\)) changes made by the transaction being analyzed. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableMemory` — when `false`, it prevents tracing of memory changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableMemory`. * `enableReturnData` — when `false`, it prevents the method from tracing the return data produced by the transaction being analyzed. The return data tracing can be particularly resource-intensive, requiring a significant amount of time and processing power to complete. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableReturnData`. * `timeout` — setting that allows developers to customize the method's timeout period for JavaScript-based tracing calls. The default timeout is `5s`, and you can find the values formats in the [Go documentation](https://pkg.go.dev/time#ParseDuration). > [!NOTE] > > It's worth noting that when using one of the three built-in tracers in Ethereum clients, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData`settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the [Struct/opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#structopcode-logger). ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ### `Struct/opcode logger` response * `object` — the `Struct/opcode logger` traces object: * `pc` — the current program counter of the transaction, which represents the location of the current opcode being executed. A register that keeps track of the address of the next instruction to be executed in a program. * `op` — the current [opcode](https://ethereum.org/en/developers/docs/evm/opcodes/) being executed, such as PUSH1, ADD, or SWAP1. A code that represents a specific operation or instruction that a computer or processor can understand and execute. * `gas` — the amount of gas remaining in the transaction at the current step of execution. * `gasCost` — the amount of gas consumed by the current opcode being executed. * `depth` — the current depth of the call stack for the transaction, reflecting the number of active frames representing nested function calls. * `stack` — the contents of the EVM stack at the current step of the transaction's execution. The stack is a data structure used by the EVM to manage and manipulate data during the execution of opcodes * `memory` — the contents of the memory at the current step of execution. A temporary data storage location that is available to smart contracts during execution. ## `debug_traceTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txhash) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. const tracer = {}; const traces = await provider.send("debug_traceTransaction", [txhash, tracer]); console.log(traces); }; traceTransaction("0x0e77e2cf5a6999f616b56f1378b6c7b127b5a4e20cb2e12611d808af0979dacf") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) tx_hash = "0x0e77e2cf5a6999f616b56f1378b6c7b127b5a4e20cb2e12611d808af0979dacf" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. tracer = { "tracer": '4byteTracer' } tx_traces = web3.provider.make_request('debug_traceTransaction', [tx_hash, tracer]) print(tx_traces) ``` ## Use case A potential use case for the `debug_traceTransaction` method is to inspect failed transactions on the BNB blockchain by using the built-in `callTracer` tracer to extract the error and reason. You can effectively utilize this feature by creating a script that analyzes the latest block's transactions and selectively extracts data only from the failed ones. # eth_uninstallFilter | BNB Chain Source: https://docs.chainstack.com/reference/bnb-uninstallfilter openapi/bnb_node_api/eth_uninstallFilter.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method used to remove a filter previously created using one of the following methods: eth_newFilter eth_newPendingTransactionFilter eth_newBlockFilter. BNB API method used to remove a filter previously created using one of the following methods: * [eth\_newFilter](/reference/bnb-newfilter) * [eth\_newPendingTransactionFilter](/reference/bnb-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/bnb-newblockfilter) Upon successful execution, the filter is removed and will no longer emit events. This method can be useful for optimizing resource usage when a developer no longer needs to monitor certain events or blocks. It can also be used to clean up unused filters in order to reduce clutter in the client-side filter list. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/bnb-newfilter) > * [eth\_newPendingTransactionFilter](/reference/bnb-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/bnb-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `string` — the filter ID that you want to uninstall ## Response * `boolean` — a boolean value indicating whether the filter was successfully uninstalled. `true` if successfully removed, and `false` if not. ## `eth_uninstallFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function removeFilter(filter) { try { const removed = await provider.send('eth_uninstallFilter', [filter]); console.log(removed); // Log whether the filter is removed } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' removeFilter(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def remove_filter(filter_id): try: removed = web3.eth.uninstall_filter(filter_id) return removed except Exception as e: print(e) filter_id = '0x0' print(remove_filter(filter_id)) ``` ## Use case One use case for `eth_uninstallFilter` is to optimize resource usage in a DApp. When a DApp needs to monitor events on the blockchain, it can create a filter using [eth\_newFilter](/reference/bnb-newfilter),[eth\_newPendingTransactionFilter](/reference/bnb-newpendingtransactionfilter), or [eth\_newBlockFilter](/reference/bnb-newblockfilter) to listen for specific events or blocks. However, once the DApp no longer needs to monitor these events or blocks, it can use `eth_uninstallFilter` to stop watching for them. By doing so, the DApp can reduce the number of active filters and free up resources, such as network bandwidth and computational power, that would otherwise be used to maintain the filter. For example, let's say that a DApp is monitoring incoming blocks, logs batches of 50 blocks, and then uses `uninstallFilter` to remove the filter. # eth_chainId | Polygon Source: https://docs.chainstack.com/reference/chainid openapi/polygon_node_api/chain_info/eth_chainId.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the current chain ID. eth_chainId JSON-RPC method available on the Polygon blockchain via Chainstack. Polygon API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and generally verify if a network is the desired one. It was introduced in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). ## Parameters * `none` ## Response * `quantity` — EIP-155 Chain ID ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const chainId = async () => { // This will return the value in Hex const chainId = await provider.send("eth_chainId"); console.log(`Hex Chain ID: ${chainId}`); }; chainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.chain_id) ``` ## Use case One possible use case for the `eth_chainId` method in Polygon is to determine which chain a user is connected to when using a DApp with the MetaMask browser extension. This can be useful to provide a customized user experience based on the specific chain that the user is interacting with. Here is an example of how the `eth_chainId` method might be used in this context with MetaMask. Keep in mind that the Chain ID is returned as a hexadecimal value: ```js JavaScript theme={"system"} // Check which network is selected on MetaMask async function checkChain() { // Declare the desired chain ID to match const desiredChainId = '0x89'; // Polygon mainnet chain ID // Retrieve the current Chain ID selected by the user ethereum.request({ method: 'eth_chainId' }).then(chainId => { // compare the actual chain ID it to the declared chain ID if (chainId !== desiredChainId) { console.log(`You are currently on the wrong network. Please switch to the mainnet.`); // Call the promtSwitch function promtSwitch() } else { console.log("This is the correct network") } }) } // Prompt user to switch to a determined network based on the chain ID async function promtSwitch() { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x89' // chainId must be in HEX with 0x in front }], }); } ``` In this case, the `checkChain` function is called to get the current chain ID using the Ethereum object provided by MetaMask. The chain ID is then checked to determine if the user is connected to the Polygon mainnet and calls the `promtSwitch` function to prompt the user to switch chain in case the chain ID returned does not match the `desiredChainId` constant. # Get tokens Source: https://docs.chainstack.com/reference/chainstack-faucet-get-tokens-rpc-method openapi/faucet_api/get_tokens.json POST /v1/faucet/{chain} Chainstack API method to request testnet funds from the Chainstack faucet for Ethereum, BNB Smart Chain, Polygon, Base, Solana, TON, and other supported test networks. Chainstack API method to request testnet funds. Developers use testnet funds for various purposes, primarily to test and develop decentralized applications (DApps), smart contracts, and other blockchain-based projects within a safe and controlled environment. > [!NOTE] > > You can also request funds directly from the [Chainstack Faucet](https://faucet.chainstack.com) page. ## Path parameters * `chain` — the test network you want to receive funds on. * `sepolia` — receive testnet ETH on Ethereum Sepolia. * `hoodi` — receive testnet ETH on Ethereum Hoodi. * `bnb-testnet` — receive testnet BNB on BNB Smart Chain testnet. * `amoy` — receive testnet POL on Polygon Amoy. * `zksync-testnet` — receive testnet ETH on ZKsync Sepolia. * `base` — receive testnet ETH on Base Sepolia. * `hyperevm` — receive testnet HYPE on Hyperliquid testnet. * `robinhood` — receive testnet ETH on Robinhood Chain testnet. * `monad` — receive testnet MON on Monad testnet. * `plasma` — receive testnet XPL on Plasma testnet. * `solana` — receive SOL on Solana devnet. * `ton` — receive TON on TON testnet. > [!WARNING] > > **Spam protection system** > > To safeguard against misuse, the Chainstack faucet implements two key checks before disbursing funds: > > 1. The requesting address must have a minimum balance of 0.08 ETH on the Ethereum mainnet. > 2. The address should have a history of holding ETH on the mainnet; it should not have been recently empty. > > These checks apply to the EVM networks. A `solana` request checks the address's balance on Solana mainnet instead, and a `ton` request has no mainnet check. > > These precautions help us maintain the integrity of the Chainstack faucet, ensuring fair and equitable access for all users. > [!NOTE] > > **Faucet API base URL** > > `https://api.chainstack.com/v1/faucet/{chain}` ## Body parameters * `address` — the address that will receive the funds. ## Response * `url`— the block explorer URL, including the transaction hash, to monitor the status. ## Request funds from this page You can already try this API from this page by doing the following: 1. [Get your API key](/reference/platform-api-getting-started#create-api-key) and paste it into the **Bearer** field in the **AUTHENTICATION** section. 2. At the bottom of the page, select which network you want to receive funds from. 3. At the bottom of the page, paste your Ethereum address in the **address** field. ## Code examples The Faucet API is a great option for developers who don't want to spend time hunting for testnet tokens. Here's an example of a script that automatically refills your wallet by calling the Chainstack Faucet API every 24 hours, ensuring a seamless daily refill. ```javascript JavaScript theme={"system"} // npm i axios const axios = require('axios'); // API config const chain = 'sepolia' // hoodi const address = 'YOUR_ADDRESS'; const apiUrl = `https://api.chainstack.com/v1/faucet/${chain}`; const apiKey = 'YOUR_CHAINSTACK_API_KEY'; const fillWallet = async () => { console.log(`Sending faucet request for address ${address}`) try { const response = await axios.post(apiUrl, { address }, { headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, }); console.log('API call successful:', response.data); } catch (error) { console.error('Error making API call:', error.response.data); } // Schedule the next API call in 24 hours (24 * 60 * 60 * 1000 milliseconds) setTimeout(makeApiCall, 24 * 60 * 60 * 1000); }; // Make the first API call immediately fillWallet(); ``` ```python Python theme={"system"} # pip install requests import requests import time # API config chain = 'sepolia' # hoodi address = 'YOUR_ADDRESS' api_url = f'https://api.chainstack.com/v1/faucet/{chain}' api_key = 'YOUR_CHAINSTACK_API_KEY' def fill_wallet(): print(f'Sending faucet request for address {address}') try: response = requests.post( api_url, json={'address': address}, headers={ 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json', }, ) print('API call successful:', response.json()) except Exception as error: print('Error making API call:', error) # Schedule the next API call in 24 hours (24 * 60 * 60 seconds) time.sleep(24 * 60 * 60) # Make the first API call immediately while True: fill_wallet() ``` ## Use cases Here are some use cases testnet funds are used for: * Smart contract development — with testnet funds, developers can deploy and interact with smart contracts, finding and fixing any issues before moving to the mainnet, where the real money is involved. * Testing DApps and infrastructure — testnet funds allow developers to try out transactions, see how user interfaces work, and check the overall functionality of their DApps and blockchain components without risking real assets. * Trying out new features — testnets are perfect for exploring and testing new features or updates to blockchain protocols and applications. It's like a sandbox where developers can play around and validate their ideas before taking them live on the mainnet. * Stress testing and scalability — developers use testnet funds to put their DApps or smart contracts through the wringer, seeing how they perform under various conditions and loads. This helps them spot any issues and fix them before going live. * Learning and training — testnet funds create a risk-free space for developers, researchers, and enthusiasts to learn about blockchain technology, smart contracts, and DApps. It's a great way to build expertise and understanding in the field. # Chainstack Faucet API: free testnet tokens for development Source: https://docs.chainstack.com/reference/chainstack-faucet-introduction Get free testnet tokens from the Chainstack faucet — via the faucet UI, the Faucet API, or the Chainstack MCP server's request_testnet_funds tool for AI agents. The Chainstack Faucet API is a faucet API designed to facilitate the process of requesting testnet funds. By utilizing a simple API call with the Chainstack API key as authentication, users can effortlessly acquire the necessary testnet funds for their projects. This faucet API integrates seamlessly with existing development workflows and prioritizes ease of use, making it an ideal solution for blockchain professionals focused on Ethereum-based projects. Adopt the Chainstack Faucet API for a streamlined approach to testnet fund acquisition and enhance your blockchain development experience. > [!NOTE] > > You can also request funds directly from the [Chainstack Faucet](https://faucet.chainstack.com) page. > [!NOTE] > > AI agents can also call the Chainstack faucet through the [Chainstack MCP server](/docs/chainstack-mcp-server) using the `request_testnet_funds` tool — fund testnet addresses directly from your coding environment, no UI clicks, no copy-paste. Uses your Chainstack API key. ## Faucet limitations Due to the recent scarcity of testnet funds, the Chainstack Faucet API enforces stringent criteria for receiving testnet tokens. > [!NOTE] > > To protect the faucet, a balance of at least 0.08 ETH on mainnet is required. ### Authentication A Chainstack API key serves as the method of authentication. > [!NOTE] > > Learn how to [sign up](/docs/manage-your-account#create-an-account) and [generate an API key](/reference/platform-api-getting-started#create-api-key). ### Amount limits Test tokens are designed for development purposes rather than accumulation. To ensure fairness, users are restricted to receiving up to a limited amount of test tokens per day, as developers generally do not require large amounts for testing their DApps. The Chainstack Faucet API adopts a "top-up" approach. If your wallet already contains the maximum amount of test tokens or more, you will be ineligible to receive additional tokens. However, if your wallet holds less than the maximum amount, the faucet will dispense the difference to top up your wallet balance. ### Frequency limits The Chainstack Faucet API permits users to obtain test tokens once every 24 hours to maintain resource availability. If another request is submitted within this time frame, the API will return an error message specifying the time remaining before you become eligible for another request. # Create a network with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-create-network post /v1/networks/ Create a new blockchain network in a Chainstack project using the Platform API. Send a POST request to /v1/networks/ with the network configuration. # Create a node with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-create-node post /v1/nodes/ Provision a new dedicated or shared blockchain node on Chainstack using the Platform API. POST to /v1/nodes/ with the node configuration payload. # Create a project with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-create-project post /v1/projects/ Create a new project to organize your blockchain networks and nodes on Chainstack. Send a POST request to /v1/projects/ with the project details. # Delete a network with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-delete-network delete /v1/networks/{id}/ Permanently delete a blockchain network from a Chainstack project using the Platform API. Send a DELETE request to /v1/networks/{id}/ by network ID. # Delete a node with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-delete-node delete /v1/nodes/{id}/ Decommission and remove a Chainstack blockchain node using the Platform API. Send a DELETE request to /v1/nodes/{id}/ targeting the node by its ID. # Delete a project with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-delete-project delete /v1/projects/{id}/ Delete a project and its associated resources from Chainstack using the Platform API. Send a DELETE request to /v1/projects/{id}/ identified by project ID. # Request Sepolia testnet ETH from the Chainstack faucet Source: https://docs.chainstack.com/reference/chainstack-platform-api-faucet-sepolia post /v1/faucet/sepolia Request free Sepolia testnet ETH from the Chainstack faucet using the Platform API. POST to /v1/faucet/sepolia with the destination wallet address. # Get organization details from the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-get-organizaton-info get /v1/organization/ Retrieve account, billing, and member information for your Chainstack organization. Send a GET request to /v1/organization/ to fetch the details. # List all networks with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-list-all-networks get /v1/networks/ List every blockchain network across your Chainstack projects using the Platform API. Send a GET request to /v1/networks/ to retrieve the paginated list. # List all nodes with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-list-all-nodes get /v1/nodes/ List every blockchain node deployed across your Chainstack organization using the Platform API. Send a GET request to /v1/nodes/ for the full list. # List all projects with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-list-all-projects get /v1/projects/ List all projects in your Chainstack organization using the Platform API. Send a GET request to /v1/projects/ to retrieve the paginated project list. # Retrieve a network with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-retrieve-network get /v1/networks/{id}/ Retrieve detailed information about a specific Chainstack blockchain network. Send a GET request to /v1/networks/{id}/ using the network's unique ID. # Retrieve a node with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-retrieve-node get /v1/nodes/{id}/ Retrieve configuration, status, and endpoint details for a Chainstack node. Send a GET request to /v1/nodes/{id}/ using the node's unique identifier. # Retrieve a project with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-retrieve-project get /v1/projects/{id}/ Retrieve detailed information about a specific Chainstack project, including its network count and creator. Send a GET request to /v1/projects/{id}/ by project ID. # List project members with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-retrieve-project-members get /v1/projects/{id}/members/ List all members and their roles for a specific Chainstack project using the Platform API. Send a GET request to /v1/projects/{id}/members/ by project ID. # Update a network with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-update-network patch /v1/networks/{id}/ Update the configuration or metadata of an existing Chainstack blockchain network. Send a PATCH request to /v1/networks/{id}/ with the updated fields. # Update a node with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-update-node patch /v1/nodes/{id}/ Update the name, configuration, or other properties of a Chainstack blockchain node. Send a PATCH request to /v1/nodes/{id}/ with the modified fields. # Update organization info with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-update-organization-info patch /v1/organization/ Update your Chainstack organization's name, contact details, or billing information using the Platform API. Send a PATCH request to /v1/organization/. # Update a project with the Chainstack Platform API Source: https://docs.chainstack.com/reference/chainstack-platform-api-update-project patch /v1/projects/{id}/ Update the name or description of an existing Chainstack project using the Platform API. Send a PATCH request to /v1/projects/{id}/ with the updated fields. # Bulk update node access rules status with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-bulk-update-node-access-rules-status post /v2/nodes/{node_id}/access-rules/bulk-update-status/ Activate or deactivate every access rule on a node in one request using the Chainstack Platform API v2. Send a POST request to /v2/nodes/{node_id}/access-rules/bulk-update-status/. # Create a node with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-create-node post /v2/nodes/ Create a blockchain node using the Chainstack Platform API v2 with the new deployment options model. Send a POST request to /v2/nodes/ with the configuration. # Create a node access rule with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-create-node-access-rule post /v2/nodes/{node_id}/access-rules/ Restrict a node endpoint to an IP address or origin using the Chainstack Platform API v2. Send a POST request to /v2/nodes/{node_id}/access-rules/ with the rule type and value. # Create a project with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-create-project post /v2/projects/ Create a new project using the Chainstack Platform API v2 to organize networks and nodes. Send a POST request to /v2/projects/ with the project payload. # Delete a node with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-delete-node delete /v2/nodes/{id}/ Decommission and remove a blockchain node using the Chainstack Platform API v2. Send a DELETE request to /v2/nodes/{id}/ targeting the node by its ID. # Delete a node access rule with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-delete-node-access-rule delete /v2/nodes/{node_id}/access-rules/{id}/ Remove an access rule from a node using the Chainstack Platform API v2. Send a DELETE request to /v2/nodes/{node_id}/access-rules/{id}/. # Delete a project with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-delete-project delete /v2/projects/{id}/ Delete an empty project using the Chainstack Platform API v2. Send a DELETE request to /v2/projects/{id}/; a project that still has active nodes is refused with 403. # Export node usage to CSV with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-export-node-usage get /v2/reporting/node-usage/export/ Download the full node usage report for your organization as a CSV file using the Platform API v2. Send a GET request to /v2/reporting/node-usage/export/. # Get organization name and ID with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-get-organization get /v2/organization/ Retrieve the name and ID of your organization using the Chainstack Platform API v2. Send a GET request to /v2/organization/. # List all nodes with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-list-all-nodes get /v2/nodes/ List every blockchain node in your Chainstack organization using the Platform API v2. Send a GET request to /v2/nodes/ for the paginated node list. # List all projects with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-list-all-projects get /v2/projects/ List all projects in your Chainstack organization with the Platform API v2. Send a GET request to /v2/projects/ to retrieve the paginated project list. # List deployment options with Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-list-deployment-options get /v2/deployment-options/ List the blockchain and cloud combinations your organization's plan allows on Chainstack, with the region and features of each. Send a GET request to /v2/deployment-options/ to fetch them. # List node access rules with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-list-node-access-rules get /v2/nodes/{node_id}/access-rules/ List the IP address and origin access rules on a node using the Chainstack Platform API v2. Send a GET request to /v2/nodes/{node_id}/access-rules/, optionally filtered by type and status. # List node usage with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-list-node-usage get /v2/reporting/node-usage/ Retrieve per-node usage metrics — compute hours, GB-hours, request units, and bloXroute requests — using the Platform API v2. Send a GET request to /v2/reporting/node-usage/ for the paginated usage report. # Retrieve a node with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-retrieve-node get /v2/nodes/{id}/ Retrieve configuration, status, and endpoint details for a node using the Chainstack Platform API v2. Send a GET request to /v2/nodes/{id}/ by node ID. # Retrieve a node access rule with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-retrieve-node-access-rule get /v2/nodes/{node_id}/access-rules/{id}/ Retrieve the type, value, and status of a node access rule using the Chainstack Platform API v2. Send a GET request to /v2/nodes/{node_id}/access-rules/{id}/. # Retrieve a project with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-retrieve-project get /v2/projects/{id}/ Retrieve detailed information about a specific Chainstack project using the Platform API v2. Send a GET request to /v2/projects/{id}/ using the project ID. # Retrieve project members with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-retrieve-project-members get /v2/projects/{id}/members/ List the member organizations of a consortium project using the Chainstack Platform API v2. Send a GET request to /v2/projects/{id}/members/. # Update a node with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-update-node patch /v2/nodes/{id}/ Update the configuration or properties of a blockchain node using the Chainstack Platform API v2. Send a PATCH request to /v2/nodes/{id}/ with new fields. # Update a node access rule with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-update-node-access-rule patch /v2/nodes/{node_id}/access-rules/{id}/ Change the value of a node access rule, or activate or deactivate the rule, using the Chainstack Platform API v2. Send a PATCH request to /v2/nodes/{node_id}/access-rules/{id}/. # Update organization name with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-update-organization patch /v2/organization/ Change the name of your organization using the Chainstack Platform API v2. Send a PATCH request to /v2/organization/ with the new name. # Update a project with the Chainstack Platform API v2 Source: https://docs.chainstack.com/reference/chainstack-platform-api-v2-update-project patch /v2/projects/{id}/ Update the name or description of a project using the Chainstack Platform API v2. Send a PATCH request to /v2/projects/{id}/ with the updated values. # web3_clientVersion | Polygon Source: https://docs.chainstack.com/reference/clientversion openapi/polygon_node_api/client_info/web3_clientVersion.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the client type and version running on the Polygon node. web3_clientVersion on Polygon via Chainstack. Polygon API method that returns the client type and version running on the Polygon node. This information can be useful to developers to verify that a node they are connecting to is compatible with their needs. ## Parameters * `none` ## Response * `string` — a string identifying the type of client, version, operating system, and language version running on the node ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const clientVersion = async () => { const version = await provider.send("web3_clientVersion"); console.log(`Client version: ${version}`); }; clientVersion(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) client_version = web3.provider.make_request('web3_clientVersion', []) print(client_version) ``` ## Use case A use case for the `web3_clientVersion` method can be to verify which client is running to then decide which method to run. Let's say you have a DApp that needs to retrieve all of the transactions receipts in a block; the Erigon and Bor clients both have a method for this, but the `eth_getBlockReceipts` method from Erigon will not work on Bor, and the `eth_getTransactionReceiptsByBlock` method from Bor will not work on Erigon. You can use the `web3_clientVersion` method in your logic to first identify which method must be used. Here is an implementation of this use case using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // getBlockReceipts Erigon method async function getBlockReceipts(blockId) { const receipts = await provider.send("eth_getBlockReceipts", [blockId]); return receipts } // getTransactionReceiptsByBlock Bor method async function getTransactionReceiptsByBlock(blockId) { const receipts = await provider.send("eth_getTransactionReceiptsByBlock", [blockId]); return receipts } // Get client version async function getClientVersion() { const clientVersion = await provider.send("web3_clientVersion", []); return clientVersion } // Extract the client's name async function getClientName() { const clientVersion = await getClientVersion(); const clientName = clientVersion.split('/')[0]; return clientName } // Get the receipts based on the client async function getAllReceipts(blockId) { const clientName = await getClientName() if (clientName === 'bor') { console.log('Client is Bor'); const receipts = getTransactionReceiptsByBlock(blockId) return receipts } else if (clientName === 'erigon') { console.log('Client is Erigon'); const receipts = getBlockReceipts(blockId) return receipts } else { // Do something else if version is neither Bor nor Erigon console.log('Client version is not Bor or Erigon'); } } async function main() { try { const receipts = await getAllReceipts('latest'); console.log(receipts); } catch (error) { console.error(error); } } main(); ``` The `getBlockReceipts` function uses `provider.send` to call the `eth_getBlockReceipts` JSON-RPC method directly with a single parameter `blockId` and returns the resulting receipts. The `getTransactionReceiptsByBlock` function similarly uses `provider.send` to call the `eth_getTransactionReceiptsByBlock` JSON-RPC method with a single parameter `blockId` and returns the resulting receipts. The `getClientVersion` function uses `provider.send` to call the `web3_clientVersion` JSON-RPC method with no parameters and returns the resulting client version. The `getClientName` function calls `getClientVersion` to get the client version and extracts the name of the client by splitting the version string at the first `/` character. The `getAllReceipts` function calls `getClientName` to get the name of the client and then selects the appropriate method to use for retrieving the receipts. If the client is Bor, it calls `getTransactionReceiptsByBlock`, and if the client is Erigon, it calls `getBlockReceipts`. If the client is neither Bor nor Erigon, the function logs a message and returns null. # web3_clientVersion | Cronos Source: https://docs.chainstack.com/reference/cronos-clientversion openapi/cronos_node_api/web3_clientVersion.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns the client type and version running on the Cronos node. web3_clientVersion on Cronos via Chainstack. Cronos API method that returns the client type and version running on the Cronos node. This information can be useful to developers to verify that a node they are connecting to is compatible with their needs. ## Parameters * `none` ## Response * `string` — a string identifying the type of client, version, operating system, and language version running on the node ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const clientVersion = async () => { const version = await provider.send("web3_clientVersion"); console.log(`Client version: ${version}`); }; clientVersion(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) client_version = web3.provider.make_request('web3_clientVersion', []) print(client_version) ``` ## Use case A use case for the `web3_clientVersion` method can be to verify which client is running to then decide which method to run. > [!NOTE] > > Read [Expanding your blockchain horizons: The eth\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) to learn how to build a program to emulate `eth_getBlockReceipts` on any EVM-compatible chain. # eth_estimateGas | Cronos Source: https://docs.chainstack.com/reference/cronos-estimategas openapi/cronos_node_api/eth_estimateGas.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns an estimate of gas units needed for a given transaction. Available on Cronos via Chainstack JSON-RPC nodes. Cronos API method that returns an estimate of gas units needed for a given transaction. It estimates the maximum amount of gas units required to complete the transaction based on the network's current state and the transaction's parameters. A client or wallet can use this estimate to determine the appropriate amount of gas to include in a transaction to ensure the network processes it. ## Parameters * `object` — the transaction call object, same as in `eth_call`; the `from` field is optional, and the `nonce` field is omitted: * `from` — (optional) the address string used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — the estimated amount of gas units needed, represented as a hexadecimal string. ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimateGas = async () => { // The transaction to estimate is a simple transfer in this example. const transactionObj = { from: "0x25aA97464F38a1506a16160bbc03cfC6DD863da3", to: "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8", } const gasUnits = await provider.estimateGas(transactionObj); console.log(`Estimated gas units required: ${gasUnits}`); }; estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.estimate_gas({"from":"0x25aA97464F38a1506a16160bbc03cfC6DD863da3","to":"0xbe0eb53f46cd790cd13851d5eff43d12404d33e8"}, "latest" )) ``` ## Use case A practical use case for `eth_estimateGas` is to calculate the gas cost required for a transaction that transfers a value from one Ethereum address (`fromAddress`) to another (`toAddress`). The gas cost is an important factor in transactions, as it determines the amount of fees that need to be paid by the sender to the network for processing the transaction. # eth_blockNumber | Cronos Source: https://docs.chainstack.com/reference/cronos-eth-blocknumber openapi/cronos_node_api/eth_blockNumber.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns the latest block number of the blockchain. Call eth_blockNumber on Cronos through Chainstack nodes. Cronos API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0x69B5B` means that the block is the 432,987th block in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to and encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. > [!NOTE] > > Use the [Chainstack EVM Swiss Knife to convert hexadecimal to decimal](https://web3tools.chainstacklabs.com/hexadecimal-decimal). ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockNumber = async () => { const block_Number = await provider.getBlockNumber(); console.log(block_Number); }; eth_getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.block_number) ``` ## Use case A possible use case for the `eth_blockNumber` method in Cronos is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. # eth_call | Cronos Source: https://docs.chainstack.com/reference/cronos-ethcall openapi/cronos_node_api/eth_call.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. Cronos API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. This can be useful for testing and debugging by simulating transfers or smart contract transactions and retrieving data from the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > **Disclaimer** > > The interactive example in this page uses `eth_call` to call the `decimals()` method from the [WCRO Token smart contract on Cronos Chain](https://cronoscan.com/token/0x5c7f8a570d578ed84e63fdfa7b1ee72deae1ae23#readContract). > > You can use the [Chainstack EVM Knife](https://web3tools.chainstacklabs.com/generate-solidity-functions-signature) to generate the function signatures from smart contracts. ## Parameters * `object` — the transaction call object: * `from` — (optional) the address string used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters. See the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` —the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized` —the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending` —the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `data` — the result of executing the specified call on the Ethereum Virtual Machine (EVM), encoded as a hexadecimal string. ## `eth_call` code examples The following code snippets clearly demonstrate how to simulate a standard ether transfer between two accounts. Note that when using `eth_call` to simulate a transfer or a smart contract transaction, the sender account must have a sufficient balance to cover the associated gas fee. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const simulateTransfer = async () => { try { // Define the accounts to transfer between const fromAddress = "0x71F0cDb17454ad7EeB7e26242292fe0E0189645a"; const toAddress = "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"; // Define the value to transfer const value = ethers.parseEther("1"); // Build transaction object const transactionObject = { from: fromAddress, to: toAddress, gasLimit: ethers.toQuantity(21000), value: value } // Use eth_call to simulate the transfer on the latest block const result = await provider.call(transactionObject); console.log(`Result: ${result}`); } catch (error) { console.error(`Error: ${error.message}`); } } simulateTransfer(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the accounts to transfer between from_address = web3.to_checksum_address('0x71F0cDb17454ad7EeB7e26242292fe0E0189645a') to_address = web3.to_checksum_address('0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a') # Define the value to transfer (in Ether) value = web3.to_wei(1, 'ether') # Build the transaction object transaction = { 'from': from_address, 'to': to_address, 'gas': 21000, # 21000 is the standard for a regular transfer 'value': value, 'data': '', } # Use eth_call to simulate the transfer result = web3.eth.call(transaction, 'latest') print(result) ``` Executing this code will produce a return value of `0x`, indicating a successful transaction. This implies that the transaction will be executed without errors when the code is used to send the transaction. ## Use case A common use case for `eth_call` is calling smart contract functions. For example, interact with an ERC-20 token smart contract deployed on the Cronos network and retrieve the balance of a specific account. # eth_getBalance | Cronos Source: https://docs.chainstack.com/reference/cronos-getbalance openapi/cronos_node_api/eth_getBalance.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns the balance of a specific account in Wei, the smallest unit of ether. Use it on Cronos via Chainstack. Cronos API method that returns the balance of a specific account in [Wei](https://www.investopedia.com/terms/w/wei.asp), the smallest unit of ether. This method allows developers to retrieve the current balance of a Cronos account for various purposes, such as checking available funds or displaying balance information. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to check the balance of. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block * `pending`— the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — the integer value of the current balance in Wei ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const getBalance = async (address, block) => { const balance = await provider.send("eth_getBalance", [address, block]); console.log(balance); }; getBalance("0xeB32dab5d38FF953Cc323ACe8Efbdf4F897f1b1F", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" address = web3.to_checksum_address("0xeB32dab5d38FF953Cc323ACe8Efbdf4F897f1b1F") balance = web3.eth.get_balance(address, "latest") print(balance) ``` ## Use case One practical use case for `eth_getBalance` is to check the balance of an account for a program that scans the balance periodically and fills up the account when the balance drops below a certain value. # eth_getBlockByHash | Cronos Source: https://docs.chainstack.com/reference/cronos-getblockbyhash openapi/cronos_node_api/eth_getBlockByHash.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos Chain API method that returns information about the block matching the given block hash. Available on Cronos via Chainstack. Cronos Chain API method that returns information about the block matching the given block hash. This method can be useful for analyzing a block and its transactions. ## Parameters * `hash` — the hash of the block * `boolean` — if `true`, it returns the details of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block is pending. * `parenthash` — the previous block's hash used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block is pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the block logs, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot`— the root of the block's receipts trie. This is a 32-byte hash of the root node of the receipts trie for all transactions in the block. It is used to verify the integrity of the receipt data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. Cronos uses Tendermint consensus with Proof of Authority (PoA) and this value is not relevant. * `extradata` — the extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/cronos-gettransactionbyhash) for the exact shape. * `uncles` — the array of uncle hashes. ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByHash = async () => { const blockByNumber = await provider.send("eth_getBlockByHash", ["0x2230d083edc1173cbc7a7ab977318d6a8fc53fec6f147efb0c6544a9c4fc18fd",false]); console.log(blockByNumber); }; eth_getBlockByHash(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block("0x2230d083edc1173cbc7a7ab977318d6a8fc53fec6f147efb0c6544a9c4fc18fd", False)) ``` ## Use case The `eth_getBlockByHash` is excellent for analytics purposes. For example, it can be used to quickly find out the hashes of transactions that create new smart contracts. The `transactions` field, an array of all the transactions within the block, is returned in the response. By iterating through this array and examining each transaction's `to` field, a user can determine which transactions in the block are creating new smart contracts. # eth_getBlockByNumber | Cronos Source: https://docs.chainstack.com/reference/cronos-getblockbynumber openapi/cronos_node_api/eth_getBlockByNumber.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns information about the block matching the given block number. eth_getBlockByNumber on Cronos via Chainstack. Cronos API method that returns information about the block matching the given block number. This method can retrieve a wide range of information about the block, such as its hash, timestamp, difficulty, and gas limit. You can also retrieve transaction objects included in the block if needed. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). * `boolean` — if `true`, it returns the details of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — a block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block is pending. * `parenthash`— the previous block's hash used to generate the current block, also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block is pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the block logs, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot`— the root of the block's receipts trie. This is a 32-byte hash of the root node of the receipts trie for all transactions in the block. It is used to verify the integrity of the receipt data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. Cronos uses Tendermint consensus with Proof of Authority (PoA) and this value is not relevant. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/cronos-gettransactionbyhash) for the exact shape. * `uncles` — array of uncle hashes. ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByNumber = async () => { const blockByNumber = await provider.send("eth_getBlockByNumber", ["latest",false]); console.log(blockByNumber); }; eth_getBlockByNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block("latest", False)) ``` ## Use case The `eth_getBlockByNumber` method can determine whether a block you are analyzing has been included in the main chain or was forked due to a 'reorg'. A block hash is generated through a cryptographic hash function. This function takes the block's data, including transactions and other information, and the hash of the parent block (the previous block) and outputs a unique, fixed-length digest. This hash represents the current block within the blockchain. # eth_getBlockTransactionCountByHash | Cronos Source: https://docs.chainstack.com/reference/cronos-getblocktransactioncountbyhash openapi/cronos_node_api/eth_getBlockTransactionCountByHash.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. Cronos API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. ## Parameters * `hash` — the block hash of the requested block. ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockHash) => { const count = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); console.log(count); }; getTransactionsCount('0x41d5e705926b5b3fac109e8962a87da71a61373e43ba05e47c22138cb30b86bd'); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block_transaction_count("0x41d5e705926b5b3fac109e8962a87da71a61373e43ba05e47c22138cb30b86bd")) ``` ## Use case `eth_getBlockTransactionCountByHash` is a useful tool for analyzing transaction volume on the Cronos blockchain. On average, a new block is generated on the Cronos mainnet every 5 seconds, resulting in approximately 720 blocks per hour. Using a Web3 library, one can inspect the past 720 blocks starting from the latest block, retrieve the hash of each block, and use `eth_getBlockTransactionCountByHash` to find the number of transactions in each block. # eth_getBlockTransactionCountByNumber | Cronos Source: https://docs.chainstack.com/reference/cronos-getblocktransactioncountbynumber openapi/cronos_node_api/eth_getBlockTransactionCountByNumber.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. Cronos API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockId) => { const count = await provider.send("eth_getBlockTransactionCountByNumber", [blockId]); console.log(count); }; getTransactionsCount('latest') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.get_block_transaction_count(13754975)) # A hex value starting with "0x" is accepted as well. ``` ## Use case `eth_getBlockTransactionCountByNumber` can be used to analyze how many transactions are included on the Cronos blockchain in a certain period. For instance, on average, a new block is generated on the Cronos mainnet every 5 seconds, resulting in approximately 720 blocks per hour. Using a Web3 library, one can inspect the past 720 blocks starting from the latest block and use `eth_getBlockTransactionCountByNumber` to find the number of transactions in each block to sum them. # eth_getCode | Cronos Source: https://docs.chainstack.com/reference/cronos-getcode openapi/cronos_node_api/eth_getCode.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that retrieves the compiled bytecode of a smart contract, providing its address as a parameter. Cronos via Chainstack. Cronos API method that retrieves the compiled bytecode of a smart contract, providing its address as a parameter. This method returns a hexadecimal string representing the smart contract's bytecode. Developers can use this bytecode to verify whether a smart contract is legitimate and ensure that it performs its intended functions. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response `data` — the compiled bytecode of a smart contract. Returns `0x` if the address is not associated with a smart contract. ## `eth_getCode` code examples The following example retrieves the compiled bytecode from the [WCRO token smart contract](https://cronoscan.com/token/0x5c7f8a570d578ed84e63fdfa7b1ee72deae1ae23). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getCode = async (address, block) => { const code = await provider.send("eth_getCode", [address, block]); console.log(code); }; getCode("0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" code = web3.eth.get_code("0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23", "latest") print(web3.to_hex(code)) # Convert the Bytes result into HEX. ``` ## Use case A practical use case for the `eth_getcode` method is verifying whether an address is associated with a smart contract. This can be particularly useful for blockchain explorers, auditors, and DApp developers, who must ensure that the account they interact with is legitimate. # eth_gasPrice | Cronos Source: https://docs.chainstack.com/reference/cronos-getgasprice openapi/cronos_node_api/eth_gasPrice.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns the current gas base fee of the network. Chainstack supports eth_gasPrice on Cronos JSON-RPC nodes. Cronos API method that returns the current gas base fee of the network. The gas price is the quantity of the native token the transaction's sender must pay per unit of gas consumed. The value returned is in Wei. ## Parameters * `none` ## Response * `quantity` — the integer value of the current gas base fee, returned in Wei ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(node_url); async function getGasPrice() { const baseFee = await provider.send("eth_gasPrice"); console.log(`The base gas fee is: ${baseFee} Wei`); } getGasPrice() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.gas_price) ``` ## Use case You can use `eth_gasPrice` to calculate the total gas value to send with a transaction based on the base and priority fee system. Ethereum's London hard fork implemented This concept with EIP-1559. EIP-1559 aimed to solve the problem of network congestion by implementing a dynamic fee market mechanism, which adjusts the fee required to process a transaction based on network demand. With the current system, the total gas price comprises a **base fee** determined by the network's load and a **priority fee** added by the user. > [!NOTE] > > **Note** > > The `eth_gasPrice` method returns the base fee. # eth_getLogs | Cronos Source: https://docs.chainstack.com/reference/cronos-getlogs openapi/cronos_node_api/eth_getLogs.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns an array of all logs that match a given filter object. Available on Cronos via Chainstack JSON-RPC nodes. Cronos API method that returns an array of all logs that match a given filter object. Logs are records of smart contract-generated events on the network and contain valuable information about transactions and smart contract interactions. Some common use cases for `eth_getLogs` include monitoring smart contract events, tracking token transfers, and analyzing blockchain data. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. > * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. > * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. > * `earliest` — the earliest available or genesis block. > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `array` — an array of log objects that match the specified filter or an empty array if there have been no new events since the last poll: * `address` — the contract address from which the event originated * `topics` — an array of 32-byte data fields containing indexed event parameters * `data` — the non-indexed data that was emitted along with the event * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the event index within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getLogs` code examples The following examples retrieve the logs of the Transfer event from the [WCRO token](https://cronoscan.com/token/0x5c7f8a570d578ed84e63fdfa7b1ee72deae1ae23) smart contract on Cronos. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getLogs = async () => { const latestBlock = await provider.getBlockNumber(); const filter = { fromBlock: latestBlock - 10, toBlock: 'latest', address: '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const logs = await provider.getLogs(filter); console.log(logs); }; getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) latest_block = web3.eth.block_number filter = { 'fromBlock': latest_block - 10, 'toBlock': 'latest', 'address': '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } logs = web3.eth.get_logs(filter) print(logs) ``` > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about the `eth_getLogs` method. ## Block range limitations While `eth_getLogs` is a powerful tool, it's crucial to understand its limitations, particularly when working with different EVM-compatible chains, as these networks often have different constraints. The `eth_getLogs` method allows you to select a range of blocks from which to get events and is important for exercising proper management. `eth_getLogs` block range is capped by your [subscription plan](/docs/limits#evm-range-limits). On high-activity chains or contracts, query smaller ranges and paginate to avoid oversized responses or timeouts. ## Use case One use case for `eth_getLogs` is to retrieve the transfer events for a specific Token ID from an ERC-721 smart contract. ERC-721 is a token standard for non-fungible tokens (NFTs), and each token transfer is recorded as an event on the blockchain. By retrieving these events, developers can track the movement of NFTs and build applications that interact with them. # eth_getProof | Cronos Source: https://docs.chainstack.com/reference/cronos-getproof openapi/cronos_node_api/eth_getProof.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns a Merkle proof for a specific account, contract storage, or both for a given block. Cronos via Chainstack. Cronos API method that returns a Merkle proof for a specific account, contract storage, or both for a given block. Merkle proofs enable users to verify the existence and authenticity of data within a Merkle trie, a data structure that helps optimize and secure data retrieval in blockchains. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > On Global Nodes, this method returns proofs for recent blocks only. An older block returns `-32000` with `target version is pruned`. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). ## Parameters * `address` — Ethereum address of the account for which the proof is requested. * `array` — the array of 32-byte storage keys that need to be proven and included. See [eth\_getStorageAt](/reference/cronos-getstorageat). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `balance` — represents the account balance. Refer to [eth\_getBalance](/reference/cronos-getbalance). * `codeHash` — indicates the code hash associated with the account. For a simple account without any code, the value will be `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. * `nonce` — refers to the account's nonce. See [eth\_getTransactionCount](/reference/cronos-gettransactioncount). * `storageHash` — represents the SHA3 hash of the StorageRoot. All storage data will provide a Merkle proof that starts with this `rootHash`. This hash serves as the starting point for obtaining a Merkle proof for all storage entries linked to the account. * `accountProof` — an array of RLP-serialized Merkle trie nodes, starting with the `stateRoot` node and following the path of the SHA3 (address) as the key, which can be used to verify the existence and authenticity of data within the trie. * `storageProof` — contains the requested storage entries in an array, with each entry being an object that includes the following properties: * `key` — the requested storage key * `value`— the value associated with the storage key * `proof` — an array of RLP-serialized Merkle trie nodes, starting with the `storageHash` node and following the path of the SHA3 (key), which can be used to verify the existence and authenticity of data within the trie. ## `eth_getProof` code examples The following examples retrieve the Merkle proof for the USDC contract. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getProof() { // Get the storage slot hash (value = keccak256(storage address + slot index)) const storageSlotHash = '0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c'; // Define the contract address for WCRO const contractAddress = '0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23'; // Get the Merkle proof of USDT's smart contract using a custom JSON-RPC call const proof = await provider.send('eth_getProof', [contractAddress, [storageSlotHash], 'latest']); const block = await provider.send("eth_getBlockByNumber", ["latest",false]); // The state root is crucial for Merkle verification const stateRoot = block.stateRoot; console.log('state root:', stateRoot); console.log(proof); } ``` ```python web3.py theme={"system"} from web3 import Web3 httpUrl = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(httpUrl)) proof = web3.eth.get_proof('0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23', ["0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c"], "latest") print(proof) ``` > [!NOTE] > > Read [Deep dive into Merkle proofs and eth\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) to learn more about the Merkle trie and how to use `eth_getProof`. ## Use case A use case for the `eth_getProof` method can be when a decentralized application (DApp) interacts with a smart contract on the Cronos blockchain. The DApp needs to verify the state of the contract's storage variables without downloading the entire blockchain data. By using `eth_getProof`, the DApp can request Merkle proof for specific storage slots in the contract. This proof can then be used to cryptographically verify the authenticity and accuracy of the storage data, ensuring the DApp is working with the correct contract state. # eth_getStorageAt | Cronos Source: https://docs.chainstack.com/reference/cronos-getstorageat openapi/cronos_node_api/eth_getStorageAt.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns the data stored at a specific storage slot within a smart contract. Use it on Cronos via Chainstack. Cronos API method that returns the data stored at a specific storage slot within a smart contract. It can help developers read a smart contract's internal state, like user data or balances, to inform decisions and build more advanced applications that interact with smart contracts on the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity` — the index of the storage position to retrieve the data from * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response `data` — the data stored in the storage slot. Returned as a hexadecimal string. ## `eth_getStorageAt` code examples The following example returns the storage value at slot `2` from the [WCRO Token on the Cronos mainnet](https://cronoscan.com/token/0x5c7f8a570d578ed84e63fdfa7b1ee72deae1ae23#readContract). In this case, this is the decimals value. This method can retrieve the different storage values in time by querying different blocks in the past. Note that to query a state in the past, you need an [archive node](https://chainstack.com/evm-nodes-a-dive-into-the-full-vs-archive-mode). > [!NOTE] > > cURL needs a `HEX String` starting with `0x` to identify the block if you want to use a hex integer block number as a parameter.\ > For example, block number `14000000` will be `0xD59F80`. Use the [Chainstack EVM Knife](https://web3tools.chainstacklabs.com/hexadecimal-decimal) to make those conversions. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getstorage = async (address, slot, block) => { const storage = await provider.send("eth_getStorageAt", [address, slot, block]); console.log(storage); }; getstorage("0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23", "2", "latest" ) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" storage = web3.eth.get_storage_at("0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23", 2, "latest") print(web3.to_hex(storage)) ``` ## Use case One use case for `eth_getStorageAt` is analyzing how the value stored at a specific storage slot of a smart contract changes over time. For example, a developer may want to track the change in a specific variable of a smart contract, such as the total supply of a token, and perform some analysis on it. # eth_getTransactionByHash | Cronos Source: https://docs.chainstack.com/reference/cronos-gettransactionbyhash openapi/cronos_node_api/eth_getTransactionByHash.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns information about a transaction from its hash. It is used to retrieve information about a specific transaction by its hash. Cronos API method that returns information about a transaction from its hash. It is used to retrieve information about a specific transaction by its hash. The method returns an object with details about the transaction, such as the sender, recipient, gas used, transaction value, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the transaction's sender is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the transaction sender is willing to pay per gas unit. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction., usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet; it essentially identifies how many transactions an account has made. It is used to ensure each transaction is executed only once. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. This field is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (transactionHash) => { const transaction = await provider.send("eth_getTransactionByHash", [transactionHash]); console.log(transaction); }; getTransaction("0x2fe2d1f60c3abc7642e0a4c7929d356fc1009a0c1e60722b285d663ee7a0f636") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction("0x2fe2d1f60c3abc7642e0a4c7929d356fc1009a0c1e60722b285d663ee7a0f636")) ``` ## Use case One use case for `eth_getTransactionByHash` is to inspect a specific transaction and display information about it, such as the address sent, the block it was included in, and the number of transactions the sender sent so far (nonce). This information can be useful for monitoring and analyzing transactions. # eth_getTransactionCount | Cronos Source: https://docs.chainstack.com/reference/cronos-gettransactioncount openapi/cronos_node_api/eth_getTransactionCount.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns the number of transactions sent from an address at the selected block. Chainstack Cronos reference. Cronos API method that returns the number of transactions sent from an address at the selected block. This value is also called `nonce`; it is an important piece of information, especially to ensure that a transaction is not sent twice. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`— the address to retrieve the transaction count. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — an integer value identifying the number of transactions sent from an address at the specified block. ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getNonce = async (address, blockId) => { const nonce = await provider.send("eth_getTransactionCount", [address, blockId]); console.log(nonce); }; getNonce("0xc37D7385c4A717636E12ad985daF883E2d5869D5", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_count("0xc37D7385c4A717636E12ad985daF883E2d5869D5", "latest")) ``` ## Use case One of the most common use cases for `eth_getTransactionCount`is to create the transaction object built in a script designed to send a transaction. The nonce field is required, and it is retrieved using the `eth_getTransactionCount` method. # net_listening | Cronos Source: https://docs.chainstack.com/reference/cronos-listening openapi/cronos_node_api/net_listening.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns a boolean value indicating whether the client is currently listening for network connections or not. Cronos API method that returns a boolean value indicating whether the client is currently listening for network connections or not. The `net_listening` method can be useful in many scenarios where monitoring the status of an Ethereum client's network connection is important to ensure it works properly. ## Parameters * `none` ## Response * `boolean` — a boolean value that indicates whether a node is actively seeking peer connections. `true` if the client is actively listening for connections, `false` if not. ## `net_listening` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const isListening = async () => { const listen = await provider.send("net_listening"); console.log(listen); } isListening() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) is_listening = web3.provider.make_request('net_listening', []) print(is_listening) ``` ## Use case A method like `net_listening` can be used to monitor the network status of the Cronos node a developer connects to. Imagine a blockchain explorer where users can search for transactions, addresses, and other information on the blockchain. To provide accurate information to its users, the website needs to be able to monitor the current status of its connection. # eth_maxPriorityFeePerGas | Cronos Source: https://docs.chainstack.com/reference/cronos-maxpriorityfeepergas openapi/cronos_node_api/eth_maxPriorityFeePerGas.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns an estimate of the current priority fee per gas. eth_maxPriorityFeePerGas on Cronos via Chainstack. Cronos API method that returns an estimate of the current priority fee per gas. This fee is the tip that users may pay miners,post-merge, to validators on top of the base fee to incentivize the inclusion of their transactions in a block. This method helps users adjust their transaction fees in a dynamic market, ensuring transactions are processed promptly without overpaying. ## Parameters * `none` ## Response * `quantity` — the estimated priority fee per gas needed, represented as a hexadecimal string. ## `eth_maxPriorityFeePerGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimatePriorityFee = async () => { const priorityFeePerGas = await provider.getFeeData(); console.log(`Estimated priority fee per gas: ${priorityFeePerGas.maxPriorityFeePerGas}`); }; estimatePriorityFee(); ``` ```python web3.py theme={"system"} from web3 import Web3 NODE_URL = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(NODE_URL)) eth_max_priority_fee_per_gas = web3.provider.make_request('eth_maxPriorityFeePerGas', []) print(eth_max_priority_fee_per_gas) ``` ## Use Case A practical use case for `eth_maxPriorityFeePerGas` using `ethers.js` is to calculate transaction fees dynamically for applications and wallets to accurately estimate the cost of transactions, especially during periods of high network congestion. This method ensures transactions are confirmed promptly without overpaying. By fetching the current priority fee (`maxPriorityFeePerGas`) and the maximum fee per gas (`maxFeePerGas`), applications can dynamically calculate the total transaction fee required for prompt processing under the current network conditions. # eth_newBlockFilter | Cronos Source: https://docs.chainstack.com/reference/cronos-newblockfilter openapi/cronos_node_api/eth_newBlockFilter.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that creates a filter that watches for new blocks on the blockchain. eth_newBlockFilter on Cronos via Chainstack. Cronos API method that creates a filter that watches for new blocks on the blockchain. It returns a filter ID, which can be used to retrieve the results using the `eth_getFilterChanges` method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real-time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter. The filters created are stored on the blockchain client instance. The filter fill is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_uninstallFilter](/reference/cronos-uninstallfilter) to remove the filter ## `eth_newBlockFilter` code examples > [!NOTE] > > **web3.eth.filterdeprecation** > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newBlockFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') return blocks_filter except Exception as e: print(e) blocks = get_new_blocks() filter_id = blocks.filter_id print(filter_id) ``` ## Use case One use case for `eth_newBlockFilter` in a simple DApp is to listen for new blocks and update the user interface with the latest block information. When the DApp starts, it creates a new filter using `eth_newBlockFilter` to listen for new blocks. When a new block is added to the blockchain, the filter is triggered, and the DApp retrieves the latest block information using a Web3 library. # eth_newFilter | Cronos Source: https://docs.chainstack.com/reference/cronos-newfilter openapi/cronos_node_api/eth_newFilter.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that generates a filter object based on the filter parameters. Available on Cronos via Chainstack JSON-RPC nodes. Cronos API method that generates a filter object based on the filter parameters. It returns a filter ID, which can be used to retrieve the filter results using the `eth_getFilterChanges` method. By creating a filter for specific events, developers can receive notifications when those events occur and use them to trigger actions in their applications. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. > * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. > * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. > * `earliest` — the earliest available or genesis block. > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `result` — a hexadecimal string representing the ID of the newly created filter > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). ## `eth_newFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const createFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x5C0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23' topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const createFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ## Use case You can use `eth_newFilter` to create a filter for a specific action on a smart contract, for example, to monitor the transfer transactions from the [Wrapped CRO token](https://cronoscan.com/token/0x5c7f8a570d578ed84e63fdfa7b1ee72deae1ae23). The idea is to create a filter using the `eth_newFilter` method to monitor an ERC-20 smart contract, WCRO in this case. # eth_newPendingTransactionFilter | Cronos Source: https://docs.chainstack.com/reference/cronos-newpendingtransactionfilter openapi/cronos_node_api/eth_newPendingTransactionFilter.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that creates a filter that listens for new pending transactions on the blockchain. Use it on Cronos via Chainstack. Cronos API method that creates a filter that listens for new pending transactions on the blockchain. It returns a filter ID, which can be used to retrieve the results using the `eth_getFilterChanges method`. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real-time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter ## Code examples > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). ## `eth_newPendingTransactionFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newPendingTransactionFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_pending_transactions(): try: blocks_filter = web3.eth.filter('pending') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) new_filter = get_new_pending_transactions() print(new_filter) ``` ## Use case One way to use the `eth_newPendingTransactionFilter` method is to listen for new pending transactions at predefined intervals and extract specific data from them. For instance, a decentralized application might check for pending transactions every second and identify those that transfer a value greater than a certain amount of the CRO token. This could be useful for real-time monitoring high-value transactions or detecting potential fraud or security threats. # net_peerCount | Cronos Source: https://docs.chainstack.com/reference/cronos-peercount openapi/cronos_node_api/net_peerCount.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns the number of peers connected to the node. Cronos developers can call net_peerCount via Chainstack. Cronos API method that returns the number of peers connected to the node. This method can be useful for developers who want to monitor their node's connectivity and ensure it functions as expected. By checking the number of connected peers, developers can verify that their node receives updates and is properly synchronized with the network. ## Parameters * `none` ## Response * `quantity` — an integer value indicating the current number of peers connected to the node ## `net_peerCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const peersCount = async () => { const peers = await provider.send("net_peerCount"); console.log(`Peers connected: ${peers}`); } peersCount() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) peers = web3.provider.make_request('net_peerCount', []) print(peers) ``` ## Use case One practical use case for the `net_peerCount` method is to monitor the number of peers connected to a node and take some action if the number drops below a certain threshold. For example, a developer might want to send a notification to check the node if the peer count is low for more than 10 minutes. # eth_sendRawTransaction | Cronos Source: https://docs.chainstack.com/reference/cronos-sendrawtransaction openapi/cronos_node_api/eth_sendRawTransaction.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that allows submitting a signed transaction to the network. Reference for eth_sendRawTransaction on Cronos via Chainstack. Cronos API method that allows submitting a signed transaction to the network. Once a transaction is signed, you can use the `eth_sendRawTransaction` method to submit the signed transaction to the Cronos network for processing. > [!WARNING] > > Note that the interactive example in this page will not work, due to the fact that `eth_sendRawTransaction` is used to modify the state of the blockchain, it is not possible to duplicate the same request. ## Parameters * `data`—the signed transaction. The serialized transaction data includes transaction metadata such as the nonce, gas price, gas limit, recipient address, and data payload, as well as the digital signature generated using the private key associated with the address that is sending the transaction. Typically, the transaction is signed with a library such as web3.py, web3.js, or ethers.js using the sender's private key. ## Response * `result` — the unique hash identifying the transaction or the zero hash if the transaction is unavailable. The zero hash is a special value representing an invalid or null hash. ## `eth_sendRawTransaction` code examples The following examples demonstrate using Web3 libraries to transfer CRO. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function sendEth(value) { // Define the sender and receiver addresses, and the private key const sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a'; const receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114'; const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Define the gas limit const gasLimit = await provider.estimateGas({ from: sender, to: receiver, value: ethers.parseEther(value), }); // Get the transaction count for the sender address const nonce = await provider.getTransactionCount(sender); // Define the transaction object const transactionObject = { to: receiver, gasPrice: ethers.parseUnits('50', 'gwei'), gasLimit: gasLimit.toString(), nonce: nonce, value: ethers.parseEther(value), }; // Sign the transaction using the private key const wallet = new ethers.Wallet(privateKey, provider); console.log(`Sending transaction...`) const transaction = await wallet.sendTransaction(transactionObject); console.log('Transaction hash:', transaction.hash); } sendEth("2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the sender and receiver addresses, and the private key sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a' receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114' private_key = '1111111111111111111111111111111111111111111111111111111111111111' # Estimate gas limit gas_limit = web3.eth.estimate_gas({ 'from': sender, 'to': receiver }) # Build the transaction object transaction = { 'nonce': web3.eth.get_transaction_count(sender), 'to': receiver, 'value': web3.to_wei(1, 'ether'), # value to send 'gas': gas_limit, 'gasPrice': web3.eth.gas_price, 'chainId': web3.eth.chain_id } # Sign the transaction signed_tx = web3.eth.account.sign_transaction(transaction, private_key) print(f'Signed transaction: {signed_tx}') # Send the transaction print('Sending transaction...') tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) print(f'Transaction hash: {web3.toHex(tx_hash)}') ``` > [!NOTE] > > Note that you will need to edit the `amountToSend`, `recipientAddress`, and `secretKey` fields to be able to use this code. ## Use case One potential use case for `eth_sendRawTransaction` is to automatically swap CRO for a token when its price reaches a certain level. `eth_sendRawTransaction` will allow custom transaction parameters, such as gas price or gas limit, to be specified to optimize the speed and cost of the transaction. For instance, you could build a program monitoring the price of a specific token and execute a `swapExactETHForTokens` transaction when the token reaches a certain price level, it could use `eth_sendRawTransaction` to submit a raw transaction with a custom gas price and limit optimized for the current network conditions. # eth_syncing | Cronos Source: https://docs.chainstack.com/reference/cronos-syncing openapi/cronos_node_api/eth_syncing.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns an object with information about the current synchronization status of the node, or false if the node is fully synced. Cronos API method that returns an object with information about the current synchronization status of the node, or `false` if the node is fully synced. This method can track the progress of a node's synchronization with the Cronos blockchain. The returned object contains data such as the starting block, current block, and highest block of the node, allowing developers to monitor and estimate the time remaining for the node to sync fully. ## Parameters * `none` ## Response * `result` — the boolean value `false` when not syncing or a JSON object when syncing: * `startingBlock` — the block number where the synchronization process started, encoded as hexadecimal. * `currentBlock` — the block number that the node has currently processed, same as `eth_blockNumber`, encoded as hexadecimal. * `highestBlock` — the block number of the latest block in the blockchain known to the node. ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const syncStatus = async () => { const status = await provider.send("eth_syncing", []); console.log(status); }; syncStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" print (web3.eth.syncing) ``` ## Use case The `eth_syncing` method can be useful for developers building applications that interact with the Cronos blockchain. For example, you may want to provide feedback to your application's user about the status of the node the application is connecting to. # eth_uninstallFilter | Cronos Source: https://docs.chainstack.com/reference/cronos-uninstallfilter openapi/cronos_node_api/eth_uninstallFilter.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method used to remove a filter previously created using one of the following methods:. Use it on Cronos via Chainstack. Cronos API method used to remove a filter previously created using one of the following methods: * [eth\_newFilter](/reference/cronos-newfilter) * [eth\_newPendingTransactionFilter](/reference/cronos-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/cronos-newblockfilter) Upon successful execution, the filter is removed and will no longer emit events. This method can be useful for optimizing resource usage when a developer no longer needs to monitor certain events or blocks. It can also be used to clean up unused filters in order to reduce clutter in the client-side filter list. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/cronos-newfilter) > * [eth\_newPendingTransactionFilter](/reference/cronos-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/cronos-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `string` — the filter ID that you want to uninstall ## Response * `boolean` — a boolean value indicating whether the filter was successfully uninstalled. `true` if successfully removed, and `false` if not. ## `eth_uninstallFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function removeFilter(filter) { try { const removed = await provider.send('eth_uninstallFilter', [filter]); console.log(removed); // Log whether the filter is removed } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' removeFilter(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def remove_filter(filter_id): try: removed = web3.eth.uninstall_filter(filter_id) return removed except Exception as e: print(e) filter_id = '0x0' print(remove_filter(filter_id)) ``` ## Use case One use case for `eth_uninstallFilter` is to optimize resource usage in a DApp. When a DApp needs to monitor events on the blockchain, it can create a filter using [eth\_newFilter](/reference/cronos-newfilter),[eth\_newPendingTransactionFilter](/reference/cronos-newpendingtransactionfilter), or [eth\_newBlockFilter](/reference/cronos-newblockfilter) to listen for specific events or blocks. However, once the DApp no longer needs to monitor these events or blocks, it can use `eth_uninstallFilter` to stop watching for them. By doing so, the DApp can reduce the number of active filters and free up resources, such as network bandwidth and computational power, that would otherwise be used to maintain the filter. For example, let's say that a DApp is monitoring incoming blocks, logs batches of 50 blocks, and then uses `uninstallFilter` to remove the filter. # Custom JS tracer | Fantom Source: https://docs.chainstack.com/reference/custom-js-tracer-fantom Reference for the Custom JS tracer JSON-RPC method on the Fantom blockchain via Chainstack nodes. Parameters, response, and examples included. > [!NOTE] > > **Available on customized Dedicated Nodes only** > > Custom JavaScript tracers are available as customized solutions on the [Enterprise plan](https://chainstack.com/pricing/) on [Dedicated Nodes](/docs/dedicated-node). In the context of Fantom transactions, custom JavaScript tracing is a method used to filter and extract particular data from the comprehensive data produced during a transaction's execution inside the Ethereum Virtual Machine (EVM). Custom JavaScript tracers enable customers to concentrate on specific data points pertinent to their needs rather than reviewing the entire transaction trail, which might be a massive quantity of data. > [!NOTE] > > [Pre-built tracers](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) are also available, but allow for less fine tuning. These JavaScript-written tracers can be constructed using a number of different methods to specify how they interact with transaction data. Users can design unique tracers that meet their needs by giving a JavaScript expression and the tracer option when using one of the tracing methods. ## Developing a custom JavaScript tracer Transaction traces provide complete information about the EVM status during a transaction's execution, but often users are only interested in a subset of this data. JavaScript trace filters can be used to isolate the required information. > [!NOTE] > > Custom JavaScript tracing is available with any of the tracing methods. When the tracer option is specified in one of the tracing methods, JavaScript-based tracing is enabled. The tracer should resolve to an object with result and fault methods. Four additional methods exist: setup, step, enter, and exit. * Setup — invoked once at the beginning of the tracer construction. It accepts a single argument, `config`, which is used to pass options to the tracer. * Step — a function called for each step of the EVM or when an error occurs. It takes two arguments, `log` and `db`, and is used to trace the specified transaction. * Result — a function that returns a JSON-serializable value to the RPC caller. It takes two arguments, `ctx` and `db`. * Fault — a function invoked when an error occurs during opcode execution, which was not reported in step. It takes the same arguments as step: `log` and `db`. * Enter and exit — invoked on stepping in and out of an internal call, respectively. They are used for `CALL` variants, `CREATE` variants, and transfers implied by a `SELFDESTRUCT`. > [!NOTE] > > Find more details in the [Geth documentation](https://geth.ethereum.org/docs/developers/evm-tracing/custom-tracer#custom-javascript-tracing). ## Custom JavaScript tracer example Here you can find a summary of the steps to create a custom JavaScript tracer and code examples: **Step 1** Choose the appropriate RPC method. Depending on what you want to trace, select the appropriate RPC method, such as `debug_traceTransaction()` for individual transactions. **Step 2** Define the tracer object. Create a JavaScript object that defines the behavior of the custom tracer. This object should include methods like setup, step, result, fault, enter, and exit. Each of these methods will determine how the tracer interacts with the transaction data during its execution. The following tracer logs the gas used at each step during the execution of a transaction: ```javascript tracer theme={"system"} { gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {} } ``` **Step 3** Pass the tracer object to the RPC method. Convert the tracer object to a string and pass it as an argument to the chosen RPC method. The tracer object should be defined within single quotes to ensure it is interpreted as a JavaScript expression. For example, using the `debug_traceTransaction()` method with the custom tracer defined above would look like this: ```javascript debug_traceTransaction theme={"system"} debug.traceTransaction('0x1e60e4b78c1097d01226dca6dad808f1de05efd705670a28ae8d57171c91d4aa', { tracer: '{gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {}}' }); ``` ### Run the custom tracer with curl To run a custom JavaScript tracer using curl, you need to make an HTTP POST request to your Ethereum client with the appropriate JSON-RPC payload. Create a JSON object that includes the JSON-RPC version, method, and tracer object (converted to a string). For example, using the previous tracer example for the `debug_traceTransaction` method: ```shell JSON object theme={"system"} { "jsonrpc": "2.0", "id": 1, "method": "debug_traceTransaction", "params": [ "0x19b65f065ba8ebe235f0180bfcbc62ec0e7a043073f9f11f46c5775e898ac763", { "tracer": "{gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {}}" } ] } ``` The full curl request will look like the following: ```shell Custom tracer request theme={"system"} curl --location 'YOUR_CHAINSTACK_NODE_URL' \ --header 'Content-Type: application/json' \ --data '{"jsonrpc":"2.0","id":1,"method":"debug_traceTransaction","params":["0x1e60e4b78c1097d01226dca6dad808f1de05efd705670a28ae8d57171c91d4aa",{"tracer":"{gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {}}" }]}' ``` # Custom JS tracing | Ethereum Source: https://docs.chainstack.com/reference/custom-js-tracing-ethereum Reference for the Custom JS tracing JSON-RPC method on the Ethereum blockchain via Chainstack nodes. Parameters, response, and examples included. > [!NOTE] > > **Available on customized Dedicated Nodes only** > > Custom JavaScript tracers are available as customized solutions on the [Enterprise plan](https://chainstack.com/pricing/) on [Dedicated Nodes](/docs/dedicated-node). In the context of Ethereum transactions, custom JavaScript tracing is a method used to filter and extract particular data from the comprehensive data produced during the execution of a transaction inside the Ethereum Virtual Machine (EVM). Custom JavaScript tracers enable customers to concentrate on specific data points pertinent to their needs rather than reviewing the entire transaction trail, which might be a massive quantity of data. > [!NOTE] > > [Pre-built tracers](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) are also available, but allow for less fine tuning. These JavaScript-written tracers can be constructed using a number of different methods to specify how they interact with transaction data. Users can design unique tracers that meet their needs by giving a JavaScript expression and the tracer option when using one of the tracing methods. ## Developing a custom JavaScript tracer Transaction traces provide complete information about the EVM status during a transaction's execution, but often users are only interested in a subset of this data. JavaScript trace filters can be used to isolate the required information. > [!NOTE] > > Custom JavaScript tracing is available with any of the tracing methods. When specifying the tracer option in one of the tracing methods, JavaScript-based tracing is enabled. The tracer should resolve to an object with result and fault methods. There are four additional methods: setup, step, enter, and exit. * Setup — invoked once at the beginning of the tracer construction. It accepts a single argument, `config`, which is used to pass options to the tracer. * Step — a function called for each step of the EVM or when an error occurs. It takes two arguments, `log` and `db`, and is used to trace the specified transaction. * Result — a function that returns a JSON-serializable value to the RPC caller. It takes two arguments, `ctx` and `db`. * Fault — a function invoked when an error occurs during opcode execution, which was not reported in step. It takes the same arguments as step: `log` and `db`. * Enter and exit — invoked on stepping in and out of an internal call, respectively. They are used for `CALL` variants, `CREATE` variants, and transfers implied by a `SELFDESTRUCT`. > [!NOTE] > > Find more details in the [Geth documentation](https://geth.ethereum.org/docs/developers/evm-tracing/custom-tracer#custom-javascript-tracing). ## Custom JavaScript tracer example Here you can find a summary of the steps to create a custom JavaScript tracer and code examples: 1. Choose the appropriate RPC method. Depending on what you want to trace, select the appropriate RPC method, such as `debug_traceTransaction()` for individual transactions. 2. Define the tracer object. Create a JavaScript object that defines the behavior of the custom tracer. This object should include methods like setup, step, result, fault, enter, and exit. Each of these methods will determine how the tracer interacts with the transaction data during its execution. The following tracer logs the gas used at each step during the execution of a transaction: ```javascript tracer theme={"system"} { gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {} } ``` 3. Pass the tracer object to the RPC method. Convert the tracer object to a string and pass it as an argument to the chosen RPC method. The tracer object should be defined within single quotes to ensure it is interpreted as a JavaScript expression. For example, using the `debug_traceTransaction()` method with the custom tracer defined above would look like this: ```javascript debug_traceTransaction theme={"system"} debug.traceTransaction('0x1e60e4b78c1097d01226dca6dad808f1de05efd705670a28ae8d57171c91d4aa', { tracer: '{gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {}}' }); ``` ### Run the custom tracer with curl To run a custom JavaScript tracer using curl, you need to make an HTTP POST request to your Ethereum client with the appropriate JSON-RPC payload. Create a JSON object that includes the JSON-RPC version, method, and tracer object (converted to a string). For example, using the previous tracer example for the `debug_traceTransaction` method: ```shell JSON object theme={"system"} { "jsonrpc": "2.0", "id": 1, "method": "debug_traceTransaction", "params": [ "0x1e60e4b78c1097d01226dca6dad808f1de05efd705670a28ae8d57171c91d4aa", { "tracer": "{gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {}}" } ] } ``` The full curl request will look like the following: ```shell Custom tracer request theme={"system"} curl --location 'YOUR_CHAINSTACK_NODE_URL' \ --header 'Content-Type: application/json' \ --data '{"jsonrpc":"2.0","id":1,"method":"debug_traceTransaction","params":["0x1e60e4b78c1097d01226dca6dad808f1de05efd705670a28ae8d57171c91d4aa",{"tracer":"{gasUsed: [], step: function(log) { this.gasUsed.push(log.getGas()); }, result: function() { return this.gasUsed; }, fault: function() {}}" }]}' ``` # eth_createAccessList | Base Source: https://docs.chainstack.com/reference/debug-createaccesslist openapi/base_node_api/eth_createAccessList.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_createAccessList generates an access list for a transaction based on specific default values. Base via Chainstack. Base API method `eth_createAccessList` generates an access list for a transaction based on specific default values. An access list includes addresses and storage keys the transaction intends to access, facilitating more efficient gas usage. ## Parameters * `transactionObject` — an object containing transaction parameters with default values: * `from` — the address of the sender. * `data` — the data being sent with the transaction. * `blockParameter` — specifies the blockchain state to use for generating the access list, with a default value of `"pending"`. ## Response * `result` — an object containing: * `accessList` — the generated access list, detailing addresses and storage keys the transaction will access. * `gasUsed` — the estimated gas used to execute the transaction with the generated access list. ## Use case The `eth_createAccessList` method is crucial for: * Preparing transactions that optimize gas usage by explicitly stating which addresses and storage keys will be accessed. * Developers aiming to reduce transaction costs and improve efficiency on the Base network. * Transactions that comply with EIP-2930, which introduces access lists as a transaction type to specify state access. This method provides a practical approach to optimizing transaction execution and gas usage on the Base blockchain by pre-determining the access list. # eth_feeHistory | Base Source: https://docs.chainstack.com/reference/debug-feehistory openapi/base_node_api/eth_feeHistory.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_feeHistory retrieves historical gas fee data, providing insights into gas price trends and block utilization over time. Base API method `eth_feeHistory` retrieves historical gas fee data, providing insights into gas price trends and block utilization over time. This method is valuable for estimating transaction fees and understanding network congestion. ## Parameters * `blockCount` — the number of blocks to include in the history. * `newestBlock` — the newest block (by number or one of the strings `"earliest"`, `"latest"`, or `"pending"`) to include in the history. * `rewardPercentiles` — an array of percentiles to calculate effective priority fees over the requested blocks. ## Response * `result` — an object containing: * `oldestBlock` — the oldest block number in the history. * `baseFeePerGas` — an array of base fees per gas for each block. * `gasUsedRatio` — an array of ratios of gas used to gas limit for each block. * `reward` — an array of arrays containing the priority fees at the specified percentiles for each block. ## Use case The `eth_feeHistory` method is essential for: * Wallets and interfaces providing users with data-driven gas price suggestions. * Applications and services monitoring Base network congestion and optimizing transaction fee estimates. * Analysts studying Base gas fee trends and block space utilization. # eth_gasPrice | Base Source: https://docs.chainstack.com/reference/debug-gasprice openapi/base_node_api/eth_gasPrice.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method eth_gasPrice retrieves the current gas price, which is crucial for executing transactions on the Base network. On Base. Base API method `eth_gasPrice` retrieves the current gas price, which is crucial for executing transactions on the Base network. The gas price is determined by the demand for transaction processing and the supply of miners willing to process transactions. ## Parameters * `none` — This method does not require any parameters. ## Response * `result` — the current gas price, encoded as a hexadecimal string. This value fluctuates based on network activity and miner demand. ## Use case The `eth_gasPrice` method is essential for: * Users and applications preparing to send transactions, enabling them to set an appropriate gas price to ensure timely transaction processing. * Wallets and interfaces that provide gas price suggestions to optimize for cost and confirmation time. * Developers and analysts monitoring the Base network's congestion and transaction costs. # trace_transaction | Base Source: https://docs.chainstack.com/reference/debug-tracetransaction openapi/base_node_api/trace_transaction.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method trace_transaction provides a detailed trace of a single transaction's execution. Available on Base via Chainstack. Base API method `trace_transaction` provides a detailed trace of a single transaction's execution. This method is essential for developers, auditors, and blockchain analysts who need to examine the execution steps of a transaction, including contract calls, state changes, and event emissions. It offers insights into the transaction's behavior on the Ethereum Virtual Machine (EVM) and its impact on the blockchain state. ## Parameters * `transactionHash` — The hash of the transaction you wish to trace, specified as a hexadecimal string. ## Response * `result` — An array containing the trace data for the specified transaction. Each element in the array represents a step in the transaction's execution, detailing operations such as calls to other contracts, value transfers, and the execution outcomes. ## Use case The `trace_transaction` method is invaluable for conducting in-depth analyses of transactions. Developers can use it to debug and optimize smart contract interactions, auditors can employ it to ensure transaction security and integrity, and analysts might use it to investigate transaction patterns or detect anomalies. This method provides a detailed view of how a transaction interacts with the blockchain, which is crucial for understanding its effects and ensuring its correctness. # debug traceBlockByHash | Fantom Chain Source: https://docs.chainstack.com/reference/debug_traceblockbyhash-fantom-chain Fantom API method that traces the execution of a block. This method can debug and analyze smart contracts and transactions on the Fantom blockchain. Fantom API method that traces the execution of a block. This method can debug and analyze smart contracts and transactions on the Fantom blockchain. It provides a detailed trace of the execution of a block, including information on all the transactions and calls that interacted with the block, as well as the gas used, memory, storage operations, and other performance metrics for each operation. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `hash` — the hash of the block to be traced. * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. ## JSON-RPC example ```shell theme={"system"} curl https://fantom-mainnet.core.chainstack.com/4ab982aa70a7baead515ffdb5915df3f \ -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"debug_traceBlockByHash","params":["0x00043882000007ac65a7650d8c4cd06636dbde73d1e9458378aecc4c652184b2", {"tracer": "4byteTracer"}]}' ``` ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call. Encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ## `debug_traceBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByHash = async (blockHash) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer const tracer = { tracer: '4byteTracer' }; const traces = await provider.send("debug_traceBlockByHash", [blockHash, tracer]); console.log(traces); }; traceBlockByHash("0x00043882000007ac65a7650d8c4cd06636dbde73d1e9458378aecc4c652184b2") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block_hash = "0x00043882000007ac65a7650d8c4cd06636dbde73d1e9458378aecc4c652184b2" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer tracer = { "tracer": '4byteTracer' } block_traces = web3.provider.make_request('debug_traceBlockByHash', [block_hash, tracer]) print(block_traces) ``` ## Use case One practical use case for `debug_traceBlockByHash` with the `4byteTracer` tracer would be for auditing or analysis purposes of a blockchain. For example, if there are token contracts on the blockchain and the transfer function of these contracts is used to send tokens between users, a developer could use the `debug_traceBlockByHash` method with the `4byteTracer` tracer to trace the transactions in a given block and count how many times the transfer function was called. # debug traceBlockByNumber | Fantom Source: https://docs.chainstack.com/reference/debug_traceblockbynumber-fantom Fantom debug_traceBlockByNumber method — trace the execution of all transactions in a block by number, including internal calls and gas usage details. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. ## JSON-RPC example ```shell theme={"system"} curl https://fantom-mainnet.core.chainstack.com/4ab982aa70a7baead515ffdb5915df3f \ -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"debug_traceBlockByNumber","params":["latest", {"tracer": "callTracer"}]}' ``` ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ## `debug_traceBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByNumber = async (block) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer const tracer = { tracer: 'callTracer' }; const traces = await provider.send("debug_traceBlockByNumber", [block, tracer]); console.log(traces); }; traceBlockByNumber("latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block = "latest" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer tracer = { "tracer": 'callTracer' } block_traces = web3.provider.make_request('debug_traceBlockByNumber', [block, tracer]) print(block_traces) ``` ## Use case One use case for `traceBlockByNumber` would be to trace a block, calculate how many transactions are in it, and then display the number of transactions and the average gas used. To accomplish this, you can use `traceBlockByNumber` to retrieve the trace of each transaction in the block and then iterate over the transactions to count the total number of transactions and calculate the total gas used. Once you have this information, you can divide the total gas used by the total number of transactions to get the average gas used. # debug traceTransaction | Fantom Source: https://docs.chainstack.com/reference/debug_tracetransaction-fantom Fantom debug_traceTransaction method — replay a transaction to get a step-by-step execution trace with opcodes, gas usage, and state change details. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `hash` — the hash identifying a transaction. * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) in the debug and trace overview. You can also use additional configuration parameters. The following settings are available: * `disableStorage` — when enabled, it prevents tracing of storage changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. By default, `debug_traceTransaction` traces both memory and storage changes, but storage tracing can be particularly resource-intensive, especially for large transactions. * `disableStack` — when enabled, it skips tracing of [stack](https://en.wikipedia.org/wiki/Stack_\(abstract_data_type\)) changes made by the transaction being analyzed. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableMemory` — when `false`, it prevents tracing of memory changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableMemory`. * `enableReturnData` — when `false`, it prevents the method from tracing the return data produced by the transaction being analyzed. The return data tracing can be particularly resource-intensive, requiring a significant amount of time and processing power to complete. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableReturnData`. * `timeout` — setting that allows developers to customize the method's timeout period for JavaScript-based tracing calls. The default timeout is `5s`, and you can find the values formats in the [Go documentation](https://pkg.go.dev/time#ParseDuration). > [!NOTE] > > It's worth noting that when using one of the three built-in tracers in Ethereum clients, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData`settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the [Struct/opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#structopcode-logger). ## JSON-RPC example ```shell theme={"system"} curl https://fantom-mainnet.core.chainstack.com/4ab982aa70a7baead515ffdb5915df3f \ -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"debug_traceTransaction","params":["0xa2b1e35f59d184da1996b37803ed2e8e81057be7d07a93af48a410fd9338d616", {"tracer": "callTracer"}]}' ``` ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ### `Struct/opcode logger` response * `object` — the `Struct/opcode logger` traces object: * `pc` — the current program counter of the transaction, which represents the location of the current opcode being executed. A register that keeps track of the address of the next instruction to be executed in a program. * `op` — the current [opcode](https://ethereum.org/en/developers/docs/evm/opcodes/) being executed, such as PUSH1, ADD, or SWAP1. A code that represents a specific operation or instruction that a computer or processor can understand and execute. * `gas` — the amount of gas remaining in the transaction at the current step of execution. * `gasCost` — the amount of gas consumed by the current opcode being executed. * `depth` — the current depth of the call stack for the transaction, reflecting the number of active frames representing nested function calls. * `stack` — the contents of the EVM stack at the current step of the transaction's execution. The stack is a data structure used by the EVM to manage and manipulate data during the execution of opcodes * `memory` — the contents of the memory at the current step of execution. A temporary data storage location that is available to smart contracts during execution. ## `debug_traceTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txhash) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. const tracer = {}; const traces = await provider.send("debug_traceTransaction", [txhash, tracer]); console.log(traces); }; traceTransaction("0xa2b1e35f59d184da1996b37803ed2e8e81057be7d07a93af48a410fd9338d616") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) tx_hash = "0xa2b1e35f59d184da1996b37803ed2e8e81057be7d07a93af48a410fd9338d616" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. tracer = { "tracer": '4byteTracer' } tx_traces = web3.provider.make_request('debug_traceTransaction', [tx_hash, tracer]) print(tx_traces) ``` ## Use case A potential use case for the `debug_traceTransaction` method is to inspect failed transactions on the Fantom blockchain by using the built-in `callTracer` tracer to extract the error and reason. You can effectively utilize this feature by creating a script that analyzes the latest block's transactions and selectively extracts data only from the failed ones. ## Try the `debug_traceTransaction` RPC method yourself # Enable Debug & Trace APIs for your Ethereum Node Source: https://docs.chainstack.com/reference/enable-debug-trace-apis-for-your-ethereum-node Reference docs for the Enable Debug & Trace APIs for your Ethereum Node JSON-RPC method on the Chainstack blockchain, available via Chainstack JSON-RPC nodes. ## Introduction The debug and trace API methods are special RPC methods that allow you to replay transactions in the Ethereum Virtual Machine to get the execution details in the exact same way as they happened on the chain. To get the debug and trace API methods enabled on your Chainstack node on the [paid plans](/docs/manage-your-billing#manage-your-organization-subscription-plan-and-support-level). # eth_estimateGas | Polygon Source: https://docs.chainstack.com/reference/estimategas openapi/polygon_node_api/gas_data/eth_estimateGas.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns an estimate of gas units needed for a given transaction. eth_estimateGas on Polygon via Chainstack. Polygon API method that returns an estimate of gas units needed for a given transaction. It provides an estimate of the maximum amount of gas units required to complete the transaction based on the network's current state and the transaction's parameters. A client or wallet can use this estimate to determine the appropriate amount of gas to include in a transaction to ensure that the network processes it. ## Parameters * `object` — the transaction call object, same as in `eth_call`; the `from` field is optional, and the `nonce` field is omitted: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — the estimated amount of gas units needed, represented as a hexadecimal string. ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimateGas = async () => { // The transaction to estimate is a simple transfer in this example. const transactionObj = { from: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", to: "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8", } const gasUnits = await provider.estimateGas(transactionObj); console.log(`Estimated gas units required: ${gasUnits}`); }; estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.estimate_gas({"from":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","to":"0xbe0eb53f46cd790cd13851d5eff43d12404d33e8"}, "latest" )) ``` ## Use case A practical use case for `eth_estimateGas` is to calculate the gas cost required for a transaction that transfers a value from one Polygon address (`fromAddress`) to another (`toAddress`). The gas cost is an important factor in transactions, as it determines the amount of fees that need to be paid by the sender to the network for processing the transaction. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function estimateGasCost(from, to, value) { try { const gasEstimate = await provider.estimateGas({ from: from, to: to, value: value }); return gasEstimate } catch (error) { console.error(`Error estimating gas cost: ${error}`); } } async function main() { const fromAddress = '0x65bf36d6499a504100eb504f0719271f5c4174ec'; const toAddress = '0x81ab6Fd4A68d0aDDFAD65F9F97Bc1427dCd40C8E'; const value = ethers.parseEther("25"); // 25 MATIC in wei const estimatedGas= await estimateGasCost(fromAddress, toAddress, value) if (estimatedGas) { console.log(`Estimated gas cost: ${estimatedGas}`); } } main() ``` The `estimateGasCost` function wraps the call to `provider.estimateGas` in a `try-catch` block so that any errors that occur during the estimation process can be handled gracefully. If an error occurs, the error message is logged to the console. If the estimate is successful, the estimated gas cost is returned. The main function is the entry point of the code. It sets the `fromAddress`, `toAddress`, and value for the transaction, and then calls the `estimateGasCost` function to get the estimated gas cost. The estimated gas cost is logged to the console if the estimate is successful, but it is available to be used for further processing in the `estimatedGas` constant. # eth_call | Polygon Source: https://docs.chainstack.com/reference/ethcall openapi/polygon_node_api/execute_transactions/eth_call.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. Polygon API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. This can be useful for testing and debugging by simulating transfers or smart contract transactions and retrieving data from the blockchain. > [!NOTE] > > **Disclaimer** > > The interactive example in this page uses `eth_call` to call the `lpToken` method from [SushiSwap MiniChefV2 smart contract on Polygon](https://polygonscan.com/address/0x0769fd68dfb93167989c6f7254cd0d766fb2841f#code). It returns the address of the liquidity pool at the index specified in the data. In this case, the input is 1, and it returns the [current address for the USDC-WETH pool for SushiSwap](https://polygonscan.com/address/0x34965ba0ac2451a34a0471f04cca3f990b8dea27) on Polygon. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `object` — the transaction call object: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters. See the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `data` — the result of executing the specified call on the Ethereum Virtual Machine (EVM), encoded as a hexadecimal string. ## `eth_call` code examples The following code snippets provide a clear demonstration of how to simulate a standard MATIC transfer between two accounts. Note that when using `eth_call` to simulate a transfer or a smart contract transaction, the sender account must have a sufficient balance to cover the associated gas fee. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const simulateTransfer = async () => { try { // Define the accounts to transfer between const fromAddress = "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5"; const toAddress = "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"; // Define the value to transfer const value = ethers.parseEther("1"); // Build transaction object const transactionObject = { from: fromAddress, to: toAddress, gasLimit: ethers.toQuantity(21000), value: value } // Use eth_call to simulate the transfer on the latest block const result = await provider.call(transactionObject); console.log(`Result: ${result}`); } catch (error) { console.error(`Error: ${error.message}`); } } simulateTransfer(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the accounts to transfer between from_address = web3.to_checksum_address('0xAd80f9fcc2a315aBB6007F6fd6cCEC74C1168b9a') to_address = web3.to_checksum_address('0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a') # Define the value to transfer (in Ether) value = web3.to_wei(1, 'ether') # Build the transaction object transaction = { 'from': from_address, 'to': to_address, 'gas': 21000, # 21000 is the standard for a regular transfer 'value': value, 'data': '', } # Use eth_call to simulate the transfer result = web3.eth.call(transaction, 'latest') print(result) ``` Executing this code will produce a return value of `0x`, indicating a successful transaction. This implies that the transaction will be executed without any errors when the code is used to send the transaction. ## Use case A common use case for `eth_call` is calling smart contract functions. For example, interact with an ERC-20 token smart contract deployed on the Polygon network and retrieve the balance of a specific account. The following code uses the ethers.js library to call the `balanceOf` function of the [APE token smart contract](https://polygonscan.com/token/0xB7b31a6BC18e48888545CE79e83E06003bE70930#code) on the Polygon network. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Contract address and ABI for the token const CONTRACT_ADDRESS = "0xB7b31a6BC18e48888545CE79e83E06003bE70930"; const ABI = [ { "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "balance", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" } ]; // Asynchronously fetches the balance of an account for the given address async function getAccountBalance(address) { try { // Create a new contract instance const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider); // Call the balanceOf function to retrieve the account balance in wei using eth_call const balanceInWei = await contract.balanceOf(address); // Convert the balance from wei to ethers const balanceInEth = ethers.formatEther(balanceInWei); // Log the account balance in ethers to the console console.log(`The account balance for address ${address} is: ${balanceInEth} APE`); } catch (error) { console.error("Error occurred while fetching account balance:", error); } } // Call the getAccountBalance function with a sample account address getAccountBalance("0xf07f7da6381fa11a4b47ea31164828a1bb9cdeaf"); ``` The `getAccountBalance` function is called with the address of the account whose balance we want to retrieve. This function creates a new contract instance, calls the `balanceOf` function of the smart contract with the provided address, and retrieves the account balance in Wei. The balance is then converted to the ethers unit using the `formatEther` function of the `ethers` object. Finally, the account balance is printed to the console for easy viewing. # Accounts info | Ethereum Source: https://docs.chainstack.com/reference/ethereum-accounts-info-rpc-methods Ethereum account information JSON-RPC methods — retrieve balances, nonces, contract code, and storage data for accounts on the Ethereum network. Retrieving account information from the Ethereum blockchain can be done by using the following methods: > [card] [eth\_getTransactionCount](/reference/ethereum-gettransactioncount) > [card] [eth\_getBalance](/reference/ethereum-getbalance) > [card] [eth\_getCode](/reference/ethereum-getcode) > [card] [eth\_getStorageAt](/reference/ethereum-getstorageat) > [card] [eth\_getProof](/reference/getproof) # Genesis data Source: https://docs.chainstack.com/reference/ethereum-beacon-genesis GET https://ethereum-mainnet.core.chainstack.com/beacon/{apiKey}/eth/v1/beacon/genesis Retrieve the Ethereum Beacon Chain genesis state with the eth/v1/beacon/genesis endpoint, including genesis time, validators root, and fork version. # Genesis data ```bash theme={"system"} GET https://ethereum-mainnet.core.chainstack.com/beacon/{apiKey}/eth/v1/beacon/genesis ``` Developers can use this method to ensure they are interacting with the correct version of the Ethereum Beacon Chain and synchronize with the network. ## Parameters * `none` ## Response * `object` — genesis information. * `genesis_time` — the UNIX timestamp of when the Beacon Chain's genesis event occurred. This is important because it marks the starting point of the Ethereum Beacon Chain network. * `genesis_validators_root` — the Merkle root of the initial set of validators at the time of the genesis event. Validators are participants in the PoS system that propose and attest to new blocks. * `genesis_fork_version` — the fork version of the Ethereum protocol active during the genesis event. This helps clients understand the version of the network they are interacting with. ### 200 Beacon genesis information successfully retrieved **data** `object` *required* **genesis\_time** `date-time` *required* The genesis time as an ISO 8601 formatted string. **genesis\_validators\_root** `string` *required* The 32-byte genesis validators root as a hex string. **genesis\_fork\_version** `string` *required* The 4-byte genesis fork version as a hex string. ### 500 Internal server error **code** `integer` *required* **message** `string` *required* ```json 200 theme={"system"} { "data": { "genesis_time": "2025-03-21T15:09:52.470Z", "genesis_validators_root": "string", "genesis_fork_version": "string" } } ``` ```json 500 theme={"system"} { "code": 0, "message": "string" } ``` # Blocks info | Ethereum Source: https://docs.chainstack.com/reference/ethereum-blocks-rpc-methods Ethereum block information JSON-RPC methods — query block details including transactions, timestamps, block number, headers, and uncle counts. Retrieving block information from the Ethereum blockchain can be done by using the following methods: > [card] [eth\_blockNumber](/reference/ethereum-getblocknumber) > [card] [eth\_getBlockByHash](/reference/ethereum-getblockbyhash) > [card] [eth\_getBlockByNumber](/reference/ethereum-getblockbynumber) > [card] [eth\_getBlockTransactionCountByHash](/reference/ethereum-getblocktransactioncountbyhash) > [card] [eth\_getBlockTransactionCountByNumber](/reference/ethereum-getblocktransactioncountbynumber) > [card] [eth\_newBlockFilter](/reference/ethereum-newblockfilter) This information can be used to track the state of the blockchain and for: * **Blockchain analysis** — insights into the overall activity on the network, such as the number of processed transactions and average transaction fees, can be obtained. * **Block explores** — web-based tools that provide access to information on a blockchain, including block data, transactions, and addresses, allowing users to search, view, and analyze the network's activity. * **Smart contract analysis** — the activity of specific smart contracts * **DApp development** — block information can be utilized in the development of decentralized applications. # Chain info | Ethereum Source: https://docs.chainstack.com/reference/ethereum-chain-data-rpc-methods Ethereum chain data RPC methods for querying network details like chain ID, syncing status, and protocol version through Chainstack node endpoints. For example, if a developer wants to build a DApp on Ethereum, they would use these methods to get information about the network they are connecting to. They could use the chain ID information to make sure they are connecting to the right network and to ensure the security of their DApp; for example, distinguishing between the main network and a test network. Retrieving chain information from the Ethereum blockchain can be done by using the following methods: > [card] [eth\_chainId](/reference/chainid) > [card] [eth\_syncing](/reference/ethereum-syncing) # clearSubscriptions | Ethereum Source: https://docs.chainstack.com/reference/ethereum-clearsubscriptions Use eth_clearSubscriptions on Ethereum via ethers.js to remove all active WebSocket subscriptions from your Chainstack node connection at once. The subscriptions available using the ethers.js `provider.on` method are: > [card] [subscribe('newBlockHeaders')](/reference/ethereum-subscribenewblockheaders) > [card] [subscribe('pendingTransactions')](/reference/ethereum-subscribependingtransactions) > [card] [subscribe('logs')](/reference/ethereum-subscribelogs) > [card] [subscribe('syncing')](/reference/ethereum-subscribesyncing) ## Parameters `boolean` — keep the [subscribe("syncing")](/reference/ethereum-subscribesyncing) subscription if `true`. ## Response `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `clearSubscriptions` code example ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function clearSubscriptions() { try { await provider.removeAllListeners(); console.log("Subscriptions were canceled successfully"); return process.exit(1) } catch (error) { console.error("Failed to cancel subscriptions:", error); return process.exit(1) } } clearSubscriptions() ``` ## Use case A way to use the `clearSubscriptions` method is to place it in a timer to clear all of the subscriptions after a pre-determined about. The following code is an example using the [subscribe("newBlockHeaders")](/reference/ethereum-subscribenewblockheaders) method, which will be stopped and removed after one minute using `clearSubscriptions`: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToNewBlocks() { try { // Subscribe to new blocks by listening for the 'block' event provider.on("block", handleNewBlock); console.log("New subscription: block"); // Attach a listener that logs any provider errors provider.on("error", handleError); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs the received block number async function handleNewBlock(blockNumber) { const block = await provider.getBlock(blockNumber); console.log(block); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } async function clearSubscriptions() { try { await provider.removeAllListeners(); console.log("Subscriptions were canceled successfully"); return process.exit(1) } catch (error) { console.error("Failed to cancel subscriptions:", error); return process.exit(1) } } async function main() { subscribeToNewBlocks(); // Run clearSubscriptions() once after 60 seconds setTimeout(clearSubscriptions, 60000); } main() ``` The code starts by defining an async function called `subscribeToNewBlocks` that creates a new subscription to new blocks by registering a listener for the `block` event using the `provider.on` method. The provider emits this event for every new block, and you can attach additional listeners, such as one for the `error` event. The code defines two event listener functions `handleNewBlock` and `handleError` that log messages to the console when these events occur. The `subscribeToNewBlocks` function is called from the `main` function, which is also defined in the code. The `main` function calls `subscribeToNewBlocks` to create a new subscription and then schedules the `clearSubscriptions` function to run once after 60 seconds using the `setTimeout` function. The `clearSubscriptions` function uses the `provider.removeAllListeners` method to cancel any existing subscriptions and log a message to the console if the cancellation was successful. # Client information | Ethereum Source: https://docs.chainstack.com/reference/ethereum-client-data-rpc-methods Ethereum client information JSON-RPC methods — check client version, network ID, chain ID, listening status, and peer count for your node. Retrieving blockchain client information from an Ethereum node can be done by using the following methods: > [card] [web3\_clientVersion](/reference/ethereum-clientversion) > [card] [net\_listening](/reference/ethereum-listening) > [card] [net\_peerCount](/reference/ethereum-peercount) # web3_clientVersion | Ethereum Source: https://docs.chainstack.com/reference/ethereum-clientversion openapi/ethereum_node_api/client_info/web3_clientVersion.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the client type and version running on the Ethereum node. web3_clientVersion on Ethereum via Chainstack. Ethereum API method that returns the client type and version running on the Ethereum node. This information can be useful to developers to verify that a node they are connecting to is compatible with their needs. ## Parameters * `none` ## Response * `string` — a string identifying the type of client, version, operating system, and language version running on the node ## `web3_clientVersion` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const clientVersion = async () => { const version = await chainstack.send("web3_clientVersion"); console.log(`Client version: ${version}`); }; clientVersion(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) client_version = web3.provider.make_request('web3_clientVersion', []) print(client_version) ``` ## Use case A use case for the `web3_clientVersion` method can be to verify which client is running to then decide which method to run. Let's say you have a DApp that needs to retrieve all of the transaction receipts in a block; the Erigon client has a method for this, `eth_getBlockReceipts`, but with Geth, this method is only available starting from V1.13.0. You can use the `web3_clientVersion` method in your logic to identify which client you are using to see if you have the method available. > [!NOTE] > > Read [Expanding your blockchain horizons: The eth\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) to learn how to build a program to emulate `eth_getBlockReceipts` on any EVM-compatible chain. Here is an implementation of this use case using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); // Call eth_getBlockReceipts, which has no dedicated ethers helper async function getBlockReceipts(blockNumber) { return chainstack.send("eth_getBlockReceipts", [blockNumber]); } // Get client version async function getClient() { const client = await chainstack.send("web3_clientVersion", []); return client; } // Extract the client's name and version async function getClientName() { const clientVersion = await getClient(); // Split the clientVersion string by '/' let parts = clientVersion.split('/'); // Split the second part by '-' and take the first element let version = parts[1].split('-')[0]; // Concatenate the client name and version let clientNameWithVersion = parts[0] + '/' + version; console.log(clientNameWithVersion); return clientNameWithVersion; } // Parse version from version string function parseVersion(versionString) { const versionMatch = versionString.match(/(\d+\.\d+\.\d+)/); return versionMatch ? versionMatch[1] : null; } // Compare versions function isVersionGreaterThanOrEqual(v1, v2) { const v1Parts = v1.split('.').map(Number); const v2Parts = v2.split('.').map(Number); for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) { const num1 = v1Parts[i] || 0; const num2 = v2Parts[i] || 0; if (num1 > num2) return true; if (num1 < num2) return false; } return true; // versions are equal } // Get the receipts based on the client async function getAllReceipts(blockId) { const clientNameWithVersion = await getClientName(); const [clientName, clientVersion] = clientNameWithVersion.split('/'); if (clientName === 'Geth') { const version = parseVersion(clientVersion); if (isVersionGreaterThanOrEqual(version, '1.13.0')) { console.log('Client is Geth with supported version.'); const receipts = await getBlockReceipts(blockId); return receipts; } else { console.log('Client is Geth, but version is not supported.'); } } else if (clientName === 'erigon') { console.log('Client is Erigon'); const receipts = await getBlockReceipts(blockId); return receipts; } else { console.log('Client version is not Geth or Erigon'); } } async function main() { try { const receipts = await getAllReceipts('latest'); if (receipts !== undefined) { console.log(receipts); } } catch (error) { console.error(error); } } main(); ``` Let's break down the code: 1. **The `getBlockReceipts` Method**:\ The `getBlockReceipts` function is a helper that calls the `eth_getBlockReceipts` JSON-RPC method directly with `chainstack.send`, passing a single parameter `blockNumber`. Because ethers.js does not expose a dedicated helper for this method, `provider.send` is used to issue the raw JSON-RPC call and return the resulting receipts. 2. **The `getClient` Function**:\ This function retrieves the client version by calling the `web3_clientVersion` JSON-RPC method without parameters through `chainstack.send`. It returns the full client version string. 3. **The `getClientName` Function**:\ `getClientName` calls `getClient` to get the complete client version string. It then extracts both the client's name and its version by splitting the version string at the first `/` and `-` characters. The function concatenates these parts to form a string in the format "ClientName/Version". 4. **The `getAllReceipts` Function**:\ This function uses `getClientName` to retrieve the name and version of the client in a combined format. It checks if the client is `Geth` and, if so, further verifies whether its version is greater than or equal to `1.13.0` using the `parseVersion` and `isVersionGreaterThanOrEqual` functions. If the client is `Geth` with a supported version or `Erigon`, the function proceeds to fetch block receipts using the `getBlockReceipts` helper. If the client is neither a supported version of `Geth` nor `Erigon`, it logs a message indicating this. 5. **Version Parsing and Comparison**:\ Additional functions `parseVersion` and `isVersionGreaterThanOrEqual` are included for parsing the version string and comparing it against a specified minimum version. `parseVersion` extracts the semantic version number from the client version string, and `isVersionGreaterThanOrEqual` compares this version against the specified minimum version, `1.13.0` in this case. 6. **Main Function and Execution Flow**:\ The `main` function serves as the entry point for execution. It attempts to call `getAllReceipts` with a specified block identifier, handling any exceptions and logging the results if available. # Debug and Trace | Ethereum Source: https://docs.chainstack.com/reference/ethereum-debug-trace-rpc-methods Ethereum debug and trace API methods for inspecting smart contract execution, analyzing gas usage, and tracing transactions on Chainstack nodes. Developers can access the debug and trace tools on the Ethereum blockchain with the following methods: > [card] [debug\_traceBlockByHash](/reference/ethereum-traceblockbyhash) > [card] [debug\_traceBlockByNumber](/reference/ethereum-traceblockbynumber) > [card] [debug\_traceTransaction](/reference/ethereum-tracetransaction) > [card] [debug\_traceCall](/reference/ethereum-tracecall) > [card] [trace\_transaction](/reference/ethereum-trace_transaction) > [card] [trace\_block](/reference/ethereum-trace_block) ## Pre-built native tracers > [!NOTE] > > ### Custom JavaScript tracers need a Dedicated Node > > The tracers listed below are native (built-in) tracers—you select one by name, and they run on all Ethereum nodes. Custom JavaScript tracers, where you pass raw JavaScript as the `tracer`, are available as customized solutions on the [Enterprise plan](https://chainstack.com/pricing/) on [Dedicated Nodes](/docs/dedicated-node). Developers can customize the type of tracing using various debug and trace methods, and there are numerous pre-built tracers available to choose from. Here is a list of the pre-built tracers that can be utilized: ## `4byteTracer` This tracer looks for function signatures that are 4 bytes long during contract execution and gathers information about the method identifiers and the size of the provided data. This information can be helpful in matching a signature against the data size. ## `callTracer` This tracer extracts information about all internal calls made by a transaction, providing visibility into how contracts interact with each other. ## `prestateTracer` This tracer returns sufficient information about an account to create a local execution of the transaction. This procedure runs the given transaction again and monitors every aspect of the state that is modified or accessed during execution. ## `noopTracer` This tracer does nothing but respond with a blank object. This a no operations tracer for testing setup. ## `flatCallTracer` This tracer returns call traces in the flat, Parity-style format—the same structure as the `trace_*` namespace. Instead of the nested tree produced by `callTracer`, it returns a flattened list of call frames, which is convenient for tooling built around the OpenEthereum/Parity trace format. > [!NOTE] > > Available on Ethereum only. ## `muxTracer` This tracer runs several native tracers in a single pass and returns their combined output. Pass a `tracerConfig` object keyed by tracer name—for example, `callTracer` and `prestateTracer`, each with its own configuration—and the response is an object keyed by each tracer's name. This avoids re-executing the transaction once per tracer. > [!NOTE] > > Available on Ethereum only. ## `erc7562Tracer` This tracer enforces the [ERC-7562](https://eips.ethereum.org/EIPS/eip-7562) account-abstraction validation rules, tracking opcode and storage access during the validation phase of an ERC-4337 user operation. Bundlers use it to detect rule violations before a user operation enters the mempool. > [!NOTE] > > Available on Ethereum only. ## Custom JavaScript tracers JavaScript-based tracers can be created through various approaches, allowing them to interact with transaction information in diverse ways. By providing a JavaScript formula along with the tracking option when employing any of the available tracking techniques, users can craft tailor-made trackers to suit their specific requirements. Find a guide to [Custom JS tracing | Ethereum](/reference/custom-js-tracing-ethereum) on the next page. # eth_estimateGas | Ethereum Source: https://docs.chainstack.com/reference/ethereum-estimategas openapi/ethereum_node_api/gas_data/eth_estimateGas.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns an estimate of gas units needed for a given transaction. eth_estimateGas on Ethereum via Chainstack. Ethereum API method that returns an estimate of gas units needed for a given transaction. It provides an estimate of the maximum amount of gas units required to complete the transaction based on the network's current state and the transaction's parameters. A client or wallet can use this estimate to determine the appropriate amount of gas to include in a transaction to ensure that the network processes it. ## Parameters * `object` — the transaction call object, same as in `eth_call`; the `from` field is optional, and the `nonce` field is omitted: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — the estimated amount of gas units needed, represented as a hexadecimal string. ## `eth_estimateGas` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const estimateGas = async () => { // The transaction to estimate is a simple transfer in this example. const transactionObj = { from: "0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324", to: "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8", }; const gasUnits = await chainstack.estimateGas(transactionObj); console.log(`Estimated gas units required: ${gasUnits}`); }; estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.estimate_gas({"from":"0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324","to":"0xbe0eb53f46cd790cd13851d5eff43d12404d33e8"}, "latest" )) ``` ## Use case A practical use case for `eth_estimateGas` is to calculate the gas cost required for a transaction that transfers a value from one Ethereum address (`fromAddress`) to another (`toAddress`). The gas cost is an important factor in transactions, as it determines the amount of fees that need to be paid by the sender to the network for processing the transaction. ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const provider = new ethers.ChainstackProvider("mainnet"); async function estimateGasCost(from, to, value) { try { const gasEstimate = await provider.estimateGas({ from: from, to: to, value: value }); return gasEstimate } catch (error) { console.error(`Error estimating gas cost: ${error}`); } } async function main() { const fromAddress = '0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13'; const toAddress = '0x81ab6Fd4A68d0aDDFAD65F9F97Bc1427dCd40C8E'; const value = ethers.parseEther("25") const estimatedGas= await estimateGasCost(fromAddress, toAddress, value) if (estimatedGas) { console.log(`Estimated gas cost: ${estimatedGas}`); } } main() ``` The `estimateGasCost` function wraps the call to `provider.estimateGas` in a `try-catch` block so that any errors that occur during the estimation process can be handled gracefully. If an error occurs, the error message is logged to the console. If the estimate is successful, the estimated gas cost is returned. The main function is the entry point of the code. It sets the `fromAddress`, `toAddress`, and value for the transaction, and then calls the `estimateGasCost` function to get the estimated gas cost. The estimated gas cost is logged to the console if the estimate is successful, but it is available to be used for further processing in the `estimatedGas` constant. # eth_call | Ethereum Source: https://docs.chainstack.com/reference/ethereum-ethcall openapi/ethereum_node_api/execute_transactions/eth_call.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. Ethereum API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. This can be useful for testing and debugging by simulating transfers or smart contract transactions and retrieving data from the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > **Disclaimer** > > The interactive example in this page uses `eth_call` to call the `poolInfo` method from [SushiSwap MasterChef LP staking pool smart contract on Ethereum](https://etherscan.io/address/0xc2edad668740f1aa35e4d8f227fb8e17dca888cd#code). It returns the address of the liquidity pool at the index specified in the data, plus other data. In this case, the input is 1, and it returns the [current address for the USDC-WETH pool for SushiSwap](https://etherscan.io/address/0x397FF1542f962076d0BFE58eA045FfA2d347ACa0#code) on Ethereum. ## Parameters * `object` — the transaction call object: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters. See the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `data` — the result of executing the specified call on the Ethereum Virtual Machine (EVM), encoded as a hexadecimal string. ## `eth_call` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). The following code snippets provide a clear demonstration of how to simulate a standard ether transfer between two accounts. Note that when using `eth_call` to simulate a transfer or a smart contract transaction, the sender account must have a sufficient balance to cover the associated gas fee. ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const simulateTransfer = async () => { try { // Define the accounts to transfer between const fromAddress = "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5"; const toAddress = "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"; // Define the value to transfer const value = ethers.parseEther("1"); // Build transaction object const transactionObject = { from: fromAddress, to: toAddress, gasLimit: ethers.toQuantity(21000), value: value, }; // Use eth_call to simulate the transfer on the latest block const result = await chainstack.call(transactionObject); console.log(`Result: ${result}`); } catch (error) { console.error(`Error: ${error.message}`); } }; simulateTransfer(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the accounts to transfer between from_address = web3.to_checksum_address('0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5') to_address = web3.to_checksum_address('0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a') # Define the value to transfer (in Ether) value = web3.to_wei(1, 'ether') # Build the transaction object transaction = { 'from': from_address, 'to': to_address, 'gas': 21000, # 21000 is the standard for a regular transfer 'value': value, 'data': '', } # Use eth_call to simulate the transfer result = web3.eth.call(transaction, 'latest') print(result) ``` Executing this code will produce a return value of `0x`, indicating a successful transaction. This implies that the transaction will be executed without any errors when the code is used to send the transaction. ## Use case A common use case for `eth_call` is calling smart contract functions. For example, interact with an ERC-20 token smart contract deployed on the Ethereum network and retrieve the balance of a specific account. The following code uses the `ethers.js` library to call the `balanceOf` function of the [APE token smart contract](https://etherscan.io/token/0x4d224452801aced8b2f0aebe155379bb5d594381#code) on the Ethereum network. ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); // Contract address and ABI for the token const CONTRACT_ADDRESS = "0x4d224452801ACEd8B2F0aebE155379bb5D594381"; const ABI = [ { "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "balance", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" } ]; // Asynchronously fetches the balance of an account for the given address async function getAccountBalance(address) { try { // Create a new contract instance const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, chainstack); // Call the balanceOf function to retrieve the account balance in wei using eth_call const balanceInWei = await contract.balanceOf(address); // Convert the balance from wei to ether const balanceInEth = ethers.formatEther(balanceInWei); // Log the account balance in ether to the console console.log(`The account balance for address ${address} is: ${balanceInEth} APE`); } catch (error) { console.error("Error occurred while fetching account balance:", error); } } // Call the getAccountBalance function with a sample account address getAccountBalance("0xf07f7da6381fa11a4b47ea31164828a1bb9cdeaf"); ``` The `getAccountBalance` function is called with the address of the account whose balance we want to retrieve. This function creates a new contract instance, calls the `balanceOf` function of the smart contract with the provided address, and retrieves the account balance in Wei. The balance is then converted to the ether unit using the `formatEther` function of the `ethers` object. Finally, the account balance is printed to the console for easy viewing. # Executing transactions | Ethereum Source: https://docs.chainstack.com/reference/ethereum-execution-rpc-methods Ethereum transaction execution JSON-RPC methods — call smart contracts, sign transactions, send raw transactions, and estimate gas on the network. Executing and simulating transactions on the Ethereum blockchain can be done by using the following methods: > [card] [eth\_call](/reference/ethereum-ethcall) > [card] [eth\_sendRawTransaction](/reference/ethereum-sendrawtransaction) > [card] [eth\_sendRawTransactionSync](/reference/ethereum-sendrawtransactionsync) # Filter handling | Ethereum Source: https://docs.chainstack.com/reference/ethereum-filters-rpc-methods Ethereum filter JSON-RPC methods — create event and block filters, poll for changes, retrieve filter logs, and manage on-chain event subscriptions. Handling filters can be done by using the following methods: > [card] [eth\_getFilterChanges](/reference/ethereum-getfilterchanges) > [card] [eth\_uninstallFilter](/reference/ethereum-uninstallfilter) # Gas data | Ethereum Source: https://docs.chainstack.com/reference/ethereum-gas-data-rpc-methods Ethereum gas data JSON-RPC methods — query current gas prices, estimate gas costs, and get fee history to build cost-effective dapps on Ethereum. Retrieving gas information from the Ethereum blockchain can be done by using the following methods: > [card] [eth\_estimateGas](/reference/ethereum-estimategas) > [card] [eth\_gasPrice](/reference/gasprice) > [card] [eth\_maxPriorityFeePerGas](/reference/ethereum-maxpriorityfeepergas) # eth_getBalance | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getbalance openapi/ethereum_node_api/accounts_info/eth_getBalance.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the balance of a specific Ethereum account in Wei, the smallest unit of ether. Ethereum via Chainstack. Ethereum API method that returns the balance of a specific Ethereum account in [Wei](https://www.investopedia.com/terms/w/wei.asp), the smallest unit of ether. This method allows developers to retrieve the current balance of an Ethereum account for various purposes, such as checking available funds or displaying balance information. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to check the balance of. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — the integer value of the current balance in Wei ## `eth_getBalance` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getBalance = async (address, block) => { const balanceHex = await chainstack.send("eth_getBalance", [address, block]); // Convert hex to decimal const balanceDecimal = parseInt(balanceHex, 16); // Convert wei to ether const balanceEther = ethers.formatEther(BigInt(balanceDecimal), "ether"); console.log(balanceEther); }; getBalance("0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324", "latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" address = web3.to_checksum_address("0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324") balance = web3.eth.get_balance(address, "latest") print(balance) ``` ## Use case One practical use case for `eth_getBalance` is to check the balance of an account for a program that scans the balance periodically and fills up the account when the balance drops below a certain value. Here's an example of how you can achieve this in ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const provider = new ethers.ChainstackProvider("mainnet"); const accountAddress = '0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324'; const minimumBalance = ethers.parseEther("10"); async function checkBalance(address) { const balance = await provider.getBalance(address, 'latest'); return balance; } async function fillUp(balance) { const minimumConverted = ethers.formatEther(minimumBalance); if (balance < minimumBalance) { console.log(`The balance of the account ${accountAddress} is below ${minimumConverted} ETH.`); console.log(`Sending more funds...`) // Call another function to fill up your account } else { console.log(`The balance of the account ${accountAddress} is above ${minimumConverted} ETH.`); console.log(`No need to send more funds.`) } } async function main() { const balance = await checkBalance(accountAddress); const balanceInEth = ethers.formatEther(balance); console.log(`The balance of the account ${accountAddress} is ${balanceInEth} ETH. \n`); await fillUp(balance); } main(); ``` The code is a simple script that checks the balance of an account on the Ethereum network and takes action if the balance falls below a specified minimum. The `accountAddress` variable holds the address for which you want to check the balance. The `minimumBalance` variable holds the minimum balance in Wei you want to check against. The `checkBalance` function takes an address as an argument and returns the balance of the account in Wei. The `fillUp` function takes the balance as an argument and converts the minimum balance from Wei to ether using the `formatEther` method from ethers.js, and checks if the balance is below the minimum balance. > [!NOTE] > > Note that the Wei value is converted only to display it, as it is more accurate to work with the Wei value. If the balance is below the minimum, it logs a message to the console indicating that more funds need to be sent and calls another function to fill up the account. If the balance is above the minimum, it logs a message indicating that no additional funds need to be sent. The main function calls the `checkBalance` and `fillUp` functions to convert the balance from Wei to ether using the `formatEther` method, and log the balance and the result of the check to the console. In summary, this script allows you to easily check the balance of an account on the Ethereum network and take action if the balance falls below a specified minimum. # eth_getBlockByHash | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getblockbyhash openapi/ethereum_node_api/blocks_info/eth_getBlockByHash.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns information about the block matching the given block hash. eth_getBlockByHash on Ethereum via Chainstack. Ethereum API method that returns information about the block matching the given block hash. This method can be useful for analyzing a block and its transactions. ## Parameters * `hash` — the hash of the block * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — the hash of the previous block that was used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ethereum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ethereum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era to track cumulative mining difficulty. After the merge to Proof of Stake (PoS), this value is no longer relevant. * `extradata` — the extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — the array of uncle hashes. ## `eth_getBlockByHash` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const eth_getBlockByHash = async () => { const blockByNumber = await chainstack.send("eth_getBlockByHash", [ "0x5f9685380eb11efc8e965324a03ef0dac46f13d37e2dc0ac8763ce37c6fd6245", false, ]); console.log(blockByNumber); }; eth_getBlockByHash(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block("0x5f9685380eb11efc8e965324a03ef0dac46f13d37e2dc0ac8763ce37c6fd6245", False)) ``` ## Use case The `eth_getBlockByHash` is excellent for analytics purposes. For example, it can be used to quickly find out the hashes of transactions that create new smart contracts. The `transactions` field, an array of all the transactions within the block, is returned in the response. By iterating through this array and examining each transaction's `to` field, a user can determine which transactions in the block are creating new smart contracts. The following example uses the web3.js library to create a function to extract this data: The previous example shows how the `eth_getBlockByHash` method might be used to know which transactions create a smart contract. When a new smart contract is created, the `to` field will be `null`. Running this function on a block hash with the transactions detail field set to `true` will inspect each transaction's `to` field. If the field is `null`, the transaction creates a smart contract, printing the transaction hash, which can then be used for further analysis. # eth_getBlockByNumber | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getblockbynumber openapi/ethereum_node_api/blocks_info/eth_getBlockByNumber.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns information about the block matching the given block number. Available on Ethereum via Chainstack. Ethereum API method that returns information about the block matching the given block number. This method can retrieve a wide range of information about the block, such as its hash, timestamp, difficulty, and gas limit. You can also retrieve transaction objects included in the block if needed. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum's Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — a block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ethereum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ethereum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era to track cumulative mining difficulty. After the merge to Proof of Stake (PoS), this value is no longer relevant. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — array of uncle hashes. ## `eth_getBlockByNumber` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const eth_getBlockByNumber = async () => { const blockByNumber = await chainstack.send("eth_getBlockByNumber", [ "latest", false, ]); console.log(blockByNumber); }; eth_getBlockByNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block("latest", False)) ``` ## Use case The `eth_getBlockByNumber` method can determine whether a block you are analyzing has been included in the main chain or was forked due to a "reorg". A block hash is generated through a cryptographic hash function, which takes the block's data, including transactions and other information, and the hash of the parent block (the previous block) and outputs a unique, fixed-length digest. This hash represents the current block within the blockchain. The script compares the parent hash of the following block with the hash under inspection. If the two values match, it can be concluded that the block was included in the main chain. Conversely, if the values do not match, it indicates that the block was not used to generate the next block's hash and has therefore been excluded from the main chain, resulting in a forked block. ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const provider = new ethers.ChainstackProvider("mainnet"); // If the hash of the block to inspect is === to the parent hash of the next block, then this is not a forked block. async function isForked(block) { const nextBlock = block + 1 console.log(`Block to inspect: ${block}`) console.log(`Next block: ${nextBlock} \n`) const blockToInspectHash = (await provider.getBlock(block, false)).hash console.log(`Block hash of the block you are inspecting: ${blockToInspectHash} \n`) const nextBlockParentHash = (await provider.getBlock(nextBlock, false)).parentHash console.log(`Parent hash of the next block: ${nextBlockParentHash} \n`) if (String(blockToInspectHash) == String(nextBlockParentHash)) { console.log(`You are inspecting the correct block`) return false } else { console.log(`You are inspecting a forked block!`) return true } } const blockToInspect = 16394103 isForked(blockToInspect) ``` In this case, the `eth_getBlockByNumber` is used to retrieve the hash of a block you want to analyze and the parent hash of the next block. # eth_blockNumber | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getblocknumber openapi/ethereum_node_api/blocks_info/eth_blockNumber.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the latest block number of the blockchain. Reference for eth_blockNumber on Ethereum via Chainstack. Ethereum API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0x69B5B` means that the block is the 432,987th block in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to, encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. ## `eth_blockNumber` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const eth_getBlockNumber = async () => { const block_Number = await chainstack.getBlockNumber(); console.log(block_Number); }; eth_getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.block_number) ``` ## Use case A possible use case for the `eth_blockNumber` method in Ethereum is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. The following code uses the ethers.js library with `ChainstackProvider` and defines a function called `getCurrentBlockNumber`. When called, this function returns the latest block number from the network, and you can use it in your DApp. ```js ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); // Define a function that returns the current block number async function getCurrentBlockNumber() { const blockNumber = await chainstack.getBlockNumber(); return blockNumber; } // Use the getCurrentBlockNumber function in your application async function main() { // Let's say you want to offer a special price on a service to a specific user // Offer expires in a week, Ethereum produces 1 block every 12 seconds which means 50400 block per week const blockInAWeek = 50400; const currentBlockNumber = await getCurrentBlockNumber(); const offerExpires = currentBlockNumber + blockInAWeek; console.log(`Your special offer will expire on block: ${offerExpires}`); } main(); ``` You can then use your application's `getCurrentBlockNumber` function to get the current block number whenever you need it. In this example, we use it to calculate on which block a special offer to a specific user will expire. We retrieve the block when we issue the offer to the user and add `50400`, which is approximately how many Ethereum blocks are produced weekly. Now, in the `offerExpires` constant, we have the last block when the user can exercise the special price. Use the `getCurrentBlockNumber` function to compare it to `offerExpires` to know if the offer is still valid. # eth_getBlockReceipts | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getblockreceipts openapi/ethereum_node_api/transaction_info/eth_getBlockReceipts.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that retrieves all transaction receipts for a given block. Available on Ethereum via Chainstack JSON-RPC nodes. Ethereum API method that retrieves all transaction receipts for a given block. Transaction receipts contain information about the execution status of a transaction and can be useful for monitoring the status of transfers or contract execution on the blockchain. > [!NOTE] > > This method is available on both the Geth and Erigon clients. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How the Merge impacts Ethereum’s application layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `result` — an array of objects with the following fields: * `Transaction receipt` — the object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the address of the contract created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects generated by this transaction, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getBlockReceipts` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const eth_getBlockReceipts = async () => { const BlockReceipts = await chainstack.send("eth_getBlockReceipts", [ "latest", ]); console.log(BlockReceipts); }; eth_getBlockReceipts(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block_receipts = web3.make_request('eth_getBlockReceipts', ['latest']) print(block_receipts) ``` ## Use case The `eth_getBlockReceipts` allows you to retrieve the receipts for all transactions in a specific block. A practical use case can be to retrieve all of the logs emitted from the transactions in a block. The method returns an array of transaction receipts containing information about the transaction and any logs generated due to the transaction execution, so the logs can be isolated. > [!NOTE] > > Read [Uncovering the power of eth\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) to learn more about transactions receipts and how to use the `eth_getBlockReceipts` method. Here is an example of how to use `eth_getBlockReceipts` in ethers.js using the `ChainstackProvider` to extract all of the logs in a specific block and return them in an array: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function retrieveTransactionLogs(blockNumber) { // Call the eth_getBlockReceipts method to retrieve the receipts for all transactions in the block let receipts = await chainstack.send("eth_getBlockReceipts", [blockNumber]); // Flatten the receipts array into a single array of logs let logs = receipts.flatMap((receipt) => receipt.logs); // Return the logs return logs; } // call retrieveTransactionLogs on the latest block of the chain async function main() { const blockNumber = "latest"; const logs = await retrieveTransactionLogs(blockNumber); console.log(logs); } main(); ``` In this example, the `retrieveTransactionLogs` function takes a block identifier as an input and returns an array of logs. The function first calls the `eth_getBlockReceipts` method using the `provider.send` method and passes in the block identifier as an argument. The result is an array of receipts, then flattened into a single array of logs using the `flatMap` function. # eth_getBlockTransactionCountByHash | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getblocktransactioncountbyhash openapi/ethereum_node_api/blocks_info/eth_getBlockTransactionCountByHash.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. Ethereum API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. ## Parameters * `hash` — the block hash of the requested block. ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByHash` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getTransactionsCount = async (blockHash) => { const count = await chainstack.send("eth_getBlockTransactionCountByHash", [ blockHash, ]); console.log(count); }; getTransactionsCount( "0xbff1ae920dd96597dd486d68b2b7c045c3bdc6d6c64dfeb9759499c9e8ca6737" ); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block_transaction_count("0xbff1ae920dd96597dd486d68b2b7c045c3bdc6d6c64dfeb9759499c9e8ca6737")) ``` ## Use case `eth_getBlockTransactionCountByHash` is a useful tool for analyzing transaction volume on the Ethereum blockchain. For instance, a [new block is generated on the Ethereum blockchain every 12 seconds](https://etherscan.io/chart/blocktime), resulting in approximately 300 blocks per hour. Using the ethers.js library, one can inspect the past 300 blocks starting from the latest block and use `eth_getBlockTransactionCountByHash` to find the number of transactions in each block. ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const provider = new ethers.ChainstackProvider("mainnet"); async function retrieveTransactionsCount() { // Get the current block number const currentBlockNumber = await provider.getBlockNumber(); // Calculate the block number from one hour ago const oneHourAgoBlockNumber = currentBlockNumber - 300; // 300 blocks/hr on average on the Ethereum mainnet // Initialize a variable to store the total number of transactions let totalTransactionCount = 0; // Loop through all blocks from the current block number to the block number from one hour ago for (let block = currentBlockNumber; block > oneHourAgoBlockNumber; block--) { // Get the hash of each block const getBlockHash = await provider.getBlock(block); const blockHash = getBlockHash.hash // Get the number of transactions in this block using each hash const transactionCount = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); // Add the number of transactions in this block to the total totalTransactionCount += Number(transactionCount); console.log(`Block #${block} has ${Number(transactionCount)} transactions.`); } console.log(`The total number of transactions in the previous hour is: ${totalTransactionCount}`); } retrieveTransactionsCount(); ``` This example connects to an Ethereum node using the ethers.js library. The `retrieveTransactionsCount` function gets the current block number, calculates the block number from one hour ago, and uses a for loop to iterate through all blocks in that range. The function then retrieves the hash of each block and the number of transactions in each block and adds it to a total transaction count. The final result, the total number of transactions in the previous hour, is logged to the console. This is a good example of how you can combine different methods. # eth_getBlockTransactionCountByNumber | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getblocktransactioncountbynumber openapi/ethereum_node_api/blocks_info/eth_getBlockTransactionCountByNumber.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the number of transactions in a block specified by block number or tag. Chainstack Ethereum reference. Ethereum API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByNumber` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getTransactionsCount = async (blockId) => { const count = await chainstack.send("eth_getBlockTransactionCountByNumber", [ blockId, ]); console.log(count); }; getTransactionsCount("0x1026220"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.get_block_transaction_count('latest')) # A hex value starting with "0x" is accepted as well. ``` ## Use case `eth_getBlockTransactionCountByNumber` can be used to analyze how many transactions are included on the Ethereum blockchain in a certain period. For instance, a [new block is generated on the Ethereum blockchain every 12 seconds](https://etherscan.io/chart/blocktime), resulting in approximately 300 blocks per hour. Using the ethers.js library, one can inspect the past 300 blocks starting from the latest block and use `eth_getBlockTransactionCountByNumber` to find the number of transactions in each block to sum them. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function retrieveTransactionCounts() { // Get the current block number const currentBlockNumber = await provider.getBlockNumber(); // Calculate the block number from one hour ago const oneHourAgoBlockNumber = currentBlockNumber - 300; // 300/blocks/hr on average on the Ethereum mainnet // Initialize a variable to store the total number of transactions let totalTransactionCount = 0; // Loop through all blocks from the current block number to the block number from one hour ago for (let block = currentBlockNumber; block > oneHourAgoBlockNumber; block--) { // Get the number of transactions in this block const transactionCount = await provider.send("eth_getBlockTransactionCountByNumber", [block]); // Make the necessery conversions from hex to decimal const stringCount = BigInt(transactionCount).toString(); const decimalCount = Number(stringCount) // Add the number of transactions in this block to the total totalTransactionCount += decimalCount; const stringBlock = BigInt(block).toString(); const decimalBlock = Number(stringBlock) console.log(`Block ${decimalBlock} has ${decimalCount} transactions.`); } console.log(`The total number of transactions in the previous hour is: ${totalTransactionCount}`); } retrieveTransactionCounts(); ``` Using `eth_getBlockTransactionCountByNumber` is a bit more straightforward compared to the [example shown using `eth_getBlockTransactionCountByHash`](/reference/ethereum-getblocktransactioncountbyhash) for this same use case, you don't need to extract the block hash. Instead, you can query the transaction count directly. This example is made using the ethers.js library, and you can notice that it requires more parsing compared to web3.js, and the results are given as hexadecimal. # eth_chainId | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getchainid openapi/ethereum_node_api/chain_info/eth_chainId.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the current chain ID. eth_chainId JSON-RPC method available on the Ethereum blockchain via Chainstack. Ethereum API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and generally verify if a network is the desired one. It was introduced in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). ## Parameters * `none` ## Response * `quantity` — EIP-155 Chain ID ## `eth_chainId` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const chainId = async () => { // This will return the value in Hex const chainId = await chainstack.send("eth_chainId"); console.log(`Hex Chain ID: ${chainId}`); }; chainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.chain_id) ``` ## Use case One possible use case for the `eth_chainId` method in Ethereum is to determine which chain a user is connected to when using a DApp with the MetaMask browser extension. This can be useful to provide a customized user experience based on the specific chain that the user is interacting with. Here is an example of how the `eth_chainId` method might be used in this context with MetaMask. Keep in mind that the Chain ID is returned as a hexadecimal value: ```js JavaScript theme={"system"} // Check which network is selected on MetaMask async function checkChain() { // Declare the desired chain ID to match const desiredChainId = '0x1'; // Ethereum mainnet chain ID // Retrieve the current Chain ID selected by the user ethereum.request({ method: 'eth_chainId' }).then(chainId => { // compare the actual chain ID it to the declared chain ID if (chainId !== desiredChainId) { console.log(`You are currently on the wrong network. Please switch to the mainnet.`); // Call the promtSwitch function promtSwitch() } else { console.log("This is the correct network") } }) } // Prompt user to switch to a determined network based on the chain ID async function promtSwitch() { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x1' // chainId must be in HEX with 0x in front }], }); } ``` In this case, the `checkChain` function is called to get the current chain ID using the Ethereum object provided by MetaMask. The chain ID is then checked to determine if the user is connected to the Ethereum mainnet and calls the `promtSwitch` function to prompt the user to switch chain in case the chain ID returned does not match the `desiredChainId` constant. # eth_getCode | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getcode openapi/ethereum_node_api/accounts_info/eth_getCode.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that retrieves the compiled bytecode of a smart contract providing its address as a parameter. Ethereum via Chainstack. Ethereum API method that retrieves the compiled bytecode of a smart contract providing its address as a parameter. This method returns a hexadecimal string representing the smart contract's bytecode. Developers can use this bytecode to verify if a smart contract is legitimate and to ensure that it performs the intended functions. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response `data` — the compiled bytecode of a smart contract. Returns `0x` if the address is not associated with a smart contract. ## `eth_getCode` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). The following example retrieves the compiled bytecode from the [APE token smart contract](https://etherscan.io/token/0x4d224452801aced8b2f0aebe155379bb5d594381#code). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getCode = async (address, block) => { const code = await chainstack.send("eth_getCode", [address, block]); console.log(code); }; getCode("0x4d224452801ACEd8B2F0aebE155379bb5D594381", "latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" code = web3.eth.get_code("0x4d224452801ACEd8B2F0aebE155379bb5D594381", "latest") print(web3.to_hex(code)) # Convert the Bytes result into HEX. ``` ## Use case A practical use case for the `eth_getcode` method is verifying whether an address is associated with a smart contract. This can be particularly useful for blockchain explorers, auditors, and DApp developers, who must ensure that the code they interact with is legitimate. Here is an example using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function verifyContractAddress(address) { try { const bytecode = await chainstack.getCode(address); return bytecode !== '0x'; } catch (error) { console.error(`Error: ${error.message}`); return false; } } async function main() { const addressToVerify = "0x4d224452801ACEd8B2F0aebE155379bb5D594381" const isContract = await verifyContractAddress(addressToVerify); if (isContract) { console.log(`📑 The address ${addressToVerify} is a smart contract.`); } else { console.log(`🚨 The address ${addressToVerify} is not a smart contract.`); } } main(); ``` The `verifyContractAddress` function takes an `address` as a parameter and returns a `boolean` value indicating whether the address is associated with a smart contract. The function works by using the `provider.getCode` method to retrieve the bytecode of the contract associated with the given address. If the returned bytecode is not equal to `0x`, then the function assumes that the address is associated with a smart contract and returns `true`. If an error occurs while calling `provider.getCode`, the function returns `false` and logs the error message to the console. The `main` function is responsible for calling `verifyContractAddress` with a specific address to verify and log the results to the console. In this example, `main` is set up to verify the address `0x4d224452801ACEd8B2F0aebE155379bb5D594381`. After calling `verifyContractAddress`, `main` logs either a success message indicating that the address is associated with a smart contract or a warning message indicating that it is not. Overall, this code provides a useful utility for verifying the legitimacy of smart contract addresses on the Ethereum blockchain. It can be useful for auditors, blockchain explorers, and DApp developers who want to ensure that they are interacting with trustworthy code on the blockchain. # eth_getFilterChanges | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getfilterchanges openapi/ethereum_node_api/filter_handling/eth_getFilterChanges.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Ethereum API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Filters can be created using one of the following methods: * [eth\_newFilter](/reference/ethereum-newfilter) * [eth\_newPendingTransactionFilter](/reference/ethereum-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/ethereum-newblockfilter) After creating a filter using one of the available methods, the resulting filter ID can be used to fetch changes by calling the `eth_getFilterChanges` method. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/ethereum-newfilter) > * [eth\_newPendingTransactionFilter](/reference/ethereum-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/ethereum-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `string` — the filter ID of the filter for which you want to retrieve the changes ## Response * `array` — an array that represents the changes that have occurred on the blockchain since the last time the filter was polled: * For filters created with `eth_newBlockFilter`: * `blockHash` — the hashes of the new blocks since the last time the filter was polled. * For filters created with `eth_newPendingTransactionFilter`: * `transactionHash` — the hashes identifying new pending transactions since the last time the filter was polled. * For filters created with `eth_newFilter`, the following event logs: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getFilterChanges` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function getFilterChanges(filter) { try { const changes = await chainstack.send("eth_getFilterChanges", [filter]); console.log(changes); // Log the with the new data } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = "0x4e7ef166cd43f188b0f8f9e218966a8f"; //'YOUR_FILTER_iD' getFilterChanges(filterId); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_filtered_events(filter_id): try: filter_changes = web3.eth.get_filter_changes(filter_id) print(filter_changes) # the array of events that match the filter except Exception as e: print(e) filter_id = '0x0' get_filtered_events(filter_id) ``` > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ## Use case The `eth_getFilterChanges` method can be used to periodically poll a filter to retrieve new data. In the following Python program, using the web3.py library creates a filter using [eth\_newBlockFilter](/reference/ethereum-newblockfilter) to poll for new blocks, and uses `eth_getFilterChanges` to poll the filter and retrieve new blocks. Here is the implementation in Python: ```python main.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') return blocks_filter except Exception as e: print(e) def get_filtered_events(filter_id): try: filter_changes = web3.eth.get_filter_changes(filter_id) return filter_changes # the array of events that match the filter except Exception as e: print(f'Error getting filter changes: {e}') def main(): # Create a block filter blocks = get_new_blocks() block_filter_id = blocks.filter_id if block_filter_id is not None: print(f'New block filter ID: {block_filter_id}') else: print('Error creating block filter') return # Loop forever and poll the filter every 10 seconds while True: print('Polling for new blocks...') new_blocks = get_filtered_events(block_filter_id) if new_blocks is not None: print(new_blocks) else: print('Error getting filter changes') time.sleep(5) if __name__ == '__main__': main() ``` Here is the explanation of the code: The `create_block_filter` function creates a new block filter with the `web3.eth.filter('latest')` method, which matches against the latest block. The resulting filter object is converted to a string, split into parts using the `split` method and the filter ID is extracted from the second part using array indexing. The filter ID is then returned by the function. The `get_filtered_events` function uses the `eth_getFilterChanges` method to retrieve the new blocks. The function takes a single parameter, `filter_id`, which is a string that represents the ID of the filter to be polled, the filter created by the `create_block_filter` function in this case. If the filter ID is valid, `get_filtered_events` calls `web3.eth.getFilterChanges(filter_id)` to retrieve the array of events that match the filter criteria. This array is then returned by the function, which is then printed to the console in the `main` function. The `main` function encapsulates the program logic, calling `create_block_filter` to create a new block filter and print the resulting filter ID to the console. The function then enters an infinite loop, calling `get_filtered_events` every 10 seconds to retrieve the changes to the blockchain that match the filter criteria. If the changes are not `None`, they are printed to the console. An error message is printed to the console if an error occurs while creating the block filter or retrieving the filter changes. # eth_gasPrice | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getgasprice openapi/ethereum_node_api/gas_data/eth_gasPrice.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the current gas base fee of the network. Ethereum developers can call eth_gasPrice via Chainstack. Ethereum API method that returns the current gas base fee of the network. The gas price is the quantity of the native token the transaction's sender must pay per unit of gas consumed. The value returned is in Wei. ## Parameters * `none` ## Response * `quantity` — the integer value of the current gas base fee, returned in Wei ## `eth_gasPrice` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function getGasPrice() { const baseFee = await chainstack.send("eth_gasPrice"); // This value is in Wei console.log( `The base gas fee is: ${ethers.formatUnits(baseFee, "gwei")} Gwei` ); } getGasPrice(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.gas_price) ``` ## Use case You can use `eth_gasPrice` to calculate the total gas value to send with a transaction based on the base and priority fee system. This concept was implemented with [EIP-1559](https://metamask.io/1559/) in Ethereum's London hard fork. EIP-1559 aimed to solve the problem of network congestion by implementing a dynamic fee market mechanism, which adjusts the fee required to process a transaction based on network demand. With the current system, the total gas price is composed of a **base fee** determined by the network's load and a **priority fee** added by the user. > [!NOTE] > > **Note** > > The `eth_gasPrice` method returns the base fee. The following script calculates the total fee to send with a transaction based on the base fee and an arbitrary priority fee that the user can choose. ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function getGas(priorityFee) { const priorityFeeWei = ethers.parseEther(String(priorityFee * 1e-9)); const baseFee = (await chainstack.getFeeData()).gasPrice; const totalFee = priorityFeeWei + baseFee return [totalFee, baseFee] } async function main() { // The priority fee is constant at 2 Gwei, the user can change it. const PRIORITY_FEE = 2; const [totalFee, baseFee] = await getGas(PRIORITY_FEE) const baseFeeGwei = ethers.formatUnits(baseFee, "gwei") console.log(`The priority fee: ${PRIORITY_FEE} Gwei.`) console.log(`The Ethereum base fee at this moment is: ${Number(baseFeeGwei).toFixed(2)} Gwei.`) const gweiFee = ethers.formatUnits(totalFee, "gwei") console.log(`The total gas fee should be at least: ${Number(gweiFee).toFixed(2)} Gwei.`) } main() ``` The `getGas` function calculates the total fee required to process a transaction. The function takes in a `priorityFee` as input representing the fee a user is willing to pay to prioritize their transaction processing. The function first converts the `priorityFee` from Gwei to Wei (the smallest unit of ether) using `ethers.parseEther`, which returns a `bigint`. Then, it uses the `getFeeData` method to retrieve the current base fee set by the network from `gasPrice`. The function finally adds the priority fee to the base fee and returns the total fee along with the base fee as an array. The `main` function is the entry point of the code. It first sets a constant `PRIORITY_FEE` to 2 Gwei. Then calls the `getGas` function with `PRIORITY_FEE` as an argument and destructures the returned array into two variables `totalFee` and `baseFee`. The code then converts `baseFee` and `totalFee` from Wei to Gwei using `ethers.formatUnits` for better readability, and logs the results to the console. The main function logs the priority fee, the base fee at that moment, and the minimum recommended total fee that should be paid to process a transaction. # eth_getLogs | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getlogs openapi/ethereum_node_api/logs_and_events/eth_getLogs.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns an array of all logs that match a given filter object. Available on Ethereum via Chainstack JSON-RPC nodes. Ethereum API method that returns an array of all logs that match a given filter object. Logs are records of smart contract-generated events on the network and contain valuable information about transactions and smart contract interactions. Some common use cases for `eth_getLogs` include monitoring smart contract events, tracking token transfers, and analyzing blockchain data. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags for `fromBlock` and `toBlock`** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. > * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. > * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. > * `earliest` — the earliest available or genesis block. > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `array` — an array of log objects that match the specified filter or an empty array if there have been no new events since the last poll: * `address` — the contract address from which the event originated * `topics` — an array of 32-byte data fields containing indexed event parameters * `data` — the non-indexed data that was emitted along with the event * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getLogs` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). The following examples retrieve the logs of the Transfer event from the [APE token](https://etherscan.io/token/0x4d224452801aced8b2f0aebe155379bb5d594381#code) smart contract on Ethereum. ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getLogs = async () => { const latestBlock = await chainstack.getBlockNumber(); const filter = { fromBlock: latestBlock - 100, toBlock: "latest", address: "0x4d224452801ACEd8B2F0aebE155379bb5D594381", topics: [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", ], }; const logs = await chainstack.getLogs(filter); console.log(logs); }; getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) latest_block = web3.eth.block_number filter = { 'fromBlock': latest_block - 10, 'toBlock': 'latest', 'address': '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } logs = web3.eth.get_logs(filter) print(logs) ``` > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about the `eth_getLogs` method. ## Block range limitations While `eth_getLogs` is a powerful tool, it's crucial to understand its limitations, particularly when working with different EVM-compatible chains, as these networks often have different constraints. The `eth_getLogs` method allows you to select a range of blocks to get events from and is important to exercise proper management. `eth_getLogs` block range is capped by your [subscription plan](/docs/limits#evm-range-limits). On high-activity chains or contracts, query smaller ranges and paginate to avoid oversized responses or timeouts. ## Use case One use case for `eth_getLogs` is to retrieve the transfer events for a specific Token ID from an ERC-721 smart contract. ERC-721 is a token standard for non-fungible tokens (NFTs), and each token transfer is recorded as an event on the blockchain. By retrieving these events, developers can track the movement of NFTs and build applications that interact with them. The following program uses `eth_getLogs` and ethers.js to track the transfers of the token number '6750' from the [BoredApeYachtClub](https://etherscan.io/token/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d?a=7682) collection on Ethereum. This will retrieve the logs and display the address that initiated the transfer and the block number where it occurred. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTokenTransferLogs(contractAddress, tokenId) { try { const tokenIdHex = ethers.toBeHex(tokenId); const tokenIdTopic = ethers.zeroPadValue(tokenIdHex, 32); const filter = { fromBlock: 19060000n, toBlock: "latest", address: contractAddress, topics: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", null, null, tokenIdTopic], }; const logs = await provider.getLogs(filter); const transferLogs = logs.map((log) => { const topics = log.topics; const fromAddress = topics[1]; const formattedFromAddress = `0x${fromAddress.slice(26)}`; if (!ethers.getAddress(formattedFromAddress)) { throw new Error(`${formattedFromAddress} is an invalid address`); } const blockNumber = log.blockNumber; // Display a summary console.log(`Address ${formattedFromAddress} transferred the token ${tokenId} in block ${blockNumber}.`); return { fromAddress: formattedFromAddress, tokenId: tokenId, blockNumber: blockNumber }; }); return transferLogs; } catch (error) { throw new Error(`Error getting transfer logs: ${error.message}`); } } async function main() { const contractAddress = "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"; const tokenId = 6750; try { const transferLogs = await getTokenTransferLogs(contractAddress, tokenId); console.log(transferLogs); } catch (error) { console.error(error.message); } } main(); ``` This code defines an async function called `getTokenTransferLogs` which takes a `contractAddress` and a `tokenId` as input parameters. Within the function, the `tokenId` is converted to hexadecimal format and then used to create a topic for a filter to get past logs for a specific token transfer. The `getLogs` method returns logs that match a certain filter. Once the logs have been retrieved, the function loops through each log and extracts the relevant information, such as the sender's address and the block number. It also checks whether the sender's address is valid in case the parsing logic fails. For each valid transfer log, the function logs a summary to the console and creates a transfer log object that includes the `fromAddress`, `tokenId`, and `blockNumber` properties. The function returns an array of transfer log objects, and an exception is thrown if there are any errors. The `main` function calls `getTokenTransferLogs` with a specific `contractAddress` and `tokenId`, and then logs the array of transfer log objects to the console. If there are any errors, an error message is logged to the console instead. # eth_getStorageAt | Ethereum Source: https://docs.chainstack.com/reference/ethereum-getstorageat openapi/ethereum_node_api/accounts_info/eth_getStorageAt.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the data stored at a specific storage slot within a smart contract. Chainstack Ethereum reference. Ethereum API method that returns the data stored at a specific storage slot within a smart contract. It can help developers to read a smart contract's internal state, like user data or balances, to inform decisions and build more advanced applications that interact with smart contracts on the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity` — the index of the storage position to retrieve the data from * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response `data` — the data stored in the storage slot. Returned as a hexadecimal string. ## `eth_getStorageAt` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). The following example returns the storage value at slot `0` from the Chainlink VRFCoordinatorV2 on the Ethereum mainnet. In this case, this is the address owning the smart contract. This method can retrieve the different storage values in time by querying different blocks in the past. Note that to query a state in the past, you need an [archive node](https://chainstack.com/evm-nodes-a-dive-into-the-full-vs-archive-mode). > [!NOTE] > > cURL needs a `HEX String` starting with `0x` to identify the block if you want to use a hex integer block number as a parameter.\ > For example, block number `14000000` will be `0xD59F80`. ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getstorage = async (address, slot, block) => { const storage = await chainstack.send("eth_getStorageAt", [ address, slot, block, ]); console.log(storage); }; getstorage("0x271682DEB8C4E0901D1a1550aD2e64D568E69909", "0", "latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" storage = web3.eth.get_storage_at("0x271682DEB8C4E0901D1a1550aD2e64D568E69909", 0, "latest") print(web3.to_hex(storage)) ``` ## Use case One use case for `eth_getStorageAt` is analyzing how the value stored at a specific storage slot of a smart contract changes over time. For example, a developer may want to track the change in a specific variable of a smart contract, such as the total supply of a token, and perform some analysis on it. Here's an example implementation of this use case using ethers.js. The following program analyzes the [Ape token](https://etherscan.io/token/0x4d224452801aced8b2f0aebe155379bb5d594381#code) smart contract on the Ethereum chain to track how the total supply changes over time. The storage slot matches the position of the variable in the smart contract; the following code snippet shows the top of the APE smart contract where the variables used in this program are declared. ```sol ERC20.sol theme={"system"} contract ERC20 is Context, IERC20 { using SafeMath for uint256; using Address for address; // Variables declared mapping (address => uint256) private _balances; // Storage slot 0x0 mapping (address => mapping (address => uint256)) private _allowances; // Storage slot 0x1 uint256 private _totalSupply; // Storage slot 0x2 string private _name; // Storage slot 0x3 string private _symbol; // Storage slot 0x4 uint8 private _decimals; // Storage slot 0x5 . . . ``` Here's an example implementation of this use case using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // You need an archive node is going more than 127 blocks in the past. async function getStorageValueOverTime() { // The address of the APE smart contract const apeAddress = '0x4d224452801ACEd8B2F0aebE155379bb5D594381'; // The index of the storage slots where the total supply and name data are stored const totalSupplySlot = 2; const nameSlot = 3; // The range of blocks to analyze const startBlock = 18000000n; const endBlock = BigInt(await provider.getBlockNumber()); for (let blockNumber = startBlock; blockNumber <= endBlock; blockNumber++) { // Retrieve the name and total supply from the smart contract storage at each block const [name, supply] = await Promise.all([ provider.getStorage(apeAddress, nameSlot, blockNumber), provider.getStorage(apeAddress, totalSupplySlot, blockNumber) ]); // Decode the name and total supply from hexadecimal format to their original values const decodedName = ethers.toUtf8String(name); const decodedSupply = BigInt(supply); // Convert the total supply from wei to ether and round to 4 decimal places const convertedSupply = ethers.formatEther(decodedSupply); const adjustedSupply = Number(convertedSupply).toFixed(4); // Output the block number, name, and total supply for each block console.log(`Block ${blockNumber}: Name = ${decodedName}, Total supply = ${adjustedSupply}`); } } async function main() { try { await getStorageValueOverTime(); } catch (error) { console.error(error); } } main(); ``` The bulk of the program is the `getStorageValueOverTime` function. The `getStorageValueOverTime` function retrieves and outputs the name and total supply of the APE smart contract at each block height between a specified start block and the current block height. The function begins by declaring constants for the APE contract address, the storage slot indices for the name and total supply data, the starting block height, and the current block height. Next, the function enters a `for` loop that iterates over every block between the start block and the current block height. Within the loop, the function retrieves the name and total supply from the smart contract storage at the current block height using the `provider.getStorage` method. The `Promise.all` method is used to retrieve the name and total supply in parallel for improved efficiency. The `ethers.toUtf8String` method is then used to convert the name from hexadecimal format to its original string value. The `BigInt` constructor is used to convert the total supply from hexadecimal format to a `bigint` value, which can be used for mathematical operations. The `ethers.formatEther` method is then used to convert the total supply from wei to ether. Finally, the `Number` and `toFixed` methods are used to round the total supply to four decimal places and output the block number, name, and total supply for each block; note that this is for readability, and you should use the raw Wei data to make calculations. Overall, the `getStorageValueOverTime` function provides a way to monitor changes in the state of a smart contract over time, which can be useful for understanding the contract's behavior and performance. # Ethereum API reference: JSON-RPC and Beacon quickstart Source: https://docs.chainstack.com/reference/ethereum-getting-started Use the Ethereum JSON-RPC and Beacon Chain APIs on Chainstack to deploy nodes, send transactions, and query the execution and consensus layers. Chain ID 1, proof of stake, 100 methods served, archive and debug & trace on mainnet and testnets. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). The Ethereum API gives developers programmatic access to the canonical EVM blockchain across both of its layers: the execution layer over JSON-RPC for accounts, contracts, logs, and transactions, and the consensus layer over the Beacon Chain REST API for validators, attestations, and finality. Chainstack serves both through a single Ethereum node endpoint. ## Ethereum API at a glance | Property | Value | | - | - | | Chain | Ethereum — Layer 1, proof-of-stake consensus | | Mainnet chain ID | `1` | | Testnet chain IDs | Sepolia `11155111`, Hoodi `560048` | | Block time | \~12 seconds | | Execution layer | JSON-RPC — `eth`, `net`, `web3`, `debug`, `trace`, `erigon` namespaces | | Consensus layer | Beacon Chain REST API — `/eth/v1/beacon`, `/eth/v1/validator`, `/eth/v1/node` | | Methods served by Chainstack | 100 — 69 execution-layer JSON-RPC methods + 31 Beacon Chain endpoints | | Real-time data | WebSocket `eth_subscribe` (newHeads, logs, newPendingTransactions, syncing) | | Debug & trace | `debug_*`, `trace_*`, and `erigon_*` namespaces | | Networks | Mainnet, Sepolia, and Hoodi, with full and archive nodes | | Request units | 1 RU per full request, 2 RUs per archive request | ## What is Ethereum Ethereum, launched in 2015, is a decentralized, open-source blockchain network that provides a platform for developers to build decentralized applications for diverse purposes. Ethereum runs on a proof-of-stake (PoS) consensus mechanism, and one of its most critical features is smart contract functionality, which lets developers create self-executing programs that automate the enforcement of contractual terms and conditions. Ethereum is the reference implementation of the Ethereum Virtual Machine (EVM), the execution environment that dozens of other chains — Base, BNB Smart Chain, Polygon, Arbitrum, Optimism, and more — adopt for compatibility. Tooling, contract bytecode, and JSON-RPC calls that work against Ethereum carry over to the wider EVM ecosystem with minimal changes. A modern Ethereum node runs two clients that share consensus and produce blocks together: * Execution layer — processes transactions, runs the EVM, and holds account and contract state. It exposes the JSON-RPC interface that applications use to read state and send transactions. * Consensus layer — runs proof-of-stake validation through the Beacon Chain, tracking validators, attestations, and finality. It exposes a REST API for validator duties and chain-finality data. ## What is the Ethereum API The Ethereum API lets developers build decentralized applications, analytics tools, wallets, and indexers. To read data from and send transactions to the Ethereum blockchain, an application connects to an Ethereum node endpoint, then calls the layer it needs: * The execution layer implements the JSON-RPC specification, a communication protocol that lets you make remote calls and execute them as if they were local. This is where you query balances, blocks, logs, and receipts, call and deploy smart contracts, and submit signed transactions. * The consensus layer exposes the Beacon Chain REST API for validator state, committees, sync committees, rewards, and finality checkpoints. A Chainstack Ethereum node serves both, so a single endpoint covers execution-layer JSON-RPC, Beacon Chain queries, and WebSocket subscriptions for real-time data. ## Methods Chainstack serves 100 Ethereum methods across the execution and consensus layers: | Layer | Namespace or path | Methods served | Notes | | - | - | - | - | | Execution | `eth_*` | 39 | Accounts, blocks, logs, gas, contract calls, transactions, subscriptions | | Execution | `debug_*` | 9 | Transaction and block tracing | | Execution | `trace_*` | 8 | OpenEthereum-style tracing and replays | | Execution | `erigon_*` | 8 | Erigon-specific block, header, and log queries | | Execution | `net_*` | 3 | Network listening status and peer count | | Execution | `web3_*` | 2 | Client version and Keccak-256 hashing | | Consensus | Beacon Chain REST | 31 | Validators, committees, rewards, finality, events | For the complete, per-method availability matrix, see [Ethereum methods](/docs/ethereum-methods). > [!NOTE] > > The `txpool_*` and `admin_*` namespaces are listed for completeness but are not served on Chainstack Ethereum nodes. See [Ethereum methods](/docs/ethereum-methods) for the full availability matrix. ### What you can build With a Chainstack Ethereum endpoint you can: * Query chain state — balances, blocks, logs, receipts, storage, proofs, and gas — through the standard `eth_*` JSON-RPC interface, compatible with ethers, viem, and web3.py. * Deploy and call Solidity contracts with Hardhat, Foundry, Remix, and Ape, using `eth_call`, `eth_estimateGas`, and `eth_sendRawTransaction`. * Debug and trace transactions with `debug_traceTransaction`, `trace_block`, `trace_replayTransaction`, and the `erigon_*` namespace on archive nodes. * Stream real-time data over WebSocket with `eth_subscribe` for `newHeads`, `logs`, `newPendingTransactions`, and `syncing`. * Read consensus-layer state — validators, committees, sync committees, rewards, and finality checkpoints — through the Beacon Chain REST API. * Index historical state at any block height — balances, contract storage, and traces — against an archive node. ## How to start using the Ethereum API You need access to an Ethereum node endpoint to use the Ethereum API. Follow these steps to sign up, deploy a node, and find your credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the Ethereum blockchain and use the Ethereum API to build. ## SDKs and tooling You can call the API directly over HTTP and WebSocket, or use a maintained library: * JavaScript and TypeScript — [ethers.js](https://github.com/ethers-io/ethers.js) and [viem](https://github.com/wevm/viem). * Python — [web3.py](https://github.com/ethereum/web3.py). * .NET — [Nethereum](https://github.com/Nethereum/Nethereum). For smart contract development, point [Foundry](https://github.com/foundry-rs/foundry), [Hardhat](https://github.com/NomicFoundation/hardhat), [Ape](https://github.com/ApeWorX/ape), or Scaffold-ETH 2 at your Chainstack endpoint. Point any library or framework at your node URL and add a chain ID. See [Ethereum tooling](/docs/ethereum-tooling) for wallet, IDE, library, and framework setup. ## Networks Chainstack supports Ethereum mainnet and the Sepolia and Hoodi testnets, with full and archive node modes, WebSocket connections, and debug & trace APIs. | Feature | Mainnet | Sepolia | Hoodi | | - | - | - | - | | Chain ID | `1` | `11155111` | `560048` | | Full nodes | Yes | Yes | — | | Archive nodes | Yes | Yes | Yes | | WebSocket | Yes | Yes | Yes | | Debug & trace APIs | Yes | Yes | Yes | > [!NOTE] > > A full node keeps recent state — the latest 128 blocks — while an archive node holds all historical state from genesis. Use an archive node for historical balances, contract storage at past blocks, and `trace_*` replays. See [Networks](/docs/protocols-networks) for the full deployment matrix. ## Frequently asked questions ### What is the Ethereum chain ID? Ethereum mainnet is chain ID `1`. The Chainstack-supported testnets are Sepolia (`11155111`) and Hoodi (`560048`). Set the chain ID in your wallet, SDK, or framework config alongside your Chainstack RPC endpoint. ### What is the difference between a full node and an archive node? A full node keeps recent state — the latest 128 blocks on Ethereum — which covers most live application reads and transaction submission. An archive node retains all historical state from genesis, so you can query balances and contract storage at any past block and run `trace_*` replays. Chainstack offers both modes on mainnet and Sepolia, and archive on Hoodi. ### Does Ethereum support debug and trace methods? Yes. Chainstack Ethereum nodes serve the `debug_*`, `trace_*`, and `erigon_*` namespaces — including `debug_traceTransaction`, `trace_block`, `trace_replayTransaction`, and `trace_filter` — for transaction tracing and smart contract debugging. Debug and trace are available on mainnet, Sepolia, and Hoodi. ### Does Chainstack support WebSocket subscriptions on Ethereum? Yes. Connect over WebSocket and use `eth_subscribe` to stream `newHeads`, `logs`, `newPendingTransactions`, and `syncing` events in real time. Use `eth_unsubscribe` to cancel a subscription. ### Which SDKs work with the Ethereum API? Any standard Ethereum library works against a Chainstack endpoint: ethers.js and viem for JavaScript and TypeScript, web3.py for Python, and Nethereum for .NET. For contracts, use Foundry, Hardhat, Ape, or Scaffold-ETH 2. See [Ethereum tooling](/docs/ethereum-tooling). ### Does Chainstack serve the Beacon Chain (consensus layer) API? Yes. Chainstack Ethereum nodes expose 31 Beacon Chain REST endpoints alongside execution-layer JSON-RPC, covering validators, committees, sync committees, rewards, finality checkpoints, and the events stream. See [Ethereum methods](/docs/ethereum-methods) for the full list. ### How are Ethereum requests priced? Pricing is 1 request unit (RU) per full request and 2 RUs per archive request. For which EVM methods bill as full versus archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). ## Related Ethereum resources * [Ethereum methods](/docs/ethereum-methods) — complete per-method availability reference * [Ethereum tooling](/docs/ethereum-tooling) — wallets, IDEs, libraries, and frameworks * [Ethereum Trader Nodes with Warp transactions](/docs/ethereum-trader-nodes) — low-latency transaction propagation * [Ethereum logs tutorial series: logs and filters](/docs/ethereum-logs-tutorial-series-logs-and-filters) — query and filter event logs * [Ethereum: how to analyze pending blocks](/docs/ethereum-how-to-analyze-pending-blocks) — inspect the mempool and pending state * [Ethereum Dencun: rundown with examples](/docs/ethereum-dencun-rundown-with-examples) — blobs and the Dencun upgrade * [Debug and trace](/reference/ethereum-debug-trace-rpc-methods) — tracing method reference and examples * [Subscriptions](/reference/ethereum-web3js-subscriptions-methods) — WebSocket subscription method reference # eth_getTransactionByBlockHashAndIndex | Ethereum Source: https://docs.chainstack.com/reference/ethereum-gettransactionbyblockhashandindex openapi/ethereum_node_api/transaction_info/eth_getTransactionByBlockHashAndIndex.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. Ethereum API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Ethereum blockchain. ## Parameters * `hash` — the hash of the block * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal ## Response * `object` — a transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockHashAndIndex` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getTransaction = async (blockHash, index) => { const transaction = await chainstack.send( "eth_getTransactionByBlockHashAndIndex", [blockHash, index] ); console.log(transaction); }; getTransaction( "0x6f5542b045b72ed449480bf363726e211635fe30702dafcd1907b93bd627b2e4", "0x3D" ); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block("0x6f5542b045b72ed449480bf363726e211635fe30702dafcd1907b93bd627b2e4", 22)) ``` ## Use case The `eth_getTransactionByBlockHashAndIndex` can be used to retrieve transaction details from a block. In the following example the `getSendersInBlock` checks how many transactions exist in a block, then receives the sender of each (`from` field) and displays it. > [!NOTE] > > Note that there are simpler ways to get this information, and this is only a practical example of how to use `eth_getTransactionByBlockHashAndIndex`, if the number of transactions in the block is already known, the first step can be skipped. ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function getSendersInBlock(blockHash) { const block = await chainstack.getBlock(blockHash); const transactions = block.transactions; console.log(`Block ${blockHash} contains ${transactions.length} transactions`); for (let i = 0; i < transactions.length; i++) { const transaction = await chainstack.send( "eth_getTransactionByBlockHashAndIndex", [blockHash, ethers.toBeHex(i)] ); console.log(`Sender of transaction at index ${i}:`, transaction.from); } } getSendersInBlock("0x6f5542b045b72ed449480bf363726e211635fe30702dafcd1907b93bd627b2e4"); ``` The number of transactions in the block is determined using `transactions.length` and the total number of transactions logged to the console. A `for` loop is then used to retrieve information about each transaction in the block. For each iteration of the loop, the code calls `eth_getTransactionByBlockHashAndIndex` with the block hash and the current index encoded as hexadecimal to retrieve information about the transaction at that position. The `from` property of the returned transaction object is logged to the console to show the sender of the transaction. # eth_getTransactionByBlockNumberAndIndex | Ethereum Source: https://docs.chainstack.com/reference/ethereum-gettransactionbyblocknumberandindex openapi/ethereum_node_api/transaction_info/eth_getTransactionByBlockNumberAndIndex.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. Ethereum API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Ethereum blockchain. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal. ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockNumberAndIndex` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getTransaction = async (blockNumber, index) => { const transaction = await chainstack.send( "eth_getTransactionByBlockNumberAndIndex", [blockNumber, index] ); console.log(transaction); }; getTransaction("latest", "0x1"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block(16819758, 22)) # Hex encoded parameters starting with "0x" are accepted as well. ``` ## Use case The `eth_getTransactionByBlockNumberAndIndex` can be used to retrieve transaction details from a block. In the following example the `getTypeInBlock` function checks how many transactions exist in a block, then retrieves the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of each (`type` field), and displays it. > [!NOTE] > > Note that there are simpler ways to get this information, and this is only a practical example of how to use `eth_getTransactionByBlockNumberAndIndex`, if the number of transactions in the block is already known, the first step can be skipped. ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function getTypeInBlock() { const latestBlock = await chainstack.getBlockNumber(); const block = await chainstack.getBlock(latestBlock, false); const transactions = block.transactions; console.log(`The latest block is ${latestBlock} and contains ${transactions.length} transactions`); for (let i = 0; i < transactions.length; i++) { const transaction = await chainstack.send( "eth_getTransactionByBlockNumberAndIndex", [ethers.toBeHex(latestBlock), ethers.toBeHex(i)] ); console.log(`The type of transaction at index ${i}:`, transaction.type); } } getTypeInBlock(); ``` The number of transactions in the block is determined using `transactions.length` and the total number of transactions logged to the console. A `for` loop is then used to retrieve information about each transaction in the block. For each iteration of the loop, the code calls `eth_getTransactionByBlockNumberAndIndex` through `chainstack.send` to retrieve information about the transaction at the current index. The `type` property of the returned transaction object is logged to the console to show the sender of the transaction. # eth_getTransactionByHash | Ethereum Source: https://docs.chainstack.com/reference/ethereum-gettransactionbyhash openapi/ethereum_node_api/transaction_info/eth_getTransactionByHash.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the information about a transaction from the transaction hash. Use it on Ethereum via Chainstack. Ethereum API method that returns the information about a transaction from the transaction hash. It is used to retrieve information about a specific transaction by its hash. The method returns an object with details about the transaction, such as the sender, recipient, gas used, transaction value, and more. If you are using this method to track your own transactions, make sure [MEV protection](/docs/mev-protection) is disabled. Otherwise you won't detect them as they go through a private pool. ## Parameters * `hash` — the hash identifying a transaction ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction., usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet; it essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. This field is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByHash` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getTransaction = async (transactionHash) => { const transaction = await chainstack.send("eth_getTransactionByHash", [ transactionHash, ]); console.log(transaction); }; getTransaction( "0xb528d706507b7e5d8f132389a667b3785cf14480a8f9aa6363f1e674a5a6099e" ); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction("0xb528d706507b7e5d8f132389a667b3785cf14480a8f9aa6363f1e674a5a6099e")) ``` ## Use case One use case for `eth_getTransactionByHash` is to inspect a specific transaction and display information about it, such as the address sent and the block in which it was included.This information can be useful for monitoring and analyzing transactions. Here's an example of how you can achieve this in ethers.js using `ChainstackProvider`: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function getTransaction(transactionHash) { // Get the transaction using the provided transaction hash const transaction = await chainstack.send("eth_getTransactionByHash", [ transactionHash, ]); // Store the desired parameters in constants const sender = transaction.from; const block = transaction.blockNumber; // Log the transaction information to the console console.log(`Transaction ${transactionHash} was sent from address ${sender}`); console.log(`It was included in block ${block}`); } getTransaction( "0xb528d706507b7e5d8f132389a667b3785cf14480a8f9aa6363f1e674a5a6099e" ); ``` In this example, the program starts by connecting to a node endpoint. Then, call the `eth_getTransactionByHash` method and pass in the transaction hash as an argument. The method returns a Promise that resolves to a transaction object, which is used to retrieve the sender address and the block number the transaction was included in. The last step is to log the information to the console. > [!NOTE] > > Note that the desired parameters are stored in constants, so you can use them for further processing. # eth_getTransactionCount | Ethereum Source: https://docs.chainstack.com/reference/ethereum-gettransactioncount openapi/ethereum_node_api/accounts_info/eth_getTransactionCount.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the number of transactions sent from an address at the selected block. Chainstack Ethereum reference. Ethereum API method that returns the number of transactions sent from an address at the selected block. This value is also called `nonce`; it is an important piece of information, especially to ensure that a transaction is not sent twice. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`— the address to retrieve the transaction count. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — an integer value identifying the number of transactions sent from an address at the specified block. ## `eth_getTransactionCount` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getNonce = async (address, blockId) => { const nonce = await chainstack.send("eth_getTransactionCount", [ // This value is returned in HEX address, blockId, ]); // Convert hex to decimal const nonceDecimal = parseInt(nonce, 16); console.log(nonceDecimal); }; getNonce("0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324", "latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_count("0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324", "latest")) ``` ## Use case One of the most common use cases for `eth_getTransactionCount`is to create the transaction object built in a script designed to send a transaction. The nonce field is required, and it is retrieved using the `eth_getTransactionCount` method. The following code shows how to build and send a transaction using ethers.js. > [!WARNING] > > **Security notice** > > You need your private key to sign the transaction; never share your private key with anyone. > > On a side note, the private key in this case must be imported with '0x' at the beginning of the string. ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); // Function to get the transaction count (nonce) async function getTransactionsCount(address) { return await chainstack.getTransactionCount(address, "latest"); } // Function to perform the transaction async function sendEther() { try { // Add your private key here (ideally from an environment variable) const privateKey = "0xPRIVATE_KEY"; // Create a wallet connected to the provider const wallet = new ethers.Wallet(privateKey, chainstack); // Retrieve the nonce for the sender's address const nonce = await getTransactionsCount(wallet.address); // Transaction parameters const toAddress = "0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13"; // Replace with the receiver's address const value = ethers.parseEther("1"); // Amount in ETH (e.g., 1 ETH) console.log(`Sending ${ethers.formatEther(value)} ETH from ${wallet.address} to ${toAddress}...`) // Send the transaction const txResponse = await wallet.sendTransaction({ to: toAddress, value: value, nonce: nonce, // Include the nonce // Optional: Specify gasLimit and fee fields if needed }); // Wait for the transaction to be mined and get the receipt const receipt = await txResponse.wait(); console.log("Transaction receipt:", receipt); } catch (error) { console.error("Transaction failed:", error); } } // Execute the function sendEther(); ``` The code sets up a direct transaction to be broadcast to the Ethereum network using ethers.js. Initially, the script establishes a connection to an Ethereum node and sets up the provider instance. The key part of the transaction process involves retrieving the `nonce` for the sender's address, which is done through the `getTransactionsCount` function. This function calls `provider.getTransactionCount`, passing the sender's address and returning the count of transactions previously sent from this address. This count, used as the `nonce`, is critical for ensuring the transaction's uniqueness and order in the blockchain. After setting up the sender's wallet with a private key, the script defines the transaction parameters, including the `toAddress` and the `value` (amount of ETH to be transferred). The `value` is defined in wei using `ethers.parseEther` and then formatted back to ETH with `ethers.formatEther` for logging purposes. The script then constructs a transaction object that includes essential fields such as `to`, `value`, and `nonce`. While `gasLimit` and the fee fields can be specified, they are optional in this script, as ethers.js will estimate and populate default values if they are not explicitly set. The transaction is sent using `wallet.sendTransaction`, which handles the transaction signing under the hood with the private key from the wallet. This approach simplifies the transaction creation and sending process, as the complex steps of manually creating, signing, and serializing the transaction are abstracted away by ethers.js. This will ultimately send a transaction using the [eth\_sendRawTransaction](/reference/ethereum-sendrawtransaction) method. Once the transaction is sent, a receipt is obtained and logged, providing details of the executed transaction. In case of any errors during the transaction process, these are caught and logged, aiding in troubleshooting and ensuring robust error handling. The `sendEther` function encapsulates the entire process, and its execution triggers the sending of the transaction, demonstrating a streamlined and efficient way to handle Ethereum transactions programmatically. # eth_getTransactionReceipt | Ethereum Source: https://docs.chainstack.com/reference/ethereum-gettransactionreceipt openapi/ethereum_node_api/transaction_info/eth_getTransactionReceipt.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that retrieves the receipt of a transaction from a transaction hash. eth_getTransactionReceipt on Ethereum via Chainstack. > [!NOTE] > > **No response in the interactive API** > > The response to the call might be too big for the web app to handle, so if you are not getting it here, copy the CURL example and run in your terminal—this will work. Ethereum API method that retrieves the receipt of a transaction from a transaction hash. It can be used to verify the success or failure of a transaction, check the execution status of a smart contract, retrieve logs, check if a transaction deployed a smart contract, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `Transaction receipt` — the receipt object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the address of the contract created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects generated by this transaction, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getTransactionReceipt` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const getTransactionReceipt = async (transactionHash) => { const receipt = await chainstack.send("eth_getTransactionReceipt", [ transactionHash, ]); console.log(receipt); }; getTransactionReceipt( "0xb528d706507b7e5d8f132389a667b3785cf14480a8f9aa6363f1e674a5a6099e" ); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_receipt("0xb528d706507b7e5d8f132389a667b3785cf14480a8f9aa6363f1e674a5a6099e")) ``` ## Use case A practical use case for `eth_getTransactionReceipt` is to verify a transaction's status and whether it was successful or not. This can be especially useful when deploying a contract, as you can use the receipt to confirm that the contract was deployed successfully and has a valid contract address. Here's an example in ethers and the `ChainstackProvider`: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function verifyTransaction(transactionHash) { // Get the transaction receipt const receipt = await chainstack.send("eth_getTransactionReceipt", [ transactionHash, ]); // Check if the receipt is not yet available if (!receipt) { console.log(`Transaction ${transactionHash} is still pending...`); return; } // Check if the transaction was successful if (receipt.status) { console.log(`✅The transaction was successful! \nHash: ${transactionHash}`); // Check if the transaction deployed a smart contract if (receipt.contractAddress != null) { console.log( `📑The smart contract was deployed at address: ${receipt.contractAddress}` ); } else { console.log(`❗This transaction did not deploy a smart contract`); } } else { console.log(`💥The transaction failed. \nHash: ${transactionHash}`); } } verifyTransaction( "0x2761f74e2f45d981a9d7553cbbcfbcc862cae416eb37a820300d4c19516d6fca" ); ``` This script holds an async function called `verifyTransaction` that takes a transaction hash as input. The function uses the web3.js library to retrieve the transaction receipt of the specified transaction using the `eth_getTransactionReceipt` method. The function first checks if the transaction receipt is not yet available, and if so, it logs a message to the console indicating that the transaction is still pending. If the receipt is available, the function checks the status property of the receipt to determine if the transaction was successful. If the transaction was successful, the function checks if a smart contract was deployed as part of the transaction. The function logs the smart contract's address to the console if a smart contract is deployed. If not, the function logs a message indicating that no smart contract was deployed. If the transaction was not successful, the function logs a message indicating that the transaction failed, along with the transaction hash. # net_listening | Ethereum Source: https://docs.chainstack.com/reference/ethereum-listening openapi/ethereum_node_api/client_info/net_listening.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns a boolean value indicating whether the client is currently listening for network connections or not. Ethereum API method that returns a boolean value indicating whether the client is currently listening for network connections or not. The `net_listening` method can be useful in many scenarios where it is important to monitor the status of an Ethereum client's network connection to ensure that it is working properly. ## Parameters * `none` ## Response * `boolean` — a boolean value that indicates whether or not a node is currently actively seeking peer connections. `true` if the client is actively listening for connections, `false` if not. ## `net_listening` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const isListening = async () => { const listen = await chainstack.send("net_listening"); console.log(listen); }; isListening(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) is_listening = web3.provider.make_request('net_listening', []) print(is_listening) ``` ## Use case A method like `net_listening` can be used to monitor the network status of the Ethereum node a developer is connecting to. Imagine a blockchain explorer where users can search for transactions, addresses, and other information on the Ethereum blockchain. In order to provide accurate information to its users, the website needs to be able to monitor the current status of its connection. Here is an example of this implementation using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); // Create a function to monitor the node status and display information to users async function monitorNetworkStatus() { try { // Check if the client is currently listening for network connections const isListening = await chainstack.send("net_listening", []); // Get the number of peers currently connected to the client const peerCount = await chainstack.send("net_peerCount", []); // Log the network status information to the console console.log(`Network status: ${isListening ? 'Listening' : 'Not listening'} | Peers: ${peerCount}`); } catch (error) { // Handle any errors that might occur console.error(`Error monitoring network status: ${error}`); } } // Call the monitorNetworkStatus function at regular intervals to continuously monitor the network status setInterval(monitorNetworkStatus, 5000); // Monitor every 5 seconds ``` The `monitorNetworkStatus` function defined in this code uses the `net_listening` method to check whether the Ethereum client is currently listening for network connections, and the [net\_peerCount](/reference/ethereum-peercount) method to get the number of peers currently connected to the client. The function logs this information to the console, which could be displayed to the user in a user-friendly format on the blockchain explorer website. By calling this function at regular intervals using the `setInterval` function, the website can continuously monitor the node's status and provide users with up-to-date information about the overall health of the node. # Logs and events | Ethereum Source: https://docs.chainstack.com/reference/ethereum-logs-rpc-methods Ethereum event log RPC methods for querying smart contract events, applying log filters, and tracking on-chain activity via Chainstack endpoints. Retrieving logs information from the Ethereum blockchain can be done by using the following methods: > [card] [eth\_getLogs](/reference/ethereum-getlogs) > [card] [eth\_newFilter](/reference/ethereum-newfilter) > [!NOTE] > > Find logs limitations in [Understanding eth\_getLogs limitations](/docs/understanding-eth-getlogs-limitations) # eth_maxPriorityFeePerGas Source: https://docs.chainstack.com/reference/ethereum-maxpriorityfeepergas openapi/ethereum_node_api/gas_data/eth_maxPriorityFeePerGas.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns an estimate of the current priority fee per gas. eth_maxPriorityFeePerGas on Chainstack via Chainstack. # `eth_maxPriorityFeePerGas` Ethereum API method that returns an estimate of the current priority fee per gas. This fee is the tip that users may pay miners,post-merge, to validators on top of the base fee to incentivize the inclusion of their transactions in a block. This method helps users adjust their transaction fees in a dynamic market, ensuring transactions are processed promptly without overpaying. ## Parameters * `none` ## Response * `quantity` — the estimated priority fee per gas needed, represented as a hexadecimal string. ## `eth_maxPriorityFeePerGas` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const estimatePriorityFee = async () => { const priorityFeePerGas = await chainstack.getFeeData(); console.log( `Estimated priority fee per gas: ${priorityFeePerGas.maxPriorityFeePerGas}` ); }; estimatePriorityFee(); ``` ```python web3.py theme={"system"} from web3 import Web3 NODE_URL = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(NODE_URL)) eth_max_priority_fee_per_gas = web3.provider.make_request('eth_maxPriorityFeePerGas', []) print(eth_max_priority_fee_per_gas) ``` ## Use Case A practical use case for `eth_maxPriorityFeePerGas` using `ethers.js` is to calculate transaction fees dynamically for applications and wallets to accurately estimate the cost of transactions, especially during periods of high network congestion. This method ensures transactions are confirmed in a timely manner without overpaying. By fetching the current priority fee (`maxPriorityFeePerGas`) and the maximum fee per gas (`maxFeePerGas`), applications can dynamically calculate the total transaction fee required for prompt processing under the current network conditions. ```javascript JavaScript theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function simulateTransaction(fromAddress, toAddress, amountEther) { const amountWei = ethers.parseEther(amountEther.toString()); const feeData = await chainstack.getFeeData(); console.log("Full gas fee data:", feeData); const tx = { from: fromAddress, to: toAddress, value: amountWei, maxPriorityFeePerGas: feeData.maxPriorityFeePerGas, maxFeePerGas: feeData.maxFeePerGas, }; console.log("Simulating transaction:", tx); try { const estimatedGas = await chainstack.estimateGas(tx); console.log(`Estimated gas for transaction: ${estimatedGas.toString()}`); } catch (error) { console.error(`Error simulating transaction: ${error}`); } } const fromAddress = "0xae2Fc483527B8EF99EB5D9B44875F005ba1FaE13"; const toAddress = "0xF135B9eD84E0AB08fdf03A744947cb089049bd79"; const amountEther = 1; // Simulating sending 1 Ether for this example simulateTransaction(fromAddress, toAddress, amountEther); ``` The `simulateTransaction` function showcases how to build a transaction with dynamically calculated fees based on the current network state, including the base and priority fees. This approach provides a more nuanced and accurate method for estimating the total transaction fee, ensuring users pay a fair amount based on real-time network conditions. It simulates the transaction's execution to estimate gas usage without actually broadcasting it to the network, allowing for cost-effective transaction planning and testing. This simulation is particularly useful for applications that require a balance between transaction cost and confirmation speed, providing a comprehensive picture of the fees needed for timely transaction processing. # eth_subscribe ("logs") | Ethereum Source: https://docs.chainstack.com/reference/ethereum-native-subscribe-logs Subscribe to real-time Ethereum event logs via WebSocket. Receive notifications whenever new logs matching your specified filter criteria are emitted. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) the event filter options: * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about topics and event logs. ## Response * `subscription` — the subscription ID. * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_subscribe("logs")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_subscribe", "params": ["logs", {"address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewLogs() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['logs', {"address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewLogs(); ``` This will generate a continuous stream of data displaying new event logs matching the filter. Use [eth\_unsubscribe](/reference/ethereum-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("logs")` method in Ethereum is used to subscribe to the logs generated by smart contracts on the blockchain. These logs contain information about events that have occurred on the blockchain, such as details of a smart contract function and outputs of transactions. A practical and real-world scenario for using the `eth_subscribe("logs")` method could be in the context of a decentralized exchange (DEX). In the context of a DEX, trades are executed using smart contracts that run on the Ethereum blockchain. When a trade is executed on a DEX, a smart contract is called to process the transaction. This smart contract generates a log that contains information about the trade, such as the amount of cryptocurrency being traded, the parties involved, and so on. By using the `eth_subscribe("logs")` method, a user or application can subscribe to the logs generated by the DEX's smart contracts. This can be useful for tracking trades in real time, monitoring the performance of the DEX, and generating reports on trading activity. For example, a trader might use the `eth_subscribe("logs")` method to monitor the logs generated by a DEX in order to identify trends in trading activity. They might look for patterns in the volume and price of trades, which could help them make more informed trading decisions. Similarly, an analytics platform might use the `eth_subscribe("logs")` method to collect data on trading activity across multiple DEXs. By analyzing this data, they could generate insights into the performance of different DEXs, which could be useful for investors and traders looking to make informed decisions about where to trade. # eth_subscribe ("newHeads") | Ethereum Source: https://docs.chainstack.com/reference/ethereum-native-subscribe-newheads Subscribe to new Ethereum block headers via WebSocket. Receive real-time notifications whenever a new block is added to the Ethereum blockchain. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `newHeads` in this case. ## Response * `subscription` — the subscription ID. * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ethereum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ethereum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era to track cumulative mining difficulty. After the merge to Proof of Stake (PoS), this value is no longer relevant. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `eth_subscribe("newHeads")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewBlocks() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewBlocks(); ``` This will generate a continuous stream of data displaying new block headers as they are added to the chain. Use [eth\_unsubscribe](/reference/ethereum-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newHeads")` method can be useful in a variety of real-world scenarios, such as: * Real-time monitoring of blockchain activity. Companies or individuals who rely on the Ethereum network for their business can use `eth_subscribe("newHeads")` to receive real-time notifications about new blocks being added to the chain. This can help them keep track of important transactions, such as high-value trades, in real time and respond quickly to any issues that may arise. * Decentralized Finance (DeFi) applications. DeFi applications, such as lending platforms, can use `eth_subscribe("newHeads")` to monitor the blockchain for new blocks that might contain important data, such as changes in interest rates or new lending opportunities. * Smart contract automation. Developers can use `eth_subscribe("newHeads")` to trigger automated smart contract actions whenever a new block is added to the chain. For example, a smart contract could automatically execute an action order whenever a new block is added that meets certain criteria. # eth_subscribe ("newPendingTransactions") | Ethereum Source: https://docs.chainstack.com/reference/ethereum-native-subscribe-newpendingtransactions Subscribe to pending Ethereum transactions via WebSocket. Receive real-time notifications when new unconfirmed transactions enter the mempool. If you are using this method to track your own transactions, make sure [MEV protection](/docs/mev-protection) is disabled. Otherwise you won't detect them as they go through a private pool. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `newPendingTransactions` in this case. * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `subscription` — the subscription ID. * `object` — a transaction response object: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_subscribe("newPendingTransactions")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newPendingTransactions"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewPendingTransactions() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newPendingTransactions', true], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewPendingTransactions(); ``` This will generate a continuous stream of data displaying new pending transactions as they are added to the mempool. Use [eth\_unsubscribe](/reference/ethereum-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newPendingTransactions")` method in Ethereum is used to subscribe to the network and receive real-time updates on all new pending transactions on the network. This can be useful in a variety of real-world scenarios, including: * Trading applications. A trading application can use this method to keep track of any new transactions that are being made on the network. This can be particularly useful for high-frequency trading, where speed is crucial. By subscribing to this method, the trading application can receive real-time updates on new trades, allowing it to quickly respond and execute trades. * Monitoring tools. Monitoring tools can use this method to keep track of any suspicious activity on the network. By subscribing to this method, monitoring tools can receive real-time updates on any new pending transactions and quickly take action if necessary. * Payment processors. Payment processors can use the `eth_subscribe("newPendingTransactions")` method to monitor incoming transactions from their customers. This can be particularly useful for businesses that rely on blockchain technology to process payments, as it allows them to track incoming payments in real time. By subscribing to this method, payment processors can receive instant notifications when new payments are received, which can help streamline their payment processing workflows. # eth_unsubscribe | Ethereum Source: https://docs.chainstack.com/reference/ethereum-native-unsubscribe Cancel any active Ethereum WebSocket subscription with eth_unsubscribe. Pass the subscription ID to stop receiving real-time event notifications. When a client subscribes to a particular event, the Ethereum node will send updates as soon as they occur. However, in some cases, the client may no longer be interested in receiving updates for a particular event. The `eth_unsubscribe` method can be used to cancel the following subscriptions: > [card] [eth\_subscribe('newHeads')](/reference/ethereum-native-subscribe-newheads) > [card] [eth\_subscribe('newPendingTransactions')](/reference/ethereum-native-subscribe-newpendingtransactions) > [card] [eth\_subscribe('logs')](/reference/ethereum-native-subscribe-logs) ## Parameters * `string` — the ID of the subscription that you want to cancel ## Response * `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `eth_unsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_unsubscribe", "params": ["0x7529626735859f32962b10b19ee2e3eb"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function removeSubscription() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_unsubscribe', params: ['0x9de3135dd3dd6c6b99bd104061a5af4f'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } removeSubscription(); ``` This will remove the subscription matching the ID passed as a parameter. # eth_newBlockFilter | Ethereum Source: https://docs.chainstack.com/reference/ethereum-newblockfilter openapi/ethereum_node_api/blocks_info/eth_newBlockFilter.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that creates a filter that watches for new blocks on the blockchain. eth_newBlockFilter on Ethereum via Chainstack. Ethereum API method that creates a filter that watches for new blocks on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/ethereum-getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real-time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter The filters created are stored on the blockchain client instance. The filter fill is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/ethereum-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/ethereum-uninstallfilter) to remove the filter ## `eth_newBlockFilter` code examples > [!NOTE] > > **web3.eth.filterdeprecation** > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const createFilter = async () => { try { const filterId = await chainstack.send("eth_newBlockFilter", []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId; } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') return blocks_filter except Exception as e: print(e) blocks = get_new_blocks() filter_id = blocks.filter_id print(filter_id) ``` > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ## Use case One use case for `eth_newBlockFilter` in a simple DApp is to listen for new blocks and update the user interface with the latest block information. When the DApp starts, it creates a new filter using `eth_newBlockFilter` to listen for new blocks. When a new block is added to the blockchain, the filter is triggered, and the DApp retrieves the latest block information using a Web3 library. Here is an implementation of this use case using web3.py: ```python main.py theme={"system"} import time from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Set up a new blocks filter def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) def watch_for_blocks(new_filter): # Wait for new blocks to arrive while True: # Retrieve the changes that match the filter changes = web3.eth.get_filter_changes(new_filter) # If there are no changes, wait for new blocks if len(changes) == 0: time.sleep(1) # Wait for 1 second continue # Process the new blocks for change in changes: block = web3.eth.get_block(change) print('New block:', block) def main(): # Create a new filter to watch for new blocks new_filter = get_new_blocks() print(new_filter) # Watch for new blocks watch_for_blocks(new_filter) if __name__ == '__main__': main() ``` The code is divided into three main functions: `get_new_blocks`, `watch_for_blocks`, and `main`. Let's take a closer look at each of them: `get_new_blocks` is responsible for setting up a new filter to watch for new blocks. The `try` block sets up the filter by calling the `eth.filter` method of the web3.py library and passing it the string `latest`. The `split` method is then used to split the resulting string at each space character, and the second part of the resulting list is extracted as the filter ID. The filter ID is returned from the function. > [!NOTE] > > The `eth.filter` method of the web3.py library does not only return the filter ID but it returns a sentence saying `Filter for 0x3c3f5a5e637d29c033e4e7a3f7e870cd`; this is the reason for the string manipulation within the `get_new_blocks` function. `watch_for_blocks` is the function that watches for new blocks. It does this by first entering an infinite loop while `True`. Within this loop, the [eth\_getFilterChanges](/reference/ethereum-getfilterchanges) method is used to retrieve any changes that match the filter ID passed to the function. If there are no changes, the code waits for 1 second using the `time.sleep` method and then continues with the loop. If there are changes, the new blocks are processed by looping through them and calling the `eth.getBlock` method of the web3 library to retrieve the details of each block. Finally, the details of the new blocks are printed on the console. `main` is the function that ties everything together. It first calls `get_new_blocks` to set up a new filter to watch for new blocks, and then passes the resulting filter ID to `watch_for_blocks` to start watching for new blocks. The filter ID is also printed to the console to confirm that the filter has been set up correctly. # eth_newFilter | Ethereum Source: https://docs.chainstack.com/reference/ethereum-newfilter openapi/ethereum_node_api/logs_and_events/eth_newFilter.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that generates a filter object based on the filter parameters. Available on Ethereum via Chainstack JSON-RPC nodes. Ethereum API method that generates a filter object based on the filter parameters. It returns a filter ID, which can be used to retrieve the results of the filter using the [eth\_getFilterChanges](/reference/ethereum-getfilterchanges) method. By creating a filter for specific events, developers can receive notifications when those events occur and use them to trigger actions in their applications. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block > * `earliest` — the earliest available or genesis block > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `result` — a hexadecimal string representing the ID of the newly created filter > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/ethereum-getfilterchanges) to retrieve updated logs * [eth\_uninstallFilter](/reference/ethereum-uninstallfilter) to remove the filter ## `eth_newFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const filter = { toBlock: "latest", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", topics: [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", ], }; const createFilter = async () => { try { const filterId = await chainstack.send("eth_newFilter", [filter]); console.log(filterId); // the filter ID returned by eth_newFilter return filterId; } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) filter = { 'toBlock': 'latest', 'address': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } def create_logs_filter(filter_object): try: logs_filter = web3.eth.filter(filter_object) return logs_filter except Exception as e: print(e) logs_filter = create_logs_filter(filter) filter_id = logs_filter.filter_id print(f'New filter ID: {filter_id}') ``` > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ## Use case You can use `eth_newFilter` to create a filter for a specific action on a smart contract, for example, to monitor the transfer transactions from the [Wrapped ETH token](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2). The idea is to create a filter using the `eth_newFilter` method to monitor an ERC-20 smart contract, WETH in this case. The following filter parameters are selected: ```javascript filter_params theme={"system"} const filter = { // Poll the latest block available toBlock: 'latest', // WETH smart contract address address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // Signature of the Transfer event. topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], }; ``` The program is then polling the filter every 2 seconds to check for any new changes using the `eth_getFilterChanges` method. Here is the implementation using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const filter = { toBlock: "latest", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", topics: [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", ], }; const createAndPollFilter = async () => { try { const filterId = await chainstack.send("eth_newFilter", [filter]); console.log(`The filter ID is: ${filterId}`); // the filter ID returned by eth_newFilter // Poll the filter every 30 seconds setInterval(async () => { const filterChanges = await chainstack.send("eth_getFilterChanges", [ filterId, ]); if (filterChanges.length === 0) { console.log("No new logs since the last poll."); } else { console.log(filterChanges); // the filter changes returned by eth_getFilterChanges } }, 2000); // 2 seconds in milliseconds return filterId; } catch (error) { console.log(error); } }; createAndPollFilter(); ``` Here's a breakdown of how the code works: The `createAndPollFilter` function is an asynchronous function that creates and polls a filter for the WETH smart contract on Ethereum. The `eth_newFilter` method creates a new filter with the specified parameters. In this case, it passes in the filter object as the filter parameter. The method returns the ID of the newly created filter, which is then logged into the console. The `setInterval` method is used to poll the filter every 2 seconds. It takes a function as the first argument and an interval in milliseconds as the second argument. The function passed to `setInterval` is an asynchronous function that calls `eth_getFilterChanges` with the filter ID returned by `eth_newFilter`. This method returns an array of all the changes to the filter since the last time it was polled. If there are no changes, it logs "No new logs since the last poll." to the console. If there are changes, it logs the filter changes to the console. The `createAndPollFilter` function returns the filter ID, and if there are any errors thrown while creating or polling the filter, the catch block logs the error to the console. # eth_newPendingTransactionFilter | Ethereum Source: https://docs.chainstack.com/reference/ethereum-newpendingtransactionfilter openapi/ethereum_node_api/transaction_info/eth_newPendingTransactionFilter.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that creates a filter that listens for new pending transactions on the blockchain. Chainstack Ethereum reference. Ethereum API method that creates a filter that listens for new pending transactions on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/ethereum-getfilterchanges) method. The `eth_newPendingTransactionFilter` method is useful for developers who must be notified of new pending transactions in real time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter ## Code examples > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/ethereum-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/ethereum-uninstallfilter) to remove the filter ## `eth_newPendingTransactionFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const createFilter = async () => { try { const filterId = await chainstack.send( "eth_newPendingTransactionFilter", [] ); console.log(filterId); // the filter ID returned by eth_newFilter return filterId; } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_pending_transactions(): try: blocks_filter = web3.eth.filter('pending') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) new_filter = get_new_pending_transactions() print(new_filter) ``` > [!NOTE] > > Read [Uncovering the power of eth\_getBlockReceipts](/docs/uncovering-the-power-of-ethgetblockreceipts) to learn more about transactions receipts and how to use the `eth_getBlockReceipts` method. ## Use case One way to use the `eth_newPendingTransactionFilter` method is to listen for new pending transactions at predefined intervals and extract specific data from them. For instance, a decentralized application might check for pending transactions every second and identify those that transfer a value greater than a certain amount of the ETH token. This could be useful for monitoring high-value transactions or detecting potential fraud or security threats in real time. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); // Create a filter using eth_newPendingTransactionFilter const createFilter = async () => { try { const filterId = await chainstack.send( "eth_newPendingTransactionFilter", [] ); console.log(filterId); // the filter ID returned by eth_newFilter return filterId; } catch (error) { console.log(error); } }; // Use the filter to extract the value from each transaction async function getValue(filter) { try { // Retrieve the list of new pending transactions const transactions = await chainstack.send("eth_getFilterChanges", [ filter, ]); // Loop through the list of transactions and process each one for (const hash of transactions) { const receipt = await chainstack.send("eth_getTransactionByHash", [hash]); // Check that the receipt is not null and has a non-null value field if (receipt && receipt.value != null) { const value = receipt.value; const decimalValue = BigInt(value).toString(); const convertedValue = ethers.formatEther(decimalValue); // Check if the transferred value is greater than or equal to 100 ETH if (convertedValue >= 100) { console.log(`This transaction is sending more than 100 ETH`); console.log(`Transaction Hash: ${hash}`); console.log(`Value transferred: ${convertedValue} \n`); } } } } catch (error) { console.error(error); // Handle errors that may occur } } // Main program setting an interval to call the `getValue` function at regular intervals async function main() { const filterId = await createFilter(); setInterval(getValue, 1000, filterId); } main(); ``` This code sets up an Ethereum filter to listen for new pending transactions and extract specific data from them. The code consists of three functions: `createFilter`, `getValue`, and `main`. Here's an overview of how each function works: The `createFilter` function sets up a new filter using the `eth_newPendingTransactionFilter` method. It returns the ID of the filter, which can be used to retrieve data from the filter later. The `getValue` function retrieves the list of new pending transactions using the [eth\_getFilterChanges](/reference/ethereum-getfilterchanges) method and loops through each transaction. For each transaction, it retrieves the transaction data using the [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) method and checks if the transaction value is greater than or equal to 100 ETH. If the value is above this threshold, the function logs information about the transaction to the console. The `main` function sets up the program by first calling the `createFilter` function to create a new filter and retrieve its ID. It then sets an interval to call the `getValue` function every 1 second, passing the filter ID as an argument to retrieve the latest transaction data at regular intervals. # net_peerCount | Ethereum Source: https://docs.chainstack.com/reference/ethereum-peercount openapi/ethereum_node_api/client_info/net_peerCount.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns the number of peers connected to the node. Reference for net_peerCount on Ethereum via Chainstack. Ethereum API method that returns the number of peers connected to the node. This method can be useful for developers who want to monitor the connectivity of their node and ensure that it is functioning as expected. By checking the number of connected peers, developers can verify that their node is receiving updates and is properly synchronized with the network. ## Parameters * `none` ## Response * `quantity` — an integer value indicating the current number of peers connected to the node ## `net_peerCount` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const peersCount = async () => { const peers = await chainstack.send("net_peerCount"); // This value is returned in HEX const decimalPeers = parseInt(peers, 16); console.log(`Peers connected: ${decimalPeers}`); }; peersCount(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) peers = web3.provider.make_request('net_peerCount', []) print(peers) ``` ## Use case One practical use case for the `net_peerCount` method is to monitor the number of peers connected to a node and take some action if the number drops below a certain threshold. For example, a developer might want to send a notification to check the node if the peer count is low for more than 10 minutes. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const MIN_PEER_COUNT = 5; // Set the minimum number of peers. let lowPeerCountTime = null; // Track the time when peer count first dropped below the threshold. /** * Checks the number of connected peers and sends a notification if the peer count has been low for more than 10 minutes. */ function checkPeerCount() { chainstack.send("net_peerCount").then((peers) => { const peerCount = parseInt(peers, 16); // The value is returned in HEX. console.log(`Number of connected peers: ${peerCount}`); if (peerCount < MIN_PEER_COUNT) { // Peer count has dropped below the threshold. if (lowPeerCountTime === null) { // This is the first time we've seen a low peer count. lowPeerCountTime = Date.now(); console.log(`Low peer count detected at ${new Date(lowPeerCountTime)}.`); } else if (Date.now() - lowPeerCountTime > 10 * 60 * 1000) { // Peer count has been low for more than 10 minutes, send a notification. console.log('Sending notification to check the node.'); // TODO: Add code here to send a notification, e.g. via email or SMS. } } else { // Peer count is above the threshold. lowPeerCountTime = null; } }); } // Check the peer count once when the script starts. checkPeerCount(); // Check the peer count every minute. setInterval(checkPeerCount, 60 * 1000); ``` This code defines a function called `checkPeerCount` that retrieves the current number of connected peers by sending the `net_peerCount` request through the provider and converting the returned hex value to a decimal number. The function then checks whether the peer count is below a minimum threshold (defined as `MIN_PEER_COUNT`), and takes appropriate action based on the result. If the peer count is below the threshold and this is the first time the count has dropped below the threshold, the function records the current time and logs a message to the console. If the peer count remains below the threshold for more than 10 minutes, the function logs a message to the console indicating that a notification should be sent and optionally sends a notification via email or SMS. If the peer count is above the threshold, the function resets the `lowPeerCountTime` variable to null. By running the `checkPeerCount` function periodically, we can ensure that we are notified if the peer count drops below a certain threshold and stays low for an extended period of time, which can indicate issues with the node's connectivity. # Ethereum API Postman Collection Source: https://docs.chainstack.com/reference/ethereum-rpc-methods-postman-collection Import the Chainstack Postman collection to test Ethereum JSON-RPC methods interactively. Pre-built requests for all supported API methods on your endpoint. ## What is Postman? [Postman](https://www.postman.com/) is a popular collaboration platform for developers that simplifies the process of building, testing, and documenting APIs. It allows developers to send HTTP requests and receive responses in a user-friendly interface, making it easy to debug and test APIs. This collection allows you to test Ethereum RPC methods using your Chainstack node conveniently. ## Get a Chainstack Ethereum endpoint Follow these steps to sign up on Chainstack, deploy an Ethereum RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Test Ethereum RPC methods in Postman You can test your Ethereum Chainstack endpoint with the following Postman collection. ### Run the collection **Step 1** Click **Run in Postman**. [ ![Run in Postman](https://mintcdn.com/chainstack/4kCLdVG8vhROt3j-/images/reference/button.svg?fit=max\&auto=format\&n=4kCLdVG8vhROt3j-\&q=85\&s=417854aa7ae0a1a626a668cf789d0923)](https://app.getpostman.com/run-collection/20722034-40a9d0ed-fe75-4900-b249-5464c1422f96?action=collection%2Ffork\&collection-url=entityId%3D20722034-40a9d0ed-fe75-4900-b249-5464c1422f96%26entityType%3Dcollection%26workspaceId%3Df1ba12a1-51de-4de6-8a9e-97b26ce685e0#?env%5BPublic-collection%5D=W10=) **Step 2** Click **Fork collection** and follow the steps to set it up in your environment. **Step 3** After the collection is set up, select a method to test, for example, `eth_chainId`. **Step 4** Hover over `{{YOUR_CHAINSTACK_ENDPOINT}}` and click **Add new variable**. **Step 5** Add your Chainstack node endpoint to the **Value** field, and select the collection as the **Scope**. Now your Chainstack endpoint is available for every method. Edit the **Body** parameters if you need and send the request using the **Send** button. # eth_sendRawTransaction | Ethereum Source: https://docs.chainstack.com/reference/ethereum-sendrawtransaction openapi/ethereum_node_api/execute_transactions/eth_sendRawTransaction.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that allows submitting a signed transaction to the network. eth_sendRawTransaction on Ethereum via Chainstack. Ethereum API method that allows submitting a signed transaction to the network. Once a transaction is signed, you can use the `eth_sendRawTransaction` method to submit the signed transaction to the Ethereum network for processing. > [!WARNING] > > Note that the interactive example in this page will not work, due to the fact that `eth_sendRawTransaction` is used to modify the state of the blockchain, it is not possible to duplicate the same request. ## Parameters * `data` — the signed transaction. The serialized transaction data, which includes the transaction metadata such as the nonce, gas price, gas limit, recipient address, and data payload, as well as the digital signature generated using the private key associated with the address that is sending the transaction. Typically, signed with a library such as web3.py, web3.js, or ethers.js using the sender's private key. ## Response * `result` — the unique hash identifying the transaction or the zero hash if the transaction is not available yet. The zero hash is a special value that represents an invalid or null hash. ## `eth_sendRawTransaction` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). > With ether.js, you can use the `ChainstackProvider` directly if you want to run this code on the Ethereum mainnet: ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); ``` The following examples demonstrate how to use Web3 libraries to make an ETH transfer on the Sepolia testnet. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function sendEth(value) { // Define the sender and receiver addresses, and the private key const sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a'; const receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114'; const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Define the gas limit const gasLimit = await provider.estimateGas({ from: sender, to: receiver, value: ethers.parseEther(value), }); // Get the transaction count for the sender address const nonce = await provider.getTransactionCount(sender); // Define the transaction object const transactionObject = { to: receiver, gasPrice: ethers.parseUnits('50', 'gwei'), gasLimit: gasLimit.toString(), nonce: nonce, value: ethers.parseEther(value), }; // Sign the transaction using the private key const wallet = new ethers.Wallet(privateKey, provider); console.log(`Sending transaction...`) const transaction = await wallet.sendTransaction(transactionObject); console.log('Transaction hash:', transaction.hash); } sendEth("2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the sender and receiver addresses, and the private key sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a' receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114' private_key = '1111111111111111111111111111111111111111111111111111111111111111' # Estimate gas limit gas_limit = web3.eth.estimate_gas({ 'from': sender, 'to': receiver }) # Build the transaction object transaction = { 'nonce': web3.eth.get_transaction_count(sender), 'to': receiver, 'value': web3.to_wei(1, 'ether'), # value to send 'gas': gas_limit, 'gasPrice': web3.eth.gas_price, 'chainId': web3.eth.chain_id } # Sign the transaction signed_tx = web3.eth.account.sign_transaction(transaction, private_key) print(f'Signed transaction: {signed_tx}') # Send the transaction print('Sending transaction...') tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) print(f'Transaction hash: {web3.toHex(tx_hash)}') ``` > [!NOTE] > > Note that you will need to edit the `amountToSend`, `recipientAddress`, and `secretKey` fields to be able to use this code. ## Use case One potential use case for `eth_sendRawTransaction` is to automatically swap ETH for a token when its price reaches a certain level. `eth_sendRawTransaction` will allow specifying custom transaction parameters, such as gas price or gas limit, to optimize the speed and cost of the transaction. For instance, you could build a program monitoring the price of a specific token and execute a `swapExactETHForTokens` transaction when the token reaches a certain price level, it could use `eth_sendRawTransaction` to submit a raw transaction with a custom gas price and limit that are optimized for the current network conditions. The following code shows how to call the `swapExactETHForTokens` function from the SushiSwap exchange on Ethereum using ethers.js: > [!NOTE] > > Note that this code only shows how to execute the `swapExactETHForTokens` function on [SushiSwap on Ethereum mainnet](https://etherscan.io/address/0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F#code), and not how to track the prices. You need to build a separate part for this. This particular example shows how to swap ETH for AAVE tokens. ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); // ABI for the swapExactETHForTokens function const sushiRouterABI = [ { "inputs":[ { "internalType":"uint256", "name":"amountOutMin", "type":"uint256" }, { "internalType":"address[]", "name":"path", "type":"address[]" }, { "internalType":"address", "name":"to", "type":"address" }, { "internalType":"uint256", "name":"deadline", "type":"uint256" } ], "name":"swapExactETHForTokens", "outputs":[ { "internalType":"uint256[]", "name":"amounts", "type":"uint256[]" } ], "stateMutability":"payable", "type":"function" } ] async function swapETHForToken(value, token, privateKey) { // Create a new wallet using the private key const wallet = new ethers.Wallet(privateKey, provider); // Get the SushiSwap Router contract and create a contract instance const sushiRouterAddress = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; // SushiSwap Router address on Ethereum Mainnet const sushiRouterContract = new ethers.Contract(sushiRouterAddress, sushiRouterABI, wallet); // Prepare the swap parameters const tokenToBuy = token; const amountOfETH = ethers.parseEther(value); // Convert the input value to Wei const minAmountOfTokens = ethers.parseUnits('0'); // Set the minimum output token amount to 0, this means that you will always receive something. const deadline = Math.floor(Date.now() / 1000) + 60 * 10; // Set the deadline to 10 minutes from now // Define the token path for the swap const path = ['0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenToBuy]; // WETH to token to buy // Parameters for the transaction object const nonce = await wallet.getTransactionCount(); const gasPrice = await chainstack.getGasPrice(); const transactionData = sushiRouterContract.interface.encodeFunctionData('swapExactETHForTokens', [ minAmountOfTokens, path, wallet.address, deadline ]); // Calculate the gas limit based on the parameters const gasLimit = await chainstack.estimateGas({to: sushiRouterAddress, value: amountOfETH, data: transactionData}); // Get the chain ID for replay-protected (EIP-155) transactions const chainId = await chainstack.getNetwork().then(network => network.chainId); // Build the transaction object const transactionObject = { to: sushiRouterAddress, value: amountOfETH, nonce: nonce, gasPrice: gasPrice, gasLimit: gasLimit, data: transactionData, chainId: chainId }; // Sign and send the transaction console.log(`Swapping tokens...`); const signedTx = await wallet.signTransaction(transactionObject); const transaction = await chainstack.sendTransaction(signedTx); console.log(`Transaction sent; transaction hash: ${transaction.hash}\n`); // Wait for the transaction to be confirmed console.log(`Validating transaction...`); const receipt = await transaction.wait(); return receipt } async function main() { try { // Set the swap parameters and private key const quantity = '0.2'; // Amount of ETH to swap const addressToBuy = '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9'; // Address of the token to buy (AAVE in this case) const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Replace with your actual private key // Execute the swap and check if successful const receipt = await swapETHForToken(quantity, addressToBuy, privateKey); if (receipt.status === 1) { console.log(`The swap was successful!`); console.log(`Transaction receipt`, receipt); } else { console.log(`The swap failed with status ${receipt.status}.`); } } catch (error) { console.error(`An error occurred during the swap: ${error.message}`); } } main(); ``` > [!WARNING] > > **Security notice** > > You need your private key to sign the transaction; never share your private key with anyone. > > On a side note, the private key in this case must be imported without '0x' at the beginning of the string. In the `swapETHForToken` function, a new wallet is created using the provided `privateKey`. Then, the instance of the SushiSwap Router contract is obtained using its address and the ABI (Application Binary Interface) for the `swapExactETHForTokens` function. This function swaps a specified amount of ETH for a token at the current market price. The `tokenToBuy`, `amountOfETH`, `minAmountOfTokens`, and `deadline` variables are used to prepare the swap parameters. The `path` variable defines the input and output token path for the swap. The `nonce`, `gasPrice`, and the `transactionData` variables define the parameters for the transaction object and encode the `swapExactETHForTokens` function call with the specified parameters. The `gasLimit` variable is calculated based on the parameters, and the `chainId` variable is obtained for replay-protected (EIP-155) transactions. Then, the `transactionObject` variable is built, including the `to` address, the `value`, the `nonce`, the `gasPrice`, the `gasLimit`, the `data`, and the `chainId`. Finally, the transaction is signed and sent, and the receipt is returned. If the status of the receipt is equal to 1, the swap was successful, and the receipt object is logged to the console. If the status is not equal to 1, the swap failed, and an error message is logged to the console. # eth_sendRawTransactionSync | Ethereum Source: https://docs.chainstack.com/reference/ethereum-sendrawtransactionsync openapi/ethereum_node_api/execute_transactions/eth_sendRawTransactionSync.json POST /cbe98cb5b5f53987d76c16d4ad716b2f Ethereum API method that submits a signed transaction and blocks until the transaction receipt is returned or the timeout expires. Ethereum API method that submits a signed transaction and blocks until the transaction receipt is returned or the timeout expires. This is a synchronous version of [`eth_sendRawTransaction`](/reference/ethereum-sendrawtransaction) — instead of returning just the transaction hash, it waits for the transaction to be included in a block and returns the full receipt. This method is defined by [EIP-7966](https://eips.ethereum.org/EIPS/eip-7966) and is available on Erigon (>= v3.4.0) and go-ethereum (>= v1.16.6). > [!NOTE] > > Because Ethereum mainnet has a \~12-second block time, sync calls often exceed short timeouts. Pick a `timeout` that fits your application — the request returns once a receipt is available or once the timeout expires, whichever comes first. ## Parameters * `signedTransactionData` — the signed transaction data in hexadecimal format. Includes nonce, gas price, gas limit, recipient address, value, data, and signature. * `timeout` (optional) — maximum wait time in milliseconds, passed as a **decimal integer** per EIP-7966 (e.g., `5000`). If omitted, the node uses its configured default. Note that other Chainstack chains may use different timeout encodings — see the [Polygon equivalent](/reference/polygon-sendrawtransactionsync), where Bor expects a hex-encoded timeout. ## Response * `result` — the full transaction receipt object once the transaction is included in a block, with the same fields as [`eth_getTransactionReceipt`](/reference/ethereum-gettransactionreceipt). If the timeout expires before inclusion, the node returns an error indicating the transaction was added to the pool but not yet processed. ## `eth_sendRawTransactionSync` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const sendTransactionSync = async () => { const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); const tx = { to: "0xRecipientAddress", value: ethers.parseEther("0"), chainId: 1, }; // Sign the transaction const signedTx = await wallet.signTransaction(tx); // Send synchronously — blocks until receipt is returned or timeout expires const receipt = await provider.send( "eth_sendRawTransactionSync", [signedTx, 5000] ); console.log(`Confirmed in block: ${receipt.blockNumber}`); console.log(`Status: ${receipt.status}`); }; sendTransactionSync(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) account = web3.eth.account.from_key("YOUR_PRIVATE_KEY") tx = { 'nonce': web3.eth.get_transaction_count(account.address), 'to': "0xRecipientAddress", 'value': 0, 'gas': 21000, 'gasPrice': web3.eth.gas_price, 'chainId': 1, # Ethereum mainnet } signed = account.sign_transaction(tx) # Send synchronously with a 5-second timeout (decimal integer per EIP-7966) receipt = web3.provider.make_request( "eth_sendRawTransactionSync", [signed.raw_transaction.hex(), 5000] ) print(f"Receipt: {receipt['result']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_sendRawTransactionSync", "params": ["SIGNED_TX_DATA", 5000], "id": 1}' ``` ## When to use Use `eth_sendRawTransactionSync` instead of `eth_sendRawTransaction` when: * You need the receipt immediately and want to avoid polling with `eth_getTransactionReceipt`. * You are building synchronous request/response flows (payments, bots, simulations). * You want to simplify transaction submission logic by getting the result in a single call. For fire-and-forget scenarios or when submitting many transactions in parallel, use [`eth_sendRawTransaction`](/reference/ethereum-sendrawtransaction). # eth_simulateV1 | Ethereum Source: https://docs.chainstack.com/reference/ethereum-simulatev1 openapi/ethereum_node_api/execute_transactions/eth_simulateV1.json POST /2f6d649e68c2f861fecd5b8a9e35139e Ethereum API method that enables simulation of complex transactions and state changes without modifying the blockchain. On Ethereum. Ethereum API method that enables simulation of complex transactions and state changes without modifying the blockchain. This method is particularly useful for testing and debugging smart contracts, estimating gas costs, and analyzing the potential outcomes of transactions before committing them to the network. > [!WARNING] > > **Data from full node only** > > Running `eth_simulateV1` will yield a full node response—i.e. only the data from the latest 128 blocks. Archive data is not supported for this call and the node will respond with `missing trie node`. ## Parameters * `object` — the simulation request object: * `blockStateCalls` — an array of block state call objects: * `blockOverrides` — (optional) an object to override block-level parameters: * `baseFeePerGas` — (optional) the base fee per gas for the block, encoded as hexadecimal. * `stateOverrides` — (optional) an object to override account states: * `
` — the address of the account to override: * `balance` — (optional) the balance to set for the account, encoded as hexadecimal. * `calls` — an array of transaction call objects: * `from` — the address from which the transaction is sent. * `to` — the address to which the transaction is directed. * `maxFeePerGas` — (optional) the maximum fee per gas the sender is willing to pay. * `value` — (optional) the value sent with the transaction, encoded as hexadecimal. * `validation` — (optional) a boolean indicating whether to perform validation checks. * `traceTransfers` — (optional) a boolean indicating whether to trace value transfers. * `quantity or tag` — the integer block number, or the string `"latest"`, `"earliest"` or `"pending"`. ## Response * `object` — the simulation result: * `blockResults` — an array of block result objects: * `blockNumber` — the block number of the simulated block. * `blockHash` — the hash of the simulated block. * `stateRoot` — the state root of the simulated block. * `receipts` — an array of transaction receipt objects for each simulated transaction. * `traces` — (if `traceTransfers` is true) an array of transfer trace objects. ## Use case `eth_simulateV1` is particularly useful for developers working on complex smart contracts or DApps. It allows for the simulation of intricate transaction sequences, including multiple contract interactions and state changes, without the need to deploy contracts or spend real gas. This can be invaluable for: * Debugging complex smart contract interactions * Estimating gas costs for multi-step transactions * Analyzing the potential outcomes of different transaction scenarios * Testing contract upgrades or migrations before deployment # subscribe ("logs") | Ethereum Source: https://docs.chainstack.com/reference/ethereum-subscribelogs Reference for the subscribe ("logs") JSON-RPC method on the Ethereum blockchain via Chainstack nodes. Parameters, response, and examples included. Subscription equivalent to [eth\_newFilter](/reference/ethereum-newfilter). `subscribe("logs")` allows developers to subscribe to real-time updates about new event logs on the Ethereum blockchain; the application will receive notifications whenever new logs matching the filter are emitted. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) The subscription options: * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `subscribe("logs")` code example > [!NOTE] > > Note that log subscriptions require a WebSocket connection. Use the provider's event listeners to react to subscription events: * `provider.on(filter, listener)` — registers a listener that fires for each new log matching the filter. * the `listener` receives the log object emitted by the contract. * `provider.off(filter)` — removes the listeners and stops the subscription. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_ENDPOINT"; const provider = new ethers.WebSocketProvider(NODE_URL); // Filter for WETH transfer events const logsFilter = { address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH contract address topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] // Transfer event signature }; async function subscribeToLogs() { try { // Create a new subscription to logs with the specified filter provider.on(logsFilter, handleLogs); console.log("Subscription created for the specified logs filter"); } catch (error) { console.error(`Error subscribing to new logs: ${error}`); } } // Listener that logs the received log data function handleLogs(log) { console.log("New log received:", log); } // Surface any provider-level errors provider.on("error", (error) => { console.error("Error when subscribing to new logs:", error); }); subscribeToLogs(); ``` ## Use case A practical use case for `subscribe("logs")` is a DApp that continuously listens for new specific event logs and isolates certain fields for analytics purposes. This is useful, for example, to only track events about a specific ERC-721 token. The following is an implementation of this concept using `ethers.js` subscriptions, this program will only track the `Transfer` event from the [MutantApeYachtClub collection](https://etherscan.io/token/0x60e4d786628fea6478f785a6d7e704777c86a7c6) smart contract. The `Transfer` event looks like this in the smart contract: ```sol IERC721.sol theme={"system"} event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); ``` > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about event logs. The following code will log all of the new `Transfer` events, and extract the block number. This can be useful to analyze when tokens are moved. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_ENDPOINT"; const provider = new ethers.WebSocketProvider(NODE_URL); // Filter for Transfer events from the MAYC smart contract const logsFilter = { address: '0x60E4d786628Fea6478F785A6d7e704777c86a7c6', // MAYC contract address topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] // Transfer event signature }; async function subscribeToLogs() { try { // Create a new subscription to logs with the specified filter provider.on(logsFilter, handleLogs); console.log("Subscription created for the specified logs filter"); } catch (error) { console.error(`Error subscribing to logs: ${error}`); } } /* Fallback functions to react to the different events */ // Listener that logs the filtered events function handleLogs(log) { const block = log.blockNumber; console.log(`A Mutant Ape was transferred in this block: ${block}`); } // Surface any provider-level errors provider.on("error", (error) => { console.error(`Error receiving logs: ${error}`); }); subscribeToLogs(); ``` This code creates a new subscription to the `logs` event by passing the filter to the `provider.on` method. This method registers a listener that fires every time a new log matching the filter is emitted. The code defines a listener function that is attached to the provider: `handleLogs`. The `handleLogs` function is called when a new log matching the filter is emitted, then extracts the block number field and logs it to the console. A separate `error` listener is attached to the provider, and it logs an error message when an error occurs. Finally, the code calls the `subscribeToLogs` function, which creates the subscription and attaches the listener. When a new event log is received, the `handleLogs` function is called to extract and log the data to the console. # subscribe ("newBlockHeaders") | Ethereum Source: https://docs.chainstack.com/reference/ethereum-subscribenewblockheaders Reference docs for the subscribe ("newBlockHeaders") JSON-RPC method on the Ethereum blockchain, available via Chainstack JSON-RPC nodes. Subscription equivalent to [eth\_newBlockFilter](/reference/ethereum-newblockfilter). `subscribe("newBlockHeaders")` allows developers to subscribe to real-time updates about new block headers on the Ethereum blockchain; the application will receive notifications whenever a new block is added to the blockchain. The notification will include information about the new block, such as its block number, hash, and timestamp. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `newBlockHeaders` in this case. In ethers.js v6 this maps to the `"block"` event on a `WebSocketProvider`. * `function` — a listener function that will be called every time a new block is received. With ethers.js v6, the listener receives the block number of the newly added block. ## Response * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ethereum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ethereum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Ethereum network since the inception of the network. It measures the overall security and integrity of the Ethereum network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `subscribe("newBlockHeaders")` code example > [!NOTE] > > Note that subscriptions require a WebSocket connection. Use a `WebSocketProvider` in ethers.js v6. Use the provider event API to attach listeners to the provider: * `provider.on("block", listener)` — fires for each new block and passes the block number to the listener. * `provider.on("error", listener)` — fires if an error is detected on the connection. * `provider.off("block", listener)` — removes a single listener for the `"block"` event. * `provider.removeAllListeners()` — removes every listener, which effectively unsubscribes. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_ENDPOINT"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToNewBlocks() { try { // Subscribe to new blocks by listening for the 'block' event provider.on("block", handleNewBlock); provider.on("error", handleError); console.log("Connected to the 'block' event"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } // Listener functions to react to the different events // Listener that fetches and logs the full block for each new block number async function handleNewBlock(blockNumber) { const blockHeader = await provider.getBlock(blockNumber); console.log("New block header:", blockHeader); } // Listener that logs any errors that occur function handleError(error) { console.error("Error when subscribing to new block header:", error); } subscribeToNewBlocks(); ``` ## Use case A practical use case for `subscribe("newBlockHeaders")` is a DApp that continuously listens for new block headers, then isolates the block number and `baseFeePerGas` for analytics purposes. The following is an implementation of this concept using ethers.js v6 subscriptions: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); let blockCount = 0; async function subscribeToNewBlocks() { try { // Subscribe to new blocks by listening for the 'block' event provider.on("block", handleNewBlock); provider.on("error", handleError); console.log("Subscribed to the 'block' event"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } async function unsubscribeFromNewBlocks() { await provider.removeAllListeners(); console.log("Successfully unsubscribed from new blocks"); return process.exit(1); } /* Listener functions to react to the different events */ // Listener that extracts block number and base fee per gas from the received block async function handleNewBlock(blockNumber) { const blockHeader = await provider.getBlock(blockNumber); const baseFeePerGas = ethers.formatUnits(blockHeader.baseFeePerGas, "gwei"); blockCount++; console.log( `Block number: ${blockNumber} \nBase Fee per Gas: ${baseFeePerGas} Gwei \n` ); if (blockCount === 100) { unsubscribeFromNewBlocks(); } } // Listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToNewBlocks(); ``` This code subscribes to new blocks by listening for the `"block"` event on a `WebSocketProvider`. Attaching a listener with `provider.on("block", ...)` starts the subscription and invokes the listener for every new block. The code defines two listener functions that are attached to the provider: `handleNewBlock` and `handleError`. The `handleNewBlock` function is called when a new block is received; it fetches the full block with `provider.getBlock` and extracts the block number and base fee per gas, formatting the latter to gwei with `ethers.formatUnits`. The `handleError` function is called when an error occurs, and it logs an error message. This code includes an `unsubscribeFromNewBlocks` function which removes the listeners with `provider.removeAllListeners` and quits the program using `return process.exit(1)` after it received 100 new blocks. This is to demonstrate its use. Finally, the code calls the `subscribeToNewBlocks` function, which creates the subscription and attaches the event listeners. When a new block header is received, the `handleNewBlock` function is called to extract the block data and log it to the console. # subscribe ("pendingTransactions") | Ethereum Source: https://docs.chainstack.com/reference/ethereum-subscribependingtransactions Reference docs for the subscribe ("pendingTransactions") JSON-RPC method on the Ethereum blockchain, available via Chainstack JSON-RPC nodes. The ethers.js subscription equivalent to [eth\_newPendingTransactionFilter](/reference/ethereum-newpendingtransactionfilter). `subscribe("pendingTransactions")` allows developers to subscribe to real-time updates about pending transactions on the Ethereum blockchain; the application will receive notifications whenever a pending transaction appears on the blockchain. If you are using this method to track your own transactions, make sure [MEV protection](/docs/mev-protection) is disabled. Otherwise you won't detect them as they go through a private pool. ## Parameters * `string` — the event name to subscribe to, `"pending"` in ethers.js, which maps to the `pendingTransactions` subscription. * `function` — a listener function that is called every time a new pending transaction is received. In ethers.js this listener receives a single parameter, the transaction hash. ## Response * `string` — the hash identifying the pending transaction. ## `subscribe("pendingTransactions")` code example > [!NOTE] > > Note that ethers.js subscriptions require a WebSocket connection, so use `ethers.WebSocketProvider`. Use the provider event API to attach listeners and manage the subscription: * `provider.on("pending", listener)` — registers a listener that runs for each new pending transaction and receives the transaction hash. * `listener` — your callback handles any errors internally, so wrap the work in a `try...catch` block. * `provider.off("pending", listener)` — removes the listener to stop receiving pending transactions. * `provider.destroy()` — closes the WebSocket connection when you are done. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; // Ensure this is your WebSocket endpoint const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToPendingTransactions() { try { // Subscribe to pending transactions await provider.on('pending', (transactionHash) => { // Listener for new pending transactions console.log('New pending transaction:', transactionHash); }); console.log('Subscription successful'); } catch (error) { console.error(`Error subscribing to pending transactions: ${error}`); } } subscribeToPendingTransactions(); ``` ## Use case A practical use case for `subscribe("pendingTransactions")` is a DApp that continuously listens for new pending transactions, then isolates the `from`, `to`, and `value` fields for analytics purposes. This is useful, for example, to only track transactions that move at least a certain amount of ETH. The following is an implementation of this concept using ethers.js subscriptions: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToPendingTransactions() { try { // Listener for new pending transactions await provider.on('pending', handleNewPending); console.log("Subscription successful"); } catch (error) { console.error(`Error subscribing to pending transactions: ${error}`); } } // Adapted to be able to call unsubscribe correctly async function unsubscribe() { try { await provider.off('pending', handleNewPending); await provider.destroy(); console.log("Successfully unsubscribed!"); process.exit(0); // Exiting normally } catch (error) { console.error(`Error unsubscribing: ${error}`); process.exit(1); // Exiting with an error } } // Listener that logs the received pending transactions and extracts from, to, and value fields async function handleNewPending(transactionHash) { try { const transaction = await provider.getTransaction(transactionHash); const from = transaction.from; const to = transaction.to; const value = transaction.value; if (value >= 1000000000000000000n) { // 1 ETH in wei console.log(`----- New pending transaction ------`); console.log(`From: ${from}`) console.log(`To: ${to}`) console.log(`Value: ${Number(ethers.formatEther(value)).toFixed(2)} ETH \n`) } } catch (error) { console.error(`Error getting transaction: ${error}`); } } subscribeToPendingTransactions(); ``` This code creates a new subscription to pending transactions using the `provider.on("pending", listener)` method on a `WebSocketProvider`. This registers a listener that runs for every new pending transaction hash. The code defines the `handleNewPending` listener function that is attached to the provider. The `handleNewPending` function is called when a new pending transaction is received; it runs the [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) method and extracts the `from`, `to`, and `value` fields. If the value transferred is above 1 ETH, the data is logged. The code includes the `unsubscribe` function that can be implemented in the logic to remove the listener, close the connection, and exit the program when a condition is met. Finally, the code calls the `subscribeToPendingTransactions` function, which creates the subscription and attaches the listener. When a new pending transaction is received, the `handleNewPending` function is called to extract the data and log it to the console. # subscribe ("syncing") | Ethereum Source: https://docs.chainstack.com/reference/ethereum-subscribesyncing Monitor Ethereum node sync status in real time with web3.js. Subscribe to syncing updates to track block download progress and chain synchronization. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `logs` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `object` — the following sync object when the node is currently syncing: * `startingBlock` — the block number from which the node began syncing. * `currentBlock` — the latest block number the node has synced to. * `highestBlock` — the estimated highest block number that needs to be synced. * `pulledStates` — the number of state entries that have already been downloaded. * `knownStates` — the estimated number of state entries to be downloaded during the sync. * `boolean` — returns `False` when the node is already in sync. ## `subscribe("syncing")` code example > [!NOTE] > > Note that ethers.js subscriptions require a WebSocket connection. Use the [provider events](https://docs.ethers.org/v6/api/providers/#ProviderEvent) to attach event listeners to the provider: * `sync` — activates for each new syncing event. * `error` — activates if an error is detected during the subscription. * `provider.removeAllListeners` — removes the listeners and stops the subscription. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToSync() { try { // Subscribe to the 'sync' event provider.on('sync', handleSync); // Attach an error listener to the provider provider.on('error', handleError); console.log('Subscribed to sync events'); } catch (error) { console.error(`Error subscribing to sync: ${error}`); } } /* Callback functions to react to the different events */ // Event listener that logs the sync status async function handleSync(sync) { console.log(sync); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error: ${error}`); } subscribeToSync(); ``` ## Use case A practical use case for `subscribe("syncing")` is a DApp that continuously listens for the status of a node and notifies the developer if the node falls behind a certain number of blocks. The following is an implementation of this concept using ethers.js subscriptions, this program will leave a notification in the console if the node falls more than 100 blocks behind. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToSync() { try { // Subscribe to the 'sync' event provider.on('sync', handleSync); // Attach an error listener to the provider provider.on('error', handleError); console.log('Subscribed to sync events'); } catch (error) { console.error(`Error: ${error}`); } } /* Callback functions to react to the different events */ // Event listener that compares the current and highest blocks async function handleSync(sync) { const currentBlock = sync.currentBlock; const highestBlock = sync.highestBlock; if (currentBlock !== undefined && highestBlock !== undefined) { const blocksBehind = highestBlock - currentBlock; console.log(`The node is ${blocksBehind} blocks behind the network.`); if (blocksBehind > 1000) { alert(`The node is ${blocksBehind} blocks behind the network. Please check your connection.`); } } } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToSync(); ``` This code creates a new subscription to the `sync` event using the `provider.on` method on a `WebSocketProvider`. This method attaches event listeners to the provider for the events you want to track. The code defines two event listener functions that are attached to the provider: `handleSync` and `handleError`. The `handleSync` function is called when a new syncing event is emitted, extracts the `currentBlock` and the `highestBlock` fields, and then compares them. If the node is more than 1,000 blocks behind, the user will receive an alert. The `handleError` function is called when an error occurs, and it logs an error message. Finally, the code calls the `subscribeToSync` function, which creates the subscription and attaches the event listeners. When a new event is received, the `handleSync` function is called to extract the data and log it to the console. # eth_syncing | Ethereum Source: https://docs.chainstack.com/reference/ethereum-syncing openapi/ethereum_node_api/chain_info/eth_syncing.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns an object with information about the current synchronization status of the node, or false if the node is fully synced. Ethereum API method that returns an object with information about the current synchronization status of the node, or `false` if the node is fully synced. This method can track the progress of a node's synchronization with the Ethereum blockchain. The returned object contains data such as the starting block, current block, and highest block of the node, allowing developers to monitor and estimate the time remaining for the node to sync fully. ## Parameters * `none` ## Response * `result` — the boolean value `false` when not syncing or a JSON object when syncing: * `startingBlock` — the block number where the synchronization process started, encoded as hexadecimal. * `currentBlock` — the block number that the node has currently processed, same as `eth_blockNumber`, encoded as hexadecimal. * `highestBlock` — the block number of the latest block in the blockchain known to the node. ## `eth_syncing` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const syncStatus = async () => { const status = await chainstack.send("eth_syncing", []); console.log(status); }; syncStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" print (web3.eth.syncing) ``` ## Use case The `eth_syncing` method on Ethereum can be useful for developers building applications that interact with the Ethereum blockchain. For example, you may want to provide feedback to your application's user about the status of the Ethereum node the application is connecting to. Here is a code example using the ethers.js library and the `ChainstackProvider`: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function syncstatus() { try { // Query the sync status const syncing = await chainstack.send("eth_syncing", []); if (syncing) { console.log( `Node is syncing. Current block: ${syncing.currentBlock}. Highest block: ${syncing.highestBlock}` ); } else { console.log("Node is fully synced."); } } catch (error) { console.error(error); } } syncstatus(); ``` In this example, the program creates a new `chainstack` instance and connects to the node. Then, it calls `eth_syncing` to get the current synchronization status of the node. If the node is fully synced, the function logs a message to the console indicating that the node is fully synced. If the node is still syncing, the function logs the current and highest block numbers known to the node. # trace_block | Ethereum Source: https://docs.chainstack.com/reference/ethereum-trace_block openapi/ethereum_node_api/debug_and_trace/trace_block.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns traces for all the transactions within a specific block. Available on Ethereum via Chainstack JSON-RPC nodes. Ethereum API method that returns traces for all the transactions within a specific block. Developers can use the `trace_block` method to gain insight into the behavior of smart contracts within a block, analyze gas usage, and optimize their contracts accordingly. This method is only available on an Erigon instance. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `result` — an object containing the traces of all of the transactions in the block: * `action` — an object that describes the action taken by the transaction: * `from` — the address of the sender who initiated the transaction. * `callType` — the type of call, `call` or `delegatecall`, two ways to invoke a function in a smart contract. `call` creates a new environment for the function to work in, so changes made in that function won't affect the environment where the function was called. `delegatecall` doesn't create a new environment. Instead, it runs the function within the environment of the caller, so changes made in that function will affect the caller's environment. * `gas` — the units of gas included in the transaction by the sender. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is null. * `value` — the value of the native token transferred along with the transaction, in Wei. * `blockHash` — the hash of the block in which the transaction was included. * `blockNumber` — the number of the block in which the transaction was included. * `error` — a string that indicates whether the transaction was successful or not. `null` if successful, `Reverted` if not. * `result` — an object that contains additional data about the execution of the transaction: * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `output` — the return value of the call, encoded as a hexadecimal string. * `subtraces` — the number of sub-traces created during execution. When a transaction is executed on the EVM, it may trigger additional sub-executions, such as when a smart contract calls another smart contract or when an external account is accessed. * `traceAddress` — an array that indicates the position of the transaction in the trace. * `transactionHash` — the hash of the transaction. * `transactionPosition` — the position of the transaction in the block. * `type` — the type of action taken by the transaction, `call` or `create`. `call` is the most common type of trace and occurs when a smart contract invokes another contract's function. `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain. ## `trace_block` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const traceBlock = async (block) => { const traces = await chainstack.send("trace_block", [block]); console.log(traces); }; traceBlock("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block = "latest" traces = web3.provider.make_request('trace_block', [block]) print(traces) ``` ## Use case A practical use case for the `trace_block` method on Ethereum can be to conduct an analysis of the most recent block on the network. For example, by examining the amount of gas supplied by all the transactions and the actual amount of gas utilized, developers can gain valuable insights into the block's behavior. This can be the first step to developing a chain explorer. The following code is an implementation of this idea using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); // A function that returns an array of result and gas fields for each transaction in a block async function traceBlock(block) { try { const traces = await chainstack.send("trace_block", [block]); const resultFields = []; const gasFields = []; for (const { result, action } of traces) { resultFields.push(result); gasFields.push(action.gas); } return [resultFields, gasFields]; } catch (error) { console.error(`Error while tracing block: ${error}`); throw error; } } // A function that calculates the total gas used by all transactions in a block function returnGasUsed(results) { const totalGasUsed = results.reduce((total, result) => { if (result !== null && result.gasUsed !== undefined) { total += Number(result.gasUsed); } return total; }, 0); return totalGasUsed; } // A function that calculates the total gas supplied to a block function returnTotalGas(gasArray) { const totalGas = gasArray.reduce((total, gas) => { if (gas !== undefined) { total += Number(gas); } return total; }, 0); return totalGas; } // The main function that runs the gas analysis async function main() { try { const block = 'latest'; const [results, gas] = await traceBlock(block); const gasUsed = returnGasUsed(results); const totalGas = returnTotalGas(gas); console.log('===== Gas analysis ====='); console.log(`Total gas supplied: ${totalGas}`); console.log(`Gas used: ${gasUsed}`); console.log(`Unused gas: ${totalGas - gasUsed}`); const gasUsedPercentage = (gasUsed / totalGas) * 100; console.log(`Percentage of gas used: ${gasUsedPercentage.toFixed(2)} %`); } catch (error) { console.error(`Error while running gas analysis: ${error}`); } } // Call the main function to start the program main(); ``` The `traceBlock` function takes a block number or tag as an input parameter and returns an array of two elements. The first element is an array of the results of each transaction in the block, which contains the `gasUsed` field, and the second element is an array of the `gas` field used by each call. The `returnGasUsed` function calculates the total gas used by all transactions in a block by iterating over the results array and summing up the `gasUsed` fields of each non-null element. The `returnTotalGas` function calculates the total gas supplied to a block by iterating over the `gasArray` array and summing up the non-undefined elements. The main function calls the `traceBlock` function with the block number or tag calculates the gas used and total gas supplied using the `returnGasUsed` and `returnTotalGas` functions, and outputs the results to the console. The `main` function is wrapped in a `try-catch` block to handle any errors that may occur during execution. Finally, the `main` function is called at the end of the script to run the gas analysis. # trace_transaction | Ethereum Source: https://docs.chainstack.com/reference/ethereum-trace_transaction openapi/ethereum_node_api/debug_and_trace/trace_transaction.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that traces a specific transaction. trace_transaction JSON-RPC method available on the Ethereum blockchain via Chainstack. Ethereum API method that traces a specific transaction. It provides a detailed record of all the steps taken by the Ethereum Virtual Machine (EVM) during the execution, including all the operations performed and the changes made to the blockchain state. This method is available on Erigon only. ## Parameters * `hash` — the hash identifying a transaction ## Response * `action` — an object that describes the action taken by the transaction: * `from` — the address of the sender who initiated the transaction. * `callType` — the type of call, `call` or `delegatecall`, two ways to invoke a function in a smart contract. `call` creates a new environment for the function to work in, so changes made in that function won't affect the environment where the function was called. `delegatecall` doesn't create a new environment. Instead, it runs the function within the environment of the caller, so changes made in that function will affect the caller's environment. * `gas` — the units of gas included in the transaction by the sender. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is null. * `value` — the value of the native token transferred along with the transaction, in Wei. * `blockHash` — the hash of the block in which the transaction was included. * `blockNumber` — the number of the block in which the transaction was included. * `error` — a string that indicates whether the transaction was successful or not. `null` if successful, `Reverted` if not. * `result` — an object that contains additional data about the execution of the transaction: * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `output` — the return value of the call, encoded as a hexadecimal string. * `subtraces` — the number of sub-traces created during execution. When a transaction is executed on the EVM, it may trigger additional sub-executions, such as when a smart contract calls another smart contract or when an external account is accessed. * `traceAddress` — an array that indicates the position of the transaction in the trace. * `transactionHash` — the hash of the transaction. * `transactionPosition` — the position of the transaction in the block. * `type` — the type of action taken by the transaction, `call` or `create`. `call` is the most common type of trace and occurs when a smart contract invokes another contract's function. `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain. ## `trace_transaction` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const traceTransaction = async (txHash) => { const traces = await chainstack.send("trace_transaction", [txHash]); console.log(traces); }; traceTransaction( "0xa63fdfaddf1c5c738f1332ff4f4e0f258d2cbad901d853221c729c8ad28a5093" ); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) tx_hash = "0xa63fdfaddf1c5c738f1332ff4f4e0f258d2cbad901d853221c729c8ad28a5093" traces = web3.provider.make_request('trace_transaction', [tx_hash]) print(traces) ``` ## Use case A practical use case of the `trace_transaction` method can be in inspecting NFT transfers, allowing you to identify internal transactions, the sender and receiver, and the amounts transferred. This can be especially useful when analyzing an ERC-721 token purchase on a marketplace like Opensea, where the transfer of funds is not always straightforward due to multiple internal calls for transferring funds to the NFT owner, paying royalties to the creator, and other factors. By using `trace_transaction`, you can gain a more comprehensive understanding of the transaction flow. The following is an implementation of the logic using ethers.js inspecting an [ERC-721 transfer from Opensea on Ethereum](https://polygonscan.com/tx/0xe2eb8be0ea71220bc298e48ba58034d49efb2d311ca45dfe228cb564cd8a3fb5): ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); // Trace a transaction using the trace_transaction RPC method async function traceTransaction(txHash) { const result = await chainstack.send("trace_transaction", [txHash]); return result.map(({ action }) => action); } // Parse a value in wei to ether function parseValue(wei) { const etherValue = ethers.formatEther(wei) return etherValue; } // Main function to run the program async function main() { const txHash = "0x853b50ccf670f6f2b573d7c58e7bf735bc4623ce434e529d4ad5176c1a5a1efa"; const transactionTraces = await traceTransaction(txHash); // Trace the specified transaction const extractedData = transactionTraces.map(({ from, to, value }) => ({ from, to, value })); // Extract relevant data from the transaction trace // Loop through each extracted data object and print relevant information for (const { from, to, value } of extractedData) { const parsedValue = parseValue(value); // Parse the value to ether // Print information about non-zero value transfers if (parsedValue !== '0') { console.log('===== Call ====='); console.log(`From address: ${from}`); console.log(`To address: ${to}`); console.log(`Value of ${parsedValue} ETH\n`); } } } main(); // Call the main function to start the program ``` A custom function called `traceTransaction` is defined, which calls the `trace_transaction` RPC method through the provider's `send` method in ethers.js. This function is used to trace the execution of an Ethereum transaction and returns an array of actions that occurred during its execution. The `parseValue` function is defined to convert the value from wei (the smallest denomination of ether) to its equivalent ether value using `ethers.formatEther`. Finally, the main function is defined, which traces the specified Ethereum transaction using the `traceTransaction` method extracts relevant data from the transaction trace, parses the transaction value to ether using the `parseValue` function, and prints information about non-zero value transfers that occurred during the transaction execution. In this case, the program extracts the `sender`, `receiver`, and `value` of each call. This allows to understand that the third and gourth transfers are some kind of royalty fee, and the last transfer is the seller of the NFT receiving the amount. ```shell Shell theme={"system"} ===== Call ===== From address: 0x82791b169a317bd1613ff54f2d8fbf889ee41964 To address: 0x000000000000ad05ccc4f10045630fb830b95127 Value of 15.4774 ETH ===== Call ===== From address: 0x000000000000ad05ccc4f10045630fb830b95127 To address: 0xb38827497daf7f28261910e33e22219de087c8f5 Value of 15.4774 ETH ===== Call ===== From address: 0x000000000000ad05ccc4f10045630fb830b95127 To address: 0xa858ddc0445d8131dac4d1de01f834ffcba52ef1 Value of 0.077387 ETH ===== Call ===== From address: 0xa858ddc0445d8131dac4d1de01f834ffcba52ef1 To address: 0xd9db270c1b5e3bd161e8c8503c55ceabee709552 Value of 0.077387 ETH ===== Call ===== From address: 0x000000000000ad05ccc4f10045630fb830b95127 To address: 0x276cd56089e7576fb80d39a763aa0d213b98e948 Value of 15.400013 ETH ``` From this, we can see that address `0x82791b169a317bd1613ff54f2d8fbf889ee41964` pays 15.47 ETH to buy an NFT, fees of 0.077 ETH are taken from the total amount, and the seller `0x276cd56089e7576fb80d39a763aa0d213b98e948` receives 15.4 ETH. The next step could be to find out what Token ID is bought during this transaction. This information is not readily available in the `trace_transaction` response. If you dig, you can find it in the `input` field of one of the calls, but it is encoded. The best solution is to run the same transaction hash using [eth\_getTransactionReceipt](/reference/ethereum-gettransactionreceipt) and decode the logs. > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn how to read and decode event logs. # debug_traceBlockByHash | Ethereum Source: https://docs.chainstack.com/reference/ethereum-traceblockbyhash openapi/ethereum_node_api/debug_and_trace/debug_traceBlockByHash.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that traces the execution of a block. debug_traceBlockByHash JSON-RPC method available on the Ethereum blockchain via Chainstack. Ethereum API method that traces the execution of a block. This method can be used to debug and analyze smart contracts and transactions on the Ethereum blockchain. It provides a detailed trace of the execution of a block, including information on all the transactions and calls that interacted with the block, as well as the gas used, memory, storage operations, and other performance metrics for each operation. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `hash` — the hash of the block to be traced. * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. * `flatCallTracer` — tracer that returns call traces in the flat, Parity-style format used by the `trace_*` namespace, as a flattened list of call frames rather than a nested tree. * `muxTracer` — tracer that runs several native tracers in a single pass, configured via a `tracerConfig` object keyed by tracer name, returning each tracer's output under its own key. * `erc7562Tracer` — tracer that enforces the ERC-7562 account-abstraction validation rules, tracking opcode and storage access during the validation phase of an ERC-4337 user operation. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call. Encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ## `debug_traceBlockByHash` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const traceBlockByHash = async (blockHash) => { // Specify the type of tracer: 4byteTracer, callTracer, prestateTracer, or muxTracer const tracer = { tracer: "4byteTracer" }; const traces = await chainstack.send("debug_traceBlockByHash", [ blockHash, tracer, ]); console.log(traces); }; traceBlockByHash( "0x4725335feac695de65deb8662235f52692522d4f623a81468cc6479b43dc9782" ); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block_hash = "0x4725335feac695de65deb8662235f52692522d4f623a81468cc6479b43dc9782" # Specify the type of tracer: 4byteTracer, callTracer, prestateTracer, or muxTracer tracer = { "tracer": '4byteTracer' } block_traces = web3.provider.make_request('debug_traceBlockByHash', [block_hash, tracer]) print(block_traces) ``` ## Use case One practical use case for `debug_traceBlockByHash` with the `4byteTracer` tracer would be for auditing or analysis purposes of a blockchain. For example, if there are token contracts on the blockchain and the transfer function of these contracts is used to send tokens between users, a developer could use the `debug_traceBlockByHash` method with the `4byteTracer` tracer to trace the transactions in a given block and count how many times the transfer function was called. The following is an implementation of this logic using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function traceTransferFunction(blockHash) { try { // Specify the type of tracer: 4byteTracer, callTracer, prestateTracer, or muxTracer const tracer = { tracer: "4byteTracer" }; const response = await chainstack.send("debug_traceBlockByHash", [ blockHash, tracer, ]); let sum = 0; for (let obj of response) { for (let key in obj.result) { const signature = key.split('-')[0]; // Look for transfer function signatures if (signature === "0xa9059cbb") { const timesCalled = obj.result[key] sum += timesCalled } } } console.log(`In this block the transfer function was called ${sum} times!`) } catch (err) { console.error(`Error in traceBlockByHash: ${err.message}`); } } traceTransferFunction("0x4725335feac695de65deb8662235f52692522d4f623a81468cc6479b43dc9782"); ``` This code calls the `debug_traceBlockByHash` method directly through the provider's `send` method to trace a block, since ethers.js does not expose a dedicated helper for it. The call uses the `4byteTracer` to identify the functions called in the block. Then, the `traceTransferFunction` function is defined, which takes a block hash as an argument. This function sends the `debug_traceBlockByHash` request with the provided block hash and uses a loop to iterate over the results. Inside the loop, the function checks for the `transfer` function from the ERC-20 standard with signature `0xa9059cbb` and counts how many times it was called in that block by adding up the times it was called in each trace. Finally, the function logs the number of times the transfer function was called in that block. The code also has error handling in case anything goes wrong with the tracing process. # debug_traceBlockByNumber | Ethereum Source: https://docs.chainstack.com/reference/ethereum-traceblockbynumber openapi/ethereum_node_api/debug_and_trace/debug_traceBlockByNumber.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that enables the tracing of the execution of a specific block using its number. Chainstack Ethereum reference. Ethereum API method that enables the tracing of the execution of a specific block using its number. This method can be used to troubleshoot and analyze smart contracts and transactions on the Ethereum blockchain. It provides an in-depth trace of the block execution, with details on all the interactions, such as transactions and calls, that took place. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. * `flatCallTracer` — tracer that returns call traces in the flat, Parity-style format used by the `trace_*` namespace, as a flattened list of call frames rather than a nested tree. * `muxTracer` — tracer that runs several native tracers in a single pass, configured via a `tracerConfig` object keyed by tracer name, returning each tracer's output under its own key. * `erc7562Tracer` — tracer that enforces the ERC-7562 account-abstraction validation rules, tracking opcode and storage access during the validation phase of an ERC-4337 user operation. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ## `debug_traceBlockByNumber` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const traceBlockByNumber = async (block) => { // Specify the type of tracer: 4byteTracer, callTracer, prestateTracer, or muxTracer const tracer = { tracer: "callTracer" }; const traces = await chainstack.send("debug_traceBlockByNumber", [ block, tracer, ]); console.log(traces); }; traceBlockByNumber("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block = "latest" # Specify the type of tracer: 4byteTracer, callTracer, prestateTracer, or muxTracer tracer = { "tracer": 'callTracer' } block_traces = web3.provider.make_request('debug_traceBlockByNumber', [block, tracer]) print(block_traces) ``` ## Use case One use case for `traceBlockByNumber` would be to trace a block, calculate how many transactions are in it, and then display the number of transactions and the average gas used. To accomplish this, you can use `traceBlockByNumber` to retrieve the trace of each transaction in the block and then iterate over the transactions to count the total number of transactions and calculate the total gas used. Once you have this information, you can divide the total gas used by the total number of transactions to get the average gas used. Here is an implementation of this idea using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function getAverageGasUsed(block) { const tracer = { tracer: "callTracer" }; const traceResult = await chainstack.send("debug_traceBlockByNumber", [ block, tracer, ]); let totalGasUsed = 0; let transactionCount = 0; for (let obj of traceResult) { const gasUsed = obj.result.gasUsed; const decimalGas = parseInt(gasUsed, 16); totalGasUsed += decimalGas; transactionCount++; } const averageGasUsed = totalGasUsed / transactionCount; console.log(`Transaction in this block: ${transactionCount}`) console.log(`Average units of gas used per transaction: ${Math.round(averageGasUsed)}`); } getAverageGasUsed("latest"); ``` First, the code creates a `ChainstackProvider` instance for Ethereum mainnet. This provider is then used in the `getAverageGasUsed` function to trace the transactions in the specified block. Inside the function, we define a tracer object with the value of `callTracer`, which will help us get more detailed information about the block. Then, we call the `debug_traceBlockByNumber` RPC method through `provider.send`, passing in the block number and the tracer object as parameters. This returns an array of objects containing information about each transaction in the block. We then declare two variables to keep track of the total gas used and the number of transactions in the block. We loop through each transaction in the block using a `for...of` loop and extract the `gasUsed` property of each transaction's result object. We convert this value from hexadecimal to decimal using `parseInt` with a radix of 16 and add it to the `totalGasUsed` variable. We also increment the `transactionCount` variable to keep track of how many transactions there are in the block. Finally, we calculate the `averageGasUsed` by dividing the `totalGasUsed` by the `transactionCount` and displaying the result. We use the `Math.round()` method to round the result to the nearest integer for readability. When we call the function, we pass in the string `"latest"` to get the average gas used for the latest block. # debug_traceCall | Ethereum Source: https://docs.chainstack.com/reference/ethereum-tracecall openapi/ethereum_node_api/debug_and_trace/debug_traceCall.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that traces the execution of eth_call within the context of a specific block's execution. Ethereum via Chainstack. Ethereum API method that traces the execution of [eth\_call](/reference/ethereum-ethcall) within the context of a specific block's execution. This method uses the final state of the parent block as its base and allows developers to trace the execution of a particular call. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `object` — the transaction call object: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. * `flatCallTracer` — tracer that returns call traces in the flat, Parity-style format used by the `trace_*` namespace, as a flattened list of call frames rather than a nested tree. * `muxTracer` — tracer that runs several native tracers in a single pass, configured via a `tracerConfig` object keyed by tracer name, returning each tracer's output under its own key. * `erc7562Tracer` — tracer that enforces the ERC-7562 account-abstraction validation rules, tracking opcode and storage access during the validation phase of an ERC-4337 user operation. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) in the debug and trace overview. You can also use additional configuration parameters. The following settings are available: * `disableStorage` — when enabled, it prevents tracing of storage changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. By default, `debug_traceTransaction` traces both memory and storage changes, but storage tracing can be particularly resource-intensive, especially for large transactions. * `disableStack` — when enabled, it skips tracing of [stack](https://en.wikipedia.org/wiki/Stack_\(abstract_data_type\)) changes made by the transaction being analyzed. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableMemory` — when `false`, it prevents tracing of memory changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableMemory`. * `enableReturnData` — when `false`, it prevents the method from tracing the return data produced by the transaction being analyzed. The return data tracing can be particularly resource-intensive, requiring a significant amount of time and processing power to complete. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableReturnData`. * `timeout` — setting that allows developers to customize the method's timeout period for JavaScript-based tracing calls. The default timeout is `5s`, and you can find the values formats in the [Go documentation](https://pkg.go.dev/time#ParseDuration). > [!NOTE] > > It's worth noting that when using one of the three built-in tracers in Ethereum clients, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData`settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the [Struct/opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#structopcode-logger). ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction., usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract, if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ### `Struct/opcode logger` response * `object` — the `Struct/opcode logger` traces object: * `pc` — the current program counter of the transaction, which represents the location of the current opcode being executed. A register that keeps track of the address of the next instruction to be executed in a program. * `op` — the current [opcode](https://ethereum.org/en/developers/docs/evm/opcodes/) being executed, such as PUSH1, ADD, or SWAP1. A code that represents a specific operation or instruction that a computer or processor can understand and execute. * `gas` — the amount of gas remaining in the transaction at the current step of execution. * `gasCost` — the amount of gas consumed by the current opcode being executed. * `depth` — the current depth of the call stack for the transaction, reflecting the number of active frames representing nested function calls. * `stack` — the contents of the EVM stack at the current step of the transaction's execution. The stack is a data structure used by the EVM to manage and manipulate data during the execution of opcodes * `memory` — the contents of the memory at the current step of execution. A temporary data storage location that is available to smart contracts during execution. ## `debug_traceCall` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const traceCall = async () => { const call = { from: "0xe7e8569267c4a3278be75a2d86fd1a9e0a6818d8", to: "0xc2edad668740f1aa35e4d8f227fb8e17dca888cd", gas: "0x1E9EF", gasPrice: "0x72AC5BA54E", data: "0x441a3e70000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000c6c3eca729cb9e", }; const block = "latest"; // Specify the type of tracer: 4byteTracer, callTracer, prestateTracer, or muxTracer. Leave empty {} for Struct/opcode logger. const tracer = {}; const traces = await chainstack.send("debug_traceCall", [ call, block, tracer, ]); console.log(traces); }; traceCall(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) call = { 'from': '0xe7e8569267c4a3278be75a2d86fd1a9e0a6818d8', 'to': '0xc2edad668740f1aa35e4d8f227fb8e17dca888cd', 'gas': '0x1E9EF', 'gasPrice': '0x72AC5BA54E', 'data': '0x441a3e70000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000c6c3eca729cb9e' } block = 'latest' # Specify the type of tracer: 4byteTracer, callTracer, prestateTracer, or muxTracer. Leave empty {} for Struct/opcode logger. tracer = { "tracer": 'prestateTracer' } tx_traces = web3.provider.make_request('debug_traceCall', [call, block, tracer]) print(tx_traces) ``` ## Use case A practical use case for the `debug_traceCall` method is to inspect the internal transactions that happened during a smart contract call. For example, a developer can examine a past transaction using the `callTracer` tracer where an account used a proxy smart contract to deploy a new instance using the `CREATE2` method. > [!NOTE] > > Read [Deploying a deterministic smart contract on Ethereum](https://chainstack.com/deploying-a-deterministic-contract-on-ethereum/) to learn more about the `CREATE2` method and how to deploy a smart contract to a predetermined address. The following code is an implementation of the logic to inspect internal transactions using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const provider = new ethers.ChainstackProvider("mainnet"); async function traceCall(call, block) { try { // Specify the type of tracer: 4byteTracer, callTracer, prestateTracer, or muxTracer. // Leave empty {} for Struct/opcode logger. const tracer = { tracer: 'callTracer' }; // Trace the call const result = await provider.send("debug_traceCall", [call, block, tracer]); const calls = result.calls; console.log(`===== Internal Calls =====`); // Extract and display the desired properties from each result object using a loop for (const callObj of calls) { const from = callObj.from; const to = callObj.to; const internalCalls = callObj.calls; console.log(`From: ${from}`); console.log(`To: ${to}`); for (const internalCall of internalCalls) { const type = internalCall.type; console.log(`Type: ${type}\n`); } } } catch (error) { console.error(`Error while tracing call: ${error}`); } } async function main() { try { const call = { from: '0xa7fb5ca286fc3fd67525629048a4de3ba24cba2e', to: '0xc77ad0a71008d7094a62cfbd250a2eb2afdf2776', gas: '0x186A0', gasPrice: '0xEE6B28000', data: '0xf3fef3a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000617c2' }; // Define the block number or block tag to trace const block = '0x100A74D'; await traceCall(call, block); } catch (error) { console.error(`Error while executing main: ${error}`); } } main(); ``` The `traceCall` function is an asynchronous function that takes two parameters: `call` and `block`. The `call` parameter is an object that contains the details of the call that needs to be traced. In this case, those are the details of a [transaction that deployed a smart contract instance using the CREATE2 method](https://etherscan.io/tx/0x424583a1739057c8cf418d39ed314548dfa2fe3a8c77f22bd4efdeaddc031bf1#internal) on the Ethereum blockchain. The block parameter is the block number or block tag to trace. The function first creates a tracer object that specifies the type of tracer to be used. In this case, it's set to `callTracer`, which means that the function will trace all the internal calls made by the smart contract during the execution of the specified call. The function then calls `provider.send` with the `debug_traceCall` method and the `call`, `block`, and `tracer` parameters. This function returns an object that contains information about the internal calls made by the smart contract during the execution of the specified call. The function then loops through the array of calls in the `result` object and extracts the `from`, `to`, and `calls` properties of each call object. The `from` property is the address of the sender of the call, the `to` property is the address of the smart contract being called, and the `calls` property is an array of objects that represent the internal calls made by the smart contract during the execution of the call. The function then loops through the `internalCalls` array of each call object and extracts the `type property` of each `internalCall` object. The `type` property represents the type of the internal call, such as a contract creation or an external function call. Finally, the function logs the extracted information to the console. The main function is also an asynchronous function that simply calls the `traceCall` function with the appropriate parameters. It also wraps the function call in a try-catch block to handle any errors that might occur during execution. # debug_traceCallMany | Ethereum Source: https://docs.chainstack.com/reference/ethereum-tracecallmany openapi/ethereum_node_api/debug_and_trace/debug_traceCallMany.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that executes a list of bundles and returns an execution trace for each call, without creating transactions on the blockchain. Ethereum API method that executes one or more bundles—each a list of transactions—against a given block and returns an execution trace for each call, without creating transactions on the blockchain. Calls within a bundle execute in sequence, so each call sees the state changes made by the calls before it. This method is akin to [eth\_call](/reference/ethereum-ethcall) batched across multiple transactions, but it returns detailed execution traces, making it invaluable for debugging complex multi-transaction interactions. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters `debug_traceCallMany` takes three positional parameters: * `array` — a list of bundles to execute. Each bundle is an object with: * `transactions` — an array of transaction call objects. Each object accepts the same fields as [debug\_traceCall](/reference/ethereum-tracecall): `from` (optional), `to`, `gas`, `gasPrice`, `value`, and `data`. * `blockOverrides` — (optional) an object that overrides fields of the execution block header, such as `number`, `time`, or `baseFeePerGas`. * `object` — the simulation context that pins where the bundles execute: * `blockNumber` — the block number or tag (for example, `latest`) to execute against. * `transactionIndex` — (optional) the index within the block at which to start; `-1` runs after all transactions in the block. * `object` — (optional) the trace configuration. Set `tracer` to one of the native tracers and pass tracer-specific options in `tracerConfig`. > [!NOTE] > > Find the complete [list of available native tracers](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) in the debug and trace overview. ### Combining tracers with `muxTracer` `muxTracer` runs several native tracers in a single pass, which is the most efficient way to collect more than one trace type for a bundle. Pass a `tracerConfig` object keyed by tracer name, and each tracer's result is returned under its own key: ```json muxTracer config theme={"system"} { "tracer": "muxTracer", "tracerConfig": { "callTracer": { "onlyTopCall": true, "withLog": false }, "prestateTracer": { "diffMode": true } } } ``` ## Response * `result` — an array with one entry per bundle. Each entry is an array holding one trace per transaction in the bundle, in execution order. When `muxTracer` is used, each trace is an object keyed by tracer name—for example, `callTracer` and `prestateTracer`—each holding that tracer's output. ## Use case `debug_traceCallMany` is essential for: * Simulating a sequence of dependent transactions—such as an approval followed by a swap—and inspecting how each call changes state before committing anything on-chain. * Forensic analysis of transaction bundles, where you need both the call tree and the prestate diff in one round trip via `muxTracer`. * MEV and risk tooling that pre-screens bundles against the current chain state. # debug_traceTransaction | Ethereum Source: https://docs.chainstack.com/reference/ethereum-tracetransaction openapi/ethereum_node_api/debug_and_trace/debug_traceTransaction.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns a transaction's traces by replaying it. Reference for debug_traceTransaction on Ethereum via Chainstack. Ethereum API method that returns a transaction's traces by replaying it. This method provides a detailed breakdown of every step in the execution of a transaction on the Ethereum blockchain, including gas usage and opcode output. What sets it apart is its ability to accurately simulate the transaction's execution path by replaying any prior transactions, making it a powerful tool for developers to identify and diagnose issues in their applications. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#ethereum) with the debug and trace API methods enabled. ## Parameters * `hash` — the hash identifying a transaction. * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. * `flatCallTracer` — tracer that returns call traces in the flat, Parity-style format used by the `trace_*` namespace, as a flattened list of call frames rather than a nested tree. * `muxTracer` — tracer that runs several native tracers in a single pass, configured via a `tracerConfig` object keyed by tracer name, returning each tracer's output under its own key. * `erc7562Tracer` — tracer that enforces the ERC-7562 account-abstraction validation rules, tracking opcode and storage access during the validation phase of an ERC-4337 user operation. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/ethereum-debug-trace-rpc-methods#pre-built-native-tracers) in the debug and trace overview. You can also use additional configuration parameters. The following settings are available: * `disableStorage` — when enabled, it prevents tracing of storage changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. By default, `debug_traceTransaction` traces both memory and storage changes, but storage tracing can be particularly resource-intensive, especially for large transactions. * `disableStack` — when enabled, it skips tracing of [stack](https://en.wikipedia.org/wiki/Stack_\(abstract_data_type\)) changes made by the transaction being analyzed. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableMemory` — when `false`, it prevents tracing of memory changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableMemory`. * `enableReturnData` — when `false`, it prevents the method from tracing the return data produced by the transaction being analyzed. The return data tracing can be particularly resource-intensive, requiring a significant amount of time and processing power to complete. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableReturnData`. * `timeout` — setting that allows developers to customize the method's timeout period for JavaScript-based tracing calls. The default timeout is `5s`, and you can find the values formats in the [Go documentation](https://pkg.go.dev/time#ParseDuration). > [!NOTE] > > It's worth noting that when using one of the three built-in tracers in Ethereum clients, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData`settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the [Struct/opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#structopcode-logger). ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ### `Struct/opcode logger` response * `object` — the `Struct/opcode logger` traces object: * `pc` — the current program counter of the transaction, which represents the location of the current opcode being executed. A register that keeps track of the address of the next instruction to be executed in a program. * `op` — the current [opcode](https://ethereum.org/en/developers/docs/evm/opcodes/) being executed, such as PUSH1, ADD, or SWAP1. A code that represents a specific operation or instruction that a computer or processor can understand and execute. * `gas` — the amount of gas remaining in the transaction at the current step of execution. * `gasCost` — the amount of gas consumed by the current opcode being executed. * `depth` — the current depth of the call stack for the transaction, reflecting the number of active frames representing nested function calls. * `stack` — the contents of the EVM stack at the current step of the transaction's execution. The stack is a data structure used by the EVM to manage and manipulate data during the execution of opcodes * `memory` — the contents of the memory at the current step of execution. A temporary data storage location that is available to smart contracts during execution. ## `debug_traceTransaction` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const traceTransaction = async (txhash) => { // Specify the type of tracer: 4byteTracer, callTracer, prestateTracer, or muxTracer. Leave empty {} for Struct/opcode logger. const tracer = {}; const traces = await chainstack.send("debug_traceTransaction", [ txhash, tracer, ]); console.log(traces); }; traceTransaction( "0x61ec8f42d408a4351a7aa1e341a7421bd42594d7752fb79962667614d6d12730" ); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) tx_hash = "0x61ec8f42d408a4351a7aa1e341a7421bd42594d7752fb79962667614d6d12730" # Specify the type of tracer: 4byteTracer, callTracer, prestateTracer, or muxTracer. Leave empty {} for Struct/opcode logger. tracer = { "tracer": '4byteTracer' } tx_traces = web3.provider.make_request('debug_traceTransaction', [tx_hash, tracer]) print(tx_traces) ``` ## Use case A potential use case for the `debug_traceTransaction` method is to inspect failed transactions on the Ethereum blockchain by using the built-in `callTracer` tracer to extract the error and reason. By creating a script that analyzes the latest block's transactions and selectively extracts data only from the failed ones, you can effectively utilize this feature. Below is an example implementation of this logic using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Trace a failed transaction using the callTracer async function traceTransaction(txHash) { const tracer = { tracer: 'callTracer' }; const result = await provider.send("debug_traceTransaction", [txHash, tracer]); return result; } // Get all transactions from the specified block number async function getTransactionsFromBlock(blockNumber) { const block = await provider.getBlock(blockNumber); const txArray = block.transactions; return txArray; } // Main function that iterates through transactions and extracts data from failed ones async function main() { try { const transactions = await getTransactionsFromBlock(18941093n); for (let i = 0; i < transactions.length; i++) { const txHash = transactions[i]; const tx = await provider.getTransactionReceipt(txHash); //console.log(tx) const txStatus = tx.status; // If transaction failed, extract data using traceTransaction method if (txStatus === 0) { // 0 means failed console.log(`Transaction ${txHash} failed!`); const traces = await traceTransaction(txHash); const error = traces.error; const reason = traces.revertReason; console.log(`Transaction failed with error: ${error}`); console.log(`Revert reason: ${reason}\n`); } } } catch (error) { console.error(`Error occurred: ${error}`); } } // Call the main function main(); ``` The code is a JavaScript implementation that utilizes the ethers.js library to extract error and revert reason data from failed transactions on the Ethereum blockchain. The code calls the `debug_traceTransaction` method directly through `provider.send` (as it is not exposed as a dedicated helper in ethers.js by default), which allows for tracing of transactions with built-in tracers such as `callTracer`. The `traceTransaction` function in the code is responsible for using the `debug_traceTransaction` method to extract error and revert reason data from a failed transaction. The function takes in a transaction hash as a parameter and specifies the `callTracer` tracer type, and calls the `debug_traceTransaction` method. The resulting data is then returned to the caller. The `getTransactionsFromBlock` function retrieves all the transactions from a specified block number and returns an array of transaction hashes. The `main` function in the code is the main entry point, which first calls the `getTransactionsFromBlock` function to retrieve all transactions from the latest block on the Ethereum blockchain. It then iterates through the array of transactions and calls `traceTransaction` for each failed transaction, extracting the error and reverting reason data. The extracted data is then printed to the console using `console.log`. The code also handles any errors that may occur during the execution of the code by wrapping the entire execution in a `try-catch` block. # Transactions info | Ethereum Source: https://docs.chainstack.com/reference/ethereum-transactions-rpc-methods Ethereum transaction info JSON-RPC methods — retrieve transaction data, receipts, and status for monitoring and analyzing on-chain network activity. Retrieving transaction information from the Ethereum blockchain can be done by using the following methods: > [card] [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) > [card] [eth\_getTransactionReceipt](/reference/ethereum-gettransactionreceipt) > [card] [eth\_getTransactionByBlockHashAndIndex](/reference/ethereum-gettransactionbyblockhashandindex) > [card] [eth\_getTransactionByBlockNumberAndIndex](/reference/ethereum-gettransactionbyblocknumberandindex) > [card] [eth\_getBlockReceipts](/reference/ethereum-getblockreceipts) > [card] [eth\_newPendingTransactionFilter](/reference/ethereum-newpendingtransactionfilter) # eth_uninstallFilter | Ethereum Source: https://docs.chainstack.com/reference/ethereum-uninstallfilter openapi/ethereum_node_api/filter_handling/eth_uninstallFilter.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method used to remove a filter previously created using one of the following methods:. Chainstack Ethereum reference. Ethereum API method used to remove a filter previously created using one of the following methods: * [eth\_newFilter](/reference/ethereum-newfilter) * [eth\_newPendingTransactionFilter](/reference/ethereum-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/ethereum-newblockfilter) Upon successful execution, the filter is removed and will no longer emit events. This method can be useful for optimizing resource usage when a developer no longer needs to monitor certain events or blocks. It can also be used to clean up unused filters in order to reduce clutter in the client-side filter list. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/ethereum-newfilter) > * [eth\_newPendingTransactionFilter](/reference/ethereum-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/ethereum-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `string` — the filter ID that you want to uninstall ## Response * `boolean` — a boolean value indicating whether the filter was successfully uninstalled. `true` if successfully removed, and `false` if not. ## `eth_uninstallFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function removeFilter(filter) { try { const removed = await chainstack.send("eth_uninstallFilter", [filter]); console.log(removed); // Log whether the filter is removed } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = "0x4e7ef166cd43f188b0f8f9e218966a8f"; //'YOUR_FILTER_iD' removeFilter(filterId); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def remove_filter(filter_id): try: removed = web3.eth.uninstall_filter(filter_id) return removed except Exception as e: print(e) filter_id = '0x0' print(remove_filter(filter_id)) ``` > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). ## Use case One use case for `eth_uninstallFilter` is to optimize resource usage in a DApp. When a DApp needs to monitor events on the blockchain, it can create a filter using [eth\_newFilter](/reference/ethereum-newfilter),[eth\_newPendingTransactionFilter](/reference/ethereum-newpendingtransactionfilter), or [eth\_newBlockFilter](/reference/ethereum-newblockfilter) to listen for specific events or blocks. However, once the DApp no longer needs to monitor these events or blocks, it can use `eth_uninstallFilter` to stop watching for them. By doing so, the DApp can reduce the number of active filters and free up resources, such as network bandwidth and computational power, that would otherwise be used to maintain the filter. For example, let's say that a DApp is monitoring incoming blocks, logs batches of 50 blocks, and then uses `uninstallFilter` to remove the filter using. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const BLOCKS_TO_FETCH = 10; const BLOCK_FETCH_INTERVAL_MS = 200; async function getNewBlocks() { const filterId = await chainstack.send("eth_newBlockFilter", []); console.log(`Created filter ${filterId}`); console.log("Listening for blocks..."); let blocks = []; while (blocks.length < BLOCKS_TO_FETCH) { const changes = await chainstack.send("eth_getFilterChanges", [filterId]); //console.log(changes) // show the changes if (changes.length === 0) { await new Promise((resolve) => setTimeout(resolve, BLOCK_FETCH_INTERVAL_MS) ); // wait for new blocks } else { blocks = blocks.concat(changes); } } const removeFilter = await chainstack.send("eth_uninstallFilter", [filterId]); // clean up filter if (removeFilter) { console.log( `We got ${BLOCKS_TO_FETCH} blocks, filter ${filterId} was removed.` ); return blocks; } } async function main() { const fiftyBlocks = await getNewBlocks(); console.log(fiftyBlocks); } main(); ``` This code fetches the latest 10 blocks using a filter and creates an array. The `BLOCKS_TO_FETCH` constant is set to 10, which represents the number of blocks to fetch. The `BLOCK_FETCH_INTERVAL_MS` constant is set to 200, which represents the time interval (in milliseconds) to wait for new blocks to arrive. The `getNewBlocks` function is defined to fetch the blocks. This function uses a loop to retrieve new blocks until it has fetched the desired number of blocks. The loop waits for new blocks to arrive for a certain amount of time before trying again. Inside the `getNewBlocks` function, the [eth\_newBlockFilter](/reference/ethereum-newblockfilter) JSON-RPC method is called to create a new filter to watch for new blocks. The filter ID is returned and stored in the `filterId` variable. An array stores the blocks fetched. Inside the loop, the [eth\_getFilterChanges](/reference/ethereum-getfilterchanges) JSON-RPC method is called to retrieve any new blocks that have arrived since the filter was created. If there are no new blocks, the code waits a certain amount of time before trying again. If there are new blocks, they are stored in the `blocks` array. Once the `blocks` array has reached the `BLOCKS_TO_FETCH` limit, the filter is uninstalled using the `eth_uninstallFilter` JSON-RPC method. The `removeFilter` variable is set to true if the filter is successfully removed. If the filter is removed successfully, a message is logged to the console indicating that `BLOCKS_TO_FETCH` blocks have been fetched, and the filter was removed. The blocks array is returned as the result of the `getNewBlocks` function. The main function is defined to call the `getNewBlocks` function and store the result in the `fiftyBlocks` variable. The `fiftyBlocks` variable is then logged to the console. # Subscriptions | Ethereum Source: https://docs.chainstack.com/reference/ethereum-web3js-subscriptions-methods Ethereum web3.js subscription methods — subscribe to new block headers, pending transactions, contract events, and other real-time blockchain updates. > [!NOTE] > > Note that all subscriptions require a WebSocket connection. ## Native EVM subscriptions Developers can use the following native methods to subscribe to events: > [card] [eth\_subscribe('newHeads')](/reference/ethereum-native-subscribe-newheads) > [card] [eth\_subscribe('newPendingTransactions')](/reference/ethereum-native-subscribe-newpendingtransactions) > [card] [eth\_subscribe('logs')](/reference/ethereum-native-subscribe-logs) The subscriptions created can be cleared using the following method: > [card] [eth\_unsubscribe](/reference/ethereum-native-unsubscribe) ## web3.js subscriptions The [web3.js](https://web3js.readthedocs.io/en/v1.2.11/getting-started.html) library also offers subscription methods. web3.js subscriptions are the successors of the deprecated `web3.eth.filter` methods and provide a way for developers to receive real-time updates about events on the blockchain without constantly polling the blockchain for new data. Subscriptions can be created using one of the following methods: > [card] [subscribe('newBlockHeaders')](/reference/ethereum-subscribenewblockheaders) > [card] [subscribe('pendingTransactions')](/reference/ethereum-subscribependingtransactions) > [card] [subscribe('logs')](/reference/ethereum-subscribelogs) > [card] [subscribe('syncing')](/reference/ethereum-subscribesyncing) The subscriptions created can be cleared using the following method: > [card] [clearSubscriptions](/reference/ethereum-clearsubscriptions) # Ethers ChainstackProvider Documentation Source: https://docs.chainstack.com/reference/ethersjs-chainstackprovider Use ethers.js ChainstackProvider to connect to Chainstack nodes via JSON-RPC. Manage wallets, deploy contracts, and interact with Ethereum and EVM chains. Recently, `ethers.js` has introduced the `ChainstackProvider`, a new addition to its **Community Providers**. > [!NOTE] > > Chainstack provider on the [ethers.js documentation](https://docs.ethers.org/v6/api/providers/thirdparty/#providers-chainstack) . `ChainstackProvider`integrates directly with`ethers.js`, enabling developers to connect seamlessly to multiple blockchain networks supported by Chainstack via JSON-RPC endpoints. The `ChainstackProvider` enhances the development of decentralized applications by offering robust and scalable connections to blockchain networks such as Ethereum Mainnet, Arbitrum, BNB Smart Chain Mainnet, and Polygon. ## Supported Networks `ChainstackProvider` supports connections to: * Ethereum Mainnet (mainnet) * Arbitrum (arbitrum) * BNB Smart Chain Mainnet (bnb) * Polygon (matic) ## Class Overview ### ChainstackProvider The `ChainstackProvider` extends `JsonRpcProvider` and implements the `CommunityResourcable` interface. It allows users to connect to Chainstack’s JSON-RPC endpoints. This provider is particularly useful for developers needing reliable network access to test and prototype blockchain applications. > [!NOTE] > > A default, highly-throttled node is used by default. Deploy a high performance node and use your authorization key to increase performance. #### Constructor ```javascript JavaScript theme={"system"} new ChainstackProvider(network?: Networkish, apiKey?: null | string) ``` **Parameters:** * `network`: Optional. The network identifier. * `apiKey`: Optional. The authorization key from your deployed Chainstack node. ## Getting Started To start using the `ChainstackProvider`you must first install `ethers.js`: ```shell Bash theme={"system"} npm install ethers ``` ### Example Usage Here's an example of creating a `ChainstackProvider` instance and call the `eth_chainId` method: ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); const chainId = async () => { // This will return the value in Hex const chainId = await chainstack.send("eth_chainId"); console.log(\`Hex Chain ID: ${chainId}\`); }; chainId(); ``` ### API Key Configuration For better performance and higher rate limits, deploy a node from Chainstack and use its authorization key to instantiate the`ChainstackProvider`. Follow these steps to sign up on Chainstack, deploy a node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](https://docs.chainstack.com/docs/manage-your-networks). **Step 3** [View node access and credentials](https://docs.chainstack.com/docs/manage-your-node#view-node-access-and-credentials). > [!NOTE] > > You must deploy a [Global Node](https://docs.chainstack.com/docs/global-elastic-node) to use the authorization key in `ChainstackProvider`. Once deployed, your node RPC ULR will look like this: > [https://ethereum-mainnet.core.chainstack.com/AUTH\_KEY](https://ethereum-mainnet.core.chainstack.com/AUTH_KEY) Now you can add the `AUTH_KEY` to the `ChainstackProvider` instance: ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet", "AUTH_KEY"); ``` # web3_clientVersion | Fantom Source: https://docs.chainstack.com/reference/fantom-clientversion openapi/fantom_node_api/web3_clientVersion.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns the client type and version running on the Fantom node. web3_clientVersion on Fantom via Chainstack. Fantom API method that returns the client type and version running on the Fantom node. This information can be useful to developers to verify that a node they are connecting to is compatible with their needs. ## Parameters * `none` ## Response * `string` — a string identifying the type of client, version, operating system, and language version running on the node ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const clientVersion = async () => { const version = await provider.send("web3_clientVersion"); console.log(`Client version: ${version}`); }; clientVersion(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) client_version = web3.provider.make_request('web3_clientVersion', []) print(client_version) ``` ## Use case A use case for the `web3_clientVersion` method can be to verify which client is running to then decide which method to run. Let's say you have a DApp that needs to retrieve all of the transaction receipts in a block; the Erigon client has a method for this, `eth_getBlockReceipts`, but with Geth, this method is only available starting from V1.13.0. You can use the `web3_clientVersion` method in your logic to identify which client you are using to see if you have the method available. > [!NOTE] > > Read [Expanding your blockchain horizons: The eth\_getBlockReceipts emulator](/docs/expanding-your-blockchain-horizons-the-eth_getblockreceipts-emulator) to learn how to build a program to emulate `eth_getBlockReceipts` on any EVM-compatible chain. # eth_estimateGas | Fantom Source: https://docs.chainstack.com/reference/fantom-estimategas openapi/fantom_node_api/eth_estimateGas.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns an estimate of gas units needed for a given transaction. Available on Fantom via Chainstack JSON-RPC nodes. Fantom API method that returns an estimate of gas units needed for a given transaction. It estimates the maximum amount of gas units required to complete the transaction based on the network's current state and the transaction's parameters. A client or wallet can use this estimate to determine the appropriate amount of gas to include in a transaction to ensure the network processes it. ## Parameters * `object` — the transaction call object, same as in `eth_call`; the `from` field is optional, and the `nonce` field is omitted: * `from` — (optional) the address string used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — the estimated amount of gas units needed, represented as a hexadecimal string. ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimateGas = async () => { // The transaction to estimate is a simple transfer in this example. const transactionObj = { from: "0x14E520976c8791d2bBDC01eCBCeB328b5de44Fb9", to: "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8", } const gasUnits = await provider.estimateGas(transactionObj); console.log(`Estimated gas units required: ${gasUnits}`); }; estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.estimate_gas({"from":"0x14E520976c8791d2bBDC01eCBCeB328b5de44Fb9","to":"0xbe0eb53f46cd790cd13851d5eff43d12404d33e8"}, "latest" )) ``` ## Use case A practical use case for `eth_estimateGas` is to calculate the gas cost required for a transaction that transfers a value from one Ethereum address (`fromAddress`) to another (`toAddress`). The gas cost is an important factor in transactions, as it determines the amount of fees that need to be paid by the sender to the network for processing the transaction. # eth_blockNumber | Fantom Source: https://docs.chainstack.com/reference/fantom-eth-blocknumber openapi/fantom_node_api/eth_blockNumber.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns the latest block number of the blockchain. Call eth_blockNumber on Fantom through Chainstack nodes. Fantom API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0x69B5B` means that the block is the 432,987th block in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to and encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. > [!NOTE] > > Use the [Chainstack EVM Swiss Knife to convert hexadecimal to decimal](https://web3tools.chainstacklabs.com/hexadecimal-decimal). ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockNumber = async () => { const block_Number = await provider.getBlockNumber(); console.log(block_Number); }; eth_getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.block_number) ``` ## Use case A possible use case for the `eth_blockNumber` method in Fantom is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. # eth_call | Fantom Source: https://docs.chainstack.com/reference/fantom-ethcall openapi/fantom_node_api/eth_call.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. Fantom API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. This can be useful for testing and debugging by simulating transfers or smart contract transactions and retrieving data from the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > **Disclaimer** > > The interactive example in this page uses `eth_call` to call the `totalSupply()` method from the [WFTM Token smart contract on Fantom Chain](https://explorer.fantom.network/tokens/0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83). ## Parameters * `object` — the transaction call object: * `from` — (optional) the address string used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters. See the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` —the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized` —the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending` —the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage > [!NOTE] > > On Fantom, the `balance` and `state` overrides return the `-32000` error `method handler crashed`. The `code`, `nonce`, and `stateDiff` overrides work. This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `data` — the result of executing the specified call on the Ethereum Virtual Machine (EVM), encoded as a hexadecimal string. ## `eth_call` code examples The following code snippets clearly demonstrate how to simulate a standard ether transfer between two accounts. Note that when using `eth_call` to simulate a transfer or a smart contract transaction, the sender account must have a sufficient balance to cover the associated gas fee. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const simulateTransfer = async () => { try { // Define the accounts to transfer between const fromAddress = "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5"; const toAddress = "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"; // Define the value to transfer const value = ethers.parseEther("1"); // Build transaction object const transactionObject = { from: fromAddress, to: toAddress, gasLimit: ethers.toQuantity(21000), value: value } // Use eth_call to simulate the transfer on the latest block const result = await provider.call(transactionObject); console.log(`Result: ${result}`); } catch (error) { console.error(`Error: ${error.message}`); } } simulateTransfer(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the accounts to transfer between from_address = web3.to_checksum_address('0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5') to_address = web3.to_checksum_address('0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a') # Define the value to transfer (in Ether) value = web3.to_wei(1, 'ether') # Build the transaction object transaction = { 'from': from_address, 'to': to_address, 'gas': 21000, # 21000 is the standard for a regular transfer 'value': value, 'data': '', } # Use eth_call to simulate the transfer result = web3.eth.call(transaction, 'latest') print(result) ``` Executing this code will produce a return value of `0x`, indicating a successful transaction. This implies that the transaction will be executed without errors when the code is used to send the transaction. ## Use case A common use case for `eth_call` is calling smart contract functions. For example, interact with an ERC-20 token smart contract deployed on the Fantom network and retrieve the balance of a specific account. # eth_getBalance | Fantom Source: https://docs.chainstack.com/reference/fantom-getbalance openapi/fantom_node_api/eth_getBalance.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns the balance of a specific account in Wei, the smallest unit of ether. Use it on Fantom via Chainstack. Fantom API method that returns the balance of a specific account in [Wei](https://www.investopedia.com/terms/w/wei.asp), the smallest unit of ether. This method allows developers to retrieve the current balance of a Fantom account for various purposes, such as checking available funds or displaying balance information. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to check the balance of. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block * `pending`— the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — the integer value of the current balance in Wei ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const getBalance = async (address, block) => { const balance = await provider.send("eth_getBalance", [address, block]); console.log(balance); }; getBalance("0x18443ddbc5cd7Fb2F07551b54A37254F06819464", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" address = web3.to_checksum_address("0x18443ddbc5cd7Fb2F07551b54A37254F06819464") balance = web3.eth.get_balance(address, "latest") print(balance) ``` ## Use case One practical use case for `eth_getBalance` is to check the balance of an account for a program that scans the balance periodically and fills up the account when the balance drops below a certain value. # eth_getBlockByHash | Fantom Source: https://docs.chainstack.com/reference/fantom-getblockbyhash openapi/fantom_node_api/eth_getBlockByHash.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom Chain API method that returns information about the block matching the given block hash. Available on Fantom via Chainstack. Fantom Chain API method that returns information about the block matching the given block hash. This method can be useful for analyzing a block and its transactions. ## Parameters * `hash` — the hash of the block * `boolean` — if `true`, it returns the details of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block is pending. * `parenthash` — the previous block's hash used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block is pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the block logs, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot`— the root of the block's receipts trie. This is a 32-byte hash of the root node of the receipts trie for all transactions in the block. It is used to verify the integrity of the receipt data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty`is the cumulative sum of the difficulty of all blocks that have been mined in the network since its inception. It measures the network's overall security and integrity. * `extradata` — the extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/fantom-gettransactionbyhash) for the exact shape. * `uncles` — the array of uncle hashes. ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByHash = async () => { const blockByNumber = await provider.send("eth_getBlockByHash", ["0x0004344d0000028b41ba91f2221f809b62389befbfe00e9f62c6c238336a32cd",false]); console.log(blockByNumber); }; eth_getBlockByHash(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block("0x0004344d0000028b41ba91f2221f809b62389befbfe00e9f62c6c238336a32cd", False)) ``` ## Use case The `eth_getBlockByHash` is excellent for analytics purposes. For example, it can be used to quickly find out the hashes of transactions that create new smart contracts. The `transactions` field, an array of all the transactions within the block, is returned in the response. By iterating through this array and examining each transaction's `to` field, a user can determine which transactions in the block are creating new smart contracts. # eth_getBlockByNumber | Fantom Source: https://docs.chainstack.com/reference/fantom-getblockbynumber openapi/fantom_node_api/eth_getBlockByNumber.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns information about the block matching the given block number. eth_getBlockByNumber on Fantom via Chainstack. Fantom API method that returns information about the block matching the given block number. This method can retrieve a wide range of information about the block, such as its hash, timestamp, difficulty, and gas limit. You can also retrieve transaction objects included in the block if needed. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). * `boolean` — if `true`, it returns the details of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — a block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block is pending. * `parenthash`— the previous block's hash used to generate the current block, also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block is pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the block logs, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot`— the root of the block's receipts trie. This is a 32-byte hash of the root node of the receipts trie for all transactions in the block. It is used to verify the integrity of the receipt data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. Fantom uses Proof of Stake (PoS) consensus and this value is not relevant. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/fantom-gettransactionbyhash) for the exact shape. * `uncles` — array of uncle hashes. ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByNumber = async () => { const blockByNumber = await provider.send("eth_getBlockByNumber", ["latest",false]); console.log(blockByNumber); }; eth_getBlockByNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block("latest", False)) ``` ## Use case The `eth_getBlockByNumber` method can determine whether a block you are analyzing has been included in the main chain or was forked due to a 'reorg'. A block hash is generated through a cryptographic hash function. This function takes the block's data, including transactions and other information, and the hash of the parent block (the previous block) and outputs a unique, fixed-length digest. This hash represents the current block within the blockchain. # eth_getBlockTransactionCountByHash | Fantom Source: https://docs.chainstack.com/reference/fantom-getblocktransactioncountbyhash openapi/fantom_node_api/eth_getBlockTransactionCountByHash.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. Fantom API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. ## Parameters * `hash` — the block hash of the requested block. ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockHash) => { const count = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); console.log(count); }; getTransactionsCount('0x0004358a000018587faea5158124f479fb25360d25029e662d336e3a3508ffb3'); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block_transaction_count("0x0004358a000018587faea5158124f479fb25360d25029e662d336e3a3508ffb3")) ``` ## Use case `eth_getBlockTransactionCountByHash` is a useful tool for analyzing transaction volume on the Fantom blockchain. On average, a new block is generated on the Fantom mainnet every 3 seconds, resulting in approximately 1,200 blocks per hour. Using a Web3 library, one can inspect the past 1,200 blocks starting from the latest block, retrieve the hash of each block, and use `eth_getBlockTransactionCountByHash` to find the number of transactions in each block. # eth_getBlockTransactionCountByNumber | Fantom Source: https://docs.chainstack.com/reference/fantom-getblocktransactioncountbynumber openapi/fantom_node_api/eth_getBlockTransactionCountByNumber.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. Fantom API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockId) => { const count = await provider.send("eth_getBlockTransactionCountByNumber", [blockId]); console.log(count); }; getTransactionsCount('latest') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.get_block_transaction_count(79035660)) # A hex value starting with "0x" is accepted as well. ``` ## Use case `eth_getBlockTransactionCountByNumber` can be used to analyze how many transactions are included on the Fantom blockchain in a certain period. For instance, on average, a new block is generated on the Fantom mainnet every 3 seconds, resulting in approximately 1,200 blocks per hour. Using a Web3 library, one can inspect the past 1,200 blocks starting from the latest block and use `eth_getBlockTransactionCountByNumber` to find the number of transactions in each block to sum them. # eth_chainId | Fantom Source: https://docs.chainstack.com/reference/fantom-getchainid openapi/fantom_node_api/eth_chainId.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and verify whether a network is the desired one. Fantom API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and verify whether a network is the desired one. It was introduced in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). ## Parameters * `none` ## Response * `quantity` — EIP-155 Chain ID > [!NOTE] > > Use the [Chainstack EVM Knife](https://web3tools.chainstacklabs.com/hexadecimal-decimal) to convert the Chain ID from Hexadecimal to Decimal. ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const chainId = async () => { // This will return the value in Hex const chainId = await provider.send("eth_chainId"); console.log(`Hex Chain ID: ${chainId}`); }; chainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.chain_id) ``` ## Use case One possible use case for the `eth_chainId` method is to determine which chain a user is connected to when using a DApp with the MetaMask browser extension. This can be useful to provide a customized user experience based on the specific chain that the user is interacting with. # eth_getFilterChanges | Fantom Source: https://docs.chainstack.com/reference/fantom-getfilterchanges openapi/fantom_node_api/eth_getFilterChanges.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Fantom API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Filters can be created using one of the following methods: * [eth\_newFilter](/reference/fantom-newfilter) * [eth\_newPendingTransactionFilter](/reference/fantom-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/fantom-newblockfilter) After creating a filter using one of the available methods, the resulting filter ID can be used to fetch changes by calling the `eth_getFilterChanges` method. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/fantom-newfilter) > * [eth\_newPendingTransactionFilter](/reference/fantom-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/fantom-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `string` — the filter ID of the filter for which you want to retrieve the changes ## Response * `array` — an array that represents the changes that have occurred on the blockchain since the last time the filter was polled: * For filters created with `eth_newBlockFilter`: * `blockHash` — the hashes of the new blocks since the last time the filter was polled. * For filters created with `eth_newPendingTransactionFilter`: * `transactionHash` — the hashes identifying new pending transactions since the last time the filter was polled. * For filters created with `eth_newFilter`, the following event logs: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getFilterChanges` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getFilterChanges(filter) { try { const changes = await provider.send('eth_getFilterChanges', [filter]); console.log(changes); // Log the with the new data } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' getFilterChanges(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_filtered_events(filter_id): try: filter_changes = web3.eth.get_filter_changes(filter_id) print(filter_changes) # the array of events that match the filter except Exception as e: print(e) filter_id = '0x0' get_filtered_events(filter_id) ``` ## Use case The `eth_getFilterChanges` method can poll a filter to periodically retrieve new data. You can, for example, create a blocks filter using [eth\_newBlockFilter](/reference/fantom-newblockfilter) and periodically poll it to get new blocks. # eth_gasPrice | Fantom Source: https://docs.chainstack.com/reference/fantom-getgasprice openapi/fantom_node_api/eth_gasPrice.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns the current gas base fee of the network. Chainstack supports eth_gasPrice on Fantom JSON-RPC nodes. Fantom API method that returns the current gas base fee of the network. The gas price is the quantity of the native token the transaction's sender must pay per unit of gas consumed. The value returned is in Wei. ## Parameters * `none` ## Response * `quantity` — the integer value of the current gas base fee, returned in Wei ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(node_url); async function getGasPrice() { const baseFee = await provider.send("eth_gasPrice"); console.log(`The base gas fee is: ${baseFee} Wei`); } getGasPrice() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.gas_price) ``` ## Use case You can use `eth_gasPrice` to calculate the total gas value to send with a transaction based on the base and priority fee system. Ethereum's London hard fork implemented This concept with EIP-1559. EIP-1559 aimed to solve the problem of network congestion by implementing a dynamic fee market mechanism, which adjusts the fee required to process a transaction based on network demand. With the current system, the total gas price comprises a **base fee** determined by the network's load and a **priority fee** added by the user. > [!NOTE] > > **Note** > > The `eth_gasPrice` method returns the base fee. # eth_getLogs | Fantom Source: https://docs.chainstack.com/reference/fantom-getlogs openapi/fantom_node_api/eth_getLogs.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns an array of all logs that match a given filter object. Available on Fantom via Chainstack JSON-RPC nodes. Fantom API method that returns an array of all logs that match a given filter object. Logs are records of smart contract-generated events on the network and contain valuable information about transactions and smart contract interactions. Some common use cases for `eth_getLogs` include monitoring smart contract events, tracking token transfers, and analyzing blockchain data. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. > * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. > * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. > * `earliest` — the earliest available or genesis block. > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `array` — an array of log objects that match the specified filter or an empty array if there have been no new events since the last poll: * `address` — the contract address from which the event originated * `topics` — an array of 32-byte data fields containing indexed event parameters * `data` — the non-indexed data that was emitted along with the event * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the event index within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getLogs` code examples The following examples retrieve the logs of the Transfer event from the [WFTM token](https://explorer.fantom.network/tokens/0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83) smart contract on Fantom. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getLogs = async () => { const latestBlock = await provider.getBlockNumber(); const filter = { fromBlock: latestBlock - 10, toBlock: 'latest', address: '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const logs = await provider.getLogs(filter); console.log(logs); }; getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) latest_block = web3.eth.block_number filter = { 'fromBlock': latest_block - 10, 'toBlock': 'latest', 'address': '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } logs = web3.eth.get_logs(filter) print(logs) ``` > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about the `eth_getLogs` method. ## Block range limitations While `eth_getLogs` is a powerful tool, it's crucial to understand its limitations, particularly when working with different EVM-compatible chains, as these networks often have different constraints. The `eth_getLogs` method allows you to select a range of blocks from which to get events and is important for exercising proper management. `eth_getLogs` block range is capped by your [subscription plan](/docs/limits#evm-range-limits). On high-activity chains or contracts, query smaller ranges and paginate to avoid oversized responses or timeouts. ## Use case One use case for `eth_getLogs` is to retrieve the transfer events for a specific Token ID from an ERC-721 smart contract. ERC-721 is a token standard for non-fungible tokens (NFTs), and each token transfer is recorded as an event on the blockchain. By retrieving these events, developers can track the movement of NFTs and build applications that interact with them. # eth_getProof | Fantom Source: https://docs.chainstack.com/reference/fantom-getproof openapi/fantom_node_api/eth_getProof.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns a Merkle proof for a specific account, contract storage, or both for a given block. Fantom via Chainstack. Fantom API method that returns a Merkle proof for a specific account, contract storage, or both for a given block. Merkle proofs enable users to verify the existence and authenticity of data within a Merkle trie, a data structure that helps optimize and secure data retrieval in blockchains. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > **No longer supported** > > The Sonic node client no longer supports `eth_getProof` for the Fantom chain. [See GitHub Issue](https://github.com/Fantom-foundation/Sonic/issues/379). ## Parameters * `address` — Ethereum address of the account for which the proof is requested. * `array` — the array of 32-byte storage keys that need to be proven and included. See [eth\_getStorageAt](/reference/fantom-getstorageat). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `balance` — represents the account balance. Refer to [eth\_getBalance](/reference/fantom-getbalance). * `codeHash` — indicates the code hash associated with the account. For a simple account without any code, the value will be `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. * `nonce` — refers to the account's nonce. See [eth\_getTransactionCount](/reference/fantom-gettransactioncount). * `storageHash` — represents the SHA3 hash of the StorageRoot. All storage data will provide a Merkle proof that starts with this `rootHash`. This hash serves as the starting point for obtaining a Merkle proof for all storage entries linked to the account. * `accountProof` — an array of RLP-serialized Merkle trie nodes, starting with the `stateRoot` node and following the path of the SHA3 (address) as the key, which can be used to verify the existence and authenticity of data within the trie. * `storageProof` — contains the requested storage entries in an array, with each entry being an object that includes the following properties: * `key` — the requested storage key * `value`— the value associated with the storage key * `proof` — an array of RLP-serialized Merkle trie nodes, starting with the `storageHash` node and following the path of the SHA3 (key), which can be used to verify the existence and authenticity of data within the trie. ## `eth_getProof` code examples The following examples retrieve the Merkle proof for the USDC contract. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getProof() { // Get the storage slot hash (value = keccak256(storage address + slot index)) const storageSlotHash = '0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c'; // Define the contract address for USDT const contractAddress = '0x8965349fb649A33a30cbFDa057D8eC2C48AbE2A2'; // Get the Merkle proof of USDT's smart contract using a custom JSON-RPC call const proof = await provider.send('eth_getProof', [contractAddress, [storageSlotHash], 'latest']); const block = await provider.send("eth_getBlockByNumber", ["latest",false]); // The state root is crucial for Merkle verification const stateRoot = block.stateRoot; console.log('state root:', stateRoot); console.log(proof); } ``` ```python web3.py theme={"system"} from web3 import Web3 httpUrl = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(httpUrl)) proof = web3.eth.get_proof('0x8965349fb649A33a30cbFDa057D8eC2C48AbE2A2', ["0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c"], "latest") print(proof) ``` > [!NOTE] > > Read [Deep dive into Merkle proofs and eth\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) to learn more about the Merkle trie and how to use `eth_getProof`. ## Use case A use case for the `eth_getProof` method can be when a decentralized application (DApp) interacts with a smart contract on the Fantom blockchain. The DApp needs to verify the state of the contract's storage variables without downloading the entire blockchain data. By using `eth_getProof`, the DApp can request Merkle proof for specific storage slots in the contract. This proof can then be used to cryptographically verify the authenticity and accuracy of the storage data, ensuring the DApp is working with the correct contract state. # eth_getStorageAt | Fantom Source: https://docs.chainstack.com/reference/fantom-getstorageat openapi/fantom_node_api/eth_getStorageAt.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns the data stored at a specific storage slot within a smart contract. Use it on Fantom via Chainstack. Fantom API method that returns the data stored at a specific storage slot within a smart contract. It can help developers read a smart contract's internal state, like user data or balances, to inform decisions and build more advanced applications that interact with smart contracts on the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity` — the index of the storage position to retrieve the data from * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response `data` — the data stored in the storage slot. Returned as a hexadecimal string. ## `eth_getStorageAt` code examples The following example returns the storage value at slot `2` from the [WFTM Token on the Fantom mainnet](https://explorer.fantom.network/tokens/0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83). In this case, this is the total supply value. This method can retrieve the different storage values in time by querying different blocks in the past. Note that to query a state in the past, you need an [archive node](https://chainstack.com/evm-nodes-a-dive-into-the-full-vs-archive-mode). > [!NOTE] > > cURL needs a `HEX String` starting with `0x` to identify the block if you want to use a hex integer block number as a parameter.\ > For example, block number `14000000` will be `0xD59F80`. Use the [Chainstack EVM Knife](https://web3tools.chainstacklabs.com/hexadecimal-decimal) to make those conversions. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getstorage = async (address, slot, block) => { const storage = await provider.send("eth_getStorageAt", [address, slot, block]); console.log(storage); }; getstorage("0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83", "2", "latest" ) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" storage = web3.eth.get_storage_at("0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83", 2, "latest") print(web3.to_hex(storage)) ``` ## Use case One use case for `eth_getStorageAt` is analyzing how the value stored at a specific storage slot of a smart contract changes over time. For example, a developer may want to track the change in a specific variable of a smart contract, such as the total supply of a token, and perform some analysis on it. # eth_getTransactionByBlockHashAndIndex | Fantom Source: https://docs.chainstack.com/reference/fantom-gettransactionbyblockhashandindex openapi/fantom_node_api/eth_getTransactionByBlockHashAndIndex.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. Fantom API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Fantom blockchain. ## Parameters * `hash` — the hash of the block * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal ## Response * `object` — a transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the transaction's sender is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the transaction sender is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender's wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) with which the transaction plans to interact. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blockHash, index) => { const transaction = await provider.send("eth_getTransactionByBlockHashAndIndex", [blockHash, index]); console.log(transaction); } getTransaction("0x000435a30000361a0502b80359a2da9de0cc91730f917e9a611983d080914900", "0x0"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block("0x000435a30000361a0502b80359a2da9de0cc91730f917e9a611983d080914900", 1)) ``` ## Use case The `eth_getTransactionByBlockHashAndIndex` can be used to retrieve transaction details from a block. # eth_getTransactionByBlockNumberAndIndex | Fantom Source: https://docs.chainstack.com/reference/fantom-gettransactionbyblocknumberandindex openapi/fantom_node_api/eth_getTransactionByBlockNumberAndIndex.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. Fantom API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. This information can track transactions, debug issues, analyze data, and build decentralized applications on the Fantom blockchain. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal. ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the transaction's sender is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the transaction sender is willing to pay per gas unit. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender's wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blocNumber, index) => { const transaction = await provider.send("eth_getTransactionByBlockNumberAndIndex", [blocNumber, index]); console.log(transaction); } getTransaction("latest", "0x1"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block(79169022, 1)) # Hex encoded parameters starting with "0x" are accepted as well. ``` ## Use case The `eth_getTransactionByBlockNumberAndIndex` can be used to retrieve transaction details from a block; for example, an analytics tool systematically scans each block for transactions by sequentially incrementing the block number and cycling through the transactions within each block using the transaction index. For each transaction, it retrieves detailed information such as the value transferred, the gas price, the sender and receiver addresses, and the input data, which can contain calls to smart contracts. # eth_getTransactionByHash | Fantom Source: https://docs.chainstack.com/reference/fantom-gettransactionbyhash openapi/fantom_node_api/eth_getTransactionByHash.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns information about a transaction from its hash. It is used to retrieve information about a specific transaction by its hash. Fantom API method that returns information about a transaction from its hash. It is used to retrieve information about a specific transaction by its hash. The method returns an object with details about the transaction, such as the sender, recipient, gas used, transaction value, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the transaction's sender is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the transaction sender is willing to pay per gas unit. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction., usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet; it essentially identifies how many transactions an account has made. It is used to ensure each transaction is executed only once. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. This field is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (transactionHash) => { const transaction = await provider.send("eth_getTransactionByHash", [transactionHash]); console.log(transaction); }; getTransaction("0x658d83eb9395221ffb8c2bf3696b88f61262527b145bd7ffdb7310edb8886e3b") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction("0x658d83eb9395221ffb8c2bf3696b88f61262527b145bd7ffdb7310edb8886e3b")) ``` ## Use case One use case for `eth_getTransactionByHash` is to inspect a specific transaction and display information about it, such as the address sent, the block it was included in, and the number of transactions the sender sent so far (nonce). This information can be useful for monitoring and analyzing transactions. # eth_getTransactionCount | Fantom Source: https://docs.chainstack.com/reference/fantom-gettransactioncount openapi/fantom_node_api/eth_getTransactionCount.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns the number of transactions sent from an address at the selected block. Chainstack Fantom reference. Fantom API method that returns the number of transactions sent from an address at the selected block. This value is also called `nonce`; it is an important piece of information, especially to ensure that a transaction is not sent twice. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`— the address to retrieve the transaction count. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — an integer value identifying the number of transactions sent from an address at the specified block. ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getNonce = async (address, blockId) => { const nonce = await provider.send("eth_getTransactionCount", [address, blockId]); console.log(nonce); }; getNonce("0x18443ddbc5cd7Fb2F07551b54A37254F06819464", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_count("0x18443ddbc5cd7Fb2F07551b54A37254F06819464", "latest")) ``` ## Use case One of the most common use cases for `eth_getTransactionCount`is to create the transaction object built in a script designed to send a transaction. The nonce field is required, and it is retrieved using the `eth_getTransactionCount` method. # eth_getTransactionReceipt | Fantom Source: https://docs.chainstack.com/reference/fantom-gettransactionreceipt openapi/fantom_node_api/eth_getTransactionReceipt.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that retrieves a transaction's receipt from a transaction hash. eth_getTransactionReceipt on Fantom via Chainstack. > [!NOTE] > > **No response in the interactive API** > > The response to the call might be too big for the web app to handle, so if you are not getting it here, copy the CURL example and run in your terminal—this will work. Fantom API method that retrieves a transaction's receipt from a transaction hash. It can be used to verify a transaction's success or failure, check a smart contract's execution status, retrieve logs, check if a transaction deployed a smart contract, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `Transaction receipt` — the receipt object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the contract address created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects this transaction generates, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionReceipt = async (transactionHash) => { const receipt = await provider.send("eth_getTransactionReceipt", [transactionHash]); console.log(receipt); }; getTransactionReceipt('0x658d83eb9395221ffb8c2bf3696b88f61262527b145bd7ffdb7310edb8886e3b') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_receipt("0x658d83eb9395221ffb8c2bf3696b88f61262527b145bd7ffdb7310edb8886e3b")) ``` ## Use case A practical use case for `eth_getTransactionReceipt` is to verify a transaction's status and whether it was successful. This can be especially useful when deploying a contract, as you can use the receipt to confirm that the contract was deployed successfully and has a valid contract address. # net_listening | Fantom Source: https://docs.chainstack.com/reference/fantom-listening openapi/fantom_node_api/net_listening.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns a boolean value indicating whether the client is currently listening for network connections or not. Fantom API method that returns a boolean value indicating whether the client is currently listening for network connections or not. The `net_listening` method can be useful in many scenarios where monitoring the status of an Ethereum client's network connection is important to ensure it works properly. ## Parameters * `none` ## Response * `boolean` — a boolean value that indicates whether a node is actively seeking peer connections. `true` if the client is actively listening for connections, `false` if not. ## `net_listening` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const isListening = async () => { const listen = await provider.send("net_listening"); console.log(listen); } isListening() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) is_listening = web3.provider.make_request('net_listening', []) print(is_listening) ``` ## Use case A method like `net_listening` can be used to monitor the network status of the Fantom node a developer connects to. Imagine a blockchain explorer where users can search for transactions, addresses, and other information on the Fantom blockchain. To provide accurate information to its users, the website needs to be able to monitor the current status of its connection. # eth_maxPriorityFeePerGas | Fantom Source: https://docs.chainstack.com/reference/fantom-maxpriorityfeepergas openapi/fantom_node_api/eth_maxPriorityFeePerGas.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns an estimate of the current priority fee per gas. eth_maxPriorityFeePerGas on Fantom via Chainstack. Fantom API method that returns an estimate of the current priority fee per gas. This fee is the tip that users may pay miners,post-merge, to validators on top of the base fee to incentivize the inclusion of their transactions in a block. This method helps users adjust their transaction fees in a dynamic market, ensuring transactions are processed promptly without overpaying. ## Parameters * `none` ## Response * `quantity` — the estimated priority fee per gas needed, represented as a hexadecimal string. ## `eth_maxPriorityFeePerGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimatePriorityFee = async () => { const priorityFeePerGas = await provider.getFeeData(); console.log(`Estimated priority fee per gas: ${priorityFeePerGas.maxPriorityFeePerGas}`); }; estimatePriorityFee(); ``` ```python web3.py theme={"system"} from web3 import Web3 NODE_URL = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(NODE_URL)) eth_max_priority_fee_per_gas = web3.provider.make_request('eth_maxPriorityFeePerGas', []) print(eth_max_priority_fee_per_gas) ``` ## Use Case A practical use case for `eth_maxPriorityFeePerGas` using `ethers.js` is to calculate transaction fees dynamically for applications and wallets to accurately estimate the cost of transactions, especially during periods of high network congestion. This method ensures transactions are confirmed promptly without overpaying. By fetching the current priority fee (`maxPriorityFeePerGas`) and the maximum fee per gas (`maxFeePerGas`), applications can dynamically calculate the total transaction fee required for prompt processing under the current network conditions. # eth_newBlockFilter | Fantom Source: https://docs.chainstack.com/reference/fantom-newblockfilter openapi/fantom_node_api/eth_newBlockFilter.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that creates a filter that watches for new blocks on the blockchain. eth_newBlockFilter on Fantom via Chainstack. Fantom API method that creates a filter that watches for new blocks on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/fantom-getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real-time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter. The filters created are stored on the blockchain client instance. The filter fill is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/fantom-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/fantom-uninstallfilter) to remove the filter ## `eth_newBlockFilter` code examples > [!NOTE] > > **web3.eth.filterdeprecation** > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newBlockFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') return blocks_filter except Exception as e: print(e) blocks = get_new_blocks() filter_id = blocks.filter_id print(filter_id) ``` ## Use case One use case for `eth_newBlockFilter` in a simple DApp is to listen for new blocks and update the user interface with the latest block information. When the DApp starts, it creates a new filter using `eth_newBlockFilter` to listen for new blocks. When a new block is added to the blockchain, the filter is triggered, and the DApp retrieves the latest block information using a Web3 library. # eth_newFilter | Fantom Source: https://docs.chainstack.com/reference/fantom-newfilter openapi/fantom_node_api/eth_newFilter.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that generates a filter object based on the filter parameters. Available on Fantom via Chainstack JSON-RPC nodes. Fantom API method that generates a filter object based on the filter parameters. It returns a filter ID, which can be used to retrieve the filter results using the [eth\_getFilterChanges](/reference/fantom-getfilterchanges) method. By creating a filter for specific events, developers can receive notifications when those events occur and use them to trigger actions in their applications. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. > * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. > * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. > * `earliest` — the earliest available or genesis block. > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `result` — a hexadecimal string representing the ID of the newly created filter > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/fantom-getfilterchanges) to retrieve updated logs * [eth\_uninstallFilter](/reference/fantom-uninstallfilter) to remove the filter ## `eth_newFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const createFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) filter = { 'toBlock': 'latest', 'address': '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } def create_logs_filter(filter_object): try: logs_filter = web3.eth.filter(filter_object) return logs_filter except Exception as e: print(e) logs_filter = create_logs_filter(filter) filter_id = logs_filter.filter_id print(f'New filter ID: {filter_id}') ``` ## Use case You can use `eth_newFilter` to create a filter for a specific action on a smart contract, for example, to monitor the transfer transactions from the [Wrapped Fantom (WFTM) token](https://explorer.fantom.network/tokens/0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83). The idea is to create a filter using the `eth_newFilter` method to monitor an ERC-20 smart contract, WFTM in this case. # eth_newPendingTransactionFilter | Fantom Source: https://docs.chainstack.com/reference/fantom-newpendingtransactionfilter openapi/fantom_node_api/eth_newPendingTransactionFilter.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that creates a filter that listens for new pending transactions on the blockchain. Use it on Fantom via Chainstack. Fantom API method that creates a filter that listens for new pending transactions on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/fantom-getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real-time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter ## Code examples > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/fantom-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/fantom-uninstallfilter) to remove the filter ## `eth_newPendingTransactionFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newPendingTransactionFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_pending_transactions(): try: blocks_filter = web3.eth.filter('pending') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) new_filter = get_new_pending_transactions() print(new_filter) ``` ## Use case One way to use the `eth_newPendingTransactionFilter` method is to listen for new pending transactions at predefined intervals and extract specific data from them. For instance, a decentralized application might check for pending transactions every second and identify those that transfer a value greater than a certain amount of the FTM token. This could be useful for real-time monitoring high-value transactions or detecting potential fraud or security threats. # net_peerCount | Fantom Source: https://docs.chainstack.com/reference/fantom-peercount openapi/fantom_node_api/net_peerCount.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns the number of peers connected to the node. Fantom developers can call net_peerCount via Chainstack. Fantom API method that returns the number of peers connected to the node. This method can be useful for developers who want to monitor their node's connectivity and ensure it functions as expected. By checking the number of connected peers, developers can verify that their node receives updates and is properly synchronized with the network. ## Parameters * `none` ## Response * `quantity` — an integer value indicating the current number of peers connected to the node ## `net_peerCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const peersCount = async () => { const peers = await provider.send("net_peerCount"); console.log(`Peers connected: ${peers}`); } peersCount() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) peers = web3.provider.make_request('net_peerCount', []) print(peers) ``` ## Use case One practical use case for the `net_peerCount` method is to monitor the number of peers connected to a node and take some action if the number drops below a certain threshold. For example, a developer might want to send a notification to check the node if the peer count is low for more than 10 minutes. # eth_sendRawTransaction | Fantom Source: https://docs.chainstack.com/reference/fantom-sendrawtransaction openapi/fantom_node_api/eth_sendRawTransaction.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that allows submitting a signed transaction to the network. Reference for eth_sendRawTransaction on Fantom via Chainstack. Fantom API method that allows submitting a signed transaction to the network. Once a transaction is signed, you can use the `eth_sendRawTransaction` method to submit the signed transaction to the Fantom network for processing. > [!WARNING] > > Note that the interactive example in this page will not work, due to the fact that `eth_sendRawTransaction` is used to modify the state of the blockchain, it is not possible to duplicate the same request. ## Parameters * `data`—the signed transaction. The serialized transaction data includes transaction metadata such as the nonce, gas price, gas limit, recipient address, and data payload, as well as the digital signature generated using the private key associated with the address that is sending the transaction. Typically, the transaction is signed with a library such as web3.py, web3.js, or ethers.js using the sender's private key. ## Response * `result` — the unique hash identifying the transaction or the zero hash if the transaction is unavailable. The zero hash is a special value representing an invalid or null hash. ## `eth_sendRawTransaction` code examples The following examples demonstrate using Web3 libraries to transfer FTM. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function sendEth(value) { // Define the sender and receiver addresses, and the private key const sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a'; const receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114'; const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Define the gas limit const gasLimit = await provider.estimateGas({ from: sender, to: receiver, value: ethers.parseEther(value), }); // Get the transaction count for the sender address const nonce = await provider.getTransactionCount(sender); // Define the transaction object const transactionObject = { to: receiver, gasPrice: ethers.parseUnits('50', 'gwei'), gasLimit: gasLimit.toString(), nonce: nonce, value: ethers.parseEther(value), }; // Sign the transaction using the private key const wallet = new ethers.Wallet(privateKey, provider); console.log(`Sending transaction...`) const transaction = await wallet.sendTransaction(transactionObject); console.log('Transaction hash:', transaction.hash); } sendEth("2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the sender and receiver addresses, and the private key sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a' receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114' private_key = '1111111111111111111111111111111111111111111111111111111111111111' # Estimate gas limit gas_limit = web3.eth.estimate_gas({ 'from': sender, 'to': receiver }) # Build the transaction object transaction = { 'nonce': web3.eth.get_transaction_count(sender), 'to': receiver, 'value': web3.to_wei(1, 'ether'), # value to send 'gas': gas_limit, 'gasPrice': web3.eth.gas_price, 'chainId': web3.eth.chain_id } # Sign the transaction signed_tx = web3.eth.account.sign_transaction(transaction, private_key) print(f'Signed transaction: {signed_tx}') # Send the transaction print('Sending transaction...') tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) print(f'Transaction hash: {web3.toHex(tx_hash)}') ``` > [!NOTE] > > Note that you will need to edit the `amountToSend`, `recipientAddress`, and `secretKey` fields to be able to use this code. ## Use case One potential use case for `eth_sendRawTransaction` is to automatically swap FTM for a token when its price reaches a certain level. `eth_sendRawTransaction` will allow custom transaction parameters, such as gas price or gas limit, to be specified to optimize the speed and cost of the transaction. For instance, you could build a program monitoring the price of a specific token and execute a `swapExactETHForTokens` transaction when the token reaches a certain price level, it could use `eth_sendRawTransaction` to submit a raw transaction with a custom gas price and limit optimized for the current network conditions. # eth_syncing | Fantom Source: https://docs.chainstack.com/reference/fantom-syncing openapi/fantom_node_api/eth_syncing.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that returns an object with information about the current synchronization status of the node, or false if the node is fully synced. Fantom API method that returns an object with information about the current synchronization status of the node, or `false` if the node is fully synced. This method can track the progress of a node's synchronization with the Fantom blockchain. The returned object contains data such as the starting block, current block, and highest block of the node, allowing developers to monitor and estimate the time remaining for the node to sync fully. ## Parameters * `none` ## Response * `result` — the boolean value `false` when not syncing or a JSON object when syncing: * `startingBlock` — the block number where the synchronization process started, encoded as hexadecimal. * `currentBlock` — the block number that the node has currently processed, same as `eth_blockNumber`, encoded as hexadecimal. * `highestBlock` — the block number of the latest block in the blockchain known to the node. ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const syncStatus = async () => { const status = await provider.send("eth_syncing", []); console.log(status); }; syncStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" print (web3.eth.syncing) ``` ## Use case The `eth_syncing` method can be useful for developers building applications that interact with the Fantom blockchain. For example, you may want to provide feedback to your application's user about the status of the node the application is connecting to. # eth_uninstallFilter | Fantom Source: https://docs.chainstack.com/reference/fantom-uninstallfilter openapi/fantom_node_api/eth_uninstallFilter.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method used to remove a filter previously created using one of the following methods:. Use it on Fantom via Chainstack. Fantom API method used to remove a filter previously created using one of the following methods: * [eth\_newFilter](/reference/fantom-newfilter) * [eth\_newPendingTransactionFilter](/reference/fantom-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/fantom-newblockfilter) Upon successful execution, the filter is removed and will no longer emit events. This method can be useful for optimizing resource usage when a developer no longer needs to monitor certain events or blocks. It can also be used to clean up unused filters in order to reduce clutter in the client-side filter list. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/fantom-newfilter) > * [eth\_newPendingTransactionFilter](/reference/fantom-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/fantom-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `string` — the filter ID that you want to uninstall ## Response * `boolean` — a boolean value indicating whether the filter was successfully uninstalled. `true` if successfully removed, and `false` if not. ## `eth_uninstallFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function removeFilter(filter) { try { const removed = await provider.send('eth_uninstallFilter', [filter]); console.log(removed); // Log whether the filter is removed } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' removeFilter(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def remove_filter(filter_id): try: removed = web3.eth.uninstall_filter(filter_id) return removed except Exception as e: print(e) filter_id = '0x0' print(remove_filter(filter_id)) ``` ## Use case One use case for `eth_uninstallFilter` is to optimize resource usage in a DApp. When a DApp needs to monitor events on the blockchain, it can create a filter using [eth\_newFilter](/reference/fantom-newfilter),[eth\_newPendingTransactionFilter](/reference/fantom-newpendingtransactionfilter), or [eth\_newBlockFilter](/reference/fantom-newblockfilter) to listen for specific events or blocks. However, once the DApp no longer needs to monitor these events or blocks, it can use `eth_uninstallFilter` to stop watching for them. By doing so, the DApp can reduce the number of active filters and free up resources, such as network bandwidth and computational power, that would otherwise be used to maintain the filter. For example, let's say that a DApp is monitoring incoming blocks, logs batches of 50 blocks, and then uses `uninstallFilter` to remove the filter. # eth_gasPrice | Polygon Source: https://docs.chainstack.com/reference/gasprice openapi/polygon_node_api/gas_data/eth_gasPrice.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the current gas base fee of the network. Chainstack supports eth_gasPrice on Polygon JSON-RPC nodes. Polygon API method that returns the current gas base fee of the network. The gas price is the quantity of the native token the transaction's sender must pay per unit of gas consumed. The value returned is in Wei. ## Parameters * `none` ## Response * `quantity` — the integer value of the current gas base fee, returned in Wei ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getGasPrice = async () => { const baseFee = await provider.send("eth_gasPrice"); console.log(`The base fee is: ${baseFee} Wei.`); }; getGasPrice() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.gas_price) ``` ## Use case You can use `eth_gasPrice` to calculate the total gas value to send with a transaction based on the base and priority fee system. This concept was implemented with [EIP-1559](https://metamask.io/1559/) in Ethereum's London hard fork. EIP-1559 aimed to solve the problem of network congestion by implementing a dynamic fee market mechanism, which adjusts the fee required to process a transaction based on network demand. with the current system, the total gas price is composed of a **base fee** determined by the network's load and a **priority fee** added by the user. > [!NOTE] > > **Note** > > The `eth_gasPrice` method returns the base fee. The following script calculates the total fee to send with a transaction based on the base fee and an arbitrary priority fee that the user can choose. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getGas(priorityFee) { const priorityFeeWei = ethers.parseUnits(String(priorityFee), 'gwei'); const baseFee = (await provider.getFeeData()).gasPrice; const totalFee = priorityFeeWei + baseFee return [totalFee, baseFee] } async function main() { // The priority fee is constant at 10 Gwei, the user can change it. const PRIORITY_FEE = 10; const [totalFee, baseFee] = await getGas(PRIORITY_FEE) const baseFeeGwei = ethers.formatUnits(baseFee, 'gwei') console.log(`The priority fee: ${PRIORITY_FEE} Gwei.`) console.log(`The Polygon base fee at this moment is: ${Number(baseFeeGwei).toFixed(2)} Gwei.`) const gweiFee = ethers.formatUnits(totalFee, 'gwei') console.log(`The total gas fee should be at least: ${Number(gweiFee).toFixed(2)} Gwei.`) } main() ``` The `getGas` function calculates the total fee required to process a transaction. The function takes in a `priorityFee` as input representing the fee a user is willing to pay to prioritize their transaction processing. The function first converts the `priorityFee` from Gwei to Wei (the smallest unit of ether) using `ethers.parseUnits`. Then, it uses the `provider.getFeeData` method to retrieve the current base fee set by the network. The function finally adds the priority fee to the base fee and returns the total fee along with the base fee as an array. The `main` function is the entry point of the code. It first sets a constant `PRIORITY_FEE` to 10 Gwei. Then calls the `getGas` function with `PRIORITY_FEE` as an argument and destructures the returned array into two variables `totalFee` and `baseFee`. The code then converts `baseFee` and `totalFee` from Wei to Gwei using `ethers.formatUnits` for better readability, and logs the results to the console. The main function logs the priority fee, the base fee at that moment, and the minimum recommended total fee that should be paid to process a transaction. # Transaction history Source: https://docs.chainstack.com/reference/get_transactions-history openapi/faucet_api/transaction_history.json GET /transactions-history The method retrieves the transaction history for all faucets. In the Bearer field, you need to provide your Chainstack API key to authorize the call. The method retrieves the transaction history for all faucets. In the `Bearer` field, you need to provide your [Chainstack API key](https://console.chainstack.com/user/settings/api-keys) to authorize the call. # Attestation data Source: https://docs.chainstack.com/reference/getattestationdata openapi/ethereum_beacon_chain_api/validatiors_info/attestation_data.json GET /validator/attestation_data The /eth/v1/validator/attestation_data method is an essential API endpoint in the Ethereum Beacon Chain, primarily used by validators. > [!NOTE] > > The interactive example will return `Slot already finalized` unless you edit the parameters. The `/eth/v1/validator/attestation_data` method is an essential API endpoint in the Ethereum Beacon Chain, primarily used by validators. This method provides the necessary data for validators to create an attestation—a process where validators confirm and endorse a particular block's data. Attestations are fundamental in the Ethereum PoS protocol, as they contribute to the consensus and validation of the blockchain. This endpoint is crucial for validators who need to fetch the latest data to attest to the state of the blockchain effectively. ## Parameters * `none` ## Response * `object` — attestation data. * `slot` — the slot number for which the attestation is being made. In the Ethereum Beacon Chain, a slot is a period during which new blocks can be proposed and attested. * `committee_index` — the index of the committee to which the validator belongs. This index indicates the group of validators responsible for attesting to a specific shard or part of the blockchain. * `beacon_block_root` — the root of the beacon block that the attestation is targeting. This root acts as an identifier for the specific block being attested. * `source` — the source checkpoint of the attestation. This includes the epoch and the root of the block at the start of the epoch. * `target` — the target checkpoint of the attestation. Similar to the source, this includes the epoch and the block root, but for the end of the attestation period. The `/eth/v1/validator/attestation_data` method is a key tool for validators in the Ethereum network. It enables them to participate actively in the consensus process, ensuring the blockchain's integrity and continuous validation. # eth_getBalance | Polygon Source: https://docs.chainstack.com/reference/getbalance openapi/polygon_node_api/account_info/eth_getBalance.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the balance of a specific Polygon account in Wei, the smallest unit of ether. Polygon via Chainstack. Polygon API method that returns the balance of a specific Polygon account in [Wei](https://www.investopedia.com/terms/w/wei.asp), the smallest unit of ether. This method allows developers to retrieve the current balance of a Polygon account for various purposes, such as checking available funds or displaying balance information. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to check the balance of * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ## Response * `quantity` — the integer value of the current balance in Wei ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getBalance = async (address, block) => { const balance = await provider.send("eth_getBalance", [address, block]); console.log(balance); }; getBalance("0xA507fF8a59De851134BC44063840A334A243a99E", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" address = web3.to_checksum_address("0xA507fF8a59De851134BC44063840A334A243a99E") balance = web3.eth.get_balance(address, "latest") print(balance) ``` ## Use case One practical use case for `eth_getBalance` is to check the balance of an account for a program that scans the balance periodically and fills up the account when the balance drops below a certain value. Here's an example of how you can achieve this in ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const accountAddress = '0xB18614D1e3A3B67A6E0c83Be98AC04157b674083'; const minimumBalance = ethers.parseEther("10"); // 10 Matic in wei async function checkBalance(address) { const balance = await provider.getBalance(address, 'latest'); return balance; } async function fillUp(balance) { const minimumConverted = ethers.formatEther(minimumBalance); if (balance < minimumBalance) { console.log(`The balance of the account ${accountAddress} is below ${minimumConverted} Matic.`); console.log(`Sending more funds...`) // Call another function to fill up your account } else { console.log(`The balance of the account ${accountAddress} is above ${minimumConverted} Matic.`); console.log(`No need to send more funds.`) } } async function main() { const balance = await checkBalance(accountAddress); const balanceInMatic = ethers.formatEther(balance); console.log(`The balance of the account ${accountAddress} is ${balanceInMatic} Matic. \n`); await fillUp(balance); } main(); ``` The code is a simple script that checks the balance of an account on the Polygon network and takes action if the balance falls below a specified minimum. The `accountAddress` variable holds the address for which you want to check the balance. The `minimumBalance` variable holds the minimum balance in Wei you want to check against. The `checkBalance` function takes an address as an argument and returns the balance of the account in Wei. The `fillUp` function takes the balance as an argument, and converts the minimum balance from Wei to MATIC using the `formatEther` method from the ethers.js utility library, and checks if the balance is below the minimum balance. > [!NOTE] > > Note that the Wei value is converted only to display it, as it is more accurate to work with the Wei value. If the balance is below the minimum, it logs a message to the console indicating that more funds need to be sent and calls another function to fill up the account. If the balance is above the minimum, it logs a message indicating that no additional funds need to be sent. The main function calls the `checkBalance` and `fillUp` functions to convert the balance from Wei to MATIC using the `formatEther` method, and log the balance and the result of the check to the console. In summary, this script allows you to easily check the balance of an account on the Polygon network and take action if the balance falls below a specified minimum. # Attestations of the beacon block by block_id Source: https://docs.chainstack.com/reference/getbeaconblockattestationsbyblockid openapi/ethereum_beacon_chain_api/validatiors_info/attestation_of_beacon_block_by_block_id.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/blocks/{block_id}/attestations The /eth/v1/beacon/blocks/{block_id}/attestations method is a key API endpoint in the Ethereum Beacon Chain. Chainstack via Chainstack. The `/eth/v1/beacon/blocks/{block_id}/attestations` method is a key API endpoint in the Ethereum Beacon Chain. It provides detailed information about the attestations included in a specified beacon block. Attestations are critical to the Ethereum PoS mechanism, where validators attest or agree on the state of the blockchain. This endpoint allows developers and network participants to access attestations within a specific block, essential for analysis, verification, and understanding the consensus process. ## Parameters * `{block_id}`: This parameter specifies the identifier of the beacon block for which the attestations are being queried. It can be a slot number, a block root, or a special value like 'genesis' or 'head'. ## Response * `array` — list of attestation objects included in the specified block. * Each `object` in the array represents an individual attestation, containing: * `aggregation_bits` — a bitfield indicating the group of validators that participated in this attestation. * `data` — the data being attested to by the validators, including: * `slot`: The slot number of the attested block. * `index`: The committee index associated with this attestation. * `beacon_block_root`: The root of the beacon block being attested. * `source`: The source checkpoint of the attestation. * `target`: The target checkpoint of the attestation. * `signature` — the collective signature of the validators who created this attestation. This signature is a key component in validating the attestation's authenticity and agreement. The `/eth/v1/beacon/blocks/{block_id}/attestations` method is instrumental in providing transparency and insight into the validation and consensus process of the Ethereum network. It enables users to verify and analyze the validators' consensus on specific blocks, enhancing the understanding and security of the blockchain. # Block root by block_id Source: https://docs.chainstack.com/reference/getbeaconblockrootbyblockid openapi/ethereum_beacon_chain_api/state/getBeaconBlockRootByBlockId.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/blocks/{block_id}/root The /eth/v1/beacon/blocks/{block_id}/root method is a specific API endpoint in the Ethereum Beacon Chain that provides the root of a given beacon block. The `/eth/v1/beacon/blocks/{block_id}/root` method is a specific API endpoint in the Ethereum Beacon Chain that provides the root of a given beacon block. The block root is a cryptographic hash that uniquely identifies each block in the Beacon Chain. This endpoint is essential for developers and network participants who need to reference or verify a specific block in the Beacon Chain, as the block root is used extensively in various blockchain operations and data structures. ## Parameters * `{block_id}`: This parameter specifies the identifier of the beacon block for which the root is being requested. The block identifier can be a slot number, a block root, or a special value like 'genesis' or 'head'. ## Response * `object` — block root information. * `root` — the root of the specified beacon block. This is a 32-byte value (represented as a hexadecimal string) that uniquely identifies the block within the Beacon Chain. * `block_id` — the identifier of the block for which this root belongs. This confirms the specific block to which the provided root corresponds. The `/eth/v1/beacon/blocks/{block_id}/root` method is an invaluable tool for accessing fundamental and unique identifiers of beacon blocks within the Ethereum network. It allows clients and developers to accurately pinpoint and reference specific blocks, facilitating various blockchain operations such as validation, attestation, and auditing processes. # Block by block_id Source: https://docs.chainstack.com/reference/getbeaconblocksbyblockid openapi/ethereum_beacon_chain_api/state/getBeaconBlocksByBlockId.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v2/beacon/blocks/{block_id} Reference for the Block by block_id JSON-RPC method on the Chainstack blockchain via Chainstack nodes. Parameters, response, and examples included. The `/eth/v2/beacon/blocks/{block_id}` method is a key API endpoint in the Ethereum Beacon Chain, designed to provide detailed information about a specific beacon block. This method is essential for developers and participants in the Ethereum network who need to access comprehensive data about a particular block, whether for analysis, verification, or development purposes. ## Parameters * `{block_id}`: This parameter specifies the identifier of the beacon block for which information is being requested. The block identifier can be a slot number, a block root, or a special value like 'genesis' or 'head'. ## Response * `object` — beacon block information. * `root` — the root of the beacon block, a unique cryptographic hash that serves as an identifier for the block. * `slot` — the slot number at which the block was proposed. In the Ethereum Beacon Chain, slots are discrete time intervals at which new blocks can be proposed. * `proposer_index` — the index of the validator who proposed the block. This index identifies the specific validator within the Beacon Chain. * `parent_root` — the root of the parent block to which this block is connected, ensuring the continuity of the blockchain. * `state_root` — the root of the beacon chain state after the block's effect. This state root is crucial for verifying the integrity of the chain's state post-block. * `body` — the body of the block, containing transactions, attestations, and other relevant data. This includes detailed information about the block's operations and effects on the Beacon Chain. The `/eth/v2/beacon/blocks/{block_id}` method is instrumental in providing a thorough understanding of individual blocks within the Ethereum network. It enables users to delve into the specifics of block structure, content, and the role each block plays in the broader context of the blockchain. # Beacon headers by block_id Source: https://docs.chainstack.com/reference/getbeaconheadersbyblockid openapi/ethereum_beacon_chain_api/state/getBeaconHeadersByBlockId.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/headers/{block_id} Reference docs for the Beacon headers by block_id JSON-RPC method on the Chainstack blockchain, available via Chainstack JSON-RPC nodes. The `/eth/v1/beacon/headers/{block_id}` method is an important API endpoint in the Ethereum Beacon Chain, focused on providing header information for a specified beacon block. Block headers are crucial in blockchain technology as they contain key information about the block without the need to process the entire block data. This endpoint is particularly valuable for developers and network participants who require quick and efficient access to block information for validation, analysis, or synchronization purposes. ## Parameters * `{block_id}`: This parameter specifies the identifier of the beacon block for which the header information is being requested. The block identifier can be a slot number, a block root, or a special value like 'genesis' or 'head'. ## Response * `object` — beacon block header information. * `root` — the root of the beacon block, serving as a unique cryptographic identifier for the block. * `canonical` — a boolean indicating whether the block is part of the canonical chain. * `header` — the actual block header information, including: * `slot`: The slot number at which the block was proposed. * `proposer_index`: The index of the validator who proposed the block. * `parent_root`: The root of the parent block, linking to the chain's continuity. * `state_root`: The root of the beacon chain state after the block's effect. * `body_root`: The root of the block's body, encapsulating transactions, attestations, and other block content. The `/eth/v1/beacon/headers/{block_id}` method is a key tool in the Ethereum ecosystem, enabling a streamlined and efficient way to access vital block header information. It aids in enhancing the blockchain's transparency and accessibility, vital for a wide range of blockchain-related operations and analyses. # Beacon headers by slot Source: https://docs.chainstack.com/reference/getbeaconheadersbyslotandparentroot openapi/ethereum_beacon_chain_api/state/getBeaconHeadersBySlotAndParentRoot.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/headers The /eth/v1/beacon/headers method is a vital API endpoint in the Ethereum Beacon Chain, designed to provide a list of recent block headers. > [!NOTE] > > **Get the non-pruned state** > > To get a correct response from the interactive API example, make sure you provide the latest or close to latest slot. For the latest slot number, see [https://beaconcha.in/](https://beaconcha.in/) The `/eth/v1/beacon/headers` method is a vital API endpoint in the Ethereum Beacon Chain, designed to provide a list of recent block headers. Block headers are significant as they contain essential data about the blocks, such as their identification, proposers, and state, without the need to download the entire block content. This method is especially useful for developers and network participants who need to quickly and efficiently access block information for purposes like verification, analysis, and network synchronization. ## Parameters * `none` ## Response * `array` — list of recent block headers. * Each `object` in the array represents a block header, containing: * `root` — the root of the block, serving as a cryptographic identifier unique to each block. * `canonical` — a boolean value indicating whether the block is part of the canonical chain. * `header` — the detailed information of the block header, including: * `slot`: The slot number at which the block was proposed, an integral part of the timekeeping system in the Beacon Chain. * `proposer_index`: The index of the validator who proposed the block, identifying the responsible party for the block's creation. * `parent_root`: The root of the parent block, crucial for maintaining the continuity and integrity of the blockchain. * `state_root`: The root of the beacon chain state after the block's effect, essential for verifying the current state of the blockchain. * `body_root`: The root of the block's body, which encapsulates key block elements like transactions and attestations. The `/eth/v1/beacon/headers` method is an indispensable tool in the Ethereum ecosystem, providing streamlined access to important block header information. It plays a critical role in maintaining the transparency, efficiency, and accessibility of the blockchain for various stakeholders. # Attestations from the beacon pool Source: https://docs.chainstack.com/reference/getbeaconpoolattestationsbyslotandcommitteeindex openapi/ethereum_beacon_chain_api/validatiors_info/beacon_pool_attestations.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/pool/attestations The /eth/v1/beacon/pool/attestations method provides access to the pool of attestations on the Ethereum Beacon Chain. On Chainstack. > [!NOTE] > > **Empty data response** > > Getting the response "data": \[] means there are no attestations in the pool for provided slot. For a quick test, change the slot value to the latest one and try again—you will get the latest attestations from the pool. The `/eth/v1/beacon/pool/attestations` method provides access to the pool of attestations on the Ethereum Beacon Chain. Attestations are critical elements within the Ethereum PoS mechanism, where validators attest to the validity of blocks. These attestations contribute to the consensus process and are integral in confirming block data and state transitions on the Beacon Chain. This endpoint is essential for developers and participants in the Ethereum network who need to track and analyze the attestations made by validators, offering a deep dive into the consensus process of Ethereum. ### Parameters * `none` ### Response * `array` — list of attestation objects. * Each `object` in the array represents an individual attestation, containing: * `aggregation_bits` — a bitfield representing the committee validators' participation in the attestation. This field shows which validators are attesting to the block. * `data` — the data being attested to, including: * `slot`: The slot number for which the attestation is made. * `index`: The committee index or position within the attesting committee. * `beacon_block_root`: The root of the beacon block being attested to. * `source`: The source checkpoint for the attestation. * `target`: The target checkpoint for the attestation. * `signature` — the combined signature of the validators who participated in the attestation. This signature verifies the validators' agreement on the attested data. The `/eth/v1/beacon/pool/attestations` method is a valuable tool for understanding the participation and decision-making process of validators on the Ethereum network, offering real-time insights into the blocks and states being endorsed by the network's validators. # Attester slashings Source: https://docs.chainstack.com/reference/getbeaconpoolattesterslashings openapi/ethereum_beacon_chain_api/validatiors_info/attester_slashings.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/pool/attester_slashings The /eth/v1/beacon/pool/attester_slashings method is an integral part of the Ethereum Beacon Chain API, providing information about attester slashings. > [!NOTE] > > **Empty response data** > > Attester slashings are infrequent events, so most of the time you will get an empty response. The `/eth/v1/beacon/pool/attester_slashings` method is an integral part of the Ethereum Beacon Chain API, providing information about attester slashings. Attester slashing occurs when a validator, responsible for attesting to the state of the blockchain, is found to have attested conflicting information. This is a severe violation in the Ethereum PoS protocol, as it undermines the network's security and consensus mechanism. This endpoint is of particular importance to network participants and developers, offering insights into instances of protocol violations and maintaining the integrity of the Beacon Chain. ### Parameters * `none` ### Response * `array` — list of attester slashing objects. * Each `object` in the array represents an attester slashing event, encompassing: * `attestation_1` and `attestation_2` — the conflicting attestations made by the slashed validator. These attestations are the evidence of the violation. They include: * `aggregation_bits`: A bitlist representing the committee members who are attesting. * `data`: The beacon block data being attested to, including the slot, beacon block root, source, and target. * `signature`: The signature of the attesting validator(s), verifying their attestation. * `validator_indices` — the indices of the validators involved in the slashing event. These indices identify the validators who have been penalized for their actions. * `message_hashes` — hashes of the attester slashing messages, serving as a cryptographic proof of the event. The `/eth/v1/beacon/pool/attester_slashings` method is a critical component for monitoring the ethical and rule-abiding behavior of validators in the Ethereum network, ensuring the overall health and trustworthiness of the blockchain. # Proposer slashings from the beacon pool Source: https://docs.chainstack.com/reference/getbeaconpoolproposerslashings openapi/ethereum_beacon_chain_api/validatiors_info/proposer_slashings.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/pool/proposer_slashings The /eth/v1/beacon/pool/proposer_slashings method is a crucial API endpoint in the Ethereum Beacon Chain, offering data on proposer slashings. > [!NOTE] > > **Empty data response** > > Proposer slashings are infrequent events, so most of the time you will have the "data": \[] response. The `/eth/v1/beacon/pool/proposer_slashings` method is a crucial API endpoint in the Ethereum Beacon Chain, offering data on proposer slashings. Proposer slashing is a serious penalty mechanism applied to validators who are found to have proposed conflicting blocks—a behavior considered harmful to the network's integrity and security. This endpoint allows developers and network participants to access information about such slashings, which is vital for maintaining network health and understanding validator behavior. ### Parameters * `none` ### Response * `array` — list of proposer slashing objects. * Each `object` in the array represents a proposer slashing event, containing: * `proposer_index` — the index of the validator who was slashed for proposing conflicting blocks. * `header_1` and `header_2` — the conflicting beacon block headers submitted by the slashed proposer. These headers are evidence of the malicious action that led to the slashing. * Each header includes: * `slot`: The slot number at which the block was proposed. * `proposer_index`: The index of the validator who proposed the block. * `parent_root`: The root of the parent block, linking the chain's continuity. * `state_root`: The root of the beacon chain state after the block's effect. * `body_root`: The root of the block's body, encapsulating the block's transactions and attestations. * `signature` — the signature of the slashed validator, associated with the malicious blocks they proposed. * `message_hash` — a hash of the slashing message, providing a cryptographic proof of the slashing event. The `/eth/v1/beacon/pool/proposer_slashings` method serves as a critical tool for monitoring and understanding the security of the Ethereum network, offering transparent access to instances where validators have acted against the network's best interests. # Voluntary exits Source: https://docs.chainstack.com/reference/getbeaconpoolvoluntaryexits openapi/ethereum_beacon_chain_api/validatiors_info/voluntary_exits.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/pool/voluntary_exits Reference for the Voluntary exits JSON-RPC method on the Chainstack blockchain via Chainstack nodes. Parameters, response, and examples included. The `/eth/v1/beacon/pool/voluntary_exits` method is an API endpoint in the Ethereum Beacon Chain that provides information about the voluntary exits of validators from the network. A voluntary exit occurs when a validator chooses to stop participating in the consensus process and exits the system. This action is irreversible and is typically taken by validators who wish to cease their operations or withdraw their staked ETH after the mandatory exit delay. This endpoint is particularly useful for developers and network participants who want to track validator exits and understand the dynamics of validator participation in the Ethereum network. ### Parameters * `none` ### Response * `array` — list of voluntary exit objects. * Each `object` in the array represents a voluntary exit, containing: * `epoch` — the epoch at which the validator has exited or is scheduled to exit. In the Ethereum Beacon Chain, epochs are sequential time periods during which blockchain operations occur. * `validator_index` — the unique index of the validator who is voluntarily exiting. This index is an identifier used within the Beacon Chain to track validators' activities and status. * `signature` — the cryptographic signature provided by the validator, confirming their intention to voluntarily exit. This ensures the authenticity and irrevocability of the exit action. * `withdrawal_credentials` — the credentials that will be used for withdrawing the staked ETH after the exit is processed and the mandatory delay has passed. * `message_hash` — a hash of the exit message, providing an additional layer of security and verification for the exit process. This endpoint provides a valuable insight into the changing landscape of validator participation on the Ethereum Beacon Chain, allowing for better network monitoring and analysis. # Retrieve blobs by block ID Source: https://docs.chainstack.com/reference/getblobsbyblockid openapi/ethereum_beacon_chain_api/state/getBlobsByBlockId.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/blobs/{block_id} The /eth/v1/beacon/blobs/{block_id} method retrieves information about blobs by block ID. Retrieve blobs by block ID on Chainstack via Chainstack. The `/eth/v1/beacon/blobs/{block_id}` method retrieves information about blobs by block ID. This endpoint is crucial for accessing detailed data contained within blobs, including the slot, epoch, and the associated data. It enables developers and users to programmatically interact with and analyze the blob data associated with Ethereum's beacon chain, which is especially useful in the context of rollups and data-intensive operations. ## Parameters * `block_id` — the block identifier to retrieve blobs for. This can be one of the following: * `head` — the head of the chain * `genesis` — the genesis block * `finalized` — the most recent finalized block * A slot number * A hex-encoded block root with `0x` prefix ## Response The response is an object containing the following fields: * `data` — an array of blob objects, each containing: * `blob` — a hexadecimal string representing the actual blob data. This field provides the raw data that rollups or other Ethereum layer 2 solutions have submitted for inclusion in a block. * `index` — the index of the blob within the block * `kzg_commitment` — the KZG commitment for the blob * `kzg_proof` — the KZG proof for the blob * `signed_block_header` — the signed block header associated with the blob # eth_getBlockByHash | Polygon Source: https://docs.chainstack.com/reference/getblockbyhash openapi/polygon_node_api/blocks_info/eth_getBlockByHash.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns information about the block matching the given block hash. eth_getBlockByHash on Polygon via Chainstack. Polygon API method that returns information about the block matching the given block hash. This method can be useful for analyzing a block and its transactions. ## Parameters * `hash` — the hash of the block * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — the hash of the previous block that was used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ethereum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ethereum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. Polygon PoS chain uses Proof of Stake consensus and this value is not relevant. * `extradata` — the extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — the array of uncle hashes. ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByHash = async () => { const blockByNumber = await provider.send("eth_getBlockByHash", ["0xdfa56bbff2aafaf7b0ac33ca0fd1393ca15830a608763680a563d0a7b4c083ff",false]); console.log(blockByNumber); }; eth_getBlockByHash(); ``` ```python web3.py theme={"system"} # Polygon is a Proof of Authority (POA) chain. Web3.py requires the use of the POA compatibility middleware. from web3 import Web3 from web3.middleware import geth_poa_middleware node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Inject the POA compatibility middleware to the innermost layer. web3.middleware_onion.inject(geth_poa_middleware, layer=0) print(web3.eth.get_block("0x7709b7cacb9a3d647afe557d51891c00ad0c00e01d5bffdbcfe6c1730d36290c", False)) ``` ## Use case The `eth_getBlockByHash` is excellent for analytics purposes. For example, it can be used to quickly find out the hashes of transactions that create new smart contracts. The `transactions` field, an array of all the transactions within the block, is returned in the response. By iterating through this array and examining each transaction's `to` field, a user can determine which transactions in the block are creating new smart contracts. The following example uses the web3.js library to create a function to extract this data: The previous example shows how the `eth_getBlockByHash` method might be used to know which transactions create a smart contract. When a new smart contract is created, the `to` field will be `null`. Running this function on a block hash with the transactions detail field set to `true` will inspect each transaction's `to` field. If the field is `null`, the transaction creates a smart contract, printing the transaction hash, which can then be used for further analysis. # getBlocksWithLimit | Solana Source: https://docs.chainstack.com/reference/getblockswithlimit openapi/solana_node_api/getBlocksWithLimit.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getBlocksWithLimit method returns a list of confirmed blocks starting from a specified slot for a maximum limit. On Solana. > [!NOTE] > > **Range limit:** maximum 500,000 blocks per request. **Archive data:** this method supports fetching historical data on [Global Nodes](/docs/global-elastic-node). See [Solana method limits](/docs/limits#solana-method-limits). # Solana `getBlocksWithLimit` method The Solana `getBlocksWithLimit` method returns a list of confirmed blocks starting from a specified slot for a maximum limit. This method provides a list of block heights that have been confirmed by the network. It is useful for retrieving a specific number of blocks for analysis or display. ## Parameters * `startSlot` — the start slot of the range. * `limit` — the maximum number of blocks to return. ## Response * `value` — an array of confirmed block heights starting from the `startSlot` up to the specified `limit`. ## Use case A practical use case for `getBlocksWithLimit` is to retrieve a specific number of confirmed blocks for analysis or display. This can be useful for block explorers, wallets, or other applications that need to display or make use of this information. # eth_chainId | Cronos Source: https://docs.chainstack.com/reference/getchainid-1 openapi/cronos_node_api/eth_chainId.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and verify whether a network is the desired one. Cronos API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and verify whether a network is the desired one. It was introduced in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). ## Parameters * `none` ## Response * `quantity` — EIP-155 Chain ID > [!NOTE] > > Use the [Chainstack EVM Knife](https://web3tools.chainstacklabs.com/hexadecimal-decimal) to convert the Chain ID from Hexadecimal to Decimal. ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const chainId = async () => { // This will return the value in Hex const chainId = await provider.send("eth_chainId"); console.log(`Hex Chain ID: ${chainId}`); }; chainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.chain_id) ``` ## Use case One possible use case for the `eth_chainId` method is to determine which chain a user is connected to when using a DApp with the MetaMask browser extension. This can be useful to provide a customized user experience based on the specific chain that the user is interacting with. # eth_getCode | Polygon Source: https://docs.chainstack.com/reference/getcode openapi/polygon_node_api/account_info/eth_getCode.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that retrieves the compiled bytecode of a smart contract providing its address as a parameter. Polygon via Chainstack. Polygon API method that retrieves the compiled bytecode of a smart contract providing its address as a parameter. This method returns a hexadecimal string representing the smart contract's bytecode. Developers can use this bytecode to verify if a smart contract is legitimate and to ensure that it performs the intended functions. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ## Response `data` — the compiled bytecode of a smart contract. Returns `0x` if the address is not associated with a smart contract. ## `eth_getCode` code examples The following example retrieves the compiled bytecode from the [LINK token smart contract](https://polygonscan.com/token/0xb0897686c545045afc77cf20ec7a532e3120e0f1#code). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getCode = async (address, block) => { const code = await provider.send("eth_getCode", [address, block]); console.log(code); }; getCode("0xb0897686c545045aFc77CF20eC7A532E3120E0F1", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" code = web3.eth.get_code("0xb0897686c545045aFc77CF20eC7A532E3120E0F1", "latest") print(web3.to_hex(code)) # Convert the Bytes result into HEX. ``` ## Use case A practical use case for the `eth_getcode` method is verifying whether an address is associated with a smart contract. This can be particularly useful for blockchain explorers, auditors, and DApp developers, who must ensure that the code they interact with is legitimate. Here is an example using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function verifyContractAddress(address) { try { const bytecode = await provider.getCode(address); return bytecode !== '0x'; } catch (error) { console.error(`Error: ${error.message}`); return false; } } async function main() { const addressToVerify = "0xc094fd7ef80be094f3d98e227a487490ab1f9b9c" const isContract = await verifyContractAddress(addressToVerify); if (isContract) { console.log(`📑The address ${addressToVerify} is a smart contract.`); } else { console.log(`🚨The address ${addressToVerify} is not a smart contract.`); } } main(); ``` The `verifyContractAddress` function takes an `address` as a parameter and returns a `boolean` value indicating whether the address is associated with a smart contract. The function works by using the `provider.getCode` method to retrieve the bytecode of the contract associated with the given address. If the returned bytecode is not equal to `0x`, then the function assumes that the address is associated with a smart contract and returns `true`. If an error occurs while calling `provider.getCode`, the function returns `false` and logs the error message to the console. The `main` function is responsible for calling `verifyContractAddress` with a specific address to verify and log the results to the console. In this example, `main` is set up to verify the address `0xc094fd7ef80be094f3d98e227a487490ab1f9b9c`. After calling `verifyContractAddress`, `main` logs either a success message indicating that the address is associated with a smart contract or a warning message indicating that it is not. Overall, this code provides a useful utility for verifying the legitimacy of smart contract addresses on the Polygon blockchain. It can be useful for auditors, blockchain explorers, and DApp developers who want to ensure that they are interacting with trustworthy code on the blockchain. # eth_getCode | Fantom Source: https://docs.chainstack.com/reference/getcode-1 openapi/fantom_node_api/eth_getCode.json POST /4ab982aa70a7baead515ffdb5915df3f Fantom API method that retrieves the compiled bytecode of a smart contract, providing its address as a parameter. Fantom via Chainstack. Fantom API method that retrieves the compiled bytecode of a smart contract, providing its address as a parameter. This method returns a hexadecimal string representing the smart contract's bytecode. Developers can use this bytecode to verify whether a smart contract is legitimate and ensure that it performs its intended functions. ## Parameters * `address` — the address of the smart contract to query. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked amount to be burned. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response `data` — the compiled bytecode of a smart contract. Returns `0x` if the address is not associated with a smart contract. ## `eth_getCode` code examples The following example retrieves the compiled bytecode from the [WFTM token smart contract](https://explorer.fantom.network/tokens/0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getCode = async (address, block) => { const code = await provider.send("eth_getCode", [address, block]); console.log(code); }; getCode("0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" code = web3.eth.get_code("0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83", "latest") print(web3.to_hex(code)) # Convert the Bytes result into HEX. ``` ## Use case A practical use case for the `eth_getcode` method is verifying whether an address is associated with a smart contract. This can be particularly useful for blockchain explorers, auditors, and DApp developers, who must ensure that the account they interact with is legitimate. # Committees by state, epoch, index, and slot Source: https://docs.chainstack.com/reference/getcommitteesbystateidepochindexandslot openapi/ethereum_beacon_chain_api/state/getCommitteesByStateIdEpochIndexAndSlot.json GET /beacon/states/{state_id}/committees The eth/v1/beacon/states/{state_id}/committees method is a significant API endpoint in the Ethereum Beacon Chain. Chainstack via Chainstack. > [!NOTE] > > **No response in the interactive API** > > The response to the call might be too big for the web app to handle, so if you are not getting it here, copy the CURL example and run in your terminal—this will work. The `eth/v1/beacon/states/{state_id}/committees` method is a significant API endpoint in the Ethereum Beacon Chain. It provides comprehensive information about the committees for a given state. In the context of Ethereum, committees are groups of validators assigned to specific duties, such as proposing and attesting to blocks. Understanding the composition and assignments of these committees is crucial for validators and network participants, as it impacts their roles and responsibilities in the network's consensus process. ## Parameters * `{state_id}`: This parameter specifies the state identifier for which the committee information is being requested. It can be a specific slot number, an epoch number, a block root, or special values like 'genesis' or 'head'. ## Response * `array` — list of committee objects. * Each `object` in the array represents a committee at the specified state, containing: * `index` — the index or identifier of the committee. This is used to distinguish different committees within the same epoch or state. * `slot` — the slot number at which the committee is assigned duties. Slots are discrete time intervals in the Beacon Chain when various operations occur. * `validators` — a list of validator indices that are members of this committee. These indices identify the specific validators assigned to the committee's duties. The `eth/v1/beacon/states/{state_id}/committees` method is vital for validators and network operators in the Ethereum ecosystem. It provides essential insights into how validators are grouped into committees and assigned duties, which is fundamental to the network's operation and security. # Deposit contract Source: https://docs.chainstack.com/reference/getconfigdepositcontract openapi/ethereum_beacon_chain_api/configuration_info/deposit_contract.json GET /config/deposit_contract The /eth/v1/config/deposit_contract method provides crucial information about the Ethereum PoS deposit contract. Chainstack via Chainstack. The `/eth/v1/config/deposit_contract` method provides crucial information about the Ethereum PoS deposit contract. The deposit contract is a key component of the Ethereum staking process, acting as the entry point for users who wish to become validators on the Beacon Chain. This contract handles the deposits of ETH required for staking, ensuring the security and validity of the staking process. Developers and users can utilize this method to access important details about the deposit contract, aiding them in participating in the staking process and ensuring their interactions are aligned with the current network protocol. ## Parameters * `none` ## Response * `object` — deposit contract information. * `address` — the Ethereum address of the deposit contract. This is where users send their ETH to participate in staking. * `chain_id` — the identifier of the blockchain where the deposit contract resides. This helps users ensure they are interacting with the correct contract on the correct network. * `minimum_deposit` — the minimum amount of ETH required for a deposit into the staking contract. This is important for users to know to meet the staking requirements. * `maximum_deposit` — the maximum amount of ETH that can be deposited in a single transaction. This helps users understand the limits of their staking investments. * `deposit_cli_version` — the version of the official deposit command-line interface (CLI) recommended for interacting with the deposit contract. This ensures users are using the correct tooling to engage with the contract. By providing direct access to deposit contract details, the `/eth/v1/config/deposit_contract` method serves as an essential resource for anyone looking to participate in Ethereum staking and for developers building applications or tools around the Ethereum staking ecosystem. # Fork schedule Source: https://docs.chainstack.com/reference/getconfigforkschedule openapi/ethereum_beacon_chain_api/configuration_info/fork_schedule.json GET /config/fork_schedule The /eth/v1/config/fork_schedule method offers information about the scheduled and historical forks of the Ethereum Beacon Chain. On Chainstack. The `/eth/v1/config/fork_schedule` method offers information about the scheduled and historical forks of the Ethereum Beacon Chain. Forks in the context of blockchain represent significant updates or changes in the protocol, impacting how the network operates. This method provides vital details about these forks, including their timing and specific changes. Developers and network participants can utilize this method to stay informed about past and upcoming forks, ensuring their systems remain compatible and up-to-date with the Ethereum Beacon Chain's evolving protocol. ## Parameters * none ## Response * `array` — list of forks. * Each `object` in the array represents a fork, containing: * `fork_name` — the name of the fork, providing a unique identifier for each network upgrade or change. * `epoch` — the epoch number at which the fork occurred or is scheduled to occur. In the context of the Beacon Chain, an epoch is a specific period in time marked by a series of slots, where new blocks are proposed and validated. * `fork_version` — the version of the Ethereum protocol that was introduced or will be introduced with this fork. Understanding these details allows users and developers to prepare for future changes and understand the historical context of the network's evolution. # Node configuration Source: https://docs.chainstack.com/reference/getconfigspec openapi/ethereum_beacon_chain_api/configuration_info/node_config.json GET /config/spec The /eth/v1/config/spec method is designed to provide a comprehensive overview of the current specification parameters of the Ethereum Beacon Chain. The `/eth/v1/config/spec` method is designed to provide a comprehensive overview of the current specification parameters of the Ethereum Beacon Chain. These parameters define the core rules and configurations that govern the behavior and operations of the network. Access to these specifications is crucial for developers and network participants for aligning their operations and understanding the fundamental mechanisms of the Beacon Chain. Utilizing this method enables developers and network operators to ensure that their systems and applications are compliant with the current specifications of the Ethereum Beacon Chain, thereby maintaining compatibility and optimal performance. ## Parameters * `none` ## Response * `object` — specification information. * `spec_version` — the version of the current Ethereum Beacon Chain specification. This helps clients and developers identify the set of rules and protocols they need to adhere to. * `constants` — a collection of the key constants used in the network's operation, such as the max number of validators, shard count, and other critical numerical values that define the network's structure and limits. * `preset_base` — indicates the base preset of the specifications, providing a foundational configuration upon which the network operates. * `config_name` — the name of the active configuration, which can be indicative of the network's operational phase or specific updates it has undergone. * `forks` — a list of all forks (both past and planned) included in the current specification, providing insight into the network's evolutionary path and future trajectory. By providing detailed information about the specifications of the Ethereum Beacon Chain, the `/eth/v1/config/spec` method serves as an essential tool for anyone looking to interact with the network effectively and with full awareness of its operational guidelines. # Debug beacon chain heads Source: https://docs.chainstack.com/reference/getdebugbeaconheadsv2 openapi/ethereum_beacon_chain_api/debug/getDebugBeaconHeadsV2.json GET /beacon/2f6d649e68c2f861fecd5b8a9e35139e/eth/v2/debug/beacon/heads The /eth/v2/debug/beacon/heads method is a debug API endpoint in the Ethereum Beacon Chain that retrieves all possible chain heads. The `/eth/v2/debug/beacon/heads` method is a debug API endpoint in the Ethereum Beacon Chain that retrieves all possible chain heads, which are the leaves of the fork choice tree. This endpoint is crucial for understanding the current state of fork choice in the beacon chain, showing all potential chain tips that the node is currently tracking and considering as possible canonical chains. ## Parameters This endpoint does not require any parameters. ## Response The response provides an array of all current chain heads: * `data` — array of chain head objects, each containing: * `root` — the block root (32-byte hex string) uniquely identifying this chain head * `slot` — the slot number of this chain head * `execution_optimistic` — boolean indicating whether this head references an unverified execution payload ## Usage notes The `/eth/v2/debug/beacon/heads` method provides critical insight into the fork choice mechanism of the beacon chain. In a healthy network, there is typically one primary head representing the canonical chain, but during network partitions, high latency periods, or chain reorganizations, multiple heads may exist temporarily. This endpoint is particularly useful for: * **Network monitoring** — detecting potential chain splits or forks * **Validator operations** — understanding which chain heads are being considered for block production * **Debugging consensus issues** — identifying when and why multiple chain heads exist * **Fork choice analysis** — studying how the beacon chain resolves competing chains Multiple heads can occur naturally during normal operation due to: * Network latency causing temporary disagreement between nodes * Competing blocks being produced for the same slot * Chain reorganizations as the network converges on the canonical chain The `execution_optimistic` flag is important for understanding whether a head has been fully verified with the execution layer. Optimistic heads may be invalidated if the execution payload is later found to be invalid. This is a debug endpoint and may not be available on all beacon nodes. It provides a real-time view of the fork choice state and is essential for advanced monitoring and debugging of consensus layer behavior. # Debug beacon state by state_id Source: https://docs.chainstack.com/reference/getdebugbeaconstatev2 openapi/ethereum_beacon_chain_api/debug/getDebugBeaconStateV2.json GET /beacon/2f6d649e68c2f861fecd5b8a9e35139e/eth/v2/debug/beacon/states/{state_id} Reference docs for the Debug beacon state by state_id JSON-RPC method on the Chainstack blockchain, available via Chainstack JSON-RPC nodes. The `/eth/v2/debug/beacon/states/{state_id}` method is a debug API endpoint in the Ethereum Beacon Chain that provides the complete BeaconState object for a given state identifier. This endpoint is essential for developers, validators, and network engineers who need to inspect the full state of the beacon chain for debugging, analysis, or monitoring purposes. The BeaconState contains comprehensive information about all validators, their balances, attestations, and other critical chain state data. ## Parameters * `{state_id}`: This parameter specifies the identifier of the beacon state to retrieve. The state identifier can be: * `head` — the canonical head in the node's view (most recent state) * `genesis` — the initial state at chain genesis * `finalized` — the most recent finalized state * `justified` — the most recent justified state * A specific slot number (e.g., `1000`) * A hex-encoded state root with 0x prefix (e.g., `0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2`) ## Response The response includes comprehensive state information: * `version` — the consensus version of the state (phase0, altair, bellatrix, capella, deneb, or electra) * `execution_optimistic` — boolean indicating if the response references an unverified execution payload * `finalized` — boolean indicating if the response references the finalized history of the chain * `data` — the complete BeaconState object containing: * `genesis_time` — Unix timestamp of chain genesis * `genesis_validators_root` — root hash of genesis validators * `slot` — current slot number * `fork` — fork version information * `latest_block_header` — most recent block header * `block_roots` — array of recent block roots * `state_roots` — array of recent state roots * `validators` — complete array of all validators with their properties * `balances` — array of all validator balances * `eth1_data` — Eth1 chain reference data * `eth1_data_votes` — pending Eth1 data votes * `eth1_deposit_index` — current deposit index from Eth1 * `previous_justified_checkpoint` — previous justified checkpoint * `current_justified_checkpoint` — current justified checkpoint * `finalized_checkpoint` — most recent finalized checkpoint ## Usage notes The `/eth/v2/debug/beacon/states/{state_id}` method is a powerful debugging tool that provides complete visibility into the beacon chain state. This endpoint returns extensive data and should be used judiciously as responses can be very large, especially for mainnet states with hundreds of thousands of validators. The endpoint supports both JSON and SSZ (Simple Serialize) response formats. To receive SSZ-encoded data, set the `Accept` header to `application/octet-stream`. SSZ format is more efficient for large state transfers. This is a debug endpoint and may not be available on all beacon nodes. It requires significant computational resources to serialize the complete state, so response times may be longer than standard API calls. # Debug fork choice Source: https://docs.chainstack.com/reference/getdebugforkchoice openapi/ethereum_beacon_chain_api/debug/getDebugForkChoice.json GET /beacon/2f6d649e68c2f861fecd5b8a9e35139e/eth/v1/debug/fork_choice The /eth/v1/debug/fork_choice method is a debug API endpoint in the Ethereum Beacon Chain that retrieves the complete fork choice context. The `/eth/v1/debug/fork_choice` method is a debug API endpoint in the Ethereum Beacon Chain that retrieves the complete fork choice context, including justified and finalized checkpoints and the entire fork choice tree with all nodes and their weights. This endpoint provides deep insight into the consensus mechanism's decision-making process for determining the canonical chain. ## Parameters This endpoint does not require any parameters. ## Response The response provides comprehensive fork choice data: * `justified_checkpoint` — the current justified checkpoint containing: * `epoch` — the epoch number of the justified checkpoint * `root` — the block root of the justified checkpoint * `finalized_checkpoint` — the current finalized checkpoint containing: * `epoch` — the epoch number of the finalized checkpoint * `root` — the block root of the finalized checkpoint * `fork_choice_nodes` — array of all nodes in the fork choice tree, each containing: * `slot` — the slot number of this block * `block_root` — the merkle root of this beacon block * `parent_root` — the merkle root of the parent beacon block * `justified_epoch` — the justified epoch from this block's perspective * `finalized_epoch` — the finalized epoch from this block's perspective * `weight` — the cumulative weight/score of this block in the fork choice algorithm * `validity` — the validity status (`valid`, `invalid`, or `optimistic`) * `execution_block_hash` — the execution layer block hash * `extra_data` — optional client-specific additional data * `extra_data` — optional additional data at the response level (client-specific) ## Usage notes The `/eth/v1/debug/fork_choice` method is an advanced debugging tool that exposes the internal state of the fork choice algorithm, which is the heart of Ethereum's consensus mechanism. This endpoint is invaluable for understanding how the beacon chain determines the canonical chain among potentially competing forks. Key concepts for understanding the response: * **Fork choice tree** — represents all known blocks and their relationships, forming a tree structure where each node is a block * **Weight** — indicates the cumulative attestation support for each block; higher weight means more validators support this chain * **Checkpoints** — justified and finalized checkpoints represent consensus milestones that cannot be reverted * **Validity states**: * `valid` — block has been fully validated * `invalid` — block failed validation * `optimistic` — block assumed valid but not yet verified by execution layer This endpoint is particularly useful for: * **Consensus debugging** — understanding why certain blocks are chosen as canonical * **Network analysis** — identifying competing forks and their relative support * **Performance monitoring** — tracking the efficiency of finalization * **Research** — studying fork choice behavior under various network conditions * **Validator operations** — understanding attestation targets and block production decisions The fork choice algorithm uses the LMD-GHOST (Latest Message Driven - Greediest Heaviest Observed SubTree) protocol, which selects the chain with the most cumulative attestation weight while respecting finality rules. This is a debug endpoint and may not be available on all beacon nodes. The data structure can be large on networks with many competing forks or during periods of network instability. # eth_getFilterChanges | Polygon Source: https://docs.chainstack.com/reference/getfilterchanges openapi/polygon_node_api/filter_handling/eth_getFilterChanges.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Polygon API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Filters can be created using one of the following methods: * [eth\_newFilter](/reference/newfilter) * [eth\_newPendingTransactionFilter](/reference/polygon-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/polygon-newblockfilter) After creating a filter using one of the available methods, the resulting filter ID can be used to fetch changes by calling the `eth_getFilterChanges` method. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter | Polygon](/reference/newfilter) > * [eth\_newPendingTransactionFilter](/reference/polygon-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/polygon-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `string` — the filter ID of the filter for which you want to retrieve the changes ## Response * `array` — an array that represents the changes that have occurred on the blockchain since the last time the filter was polled: * For filters created with `eth_newBlockFilter`: * `blockHash` — the hashes of the new blocks since the last time the filter was polled. * For filters created with `eth_newPendingTransactionFilter`: * `transactionHash` — the hashes identifying new pending transactions since the last time the filter was polled. * For filters created with `eth_newFilter`, the following event logs: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getFilterChanges` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/polygon-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getFilterChanges(filter) { try { const changes = await provider.send('eth_getFilterChanges', [filter]); console.log(changes); // Log the with the new data } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' getFilterChanges(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_filtered_events(filter_id): try: filter_changes = web3.eth.get_filter_changes(filter_id) print(filter_changes) # the array of events that match the filter except Exception as e: print(e) filter_id = '0x0' get_filtered_events(filter_id) ``` ## Use case The `eth_getFilterChanges` method can be used to periodically poll a filter to retrieve new data. In the following Python program, using the web3.py library creates a filter using [eth\_newBlockFilter](/reference/polygon-newblockfilter) to poll for new blocks, and uses `eth_getFilterChanges` to poll the filter and retrieve new blocks. Here is the implementation in Python: ```python main.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def create_block_filter(): try: # Create a new block filter block_filter = web3.eth.filter('latest') # Split the string at the space character parts = str(block_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(f'Error creating block filter: {e}') def get_filtered_events(filter_id): try: filter_changes = web3.eth.getFilterChanges(filter_id) return filter_changes # the array of events that match the filter except Exception as e: print(f'Error getting filter changes: {e}') def main(): # Create a block filter block_filter_id = create_block_filter() if block_filter_id is not None: print(f'New block filter ID: {block_filter_id}') else: print('Error creating block filter') return # Loop forever and poll the filter every 10 seconds while True: print('Polling for new blocks...') new_blocks = get_filtered_events(block_filter_id) if new_blocks is not None: print(new_blocks) else: print('Error getting filter changes') time.sleep(10) if __name__ == '__main__': main() ``` Here is the explanation of the code: The `create_block_filter` function creates a new block filter with the `web3.eth.filter('latest')` method, which matches against the latest block. The resulting filter object is converted to a string, split into parts using the `split` method and the filter ID is extracted from the second part using array indexing. The filter ID is then returned by the function. The `get_filtered_events` function uses the `eth_getFilterChanges` method to retrieve the new blocks. The function takes a single parameter, `filter_id`, which is a string that represents the ID of the filter to be polled, the filter created by the `create_block_filter` function in this case. If the filter ID is valid, `get_filtered_events` calls `web3.eth.getFilterChanges(filter_id)` to retrieve the array of events that match the filter criteria. This array is then returned by the function, which is then printed to the console in the `main` function. The `main` function encapsulates the program logic, calling `create_block_filter` to create a new block filter and print the resulting filter ID to the console. The function then enters an infinite loop, calling `get_filtered_events` every 10 seconds to retrieve the changes to the blockchain that match the filter criteria. If the changes are not `None`, they are printed to the console. An error message is printed to the console if an error occurs while creating the block filter or retrieving the filter changes. # eth_getFilterChanges | Cronos Source: https://docs.chainstack.com/reference/getfilterchanges-1 openapi/cronos_node_api/eth_getFilterChanges.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Cronos API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Filters can be created using one of the following methods: * [eth\_newFilter](/reference/cronos-newfilter) * [eth\_newPendingTransactionFilter](/reference/cronos-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/cronos-newblockfilter) After creating a filter using one of the available methods, the resulting filter ID can be used to fetch changes by calling the `eth_getFilterChanges` method. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/cronos-newfilter) > * [eth\_newPendingTransactionFilter](/reference/cronos-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/cronos-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `string` — the filter ID of the filter for which you want to retrieve the changes ## Response * `array` — an array that represents the changes that have occurred on the blockchain since the last time the filter was polled: * For filters created with `eth_newBlockFilter`: * `blockHash` — the hashes of the new blocks since the last time the filter was polled. * For filters created with `eth_newPendingTransactionFilter`: * `transactionHash` — the hashes identifying new pending transactions since the last time the filter was polled. * For filters created with `eth_newFilter`, the following event logs: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getFilterChanges` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/ethereum-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getFilterChanges(filter) { try { const changes = await provider.send('eth_getFilterChanges', [filter]); console.log(changes); // Log the with the new data } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' getFilterChanges(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_filtered_events(filter_id): try: filter_changes = web3.eth.get_filter_changes(filter_id) print(filter_changes) # the array of events that match the filter except Exception as e: print(e) filter_id = '0x0' get_filtered_events(filter_id) ``` ## Use case The `eth_getFilterChanges` method can poll a filter to periodically retrieve new data. You can, for example, create a blocks filter using [eth\_newBlockFilter](/reference/cronos-newblockfilter) and periodically poll it to get new blocks. # Finality checkpoints for a given state Source: https://docs.chainstack.com/reference/getfinalitycheckpoints openapi/ethereum_beacon_chain_api/state/getFinalityCheckpoints.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/states/{state_id}/finality_checkpoints The /eth/v1/beacon/states/{state_id}/finality_checkpoints endpoint retrieves the finality checkpoints of a specified Beacon Chain state. The `/eth/v1/beacon/states/{state_id}/finality_checkpoints` endpoint retrieves the finality checkpoints of a specified Beacon Chain state. These checkpoints are critical in understanding the finality status of various epochs in the Ethereum PoS mechanism. ## Parameters * `state_id` — the state identifier for which finality checkpoint information is requested. Acceptable values for `state_id` include: * `head` — represents the current head of the chain from the node's perspective. * `genesis` — the genesis state of the Beacon Chain. * `finalized` — the most recent state that has been finalized and is unlikely to undergo any further changes. * `justified` — the most recent state that has been justified, which is a stage preceding finalization. * `` — a specific time slot in the Ethereum protocol. * `` — the specific state root, encoded in hexadecimal. ## Response The response includes an object with the following properties: * `data` — an object containing finality checkpoint information for the specified state: * `previous_justified` — an object representing the last justified checkpoint before the current epoch. It contains: * `epoch` — the epoch number of this justified checkpoint. * `root` — the block root of this justified checkpoint. * `current_justified` — an object representing the current justified checkpoint. It contains: * `epoch` — the epoch number of the current justified checkpoint. * `root` — the block root of the current justified checkpoint. * `finalized` — an object representing the current finalized checkpoint. It contains: * `epoch` — the epoch number of the finalized checkpoint. * `root` — the block root of the finalized checkpoint. # Fork version by state ID Source: https://docs.chainstack.com/reference/getforkinformation GET https://ethereum-mainnet.core.chainstack.com/beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/states/{state_id}/fork Query the Beacon Chain fork version and epoch for any state using the eth/v1/beacon/states/{state_id}/fork endpoint to track consensus layer upgrades. `get https\://ethereum-mainnet.core.chainstack.com/beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/states/\{state\_id}/fork ` ## Parameters * `state_id` — the identifier for the state for which fork information is being requested. The `state_id` can be: * `head` — represents the latest canonical state in the node's view. * `genesis` — refers to the state at the genesis or the start of the Beacon Chain. * `finalized` — the state that is finalized and accepted by the network with minimal likelihood of reorganization. * `justified` — a state that has recently achieved justification, which is a precursor to finalization in the Ethereum 2.0 proof of stake mechanism. * `` — specific slot number in the Ethereum 2.0 protocol timeline. * `` — the specific state root of interest, encoded in hexadecimal format. ### Path Params **state\_id** `string` *required* (default: head) State identifier which can be a slot number, an epoch number, a block root, or special values like 'genesis' or 'head'. ## Response The response is an object containing the following fields: * `execution_optimistic` — a boolean indicating if the response is based on an unverified execution payload. If `true`implies that the payload might be invalidated later. The absence of this field implies a default value of `false`. * `finalized` — a boolean indicating if the response pertains to the finalized history of the chain. If absent, it suggests the need for additional calls to determine the epoch’s relation to the finalized checkpoint. * `data` — an object containing: * `previous_version` — a hexadecimal string representing the fork version of the state before the current fork. * `current_version` — a hexadecimal string indicating the fork version of the current state. * `epoch` — the epoch number at which the current version was adopted. ### 200 Successful response with fork information **epoch** `string` The epoch number at which the current version was applied. **previous\_version** `string` The previous fork version. **current\_version** `string` The current fork version. ### 400 Invalid state identifier value **code** `integer` **message** `string` **stacktraces** `string[]` ```json 200 theme={"system"} { "epoch": "string", "previous_version": "string", "current_version": "string" } ``` ```json 400 theme={"system"} { "code": 0, "message": "string", "stacktraces": [ "string" ] } ``` # getLeaderSchedule | Solana Source: https://docs.chainstack.com/reference/getleaderschedule openapi/solana_node_api/getLeaderSchedule.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getLeaderSchedule method retrieves the leader schedule for an epoch. Available on Solana via Chainstack JSON-RPC nodes. # Solana `getLeaderSchedule` method The Solana `getLeaderSchedule` method retrieves the leader schedule for an epoch. This method provides the schedule of when each validator is expected to be the leader and produce blocks. It is useful for predicting block production and for network participants to prepare for their slot as a leader. ## Parameters * `epoch` — (optional) the epoch for which the leader schedule should be returned. If not provided, the leader schedule for the current epoch is returned. * `commitment` — (optional) the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. ## Response * `value` — the leader schedule: * A mapping of public keys to an array of slot indices, indicating when each validator is expected to be the leader. ## Use case A practical use case for `getLeaderSchedule` is for validators to know when they are expected to produce blocks and for participants to predict block production on the network. # eth_getLogs | Polygon Source: https://docs.chainstack.com/reference/getlogs openapi/polygon_node_api/logs_and_events/eth_getLogs.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns an array of all logs that match a given filter object. Available on Polygon via Chainstack JSON-RPC nodes. Polygon API method that returns an array of all logs that match a given filter object. Logs are records of smart contract-generated events on the network and contain valuable information about transactions and smart contract interactions. Some common use cases for `eth_getLogs` include monitoring smart contract events, tracking token transfers, and analyzing blockchain data. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block > * `earliest` — the earliest available or genesis block > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `array` — an array of log objects that match the specified filter or an empty array if there have been no new events since the last poll: * `address` — the contract address from which the event originated * `topics` — an array of 32-byte data fields containing indexed event parameters * `data` — the non-indexed data that was emitted along with the event * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getLogs` code examples The following examples retrieve the logs of the Transfer event from the [WMATIC](https://polygonscan.com/address/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270#code) smart contract on Polygon. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getLogs = async () => { const latestBlock = await provider.getBlockNumber(); const filter = { fromBlock: latestBlock - 10, toBlock: 'latest', address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const logs = await provider.getLogs(filter); console.log(logs); }; getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) latest_block = web3.eth.block_number filter = { 'fromBlock': latest_block - 10, 'toBlock': 'latest', 'address': '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } logs = web3.eth.get_logs(filter) print(logs) ``` > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about the `eth_getLogs` method. ## Block range limitations While `eth_getLogs` is a powerful tool, it's crucial to understand its limitations, particularly when working with different EVM-compatible chains, as these networks often have different constraints. The `eth_getLogs` method allows you to select a range of blocks to get events from and is important to exercise proper management. `eth_getLogs` block range is capped by your [subscription plan](/docs/limits#evm-range-limits). On high-activity chains or contracts, query smaller ranges and paginate to avoid oversized responses or timeouts. ## Use case One use case for `eth_getLogs` is to retrieve the transfer events for a specific Token ID from an ERC-721 smart contract. ERC-721 is a token standard for non-fungible tokens (NFTs), and each token transfer is recorded as an event on the blockchain. By retrieving these events, developers can track the movement of NFTs and build applications that interact with them. The following program uses `eth_getLogs` and ethers.js to track the transfers of the token number '5975' from the [PolygonPunks](https://polygonscan.com/token/0x9498274B8C82B4a3127D67839F2127F2Ae9753f4) collection on Polygon. This will retrieve the logs and display the address that initiated the transfer and the block number where it occurred. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTokenTransferLogs(contractAddress, tokenId) { try { const tokenIdHex = ethers.toBeHex(tokenId); const tokenIdTopic = ethers.zeroPadValue(tokenIdHex, 32); const filter = { fromBlock: 'earliest', toBlock: 'latest', address: contractAddress, topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', null, null, tokenIdTopic] }; const logs = await provider.getLogs(filter); const transferLogs = logs.map((log) => { const topics = log.topics; const fromAddress = topics[1]; const formattedFromAddress = `0x${fromAddress.slice(26)}`; if (!ethers.isAddress(formattedFromAddress)) { throw new Error(`${formattedFromAddress} is an invalid address`); } const blockNumber = log.blockNumber; // Display a summary console.log(`Address ${formattedFromAddress} transferred the token ${tokenId} in block ${blockNumber}.`); return { fromAddress: formattedFromAddress, tokenId: tokenId, blockNumber: blockNumber }; }); return transferLogs; } catch (error) { throw new Error(`Error getting transfer logs: ${error.message}`); } } async function main() { const contractAddress = '0x9498274b8c82b4a3127d67839f2127f2ae9753f4'; const tokenId = '5975'; try { const transferLogs = await getTokenTransferLogs(contractAddress, tokenId); console.log(transferLogs); } catch (error) { console.error(error.message); } } main(); ``` This code defines an async function called `getTokenTransferLogs` which takes a `contractAddress` and a `tokenId` as input parameters. Within the function, the `tokenId` is converted to hexadecimal format and then used to create a topic for a filter to get past logs for a specific token transfer. The `provider.getLogs` function is an API that returns logs that match a certain filter. Once the logs have been retrieved, the function loops through each log and extracts the relevant information, such as the sender's address and the block number. It also checks whether the sender's address is a valid address; this is in case the parsing logic fails. For each valid transfer log, the function logs a summary to the console and creates a transfer log object that includes the `fromAddress`, `tokenId`, and `blockNumber` properties. The function returns an array of transfer log objects, and an exception is thrown if there are any errors. The `main` function calls `getTokenTransferLogs` with a specific `contractAddress` and `tokenId`, and then logs the array of transfer log objects to the console. If there are any errors, an error message is logged to the console instead. # eth_getProof | Ethereum Source: https://docs.chainstack.com/reference/getproof openapi/ethereum_node_api/accounts_info/eth_getProof.json POST /43bcb3d5ceb917bab4353db33b087b37 Ethereum API method that returns a Merkle proof for a specific account, contract storage, or both for a given block. Ethereum via Chainstack. Ethereum API method that returns a Merkle proof for a specific account, contract storage, or both for a given block. Merkle proofs enable users to verify the existence and authenticity of data within a Merkle trie, a data structure that helps in optimizing and securing data retrieval in blockchains. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > On Global Nodes, Ethereum Mainnet and Sepolia return proofs for the latest 10,000 blocks. An older block returns `-32602` `distance to target block exceeds maximum proof window`. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). ## Parameters * `address` — Ethereum address of the account for which the proof is requested. * `array` — the array of 32-byte storage keys that need to be proven and included. See [eth\_getStorageAt](/reference/ethereum-getstorageat). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `balance` — represents the account balance. Refer to [eth\_getBalance](/reference/ethereum-getbalance). * `codeHash` — indicates the code hash associated with the account. For a simple account without any code, the value will be `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. * `nonce` — refers to the account's nonce. See [eth\_getTransactionCount](/reference/ethereum-gettransactioncount). * `storageHash` — represents the SHA3 hash of the StorageRoot. All storage data will provide a Merkle proof that starts with this `rootHash`. This hash serves as the starting point for obtaining a Merkle proof for all storage entries linked to the account. * `accountProof` — an array of RLP-serialized Merkle trie nodes, starting with the `stateRoot` node and following the path of the SHA3 (address) as the key, which can be used to verify the existence and authenticity of data within the trie. * `storageProof` — contains the requested storage entries in an array, with each entry being an object that includes the following properties: * `key` — the requested storage key * `value`— the value associated with the storage key * `proof` — an array of RLP-serialized Merkle trie nodes, starting with the `storageHash` node and following the path of the SHA3 (key), which can be used to verify the existence and authenticity of data within the trie. ## `eth_getProof` code examples > [!NOTE] > > Learn more about the `ChainstackProvider` in `ethers.js`: [ethers ChainstackProvider Documentation](/reference/ethersjs-chainstackprovider). The following examples retrieve the Merkle proof for the USDT contract. ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); async function getProof() { // Get the storage slot hash (value = keccak256(storage address + slot index)) const storageSlotHash = "0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c"; // Define the contract address for USDT const contractAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"; // Get the Merkle proof of USDT's smart contract using a custom JSON-RPC call const proof = await chainstack.send("eth_getProof", [ contractAddress, [storageSlotHash], "latest", ]); const block = await chainstack.send("eth_getBlockByNumber", [ "latest", false, ]); // The state root is crucial for Merkle verification const stateRoot = block.stateRoot; console.log("state root:", stateRoot); console.log(proof); } getProof(); ``` ```python web3.py theme={"system"} from web3 import Web3 httpUrl = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(httpUrl)) proof = web3.eth.get_proof('0xdAC17F958D2ee523a2206206994597C13D831ec7', ["0x9c7fca54b386399991ce2d6f6fbfc3879e4204c469d179ec0bba12523ed3d44c"], "latest") print(proof) ``` > [!NOTE] > > Read [Deep dive into Merkle proofs and eth\_getProof](/docs/deep-dive-into-merkle-proofs-and-eth-getproof-ethereum-rpc-method) to learn more about the Merkle trie and how to use `eth_getProof`. ## Use case A use case for the `eth_getProof` method can be when a decentralized application (DApp) interacts with a smart contract on the Ethereum blockchain. The DApp needs to verify the state of the contract's storage variables without downloading the entire blockchain data. By using `eth_getProof`, the DApp can request Merkle proof for specific storage slots in the contract. This proof can then be used to cryptographically verify the authenticity and accuracy of the storage data, ensuring the DApp is working with the correct contract state. # Proposer duties Source: https://docs.chainstack.com/reference/getproposerduties openapi/ethereum_beacon_chain_api/validatiors_info/proposer_duties.json GET /validator/duties/proposer/{epoch} The /eth/v1/validator/duties/proposer/{epoch} method is a crucial API endpoint in the Ethereum Beacon Chain, specifically designed for validators. > [!NOTE] > > **Get the non-pruned state** > > To get a correct response from the interactive API example, make sure you provide the latest or close to latest epoch. For the latest epoch number, see [https://beaconcha.in/](https://beaconcha.in/) The `/eth/v1/validator/duties/proposer/{epoch}` method is a crucial API endpoint in the Ethereum Beacon Chain, specifically designed for validators. It provides information about which validators are scheduled to propose blocks during a specified epoch. An epoch in Ethereum is a set period during which certain blockchain activities, including block proposals, take place. This method is particularly important for validators to understand their responsibilities in the upcoming epochs and prepare for block proposal duties accordingly. ### Parameters * `{epoch}`: The specific epoch for which the proposer duties are being queried. This is a required parameter and must be specified as a part of the URL. ### Response * `array` — list of proposer duty objects. * Each `object` in the array represents a proposer duty for a specific validator, containing: * `pubkey` — the public key of the validator who is assigned the proposer duty. This key uniquely identifies the validator within the Beacon Chain network. * `validator_index` — the index of the validator within the validator registry of the Beacon Chain. * `slot` — the specific slot during the epoch at which the validator is expected to propose a block. Slots are subdivisions of an epoch, and each slot provides an opportunity for a block to be added to the Beacon Chain. The `/eth/v1/validator/duties/proposer/{epoch}` method is a vital tool for validators to effectively participate in the block proposal process, which is a key component of the consensus mechanism in Ethereum. By providing clear and timely information about their upcoming duties, validators can ensure the smooth operation and ongoing security of the Beacon Chain. # Root of a given state Source: https://docs.chainstack.com/reference/getstateroot openapi/ethereum_beacon_chain_api/state/getStateRoot.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/states/{state_id}/root The /eth/v1/beacon/states/{state_id}/root method calculates the HashTreeRoot for a state with a given stateId. Chainstack via Chainstack. The `/eth/v1/beacon/states/{state_id}/root` method calculates the HashTreeRoot for a state with a given `stateId`. The same value will be returned if `stateId` is a root. Developers can use this method to compute the HashTreeRoot of a given state, which is useful in various Ethereum operations. ## Parameters * `state_id` — the identifier of the state. This can be one of the following: * `head` — the canonical head in the node's view. * `genesis` — the very first or initial state of the Beacon Chain. * `finalized` — the latest state finalized, meaning the network has accepted it, and a reorganization is extremely unlikely. * `justified` — the recent state that has been justified is the first step before finalization in the Ethereum 2.0 Proof of Stake consensus mechanism. This means that a sufficient number of validators have agreed on this state. * `` — a slot is a period of time in the Ethereum protocol during which a block can be proposed. * `` - The state root encoded in hexadecimal. ## Response * `object` — an object with the following properties: * `execution_optimistic` — Indicates whether the response references an unverified execution payload. If true, the optimistic information may be invalidated at a later time. If this field is not present, the value should be assumed to be false. * `finalized` — Indicates whether the response references the finalized history of the chain, as determined by the fork choice. If this field is not present, additional calls are necessary to compare the epoch of the requested information with the finalized checkpoint. * `data` — Contains an object with one property: * `root` — The HashTreeRoot of the BeaconState object. * `format` — The format of the root, which is hexadecimal. * `description` — HashTreeRoot of the BeaconState object. # eth_getStorageAt | Polygon Source: https://docs.chainstack.com/reference/getstorageat openapi/polygon_node_api/account_info/eth_getStorageAt.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the data stored at a specific storage slot within a smart contract. Use it on Polygon via Chainstack. Polygon API method that returns the data stored at a specific storage slot within a smart contract. It can help developers to read a smart contract's internal state, like user data or balances, to inform decisions and build more advanced applications that interact with smart contracts on the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity` — the index of the storage position to retrieve the data from * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ## Response `data` — the data stored in the storage slot. Returned as a hexadecimal string. ## `eth_getStorageAt` code examples The following example returns the storage value at slot `0` from the [Chainlink VRFCoordinatorV2](https://polygonscan.com/address/0xAE975071Be8F8eE67addBC1A82488F1C24858067#code) on the Polygon mainnet. In this case, this is the address owning the smart contract. This method can retrieve the different storage values in time by querying different blocks in the past. Note that to query a state in the past, you need an [archive node](https://chainstack.com/evm-nodes-a-dive-into-the-full-vs-archive-mode). > [!NOTE] > > curl needs a `HEX String` starting with `0x` to identify the block if you want to use a hex integer block number as a parameter.\ > For example, block number `14000000` will be `0xD59F80`. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getstorage = async (address, slot, block) => { const storage = await provider.send("eth_getStorageAt", [address, slot, block]); console.log(storage); }; getstorage("0xAE975071Be8F8eE67addBC1A82488F1C24858067", "0", "latest" ) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" storage = web3.eth.get_storage_at("0xAE975071Be8F8eE67addBC1A82488F1C24858067", 0, "latest") print(web3.to_hex(storage)) ``` ## Use case One use case for `eth_getStorageAt` is analyzing how the value stored at a specific storage slot of a smart contract changes over time. For example, a developer may want to track the change in a specific variable of a smart contract, such as the total supply of a token, and perform some analysis on it. Here's an example implementation of this use case using ethers.js. The following program analyzes the [Wrapped Ether](https://polygonscan.com/token/0x7ceb23fd6bc0add59e62ac25578270cff1b9f619#code) smart contract on the Polygon chain to track how the total supply changes over time. The storage slot matches the position of the variable in the smart contract; the following code snippet shows the top of the WETH smart contract where the variables used in this program are declared. ```sol ERC20.sol theme={"system"} contract ERC20 is Context, IERC20 { using SafeMath for uint256; using Address for address; // Variables declared mapping (address => uint256) private _balances; // Storage slot 0x0 mapping (address => mapping (address => uint256)) private _allowances; // Storage slot 0x1 uint256 private _totalSupply; // Storage slot 0x2 string private _name; // Storage slot 0x3 string private _symbol; // Storage slot 0x4 uint8 private _decimals; // Storage slot 0x5 . . . ``` Here's an example implementation of this use case using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // You need an archive node is going more than 127 blocks in the past. async function getStorageValueOverTime() { // The address of the WETH smart contract const wethAddress = '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619'; // The index of the storage slots where the total supply and name data are stored const totalSupplySlot = 2; const nameSlot = 3; // The range of blocks to analyze const startBlock = 16000000; const endBlock = await provider.getBlockNumber(); for (let blockNumber = startBlock; blockNumber <= endBlock; blockNumber++) { // Retrieve the name and total supply from the smart contract storage at each block const [name, supply] = await Promise.all([ provider.getStorage(wethAddress, nameSlot, blockNumber), provider.getStorage(wethAddress, totalSupplySlot, blockNumber) ]); // Decode the name and total supply from hexadecimal format to their original values const decodedName = ethers.toUtf8String(name); const decodedSupply = BigInt(supply); // Convert the total supply from wei to ether and round to 4 decimal places const convertedSupply = ethers.formatEther(decodedSupply); const adjustedSupply = Number(convertedSupply).toFixed(4); // Output the block number, name, and total supply for each block console.log(`Block ${blockNumber}: Name = ${decodedName}, Total supply = ${adjustedSupply}`); } } async function main() { try { await getStorageValueOverTime(); } catch (error) { console.error(error); } } main(); ``` The bulk of the program is the `getStorageValueOverTime` function. The `getStorageValueOverTime` function retrieves and outputs the name and total supply of the WETH smart contract at each block height between a specified start block and the current block height. The function begins by declaring constants for the WETH contract address, the storage slot indices for the name and total supply data, the starting block height, and the current block height. Next, the function enters a `for` loop that iterates over every block between the start block and the current block height. Within the loop, the function retrieves the name and total supply from the smart contract storage at the current block height using the `provider.getStorage` method. The `Promise.all` method is used to retrieve the name and total supply in parallel for improved efficiency. The `ethers.toUtf8String` method is then used to convert the name from hexadecimal format to its original string value. The `BigInt` constructor is used to convert the total supply from hexadecimal format to a `bigint` value, which can be used for mathematical operations. The `ethers.formatEther` method is then used to convert the total supply from wei to ether. Finally, the `Number` and `toFixed` methods are used to round the total supply to four decimal places and output the block number, name, and total supply for each block; note that this is for readability, and you should use the raw wei data to make calculations. Overall, the `getStorageValueOverTime` function provides a way to monitor changes in the state of a smart contract over time, which can be useful for understanding the contract's behavior and performance. # Sync committee contribution Source: https://docs.chainstack.com/reference/getsynccommitteecontribution openapi/ethereum_beacon_chain_api/state/getSyncCommitteeContribution.json GET /validator/sync_committee_contribution Reference docs for the Sync committee contribution JSON-RPC method on the Chainstack blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > The interactive example will return `Slot already finalized` unless you edit the parameters. The `/eth/v1/validator/sync_committee_contribution` method is an important API endpoint in the Ethereum Beacon Chain, focusing on the contributions of validators to the sync committee. The sync committee is a group of validators responsible for contributing to the data necessary for light clients to stay in sync with the Beacon Chain. This endpoint provides details about the contributions made by validators in the sync committee. ## Parameters * `none` ## Response * `object` — sync committee contribution information. * `validator_index` — the index of the validator making the contribution. This identifies the specific validator within the Beacon Chain. * `sync_committee_index` — the index of the sync committee to which the validator belongs. This indicates the validator's position and role within the committee. * `contribution` — the actual contribution made by the validator. This includes: * `slot`: The slot number at which the contribution is targeted. * `beacon_block_root`: The root of the beacon block to which the contribution is related. * `subcommittee_index`: The subcommittee index within the sync committee, denoting the specific subgroup to which the contribution is made. * `aggregation_bits`: A bitfield representing the validators in the subcommittee that have contributed. * `signature`: The signature of the contributing validator, verifying the authenticity and commitment to the contribution. The `/eth/v1/validator/sync_committee_contribution` method is vital for understanding the participation and contributions of validators in the sync committee. It plays a key role in ensuring that light clients can effectively and efficiently synchronize with the Beacon Chain, maintaining the overall health and scalability of the Ethereum network. # Sync committees by state and epoch Source: https://docs.chainstack.com/reference/getsynccommitteesbystateidandepoch openapi/ethereum_beacon_chain_api/state/getSyncCommitteesByStateIdAndEpoch.json GET /beacon/states/{state_id}/sync_committees The /eth/v1/beacon/states/{state_id}/sync_committees method is an integral API endpoint in the Ethereum Beacon Chain. On Chainstack. The `/eth/v1/beacon/states/{state_id}/sync_committees` method is an integral API endpoint in the Ethereum Beacon Chain. It provides detailed information about the current sync committee composition for a given state. Sync committees are groups of validators responsible for contributing to the consensus process and are especially important for the functioning of light clients, which rely on these committees to stay synchronized with the main chain. ## Parameters * `{state_id}`: This parameter specifies the state identifier for which the sync committee information is being requested. It can be a specific slot number, an epoch number, a block root, or special values like 'genesis' or 'head'. ## Response * `object` — sync committee information. * `data` — the structure containing the sync committee details, including: * `validators` — a list of validator indices that are members of the sync committee. These indices identify the validators participating in the committee. * `validator_aggregates` — a list of aggregated sets of validators. Each set represents a group of validators working together within the sync committee. * `pubkeys` — the public keys of the validators in the sync committee, used for cryptographic operations and validator identification. * `aggregate_pubkey` — the combined public key of the entire sync committee. This key represents the collective entity of the committee for cryptographic purposes. The `/eth/v1/beacon/states/{state_id}/sync_committees` method is crucial for understanding the current composition and operation of sync committees within the Ethereum network. This information is vital for validators, developers, and participants who need to interact with or analyze the sync committees, ensuring effective and secure network operation. # BNB Smart Chain API reference: JSON-RPC quickstart Source: https://docs.chainstack.com/reference/getting-started-bnb-chain Use the BNB Smart Chain JSON-RPC API on Chainstack to deploy nodes, send transactions, and query the EVM-compatible PoSA blockchain by Binance. ## What is the BNB Chain? BNB Chain, formerly known as Binance Smart Chain (BSC), is a decentralized and open-source blockchain network developed by Binance, one of the world's largest cryptocurrency exchanges. Launched in 2020, the BNB Chain aims to provide a high-performance and low-cost platform for developers to build decentralized applications (DApps) and deploy smart contracts. The BNB Chain is compatible with the Ethereum Virtual Machine (EVM), which means it can run Ethereum-based DApps and smart contracts. However, it uses a different consensus mechanism called [Proof-of-Staked Authority (PoSA)](/docs/web3-infrastructure-glossary#proof-of-staked-authority-posa). Find useful BNB Chain tools in the [BNB Chain tooling](/docs/bsc-tooling) section. ## What is the BNB Chain API? The BNB Chain API allows developers to communicate with the BNB Chain blockchain to build and interact with decentralized applications. Chainstack BNB Chain nodes provide access to the BNB Chain API, which follows the Ethereum JSON-RPC specification. This allows developers to read data from and send transactions to the BNB Chain blockchain. Applications must implement the JSON-RPC protocol to communicate with a BNB Chain RPC node, enabling remote procedure calls and execution as if they were made locally. ## How to start using the BNB Chain API To use the BNB Chain API, you need access to a BNB Chain RPC node. Follow these steps to sign up on Chainstack, deploy a BNB Chain RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now, you can connect to the BNB Chain blockchain and use the BNB Chain API to build. > [!NOTE] > > **See also** > > [Build better with BNB Chain](https://chainstack.com/build-better-with-bnb-chain/) # Cronos API reference: Crypto.com EVM JSON-RPC quickstart Source: https://docs.chainstack.com/reference/getting-started-cronos Use the Cronos JSON-RPC API on Chainstack to deploy nodes, send transactions, and build on the EVM-compatible Ethermint chain by Crypto.com. ## What is the Cronos Chain? Cronos Chain is a highly scalable, interoperable, and energy-efficient blockchain network designed to support the development and operation of decentralized applications (DApps) and crypto assets. Launched as part of the Crypto.com ecosystem, Cronos aims to bridge the gap between traditional financial markets and decentralized networks. At the heart of Cronos Chain's efficiency is its utilization of the **Ethermint** protocol, which enables full compatibility with Ethereum-based applications while operating under a Proof-of-Authority (PoA) consensus mechanism. This unique blend facilitates faster transactions and significantly reduces the costs associated with smart contract operations. Cronos Chain's compatibility with the Ethereum Virtual Machine (EVM) means developers can port existing Ethereum applications onto the Cronos network. Explore the variety of Cronos Chain tools in the [Cronos Chain tooling](/docs/cronos-tooling) section. ## What is the Cronos Chain API? The Cronos Chain API enables developers to engage with the Cronos blockchain, streamlining the process of managing and developing decentralized applications. Chainstack provides access to Cronos nodes, which adhere to the Ethereum JSON-RPC specification for seamless interaction. This uniformity guarantees developers can query and transact on the Cronos Chain as intuitively as on Ethereum-based systems. Developers need to implement the JSON-RPC protocol to interact with a Cronos Chain RPC node, allowing for remote procedure calls as if they were local functions. ## How to Start Using the Cronos Chain API Connecting to the Cronos Chain API involves establishing a link to a Cronos RPC node. Follow these steps to sign up on Chainstack, deploy a Cronos RPC node, and obtain your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.fantom.foundation/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Completing these steps will equip you with the necessary tools to connect to the Cronos blockchain and start development using the Cronos Chain API. > [!NOTE] > > **Additional Resources** > > [Build better with Cronos Chain](https://chainstack.com/build-better-with-cronos/) # Fantom API reference: Lachesis DAG JSON-RPC quickstart Source: https://docs.chainstack.com/reference/getting-started-fantom Use the Fantom JSON-RPC API on Chainstack to deploy nodes, send transactions, and build on the EVM-compatible Lachesis DAG blockchain. ## What is the Fantom Chain? Fantom Chain is a high-performance, scalable, and secure smart-contract platform designed to overcome the limitations of previous-generation blockchains. Launched in 2019, Fantom aims to provide a decentralized and open-source environment for developers to build highly scalable decentralized applications (DApps) and digital assets. The core of Fantom's innovation lies in its consensus mechanism, the Lachesis Protocol, a variant of Directed Acyclic Graph (DAG) technology. This allows for near-instant transaction confirmation and significantly lowers transaction costs compared to traditional blockchains. Fantom is fully compatible with the Ethereum Virtual Machine (EVM), enabling developers to deploy Ethereum-based DApps and smart contracts on a more scalable network. Discover a range of Fantom Chain tools in the [Fantom Chain tooling](/docs/fantom-tooling) section. ## What is the Fantom Chain API? The Fantom Chain API enables developers to interact with the Fantom Chain blockchain, facilitating the development and management of decentralized applications. Chainstack Fantom Chain nodes offer access to the Fantom Chain API, adhering to the Ethereum JSON-RPC specification. This compatibility ensures developers can query data from and submit transactions to the Fantom Chain as easily as they would with Ethereum-based projects. Applications need to implement the JSON-RPC protocol to communicate with a Fantom Chain RPC node, which allows for remote procedure calls as though they were local. ## How to start using the Fantom Chain API Accessing the Fantom Chain API requires a connection to a Fantom RPC node. Follow these steps to sign up on Chainstack, deploy a Fantom RPC node, and obtain your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.fantom.foundation/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Once you complete these steps, you're ready to connect to the Fantom blockchain and start building with the Fantom Chain API. > [!NOTE] > > ### See also > > [Build better with Fantom Chain](https://chainstack.com/build-better-with-fantom/) # Ronin API reference: Axie Infinity EVM JSON-RPC Source: https://docs.chainstack.com/reference/getting-started-ronin Use the Ronin JSON-RPC API on Chainstack to deploy nodes, send transactions, and build games on the Axie Infinity EVM chain by Sky Mavis. ## What is the Ronin protocol? Ronin is an EVM blockchain crafted for developers building games with player-owned economies. Ronin is conceptualized and implemented by Sky Mavis, the creators of Axie Infinity. Find useful Ronin tools in the [Ronin tooling](/docs/ronin-tooling) section. > [!WARNING] > > ### Saigon testnet L1 to L2 migration > > On February 5, 2026, the Ronin Saigon testnet migrated from L1 to an Ethereum L2 using the OP Stack at block 45,528,550. The chain ID changed from `2021` to `202601`. > > On Chainstack Saigon testnet nodes, blocks 0–45,528,549 are served by the old L1 backend and blocks 45,528,550 onward are served by the new L2 backend. There is no block overlap—range-based methods like `eth_getLogs` cannot span both ranges in a single call. See [eth\_getLogs](/reference/ronin-getlogs) for details. > > The Ronin mainnet is also planned to migrate to an Ethereum L2 in Q1–Q2 2026. The mainnet chain ID will remain `2020`. ## What is the Ronin API The Ronin API allows developers to communicate with the Ronin blockchain to build applications. To read data from and send transactions to the Ronin blockchain, an application must connect to a Ronin RPC node. When communicating with a Ronin RPC node, the Ronin client implements a JSON-RPC specification, a communication protocol allowing one to make remote calls and execute them as if they were made locally. ## How to start using the Ronin API To use the Ronin API, you need access to a Ronin RPC node. Follow these steps to sign up on Chainstack, deploy a Ronin RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the Ronin blockchain and use the Ronin API to build. > [!NOTE] > > ### See also > > [Build better with Ronin](https://chainstack.com/build-better-with-ronin/) # Starknet API reference: Pathfinder JSON-RPC quickstart Source: https://docs.chainstack.com/reference/getting-started-starknet Use the Starknet JSON-RPC API on Chainstack via Pathfinder nodes to query the Ethereum L2 ZK-rollup and run versioned RPC API calls. > [!NOTE] > > **Feeder gateway deprecation on December 11, 2023** > > All the methods listed here are working example counterparts to the [deprecated feeder gateway](https://community.starknet.io/t/feeder-gateway-deprecation/100233). The interactive APIs are done based on the [Pathfinder node](https://github.com/equilibriumco/pathfinder). > > Try out the Pathfinder-based interactive examples in each section and see the respective counterpart at the end of each section. ## Starknet JSON-RPC version endpoints Chainstack Starknet endpoints support sending requests to different JSON-RPC API versions. For versions, see the [Pathfinder GitHub repository](https://github.com/equilibriumco/pathfinder/tree/main#json-rpc-api). To send a request to a specific version, append your Chainstack Starknet endpoint with the version URL suffix. For example, `/rpc/v0_9`. The default `/` path serves version 0.9. > [!NOTE] > > Pathfinder [0.23.0](https://github.com/equilibriumco/pathfinder/releases/tag/v0.23.0) removed JSON-RPC API versions 0.6, 0.7, and 0.8. The supported versions are 0.9 (`/rpc/v0_9`, also the default) and 0.10 (`/rpc/v0_10`). See [Pathfinder RPC version removal 0.23.0](/reference/starknet-pathfinder-0-23-0-rpc-version-removal). Check the following examples. ```Text starknet_getNonce v0.9 theme={"system"} curl --request POST \ --url https://starknet-mainnet.core.chainstack.com/365cf697a3ad6d950b4c4a911e2e4f4d/rpc/v0_9 \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "starknet_getNonce", "params": [ { "block_number": 385940 }, "0x0569b13e8164bc8000c0bbcf4887856516643af123c5bc3b01e229e92f9cfd10" ] } ' ``` ```Text starknet_getNonce v0.10 theme={"system"} curl --request POST \ --url https://starknet-mainnet.core.chainstack.com/365cf697a3ad6d950b4c4a911e2e4f4d/rpc/v0_10 \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "starknet_getNonce", "params": [ { "block_number": 385940 }, "0x0569b13e8164bc8000c0bbcf4887856516643af123c5bc3b01e229e92f9cfd10" ] } ' ``` ## What is the Starknet Starknet is a decentralized, permissionless and scalable zk-rollup, extending the Ethereum blockchain's capabilities. It is designed to enhance scalability, reduce gas fees, and maintain a high level of security. Starknet uses zk-STARKs technology to achieve these goals, providing a scalable and decentralized solution for Layer-2 scaling. Starknet provides a robust and efficient network with cross-chain bridges for seamless interactions. ## What is the Starknet API The StarkNet API allows developers to communicate with the Starknet network. ## How to start using the Starknet API To use the Starknet API, you need access to a Starknet RPC node. Follow these steps to deploy a Starknet RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to Starknet and use the Starknet API to build. # TON API reference: The Open Network JSON-RPC quickstart Source: https://docs.chainstack.com/reference/getting-started-ton Use the TON API on Chainstack to query The Open Network, send transactions, work with jettons and NFTs, and build apps on the high-throughput layer-1. 62 methods across HTTP API v2 and v3, archive on mainnet and testnet, no EVM chain ID. The TON API gives developers programmatic access to The Open Network, a non-EVM layer-1 blockchain. Chainstack serves it through two HTTP API surfaces on a single endpoint — the v2 node API (which also accepts JSON-RPC) and the v3 indexer — so one node covers real-time reads, transaction sending, jettons, NFTs, and smart contract calls. ## TON API at a glance | Property | Value | | - | - | | Chain | TON (The Open Network) — non-EVM layer-1, Proof-of-Stake | | Address model | Workchains — masterchain (`-1`) and basechain (`0`) | | EVM chain ID | None — TON is not EVM-compatible | | API surfaces | HTTP API v2 (node API, also runs JSON-RPC), HTTP API v3 (indexer), and TON API v4 (block-pinned reads and WebSocket block streams, on Global Nodes) | | Methods served by Chainstack | 62 — 28 on v2 and 34 on v3 | | Low-level protocol | ADNL on Dedicated Nodes | | Networks | Mainnet and testnet | | Node mode | Archive (all data available on every node) | | Request units | 1 RU per full request, 2 RUs per archive request | ## What is the TON protocol TON (The Open Network) is a fully decentralized layer-1 blockchain designed for fast transactions and scalability. It was originally designed by Telegram and later developed by the open TON Community. TON uses a unique sharding mechanism to achieve high throughput and low latency. TON employs a Proof-of-Stake consensus mechanism where validators stake TON coins to participate in block production and validation. Unlike EVM chains, TON has no single chain ID — it organizes state into workchains: the masterchain (workchain `-1`) coordinates the network and stores consensus data, and the basechain (workchain `0`) carries most user accounts and smart contracts. ## What is the TON API The TON API lets developers build decentralized applications on TON. It provides methods to read blockchain data, send transactions, and interact with smart contracts, jettons, and NFTs. To communicate with the TON blockchain, an application connects to a managed Chainstack TON node. Chainstack exposes two API surfaces on the same endpoint, and both serve the same data: * TON API v2 — the node API. It serves real-time requests directly off a node and also accepts JSON-RPC through the `/jsonRPC` envelope. * TON API v3 — the indexer. It serves processed, indexed data through REST endpoints, which suits complex queries and stable historical reads. On Global Nodes, the same endpoint also serves TON API v4 under `/api/v4` — account state and get-method calls pinned to a masterchain block, and WebSocket streams of new blocks. See [TON API v4](/docs/ton-choosing-v2-or-v3#ton-api-v4). The interactive examples on each TON method page are REST, but every Chainstack TON endpoint also accepts JSON-RPC, and each page has a JSON-RPC curl counterpart. > [!TIP] > > ### ADNL protocol support on Dedicated Nodes > > TON Dedicated Nodes on Chainstack support the [ADNL protocol](https://docs.ton.org/v3/documentation/network/protocols/adnl/overview) for secure, low-level network communication. This enables advanced use cases requiring direct interaction with the TON network layer. For help deciding between the surfaces, see [choosing v2, v3, or v4](/docs/ton-choosing-v2-or-v3). ## TON API surface The two surfaces differ in how they serve data, not in what data they cover. | API surface | Type | Serves off | Best for | Served by | | - | - | - | - | - | | TON API v2 | Node API + JSON-RPC | A node | Real-time reads, transaction sending | Chainstack | | TON API v3 | Indexer | An indexer | Processed data, complex and historical queries | Chainstack | | TON API v4 | Block-pinned HTTP API + WebSocket | A node, over lite-server connections | Reads at a specific block, `TonClient4`, new-block streams | Chainstack (Global Nodes) | | ADNL | Low-level network protocol | A Dedicated Node | Direct TON network-layer interaction | Chainstack (Dedicated Nodes) | ## Methods Chainstack serves 62 TON methods across the two HTTP API surfaces, all on the same endpoint: | API surface | Prefix | Methods | Availability | | - | - | - | - | | TON API v2 | `/v2/` | 28 | All served by Chainstack | | TON API v3 | `/v3/` | 34 | All served by Chainstack | For the complete, per-method reference, see [TON methods](/docs/ton-methods). Both surfaces are priced the same — see [TON billing](#how-is-ton-usage-billed-on-chainstack) below. ### What you can build With a Chainstack TON endpoint you can: * Read account state and balances with `/v2/getAddressInformation`, `/v2/getWalletInformation`, and the v3 `/v3/account` and `/v3/wallet` endpoints. * Send transactions by submitting a serialized message with `/v2/sendBoc` or `/v2/sendBocReturnHash`, and estimate fees first with `/v2/estimateFee`. * Read blocks and the masterchain with `/v2/getMasterchainInfo`, `/v2/lookupBlock`, and the v3 `/v3/blocks` endpoint. * Work with jettons (TON fungible tokens) through `/v3/jetton/masters`, `/v3/jetton/wallets`, and `/v3/jetton/transfers`. * Work with NFTs through `/v3/nft/collections`, `/v3/nft/items`, and `/v3/nft/transfers`. * Call smart contract get-methods with `/v2/runGetMethod` or `/v3/runGetMethod`. * Trace and analyze activity with the v3 indexer — `/v3/traces`, `/v3/actions`, and `/v3/events`. ## How to start using the TON API To use the TON API, you need access to a TON node. Follow these steps to sign up on Chainstack, deploy a TON node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the TON blockchain and use the TON API to build your decentralized applications. ## SDKs and tooling You can call the TON API directly over HTTP, or use a maintained SDK in your language. Point any SDK at your Chainstack endpoint: * JavaScript — [TonWeb](https://github.com/toncenter/tonweb) (`npm install tonweb`) and [Ton.js](https://github.com/ton-org/ton) (`npm install ton`). * Python — [TONsdk](https://github.com/tonfactory/tonsdk), [PyTONLib](https://github.com/toncenter/pytonlib), and [Pytoniq](https://github.com/yungwine/pytoniq). * Go — [TonUtils](https://github.com/xssnick/tonutils-go) and [tongo](https://github.com/tonkeeper/tongo). * Rust — [Tonlib-rs](https://github.com/ston-fi/tonlib-rs). * .NET — [TonSdk.NET](https://github.com/continuation-team/TonSdk.NET). For smart contract development, use [Blueprint](https://github.com/ton-org/blueprint) (`npm create ton@latest`) with FunC and the Sandbox local test environment. See [TON tooling](/docs/ton-tooling) for setup snippets across every SDK, supported wallets, and IDEs. ## Networks Chainstack supports both TON mainnet and testnet. TON nodes run in archive mode, so all historical data is available on every node, and both surfaces are served on each. | Feature | Mainnet | Testnet | | - | - | - | | TON API v2 | Yes | Yes | | TON API v3 | Yes | Yes | | TON API v4 | Yes | Yes | | Archive data | Yes | Yes | | Faucet | N/A | [Chainstack faucet](https://faucet.chainstack.com) | > [!NOTE] > > TON has no debug and trace namespace. For analyzing activity, use the v3 indexer endpoints — `/v3/traces`, `/v3/actions`, and `/v3/events`; for WebSocket streams of new blocks, use [TON API v4](/docs/ton-choosing-v2-or-v3#ton-api-v4). ## Frequently asked questions ### What is the difference between TON API v2 and v3? v2 is the node API: it serves real-time requests directly off a node and also accepts JSON-RPC. v3 is the indexer: it serves processed, structured data through REST, which suits complex queries and stable historical reads. Both are priced the same and expose the same data on Chainstack. Use v2 for the freshest data and v3 for cleaned, indexed data. See [choosing v2 or v3](/docs/ton-choosing-v2-or-v3). ### What is TON API v4? TON API v4 is an HTTP and WebSocket API in which account-state and get-method routes take a masterchain block seqno in the URL, so each result is pinned to that block. Chainstack serves it on Global Nodes at `YOUR_CHAINSTACK_ENDPOINT/api/v4`, and the `TonClient4` class in `@ton/ton` is a client for it. See [TON API v4](/docs/ton-choosing-v2-or-v3#ton-api-v4). ### Does TON have an EVM chain ID? No. TON is not EVM-compatible, so it has no numeric chain ID. Instead, TON organizes state into workchains — the masterchain (`-1`) and the basechain (`0`) — and addresses accounts and contracts by workchain. ### How many TON methods does Chainstack support? 62, all served directly on Chainstack nodes: 28 on the v2 node API and 34 on the v3 indexer. See the [TON methods reference](/docs/ton-methods) for the full per-method list. ### How do I send a transaction on TON? Serialize your message as a bag of cells (BOC) and submit it with `/v2/sendBoc`, or use `/v2/sendBocReturnHash` to get the transaction hash back. Most SDKs build and sign the BOC for you. ### What are jettons on TON? Jettons are TON's fungible token standard (TEP-74), the equivalent of ERC-20 on EVM chains. Read jetton data through the v3 indexer with `/v3/jetton/masters`, `/v3/jetton/wallets`, `/v3/jetton/transfers`, and `/v3/jetton/burns`. See [how to develop jettons](/docs/ton-how-to-develop-fungible-tokens-jettons) and [how to interact with jettons](/docs/ton-how-to-interact-with-jettons). ### How is TON usage billed on Chainstack? By request unit. Most TON requests are full (1 RU). Archive requests (2 RUs) apply to history reads — `getTransactions` and `getTransactionsStd` are always archive, and block/seqno methods are archive when the requested block is 128 or more seqno behind the tip. v2, v3, and v4 are priced the same. See [request units — TON method scope](/docs/request-units#ton-method-scope). ### Does Chainstack support the ADNL protocol? Yes, on Dedicated Nodes. ADNL is TON's low-level network protocol for secure, direct interaction with the network layer. The HTTP API v2 and v3 surfaces cover most use cases; ADNL is for advanced, low-level needs. See [TON tooling](/docs/ton-tooling). ## Related TON resources * [TON methods](/docs/ton-methods) — complete per-method API reference * [TON: choosing v2, v3, or v4](/docs/ton-choosing-v2-or-v3) — pick the right API surface * [TON tooling](/docs/ton-tooling) — SDKs, wallets, IDEs, and Blueprint setup * [TON: wallet initialization with TonWeb](/docs/ton-wallet-initialization-with-tonweb) — deploy a wallet contract end to end * [TON: how to develop fungible tokens (jettons)](/docs/ton-how-to-develop-fungible-tokens-jettons) — build jetton minter and wallet contracts * [TON: how to customize fungible tokens (jettons)](/docs/ton-how-to-customize-fungible-tokens-jettons) — extend jettons with capped supply and mint price * [TON: how to interact with jettons](/docs/ton-how-to-interact-with-jettons) — read jetton metadata, balances, and history * [TON: how to develop non-fungible tokens (NFT)](/docs/ton-how-to-develop-non-fungible-tokens) — build NFT item and collection contracts # ZKsync API reference: zkSync Era ZK-rollup JSON-RPC Source: https://docs.chainstack.com/reference/getting-started-zksync Use the ZKsync Era JSON-RPC API on Chainstack to deploy nodes, send transactions, and build on the Ethereum L2 ZK-rollup with low-cost scaling. ## What is ZKsync? ZKsync, launched as a Layer-2 scaling solution, leverages cutting-edge Zero-Knowledge (ZK) technology to enhance Ethereu's scalability without compromising its core values of decentralization, security, and user sovereignty. At its heart, zkSync aims to address Ethereum's limitations regarding transaction speed and costs, ensuring the network can support a global user base and a plethora of decentralized applications. Find useful ZKsync tools in the [ZKsync Era∎ tooling](/docs/zksync-era-tooling) section. ## What is the ZKsync API? The ZKsync API provides developers with the tools to interact seamlessly with the ZKsync Layer-2 protocol, facilitating the development of scalable and efficient Web3 applications. It encompasses the execution layer for processing transactions and a consensus layer for ensuring network security and integrity. To interact with the ZKsync blockchain, applications must connect to a ZKsync node, using the ZKsync API for data reading, transaction sending, and smart contract execution. This API follows a JSON-RPC specification, enabling remote procedure calls that feel local, thereby simplifying the development process for building on zkSync. ## How to start using the ZKsync API To use the ZKsync API, you need access to a ZKsync RPC node. Follow these steps to sign up on Chainstack, deploy a ZKsync RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now, you can connect to the ZKsync blockchain and use the ZKsync API to build. > [!NOTE] > > ### See also > > [Build better with ZKsync](https://chainstack.com/build-better-with-zksync/) # getTransaction | Solana Source: https://docs.chainstack.com/reference/gettransaction openapi/solana_node_api/getTransaction.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getTransaction method returns detailed information about a specific transaction using its signature. Solana via Chainstack. > [!NOTE] > > **Archive data:** this method supports fetching historical data on [Global Nodes](/docs/global-elastic-node). See [archive methods availability](/docs/limits#solana-archive-methods-availability). > [!WARNING] > > **Transaction version 1:** Solana mainnet now produces [version 1 transactions](https://solana.com/upgrades/larger-transaction-sizes). Pass `maxSupportedTransactionVersion: 1` to retrieve them—a lower value responds with error `-32015`. # Solana `getTransaction` method The Solana `getTransaction` method returns detailed information about a specific transaction using its signature. This method provides comprehensive data about the transaction, including its status, block information, instructions, and more. It's particularly useful for applications that need to verify transaction details or display transaction information to users. > [!NOTE] > > **Null response** > > The interactive API example is for the [mint transaction of dogwifhat (\$WIF](https://solscan.io/tx/4VAGET7z5g7ogVGmbmZ6KBtF6DS8ftLWzD65BXZWQJjwASUqLod7LhGB6mqThcqo97QcC7r7uNmBY8GwsnLAA52n)). The call will respond with null because you need an archive node to retrieve the data in the past. Chainstack does provide Solana archive nodes, so get one. ## Parameters * `signature` — the transaction signature as base-58 encoded string. * `config` (optional) — configuration object: * `encoding` — encoding for the returned transaction (default: "json"). * `maxSupportedTransactionVersion` — the highest transaction version your client can parse, as an integer in the `0`–`255` range. Set it to `1` to retrieve [version 1 transactions](https://solana.com/upgrades/larger-transaction-sizes), which Solana mainnet now produces. A lower value, or an omitted field, makes the call respond with error `-32015` when the transaction is newer than the version you declare. * `commitment` — `confirmed` or `finalized`; the commitment level `processed` is not supported here by Solana. ## Response * `slot` — the slot this transaction was processed in. * `transaction` — transaction object, either JSON format or encoded binary data. * `meta` — metadata object containing detailed information about the transaction. * `blockTime` — estimated production time of the block, as Unix timestamp. ## Use case A common use case for `getTransaction` is in wallet applications or block explorers. These applications can use this method to fetch and display detailed information about a specific transaction, allowing users to view the status, involved accounts, and other relevant data for their transactions. # eth_getTransactionByBlockHashAndIndex | Cronos Source: https://docs.chainstack.com/reference/gettransactionbyblockhashandindex openapi/cronos_node_api/eth_getTransactionByBlockHashAndIndex.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. Cronos API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Cronos blockchain. ## Parameters * `hash` — the hash of the block * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal ## Response * `object` — a transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the transaction's sender is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the transaction sender is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender's wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) with which the transaction plans to interact. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blockHash, index) => { const transaction = await provider.send("eth_getTransactionByBlockHashAndIndex", [blockHash, index]); console.log(transaction); } getTransaction("0x84fcbb099d339793e0271db167c937723202a25672a676a2066268fff6d0e945", "0x0"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block("0x84fcbb099d339793e0271db167c937723202a25672a676a2066268fff6d0e945", 1)) ``` ## Use case The `eth_getTransactionByBlockHashAndIndex` can be used to retrieve transaction details from a block. # eth_getTransactionByBlockNumberAndIndex | Cronos Source: https://docs.chainstack.com/reference/gettransactionbyblocknumberandindex openapi/cronos_node_api/eth_getTransactionByBlockNumberAndIndex.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. Cronos API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Cronos blockchain. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe`—the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or severe propagation latency. * `finalized`—the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, requiring burning at least 1/3 of the staked amount. * `earliest` — the earliest available or genesis block. * `pending`—the pending state and transactions block. This is the current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal. ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the transaction's sender is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the transaction sender is willing to pay per gas unit. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender's wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blocNumber, index) => { const transaction = await provider.send("eth_getTransactionByBlockNumberAndIndex", [blocNumber, index]); console.log(transaction); } getTransaction("latest", "0x1"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block(13766424, 3)) # Hex encoded parameters starting with "0x" are accepted as well. ``` ## Use case The `eth_getTransactionByBlockNumberAndIndex` can be used to retrieve transaction details from a block; for example, an analytics tool systematically scans each block for transactions by sequentially incrementing the block number and cycling through the transactions within each block using the transaction index. For each transaction, it retrieves detailed information such as the value transferred, the gas price, the sender and receiver addresses, and the input data, which can contain calls to smart contracts. # eth_getTransactionCount | Polygon Source: https://docs.chainstack.com/reference/gettransactioncount openapi/polygon_node_api/account_info/eth_getTransactionCount.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the number of transactions sent from an address at the selected block. Chainstack Polygon reference. Polygon API method that returns the number of transactions sent from an address at the selected block. This value is also called `nonce`; it is an important piece of information, especially to ensure that a transaction is not sent twice. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`— the address to retrieve the transaction count. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ## Response * `quantity` — an integer value identifying the number of transactions sent from an address at the specified block. ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getNonce = async (address, blockId) => { const nonce = await provider.send("eth_getTransactionCount", [address, blockId]); console.log(nonce); }; getNonce("0xe341b2f448eb190495ed4a89c01f20078b26b0f6", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_count("0xe341b2f448eb190495ed4a89c01f20078b26b0f6", "latest")) ``` ## Use case One of the most common use cases for `eth_getTransactionCount`is to create the transaction object built in a script designed to send a transaction. The nonce field is required, and it is retrieved using the `eth_getTransactionCount` method. The following code shows how to build and sign a raw transaction using ethers.js. > [!WARNING] > > **Security notice** > > You need your private key to sign the transaction; never share your private key with anyone. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Initialize wallet with the private key const PRIVATE_KEY = "PRIVATE_KEY"; const wallet = new ethers.Wallet(PRIVATE_KEY, provider); // Validate input parameters and return an error message if invalid const validateInputs = (fromAddress, toAddress) => { if (!ethers.isAddress(fromAddress)) { throw new Error(`Invalid fromAddress: ${fromAddress}`); } if (!ethers.isAddress(toAddress)) { throw new Error(`Invalid toAddress: ${toAddress}`); } }; // Async function to create a raw transaction async function createRawTransaction(fromAddress, toAddress, value) { validateInputs(fromAddress, toAddress); const nonce = await provider.getTransactionCount(fromAddress); const gasPrice = (await provider.getFeeData()).gasPrice; const gasLimit = await provider.estimateGas({ from: fromAddress, to: toAddress, }); // Build the transaction object const transactionObject = { to: toAddress, gasPrice: gasPrice, gasLimit: gasLimit, nonce: nonce, value: value, }; // Sign the transaction using the wallet and return the raw transaction const rawTransaction = await wallet.signTransaction(transactionObject); return rawTransaction; } async function main() { const rawTransaction = await createRawTransaction('0x6f46cf5569aefa1acc1009290c8e043747172d89', "0xe341b2f448eb190495ed4a89c01f20078b26b0f6" , "100000000000000") console.log(`Raw transaction: ${rawTransaction}`) } main() ``` In summary, the code creates a valid raw transaction that can be broadcasted to the network. During the process, the script retrieves important values such as the `nonce`, `gasPrice`, and `gasLimit`, builds a transaction object and signs it using a private key. First, the `createRawTransaction` function calls `validateInputs` to ensure that the `fromAddress` and `toAddress` parameters are valid addresses. If either of these addresses is invalid, the function throws an error with a descriptive message. Next, the function makes a call to `provider.getTransactionCount` with the `fromAddress` as a parameter. This method returns the number of transactions sent from the `fromAddress`, and is used as the `nonce` for the transaction. The function retrieves the `gasPrice` and `gasLimit` for the transaction using `provider.getFeeData` and `provider.estimateGas`, respectively. The [eth\_gasPrice | Polygon](/reference/gasprice) is the amount of gas that the transaction sender is willing to pay per unit of gas consumed by the transaction, while the [eth\_estimateGas | Polygon](/reference/estimategas) is an estimate of the maximum amount of gas that the transaction will consume. With the `nonce`, `gasPrice`, and `gasLimit` values, the function builds a transaction object, which includes the `toAddress`, `gasPrice`, `gasLimit`, `nonce`, and `value` of the transaction. The value is the amount transferred from the `fromAddress` to the `toAddress`. The transaction object is then signed using the wallet, which returns a `rawTransaction` that can be broadcasted to the network to execute the transaction using [eth\_sendRawTransaction | Polygon](/reference/sendrawtransaction). # eth_getTransactionReceipt | Cronos Source: https://docs.chainstack.com/reference/gettransactionreceipt openapi/cronos_node_api/eth_getTransactionReceipt.json POST /13e7d0c8fd7ed1df4f336e75b0d63a92 Cronos API method that retrieves a transaction's receipt from a transaction hash. eth_getTransactionReceipt on Cronos via Chainstack. > [!NOTE] > > **No response in the interactive API** > > The response to the call might be too big for the web app to handle, so if you are not getting it here, copy the CURL example and run in your terminal—this will work. Cronos API method that retrieves a transaction's receipt from a transaction hash. It can be used to verify a transaction's success or failure, check a smart contract's execution status, retrieve logs, check if a transaction deployed a smart contract, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `Transaction receipt` — the receipt object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the contract address created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects this transaction generates, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionReceipt = async (transactionHash) => { const receipt = await provider.send("eth_getTransactionReceipt", [transactionHash]); console.log(receipt); }; getTransactionReceipt('0x2fe2d1f60c3abc7642e0a4c7929d356fc1009a0c1e60722b285d663ee7a0f636') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_receipt("0x2fe2d1f60c3abc7642e0a4c7929d356fc1009a0c1e60722b285d663ee7a0f636")) ``` ## Use case A practical use case for `eth_getTransactionReceipt` is to verify a transaction's status and whether it was successful. This can be especially useful when deploying a contract, as you can use the receipt to confirm that the contract was deployed successfully and has a valid contract address. # Validator balances by state ID Source: https://docs.chainstack.com/reference/getvalidatorbalancesbystateidandvalidatorid openapi/ethereum_beacon_chain_api/validatiors_info/validator_balances.json GET /beacon/states/{state_id}/validator_balances The /eth/v1/beacon/states/{state_id}/validator_balances endpoint provides the balances of validators for a specified state in the Beacon Chain. The `/eth/v1/beacon/states/{state_id}/validator_balances` endpoint provides the balances of validators for a specified state in the Beacon Chain. This information is essential for tracking validators' financial stakes and rewards for participating in the Ethereum PoS mechanism. ### Parameters * `state_id` — the identifier for the state whose validator balances are being requested. The `state_id` can be one of the following: * `head` — represents the current head of the chain from the node's perspective. * `genesis` — refers to the genesis or initial state of the Beacon Chain. * `finalized` — the latest state that has been finalized and accepted by the network. * `justified` — a recently justified state, indicating preliminary consensus before finalization. * `` — a specific slot in the Ethereum protocol timeline. * `` — a specific state root encoded in hexadecimal. ### Response The response includes an object with the following fields: * `data` — an array of objects representing a validator's balance in the specified state. Each object contains: * `index` — the index number of the validator. * `balance` — the current balance of the validator in Gwei. # Validator information by state and ID or public key Source: https://docs.chainstack.com/reference/getvalidatorbystateidandindex openapi/ethereum_beacon_chain_api/validatiors_info/validator_information_by_state_id_or_public_key.json GET /beacon/states/{state_id}/validators/{index} Reference for the Validator information by state and ID or public key JSON-RPC method on the Chainstack blockchain via Chainstack nodes for dapp developers. The `/eth/v1/beacon/states/{state_id}/validators/{validator_id}` endpoint retrieves detailed information about a specific validator within a given state of the Beacon Chain. This endpoint is crucial for obtaining in-depth data about individual validators, including their status, balances, and participation in the Ethereum network. ### Parameters * `state_id` — the identifier of the state for which validator information is requested. Acceptable values for `state_id` include: * `head` — represents the latest canonical state in the node's view. * `genesis` — refers to the initial state of the Beacon Chain. * `finalized` — indicates the most recent state that has been finalized. * `justified` — denotes a recently justified state in the Proof of Stake consensus. * `` — a specific time slot in the Ethereum protocol. * `` — a specific state root in hexadecimal format. * `validator_id` — the identifier of the specific validator. This can be a validator index, public key, or a hexadecimal representation of the validator's public key. ### Response The response consists of an object with the following fields: * `data` — an array of objects, each representing a validator, with fields including: * `index` — the index number of the validator. * `balance` — the current balance of the validator in Gwei. * `status` — the current status of the validator, with possible values: * `pending_initialized` — awaiting sufficient balance for activation. * `pending_queued` — queued for activation. * `active_ongoing` — actively participating in the consensus process. * `active_exiting` — requested to exit, but not yet exited. * `active_slashed` — slashed and awaiting exit. * `exited_unslashed` — exited without being slashed. * `exited_slashed` — exited due to being slashed. * `withdrawal_possible` — eligible to withdraw funds. * `withdrawal_done` — successfully withdrawn. * `validator` — detailed information about the validator, such as: * `public_key` — the public key of the validator. * `withdrawal_credentials` — required credentials for fund withdrawal. * `effective_balance` — the validator's effective balance in Gwei. * `slashed` — indicates if the validator has been slashed. * `activation_eligibility_epoch` — eligibility epoch for activation. * `activation_epoch` — the epoch of validator activation. * `exit_epoch` — the epoch of validator exit. * `withdrawable_epoch` — the epoch after which withdrawal is possible. # Validators information by state ID Source: https://docs.chainstack.com/reference/getvalidatorinformation openapi/ethereum_beacon_chain_api/validatiors_info/validator_information_by_state_id.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/beacon/states/{state_id}/validators/{validator_index} The /eth/v1/beacon/states/{state_id}/validators endpoint retrieves information about validators associated with a specified state in the Beacon Chain. The `/eth/v1/beacon/states/{state_id}/validators` endpoint retrieves information about validators associated with a specified state in the Beacon Chain. This data is crucial for understanding validators' status, performance, and participation in the Ethereum network. ### Parameters * `state_id` — the identifier of the state for which validator information is requested. Acceptable values for `state_id` include: * `head` — represents the latest canonical state in the node's view. * `genesis` — the initial state of the Beacon Chain. * `finalized` — the most recent state that has been finalized. * `justified` — a recently justified state in the Proof of Stake consensus. * `` — a specific time slot in the Ethereum protocol. * `` — a specific state root in hexadecimal format. ### Response The response consists of an object with the following fields: * `data` — an array of objects, each representing a validator, with fields including: * `index` — the index number of the validator. * `balance` — the current balance of the validator in Gwei. * `status` — the current status of the validator, with possible values: * `pending_initialized` — awaiting sufficient balance for activation. * `pending_queued` — queued for activation. * `active_ongoing` — actively participating in the consensus process. * `active_exiting` — requested to exit, but not yet exited. * `active_slashed` — slashed and awaiting exit. * `exited_unslashed` — exited without being slashed. * `exited_slashed` — exited due to being slashed. * `withdrawal_possible` — eligible to withdraw funds. * `withdrawal_done` — successfully withdrawn. * `validator` — detailed information about the validator, such as: * `public_key` — the public key of the validator. * `withdrawal_credentials` — required credentials for fund withdrawal. * `effective_balance` — the validator's effective balance in Gwei. * `slashed` — indicates if the validator has been slashed. * `activation_eligibility_epoch` — eligibility epoch for activation. * `activation_epoch` — the epoch of validator activation. * `exit_epoch` — the epoch of validator exit. * `withdrawable_epoch` — the epoch after which withdrawal is possible. # Accounts info | Gnosis Source: https://docs.chainstack.com/reference/gnosis-accounts-info-rpc-methods Gnosis Chain account information JSON-RPC methods — retrieve balances, nonces, contract code, and storage data for accounts on the Gnosis network. Retrieving account information from the Gnosis Chain can be done by using the following methods: > [card] [eth\_getTransactionCount](/reference/gnosis-gettransactioncount) > [card] [eth\_getBalance](/reference/gnosis-getbalance) > [card] [eth\_getCode](/reference/gnosis-getcode) > [card] [eth\_getStorageAt](/reference/gnosis-getstorageat) # Beacon Chain | Gnosis Source: https://docs.chainstack.com/reference/gnosis-beacon-chain Gnosis Beacon Chain consensus layer API — the Gnosis Chain counterpart to Ethereum's Beacon Chain, using xDAI stablecoin for transactions and fees. > [!NOTE] > > For a detailed description of all the available Beacon Chain API specifications, see [Ethereum Beacon Chain](/reference/beacon-chain). # eth_blockNumber | Gnosis Source: https://docs.chainstack.com/reference/gnosis-blocknumber openapi/gnosis_node_api/blocks_info/eth_blockNumber.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the latest block number of the blockchain. Available on Gnosis via Chainstack JSON-RPC nodes. Gnosis Chain API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0x69B5B` means that the block is the 432,987th block in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to, encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockNumber = async () => { const block_Number = await provider.getBlockNumber(); console.log(block_Number); }; eth_getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.block_number) ``` ## Use case A possible use case for the `eth_blockNumber` method in Gnosis Chain is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. The following code uses the web3.js library and defines a function called `getCurrentBlockNumber`. When called, this function returns the latest block number from the network, and you can use it in your DApp. You can then use your application's `getCurrentBlockNumber` function to get the current block number whenever you need it. In this example, we use it to calculate on which block a special offer to a specific user will expire. We retrieve the block when we issue the offer to the user and add `121000`, which is approximately how many Gnosis Chain blocks are produced weekly. Now, in the `offerExpires` constant, we have the last block when the user can exercise the special price. Use the `getCurrentBlockNumber` function to compare it to `offerExpires` to know if the offer is still valid. # Blocks info | Gnosis Source: https://docs.chainstack.com/reference/gnosis-blocks-rpc-methods Gnosis Chain block information JSON-RPC methods — query block details including transactions, timestamps, height, and headers on the Gnosis network. Retrieving block information from the Gnosis Chain can be done by using the following methods: > [card] [eth\_blockNumber](/reference/gnosis-blocknumber) > [card] [eth\_getBlockByHash](/reference/gnosis-getblockbyhash) > [card] [eth\_getBlockByNumber](/reference/gnosis-getblockbynumber) > [card] [eth\_getBlockTransactionCountByHash](/reference/gnosis-getblocktransactioncountbyhash) > [card] [eth\_getBlockTransactionCountByNumber](/reference/gnosis-getblocktransactioncountbynumber) > [card] [eth\_newBlockFilter](/reference/gnosis-newblockfilter) This information can be used to track the state of the blockchain and for: * **Blockchain analysis** — insights into the overall activity on the network, such as the number of processed transactions and average transaction fees, can be obtained. * **Block explores** — web-based tools that provide access to information on a blockchain, including block data, transactions, and addresses, allowing users to search, view, and analyze the network's activity. * **Smart contract analysis** — the activity of specific smart contracts * **DApp development** — block information can be utilized in the development of decentralized applications. # Chain info | Gnosis Source: https://docs.chainstack.com/reference/gnosis-chain-data-rpc-methods Gnosis Chain data RPC methods for querying network details like chain ID, syncing status, and protocol version through Chainstack node endpoints. For example, if a developer wants to build a DApp on Gnosis Chain, they would use these methods to get information about the network they are connecting to. They could use the chain ID information to make sure they are connecting to the right network and to ensure the security of their DApp; for example, distinguishing between the main network and a test network. Retrieving chain information from the Gnosis Chain can be done by using the following methods: > [card] [eth\_chainId](/reference/chainid) > [card] [eth\_syncing](/reference/gnosis-syncing) # clearSubscriptions | Gnosis Source: https://docs.chainstack.com/reference/gnosis-clearsubscriptions Unsubscribe from all active Gnosis Chain ethers.js subscriptions at once using removeAllListeners. Useful for cleanup when resetting your event listeners. The subscriptions available using the ethers.js `provider.on` method are: > [card] [on('block')](/reference/gnosis-subscribenewblockheaders) > [card] [on(filter)](/reference/gnosis-subscribelogs) > [card] [eth\_syncing](/reference/gnosis-subscribesyncing) ## Parameters `boolean` — keep the [eth\_syncing](/reference/gnosis-subscribesyncing) subscription if `true`. ## Response `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `clearSubscriptions` code example ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function clearSubscriptions() { try { await provider.removeAllListeners(); console.log("Subscriptions were canceled successfully"); return process.exit(1) } catch (error) { console.error("Failed to cancel subscriptions:", error); return process.exit(1) } } clearSubscriptions() ``` ## Use case A way to use the `clearSubscriptions` method is to place it in a timer to clear all of the subscriptions after a pre-determined about. The following code is an example using the [block event](/reference/gnosis-subscribenewblockheaders) subscription, which will be stopped and removed after one minute using `clearSubscriptions`: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToNewBlocks() { try { // Attach event listeners for new blocks and errors provider.on('block', handleNewBlock); provider.on('error', handleError); console.log("Subscribed to new blocks"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs the received block number function handleNewBlock(blockNumber) { console.log(blockNumber); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } async function clearSubscriptions() { try { await provider.removeAllListeners(); console.log("Subscriptions were canceled successfully"); return process.exit(1) } catch (error) { console.error("Failed to cancel subscriptions:", error); return process.exit(1) } } async function main() { subscribeToNewBlocks(); // Run clearSubscriptions() once after 60 seconds setTimeout(clearSubscriptions, 60000); } main() ``` The code starts by defining an async function called `subscribeToNewBlocks` that subscribes to the `block` event using the `provider.on` method. Every time a new block is mined, ethers.js emits the `block` event with the new block number. The code defines two event listener functions, `handleNewBlock` and `handleError`, that log messages to the console when these events occur. The `subscribeToNewBlocks` function is called from the `main` function, which is also defined in the code. The `main` function calls `subscribeToNewBlocks` to create a new subscription and then schedules the `clearSubscriptions` function to run once after 60 seconds using the `setTimeout` function. The `clearSubscriptions` function uses the `provider.removeAllListeners` method to cancel any existing subscriptions and log a message to the console if the cancellation was successful. # Client information | Gnosis Source: https://docs.chainstack.com/reference/gnosis-client-data-rpc-methods Gnosis Chain client information JSON-RPC methods — check client version, network ID, chain ID, listening status, and peer count for your node. Retrieving blockchain client information from a Gnosis Chain node can be done by using the following methods: > [card] [web3\_clientVersion](/reference/gnosis-clientversion) > [card] [net\_listening](/reference/gnosis-listening) > [card] [net\_peerCount](/reference/gnosis-peercount) # web3_clientVersion | Gnosis Source: https://docs.chainstack.com/reference/gnosis-clientversion openapi/gnosis_node_api/client_info/web3_clientVersion.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the client type and version running on the Gnosis Chain node. Use it on Gnosis via Chainstack. Gnosis Chain API method that returns the client type and version running on the Gnosis Chain node. This information can be useful to developers to verify that a node they are connecting to is compatible with their needs. ## Parameters * `none` ## Response * `string` — a string identifying the type of client, version, operating system, and language version running on the node ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const clientVersion = async () => { const version = await provider.send("web3_clientVersion"); console.log(`Client version: ${version}`); }; clientVersion(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) client_version = web3.provider.make_request('web3_clientVersion', []) print(client_version) ``` ## Use case A use case for the `web3_clientVersion` method can be to verify which client version is running to then decide which function to call. Let's say you have a DApp that needs to call a different function based on the client's version. You can use `web3_clientVersion` to build this logic. Here is an implementation of this use case using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Get client version async function getClientVersion() { const clientVersion = await provider.send("web3_clientVersion"); const clientName = clientVersion.split('/')[1]; return clientName } // define a function to be run if the client version is 'vv2.0.10' function runFunction1() { console.log('Running function 1...'); } // define a function to be run if the client version is 'vv2.0.11' function runFunction2() { console.log('Running function 2...'); } // define an async function to retrieve the client version and run the appropriate function async function runBasedOnClientVersion() { try { const clientVersion = await getClientVersion(); console.log(`Client version: ${clientVersion}`); if (clientVersion === 'v1.14.7+4fe81c6b') { runFunction1(); } else if (clientVersion === 'v1.14.8+4fe81c6b') { runFunction2(); } // log an error if the client version is not recognized else { console.error('Unrecognized client version.'); } } catch (error) { console.error(error); } } // call the async function to retrieve the client version and run the appropriate function runBasedOnClientVersion(); ``` This code uses the ethers.js library to connect to a blockchain node specified by the `NODE_URL` variable and retrieves the client version using the `web3_clientVersion` method. It then runs different functions based on the client version by defining two functions `runFunction1` and `runFunction2`, and using an if statement to check the client version and call the appropriate function. The `getClientVersion` function uses `provider.send` to call the `web3_clientVersion` method directly and returns the result. This function is called within the `runBasedOnClientVersion` function to retrieve the client version. Overall, this code demonstrates how to retrieve the client version on a blockchain node using the ethers.js library and how to use the client version to run different functions based on the version. The code can be modified to add additional functions and version checks or to perform other actions based on the client version. # eth_estimateGas | Gnosis Source: https://docs.chainstack.com/reference/gnosis-estimategas openapi/gnosis_node_api/gas_data/eth_estimateGas.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns an estimate of gas units needed for a given transaction. eth_estimateGas on Gnosis via Chainstack. Gnosis Chain API method that returns an estimate of gas units needed for a given transaction. It provides an estimate of the maximum amount of gas units required to complete the transaction based on the network's current state and the transaction's parameters. A client or wallet can use this estimate to determine the appropriate amount of gas to include in a transaction to ensure that the network processes it. ## Parameters * `object` — the transaction call object, same as in `eth_call`; the `from` field is optional, and the `nonce` field is omitted: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — the estimated amount of gas units needed, represented as a hexadecimal string. ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimateGas = async () => { // The transaction to estimate is a simple transfer in this example. const transactionObj = { from: "0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324", to: "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8", } const gasUnits = await provider.estimateGas(transactionObj); console.log(`Estimated gas units required: ${gasUnits}`); }; estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.estimate_gas({"from":"0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324","to":"0xbe0eb53f46cd790cd13851d5eff43d12404d33e8"}, "latest" )) ``` ## Use case A practical use case for `eth_estimateGas` is to calculate the gas cost required for a transaction that transfers a value from one Gnosis Chain address (`fromAddress`) to another (`toAddress`). The gas cost is an important factor in transactions, as it determines the amount of fees that need to be paid by the sender to the network for processing the transaction. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function estimateGasCost(from, to, value) { try { const gasEstimate = await provider.estimateGas({ from: from, to: to, value: value }); return gasEstimate } catch (error) { console.error(`Error estimating gas cost: ${error}`); } } async function main() { const fromAddress = '0xCb6Ed7E78d27FDff28127F9CbD61d861F09a2324'; const toAddress = '0x81ab6Fd4A68d0aDDFAD65F9F97Bc1427dCd40C8E'; const value = ethers.parseEther('25'); // 25 ETH in Wei const estimatedGas= await estimateGasCost(fromAddress, toAddress, value) if (estimatedGas) { console.log(`Estimated gas cost: ${estimatedGas}`); } } main() ``` The `estimateGasCost` function wraps the call to `provider.estimateGas` in a `try-catch` block so that any errors that occur during the estimation process can be handled gracefully. If an error occurs, the error message is logged to the console. If the estimate is successful, the estimated gas cost is returned. The main function is the entry point of the code. It sets the `fromAddress`, `toAddress`, and value for the transaction, and then calls the `estimateGasCost` function to get the estimated gas cost. The estimated gas cost is logged to the console if the estimate is successful, but it is available to be used for further processing in the `estimatedGas` constant. # eth_call | Gnosis Source: https://docs.chainstack.com/reference/gnosis-ethcall openapi/gnosis_node_api/execute_transactions/eth_call.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. Gnosis Chain API method that enables instant execution of a new message call without requiring the creation of a transaction on the blockchain. This can be useful for testing and debugging by simulating transfers or smart contract transactions and retrieving data from the blockchain. > [!NOTE] > > **Disclaimer** > > The interactive example in this page uses `eth_call` to call the `lpToken` method from [SushiSwap MiniChefV2 smart contract on Gnosis Chain](https://gnosisscan.io/address/0xdDCbf776dF3dE60163066A5ddDF2277cB445E0F3#readContract). It returns the address of the liquidity pool at the index specified in the data. In this case, the input is 1, and it returns the [current address for the WETH-WBTC pool for SushiSwap](https://gnosisscan.io/address/0xe21F631f47bFB2bC53ED134E83B8cff00e0EC054) on Gnosis Chain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `object` — the transaction call object: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters. See the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `data` — the result of executing the specified call on the Ethereum Virtual Machine (EVM), encoded as a hexadecimal string. ## `eth_call` code examples The following code snippets provide a clear demonstration of how to simulate a standard xDAI transfer between two accounts. Note that when using `eth_call` to simulate a transfer or a smart contract transaction, the sender account must have a sufficient balance to cover the associated gas fee. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const simulateTransfer = async () => { try { // Define the accounts to transfer between const fromAddress = "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5"; const toAddress = "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"; // Define the value to transfer const value = ethers.parseEther("1"); // Build transaction object const transactionObject = { from: fromAddress, to: toAddress, gasLimit: ethers.toQuantity(21000), value: value } // Use eth_call to simulate the transfer on the latest block const result = await provider.call(transactionObject); console.log(`Result: ${result}`); } catch (error) { console.error(`Error: ${error.message}`); } } simulateTransfer(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the accounts to transfer between from_address = web3.to_checksum_address('0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5') to_address = web3.to_checksum_address('0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a') # Define the value to transfer (in Ether) value = web3.to_wei(1, 'ether') # Build the transaction object transaction = { 'from': from_address, 'to': to_address, 'gas': 21000, # 21000 is the standard for a regular transfer 'value': value, 'data': '', } # Use eth_call to simulate the transfer result = web3.eth.call(transaction, 'latest') print(result) ``` Executing this code will produce a return value of `0x`, indicating a successful transaction. This implies that the transaction will be executed without any errors when the code is used to send the transaction. ## Use case A common use case for `eth_call` is calling smart contract functions. For example, interact with an ERC-20 token smart contract deployed on Gnosis Chain and retrieve the balance of a specific account. The following code uses the ethers.js library to call the `balanceOf` function of the [APE token smart contract](https://etherscan.io/token/0x4d224452801aced8b2f0aebe155379bb5d594381#code) on the Gnosis Chain network. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Contract address and ABI for the token const CONTRACT_ADDRESS = "0xE2e73A1c69ecF83F464EFCE6A5be353a37cA09b2"; const ABI = [ { "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "balance", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" } ]; // Asynchronously fetches the balance of an account for the given address async function getAccountBalance(address) { try { // Create a new contract instance const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider); // Call the balanceOf function to retrieve the account balance in wei using eth_call const balanceInWei = await contract.balanceOf(address); // Convert the balance from wei to ethers const balanceInEth = ethers.formatEther(balanceInWei); // Log the account balance in ethers to the console console.log(`The account balance for address ${address} is: ${balanceInEth} APE`); } catch (error) { console.error("Error occurred while fetching account balance:", error); } } // Call the getAccountBalance function with a sample account address getAccountBalance("0xE2e73A1c69ecF83F464EFCE6A5be353a37cA09b2"); ``` The `getAccountBalance` function is called with the address of the account whose balance we want to retrieve. This function creates a new contract instance, calls the `balanceOf` function of the smart contract with the provided address, and retrieves the account balance in Wei. The balance is then converted to the ethers unit using the `ethers.formatEther` function. Finally, the account balance is printed to the console for easy viewing. # Executing transactions | Gnosis Source: https://docs.chainstack.com/reference/gnosis-excecution-rpc-methods Gnosis Chain transaction execution JSON-RPC methods — call smart contracts, sign transactions, send raw transactions, and estimate gas on the network. Executing and simulating transactions on the Gnosis Chain can be done by using the following methods: > [card] [eth\_call](/reference/gnosis-ethcall) > [card] [eth\_sendRawTransaction](/reference/gnosis-sendrawtransaction) # Filter handling | Gnosis Source: https://docs.chainstack.com/reference/gnosis-filters-rpc-methods Gnosis Chain filter JSON-RPC methods — create event and block filters, poll for changes, retrieve filter logs, and manage on-chain subscriptions. Handling filters can be done by using the following methods: > [card] [eth\_getFilterChanges](/reference/gnosis-getfilterchanges) > [card] [eth\_uninstallFilter](/reference/gnosis-uninstallfilter) # Gas data | Gnosis Source: https://docs.chainstack.com/reference/gnosis-gas-data-rpc-methods Gnosis Chain gas data JSON-RPC methods — query current gas prices and estimate gas costs for transactions on the xDAI-based Gnosis Chain network. Retrieving gas information from the Gnosis Chain can be done by using the following methods: > [card] [eth\_estimateGas](/reference/gnosis-estimategas) > [card] [eth\_gasPrice](/reference/gasprice) # eth_getBalance | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getbalance openapi/gnosis_node_api/accounts_info/eth_getBalance.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the balance of a specific Gnosis Chain account in Wei, the smallest unit of ether. On Gnosis. Gnosis Chain API method that returns the balance of a specific Gnosis Chain account in [Wei](https://www.investopedia.com/terms/w/wei.asp), the smallest unit of ether. This method allows developers to retrieve the current balance of a Gnosis Chain account for various purposes, such as checking available funds or displaying balance information. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to check the balance of. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — the integer value of the current balance in Wei ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const getBalance = async (address, block) => { const balance = await provider.send("eth_getBalance", [address, block]); console.log(balance); }; getBalance("0x7584F3c08bF632EED687f45592EA9f5690fc1D41", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" address = web3.to_checksum_address("0x7584F3c08bF632EED687f45592EA9f5690fc1D41") balance = web3.eth.get_balance(address, "latest") print(balance) ``` ## Use case One practical use case for `eth_getBalance` is to check the balance of an account for a program that scans the balance periodically and fills up the account when the balance drops below a certain value. Here's an example of how you can achieve this in ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const accountAddress = '0xE2e73A1c69ecF83F464EFCE6A5be353a37cA09b2'; const minimumBalance = ethers.parseEther("10"); // 10 ETH in wei (bigint) async function checkBalance(address) { const balance = await provider.getBalance(address, 'latest'); return balance; } async function fillUp(balance) { const minimumConverted = ethers.formatEther(minimumBalance); if (balance < minimumBalance) { console.log(`The balance of the account ${accountAddress} is below ${minimumConverted} xDAI.`); console.log(`Sending more funds...`) // Call another function to fill up your account } else { console.log(`The balance of the account ${accountAddress} is above ${minimumConverted} xDAI.`); console.log(`No need to send more funds.`) } } async function main() { const balance = await checkBalance(accountAddress); const balanceInXdai = ethers.formatEther(balance); console.log(`The balance of the account ${accountAddress} is ${balanceInXdai} xDAI. \n`); await fillUp(balance); } main(); ``` The code is a simple script that checks the balance of an account on the Gnosis Chain network and takes action if the balance falls below a specified minimum. The `accountAddress` variable holds the address for which you want to check the balance. The `minimumBalance` variable holds the minimum balance in Wei you want to check against. The `checkBalance` function takes an address as an argument and returns the balance of the account in Wei. The `fillUp` function takes the balance as an argument and converts the minimum balance from Wei to xDAI using the top-level `formatEther` method from ethers.js, and checks if the balance is below the minimum balance. > [!NOTE] > > Note that the Wei value is converted only to display it, as it is more accurate to work with the Wei value. If the balance is below the minimum, it logs a message to the console indicating that more funds need to be sent and calls another function to fill up the account. If the balance is above the minimum, it logs a message indicating that no additional funds need to be sent. The main function calls the `checkBalance` and `fillUp` functions to convert the balance from Wei to xDAI using the `formatEther` method, and log the balance and the result of the check to the console. In summary, this script allows you to easily check the balance of an account on the Gnosis Chain network and take action if the balance falls below a specified minimum. # eth_getBlockByHash | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getblockbyhash openapi/gnosis_node_api/blocks_info/eth_getBlockByHash.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns information about the block matching the given block hash. Available on Gnosis via Chainstack. Gnosis Chain API method that returns information about the block matching the given block hash. This method can be useful for analyzing a block and its transactions. ## Parameters * `hash` — the hash of the block * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — the hash of the previous block that was used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Gnosis nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Gnosis blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. Gnosis Chain uses POSDAO (Proof of Stake-based Delegated Proof of Authority) consensus and this value is not relevant. * `extradata` — the extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — the array of transaction objects. See [eth\_getTransactionByHash](/reference/gnosis-gettransactionbyhash) for the exact shape. * `uncles` — the array of uncle hashes. ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByHash = async () => { const blockByNumber = await provider.send("eth_getBlockByHash", ["0x36cc7061f388d40a1fb71f2f60da3a55f7cbe80fbd2a19e3a24dc966bb6e3228",false]); console.log(blockByNumber); }; eth_getBlockByHash(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block("0x36cc7061f388d40a1fb71f2f60da3a55f7cbe80fbd2a19e3a24dc966bb6e3228", False)) ``` ## Use case The `eth_getBlockByHash` is excellent for analytics purposes. For example, it can be used to quickly find out the hashes of transactions that create new smart contracts. The `transactions` field, an array of all the transactions within the block, is returned in the response. By iterating through this array and examining each transaction's `to` field, a user can determine which transactions in the block are creating new smart contracts. The following example uses the web3.js library to create a function to extract this data: The previous example shows how the `eth_getBlockByHash` method might be used to know which transactions create a smart contract. When a new smart contract is created, the `to` field will be `null`. Running this function on a block hash with the transactions detail field set to `true` will inspect each transaction's `to` field. If the field is `null`, the transaction creates a smart contract, printing the transaction hash, which can then be used for further analysis. # eth_getBlockByNumber | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getblockbynumber openapi/gnosis_node_api/blocks_info/eth_getBlockByNumber.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns information about the block matching the given block number. Use it on Gnosis via Chainstack. Gnosis Chain API method that returns information about the block matching the given block number. This method can retrieve a wide range of information about the block, such as its hash, timestamp, difficulty, and gas limit. You can also retrieve transaction objects included in the block if needed. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — a block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Gnosis Chain nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Gnosis blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. Gnosis Chain uses POSDAO (Proof of Stake-based Delegated Proof of Authority) consensus and this value is not relevant. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — the array of transaction objects. See [eth\_getTransactionByHash](/reference/gnosis-gettransactionbyhash) for the exact shape. * `uncles` — the array of uncle hashes. ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByNumber = async () => { const blockByNumber = await provider.send("eth_getBlockByNumber", ["latest",false]); console.log(blockByNumber); }; eth_getBlockByNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block("latest", False)) ``` ## Use case The `eth_getBlockByNumber` method can determine whether a block you are analyzing has been included in the main chain or was forked due to a "reorg". A block hash is generated through a cryptographic hash function, which takes the block's data, including transactions and other information, and the hash of the parent block (the previous block) and outputs a unique, fixed-length digest. This hash represents the current block within the blockchain. The script compares the parent hash of the following block with the hash under inspection. If the two values match, it can be concluded that the block was included in the main chain. Conversely, if the values do not match, it indicates that the block was not used to generate the next block's hash and has therefore been excluded from the main chain, resulting in a forked block. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL" const provider = new ethers.JsonRpcProvider(NODE_URL) // If the hash of the block to inspect is === to the parent hash of the next block, then this is not a forked block. async function isForked(block) { const nextBlock = blockToInspect + 1 console.log(`Block to inspect: ${block}`) console.log(`Next block: ${nextBlock} \n`) const blockToInspectHash = (await provider.getBlock(block, false)).hash console.log(`Block hash of the block you are inspecting: ${blockToInspectHash} \n`) const nextBlockParentHash = (await provider.getBlock(nextBlock, false)).parentHash console.log(`Parent hash of the next block: ${nextBlockParentHash} \n`) if (String(nextBlockParentHash) == String(nextBlockParentHash)) { console.log(`You are inspecting the correct block`) return false } else { console.log(`You are inspecting a forked block!`) return true } } const blockToInspect = 16394103 isForked(blockToInspect) ``` In this case, the `eth_getBlockByNumber` is used to retrieve the hash of a block you want to analyze and the parent hash of the next block. # eth_getBlockTransactionCountByHash | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getblocktransactioncountbyhash openapi/gnosis_node_api/blocks_info/eth_getBlockTransactionCountByHash.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. Gnosis Chain API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. ## Parameters * `hash` — the block hash of the requested block. ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockHash) => { const count = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); console.log(count); }; getTransactionsCount('0x1f9478a9ef62c5216be342e1a1cb673c61c10a91cb7ca71a31debfd4e9d1feac'); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block_transaction_count("0x1f9478a9ef62c5216be342e1a1cb673c61c10a91cb7ca71a31debfd4e9d1feac")) ``` ## Use case `eth_getBlockTransactionCountByHash` is a useful tool for analyzing transaction volume on the Gnosis Chain. For instance, a [new block is generated on the Gnosis blockchain every 5 seconds](https://etherscan.io/chart/blocktime), resulting in approximately 720 blocks per hour. Using the ethers.js library, one can inspect the past 720 blocks starting from the latest block and use `eth_getBlockTransactionCountByHash` to find the number of transactions in each block. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function retrieveTransactionsCount() { // Get the current block number const currentBlockNumber = await provider.getBlockNumber(); // Calculate the block number from one hour ago const oneHourAgoBlockNumber = currentBlockNumber - 720; // 720 blocks/hr on average on the Gnosis mainnet // Initialize a variable to store the total number of transactions let totalTransactionCount = 0; // Loop through all blocks from the current block number to the block number from one hour ago for (let block = currentBlockNumber; block > oneHourAgoBlockNumber; block--) { // Get the hash of each block const getBlockHash = await provider.getBlock(block); const blockHash = getBlockHash.hash // Get the number of transactions in this block using each hash const transactionCount = Number(await provider.send("eth_getBlockTransactionCountByHash", [blockHash])); // Add the number of transactions in this block to the total totalTransactionCount += transactionCount; console.log(`Block #${block} has ${transactionCount} transactions.`); } console.log(`The total number of transactions in the previous hour is: ${totalTransactionCount}`); } retrieveTransactionsCount(); ``` This example connects to a Gnosis Chain node using the ethers.js library. The `retrieveTransactionsCount` function gets the current block number, calculates the block number from one hour ago, and uses a for loop to iterate through all blocks in that range. The function then retrieves the hash of each block and the number of transactions in each block and adds it to a total transaction count. The final result, the total number of transactions in the previous hour, is logged to the console. This is a good example of how you can combine different methods. # eth_getBlockTransactionCountByNumber | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getblocktransactioncountbynumber openapi/gnosis_node_api/blocks_info/eth_getBlockTransactionCountByNumber.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the number of transactions in a block specified by block number or tag. Gnosis via Chainstack. Gnosis Chain API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockId) => { const count = await provider.send("eth_getBlockTransactionCountByNumber", [blockId]); console.log(count); }; getTransactionsCount('16933408') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.get_block_transaction_count('latest')) # A hex value starting with "0x" is accepted as well. ``` ## Use case `eth_getBlockTransactionCountByNumber` can be used to analyze how many transactions are included on the Gnosis Chain in a certain period. For instance, a [new block is generated on the Gnosis blockchain every 5 seconds](https://gnosisscan.io/chart/blocktime), resulting in approximately 720 blocks per hour. Using the ethers.js library, one can inspect the past 720 blocks starting from the latest block and use `eth_getBlockTransactionCountByNumber` to find the number of transactions in each block to sum them. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function retrieveTransactionCounts() { // Get the current block number const currentBlockNumber = await provider.getBlockNumber(); // Calculate the block number from one hour ago const oneHourAgoBlockNumber = currentBlockNumber - 720; // 720/blocks/hr on average on the Gnosis mainnet // Initialize a variable to store the total number of transactions let totalTransactionCount = 0; // Loop through all blocks from the current block number to the block number from one hour ago for (let block = currentBlockNumber; block > oneHourAgoBlockNumber; block--) { // Get the number of transactions in this block const transactionCount = await provider.send("eth_getBlockTransactionCountByNumber", [block]); // Make the necessery conversions from hex to decimal const stringCount = BigInt(transactionCount).toString(); const decimalCount = Number(stringCount) // Add the number of transactions in this block to the total totalTransactionCount += decimalCount; const stringBlock = BigInt(block).toString(); const decimalBlock = Number(stringBlock) console.log(`Block ${decimalBlock} has ${decimalCount} transactions.`); } console.log(`The total number of transactions in the previous hour is: ${totalTransactionCount}`); } retrieveTransactionCounts(); ``` Using `eth_getBlockTransactionCountByNumber` is a bit more straightforward compared to the [example shown using `eth_getBlockTransactionCountByHash`](/reference/gnosis-getblocktransactioncountbyhash) for this same use case, you don't need to extract the block hash. Instead, you can query the transaction count directly. This example is made using the ethers.js library, and you can notice that it requires more parsing compared to web3.js, and the results are given as hexadecimal. # eth_chainId | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getchainid openapi/gnosis_node_api/chain_info/eth_chainId.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the current chain ID. eth_chainId JSON-RPC method available on the Gnosis blockchain via Chainstack. Gnosis Chain API method that returns the current chain ID. Chain ID is used to sign replay-protected transactions and generally verify if a network is the desired one. It was introduced in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). ## Parameters * `none` ## Response * `quantity` — EIP-155 Chain ID ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const chainId = async () => { // This will return the value in Hex const chainId = await provider.send("eth_chainId"); console.log(`Hex Chain ID: ${chainId}`); }; chainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.chain_id) ``` ## Use case One possible use case for the `eth_chainId` method in Gnosis Chain is to determine which chain a user is connected to when using a DApp with the MetaMask browser extension. This can be useful to provide a customized user experience based on the specific chain that the user is interacting with. Here is an example of how the `eth_chainId` method might be used in this context with MetaMask. Keep in mind that the Chain ID is returned as a hexadecimal value: ```js JavaScript theme={"system"} // Check which network is selected on MetaMask async function checkChain() { // Declare the desired chain ID to match const desiredChainId = '0x64'; // Gnosis mainnet chain ID // Retrieve the current Chain ID selected by the user ethereum.request({ method: 'eth_chainId' }).then(chainId => { // compare the actual chain ID it to the declared chain ID if (chainId !== desiredChainId) { console.log(`You are currently on the wrong network. Please switch to the mainnet.`); // Call the promtSwitch function promtSwitch() } else { console.log("This is the correct network") } }) } // Prompt user to switch to a determined network based on the chain ID async function promtSwitch() { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x64' // chainId must be in HEX with 0x in front }], }); } ``` In this case, the `checkChain` function is called to get the current chain ID using the Gnosis Chain object provided by MetaMask. The chain ID is then checked to determine if the user is connected to the Gnosis Chain mainnet and calls the `promtSwitch` function to prompt the user to switch chain in case the chain ID returned does not match the `desiredChainId` constant. # eth_getCode | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getcode openapi/gnosis_node_api/accounts_info/eth_getCode.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that retrieves the compiled bytecode of a smart contract providing its address as a parameter. Gnosis via Chainstack. Gnosis Chain API method that retrieves the compiled bytecode of a smart contract providing its address as a parameter. This method returns a hexadecimal string representing the smart contract's bytecode. Developers can use this bytecode to verify if a smart contract is legitimate and to ensure that it performs the intended functions. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response `data` — the compiled bytecode of a smart contract. Returns `0x` if the address is not associated with a smart contract. ## `eth_getCode` code examples The following example retrieves the compiled bytecode from the [Chainlink token smart contract](https://gnosisscan.io/token/0xe2e73a1c69ecf83f464efce6a5be353a37ca09b2?a=0x15918ff7f6c44592c81d999b442956b07d26cc44). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getCode = async (address, block) => { const code = await provider.send("eth_getCode", [address, block]); console.log(code); }; getCode("0x4d224452801ACEd8B2F0aebE155379bb5D594381", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" code = web3.eth.get_code("0x4d224452801ACEd8B2F0aebE155379bb5D594381", "latest") print(web3.to_hex(code)) # Convert the Bytes result into HEX. ``` ## Use case A practical use case for the `eth_getcode` method is verifying whether an address is associated with a smart contract. This can be particularly useful for blockchain explorers, auditors, and DApp developers, who must ensure that the code they interact with is legitimate. Here is an example using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function verifyContractAddress(address) { try { const bytecode = await provider.getCode(address); return bytecode !== '0x'; } catch (error) { console.error(`Error: ${error.message}`); return false; } } async function main() { const addressToVerify = "0xB6564b9dC7Ae42070E82dcb371AF612f6598255c" const isContract = await verifyContractAddress(addressToVerify); if (isContract) { console.log(`📑The address ${addressToVerify} is a smart contract.`); } else { console.log(`🚨The address ${addressToVerify} is not a smart contract.`); } } main(); ``` The `verifyContractAddress` function takes an `address` as a parameter and returns a `boolean` value indicating whether the address is associated with a smart contract. The function works by using the `provider.getCode` method to retrieve the bytecode of the contract associated with the given address. If the returned bytecode is not equal to `0x`, then the function assumes that the address is associated with a smart contract and returns `true`. If an error occurs while calling `provider.getCode`, the function returns `false` and logs the error message to the console. The `main` function is responsible for calling `verifyContractAddress` with a specific address to verify and log the results to the console. In this example, `main` is set up to verify the address `0xB6564b9dC7Ae42070E82dcb371AF612f6598255c`. After calling `verifyContractAddress`, `main` logs either a success message indicating that the address is associated with a smart contract or a warning message indicating that it is not. Overall, this code provides a useful utility for verifying the legitimacy of smart contract addresses on Gnosis Chain. It can be useful for auditors, blockchain explorers, and DApp developers who want to ensure that they are interacting with trustworthy code on the blockchain. # eth_getFilterChanges | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getfilterchanges openapi/gnosis_node_api/filter_handling/eth_getFilterChanges.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Gnosis Chain API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Filters can be created using one of the following methods: * [eth\_newFilter](/reference/gnosis-newfilter) * [eth\_newPendingTransactionFilter](/reference/gnosis-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/gnosis-newblockfilter) After creating a filter using one of the available methods, the resulting filter ID can be used to fetch changes by calling the `eth_getFilterChanges` method. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/gnosis-newfilter) > * [eth\_newPendingTransactionFilter](/reference/gnosis-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/gnosis-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `string` — the filter ID of the filter for which you want to retrieve the changes ## Response * `array` — an array that represents the changes that have occurred on the blockchain since the last time the filter was polled: * For filters created with `eth_newBlockFilter`: * `blockHash` — the hashes of the new blocks since the last time the filter was polled. * For filters created with `eth_newPendingTransactionFilter`: * `transactionHash` — the hashes identifying new pending transactions since the last time the filter was polled. * For filters created with `eth_newFilter`, the following event logs: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getFilterChanges` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/gnosis-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getFilterChanges(filter) { try { const changes = await provider.send('eth_getFilterChanges', [filter]); console.log(changes); // Log the with the new data } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' getFilterChanges(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_filtered_events(filter_id): try: filter_changes = web3.eth.get_filter_changes(filter_id) print(filter_changes) # the array of events that match the filter except Exception as e: print(e) filter_id = '0x0' get_filtered_events(filter_id) ``` ## Use case The `eth_getFilterChanges` method can be used to periodically poll a filter to retrieve new data. In the following Python program, using the web3.py library creates a filter using [eth\_newBlockFilter](/reference/gnosis-newblockfilter) to poll for new blocks, and uses `eth_getFilterChanges` to poll the filter and retrieve new blocks. Here is the implementation in Python: ```python main.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def create_block_filter(): try: # Create a new block filter block_filter = web3.eth.filter('latest') # Split the string at the space character parts = str(block_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(f'Error creating block filter: {e}') def get_filtered_events(filter_id): try: filter_changes = web3.eth.getFilterChanges(filter_id) return filter_changes # the array of events that match the filter except Exception as e: print(f'Error getting filter changes: {e}') def main(): # Create a block filter block_filter_id = create_block_filter() if block_filter_id is not None: print(f'New block filter ID: {block_filter_id}') else: print('Error creating block filter') return # Loop forever and poll the filter every 10 seconds while True: print('Polling for new blocks...') new_blocks = get_filtered_events(block_filter_id) if new_blocks is not None: print(new_blocks) else: print('Error getting filter changes') time.sleep(10) if __name__ == '__main__': main() ``` Here is the explanation of the code: The `create_block_filter` function creates a new block filter with the `web3.eth.filter('latest')` method, which matches against the latest block. The resulting filter object is converted to a string, split into parts using the `split` method and the filter ID is extracted from the second part using array indexing. The filter ID is then returned by the function. The `get_filtered_events` function uses the `eth_getFilterChanges` method to retrieve the new blocks. The function takes a single parameter, `filter_id`, which is a string that represents the ID of the filter to be polled, the filter created by the `create_block_filter` function in this case. If the filter ID is valid, `get_filtered_events` calls `web3.eth.getFilterChanges(filter_id)` to retrieve the array of events that match the filter criteria. This array is then returned by the function, which is then printed to the console in the `main` function. The `main` function encapsulates the program logic, calling `create_block_filter` to create a new block filter and print the resulting filter ID to the console. The function then enters an infinite loop, calling `get_filtered_events` every 10 seconds to retrieve the changes to the blockchain that match the filter criteria. If the changes are not `None`, they are printed to the console. An error message is printed to the console if an error occurs while creating the block filter or retrieving the filter changes. # eth_gasPrice | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getgasprice openapi/gnosis_node_api/gas_data/eth_gasPrice.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the current gas base fee of the network. Reference for eth_gasPrice on Gnosis via Chainstack. Gnosis Chain API method that returns the current gas base fee of the network. The gas price is the quantity of the native token the transaction's sender must pay per unit of gas consumed. The value returned is in Wei. ## Parameters * `none` ## Response * `quantity` — the integer value of the current gas base fee, returned in Wei ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(node_url); async function getGasPrice() { const baseFee = await provider.send("eth_gasPrice"); console.log(`The base gas fee is: ${baseFee} Wei`); } getGasPrice() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.gas_price) ``` ## Use case You can use `eth_gasPrice` to calculate the total gas value to send with a transaction based on the base and priority fee system. This concept was implemented with [EIP-1559](https://metamask.io/1559/) in Ethereum's London hard fork. EIP-1559 aimed to solve the problem of network congestion by implementing a dynamic fee market mechanism, which adjusts the fee required to process a transaction based on network demand. With the current system, the total gas price is composed of a **base fee** determined by the network's load and a **priority fee** added by the user. > [!NOTE] > > **Note** > > The `eth_gasPrice` method returns the base fee. The following script calculates the total fee to send with a transaction based on the base fee and an arbitrary priority fee that the user can choose. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getGas(priorityFee) { const priorityFeeWei = ethers.parseUnits(String(priorityFee), "gwei"); const baseFee = (await provider.getFeeData()).gasPrice; const totalFee = priorityFeeWei + baseFee return [totalFee, baseFee] } async function main() { // The priority fee is constant at 2 Gwei, the user can change it. const PRIORITY_FEE = 2; const [totalFee, baseFee] = await getGas(PRIORITY_FEE) const baseFeeGwei = ethers.formatUnits(baseFee, "gwei") console.log(`The priority fee: ${PRIORITY_FEE} Gwei.`) console.log(`The Ethereum base fee at this moment is: ${Number(baseFeeGwei).toFixed(2)} Gwei.`) const gweiFee = ethers.formatUnits(totalFee, "gwei") console.log(`The total gas fee should be at least: ${Number(gweiFee).toFixed(2)} Gwei.`) } main() ``` The `getGas` function calculates the total fee required to process a transaction. The function takes in a `priorityFee` as input representing the fee a user is willing to pay to prioritize their transaction processing. The function first converts the `priorityFee` from Gwei to Wei (the smallest unit of ether) using `ethers.parseUnits`. Then, it uses the `provider.getFeeData` method to retrieve the current base fee set by the network. The function finally adds the priority fee to the base fee and returns the total fee along with the base fee as an array. The `main` function is the entry point of the code. It first sets a constant `PRIORITY_FEE` to 2 Gwei. Then calls the `getGas` function with `PRIORITY_FEE` as an argument and destructures the returned array into two variables `totalFee` and `baseFee`. The code then converts `baseFee` and `totalFee` from Wei to Gwei using `ethers.formatUnits` for better readability, and logs the results to the console. The main function logs the priority fee, the base fee at that moment, and the minimum recommended total fee that should be paid to process a transaction. # eth_getLogs | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getlogs openapi/gnosis_node_api/logs_and_events/eth_getLogs.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns an array of all logs that match a given filter object. Available on Gnosis via Chainstack JSON-RPC nodes. Gnosis Chain API method that returns an array of all logs that match a given filter object. Logs are records of smart contract-generated events on the network and contain valuable information about transactions and smart contract interactions. Some common use cases for `eth_getLogs` include monitoring smart contract events, tracking token transfers, and analyzing blockchain data. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. > * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. > * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. > * `earliest` — the earliest available or genesis block. > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `array` — an array of log objects that match the specified filter or an empty array if there have been no new events since the last poll: * `address` — the contract address from which the event originated * `topics` — an array of 32-byte data fields containing indexed event parameters * `data` — the non-indexed data that was emitted along with the event * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getLogs` code examples The following examples retrieve the logs of the Transfer event from the [Matic token](https://gnosisscan.io/token/0x7122d7661c4564b7c6cd4878b06766489a6028a2#readProxyContract) smart contract on Gnosis Chain. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getLogs = async () => { const latestBlock = await provider.getBlockNumber(); const filter = { fromBlock: latestBlock - 10, toBlock: 'latest', address: '0x7122d7661c4564b7C6Cd4878B06766489a6028A2', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const logs = await provider.getLogs(filter); console.log(logs); }; getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) latest_block = web3.eth.block_number filter = { 'fromBlock': latest_block - 10, 'toBlock': 'latest', 'address': '0x7122d7661c4564b7C6Cd4878B06766489a6028A2', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } logs = web3.eth.get_logs(filter) print(logs) ``` > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about the `eth_getLogs` method. ## Block range limitations While `eth_getLogs` is a powerful tool, it's crucial to understand its limitations, particularly when working with different EVM-compatible chains, as these networks often have different constraints. The `eth_getLogs` method allows you to select a range of blocks to get events from and is important to exercise proper management. `eth_getLogs` block range is capped by your [subscription plan](/docs/limits#evm-range-limits). On high-activity chains or contracts, query smaller ranges and paginate to avoid oversized responses or timeouts. ## Use case One use case for `eth_getLogs` is to retrieve the transfer events for a specific Token ID from an ERC-721 smart contract. ERC-721 is a token standard for non-fungible tokens (NFTs), and each token transfer is recorded as an event on the blockchain. By retrieving these events, developers can track the movement of NFTs and build applications that interact with them. # eth_getStorageAt | Gnosis Source: https://docs.chainstack.com/reference/gnosis-getstorageat openapi/gnosis_node_api/accounts_info/eth_getStorageAt.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the data stored at a specific storage slot within a smart contract. Chainstack Gnosis reference. Gnosis Chain API method that returns the data stored at a specific storage slot within a smart contract. It can help developers to read a smart contract's internal state, like user data or balances, to inform decisions and build more advanced applications that interact with smart contracts on the blockchain. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the smart contract to query * `quantity` — the index of the storage position to retrieve the data from * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response `data` — the data stored in the storage slot. Returned as a hexadecimal string. ## `eth_getStorageAt` code examples The following example returns the storage value at slot `7` from the [USDC smart contract](https://gnosisscan.io/address/0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83#code) on the Gnosis Chain mainnet. In this case, this is the address owning the smart contract. This method can retrieve the different storage values in time by querying different blocks in the past. Note that to query a state in the past, you need an [archive node](https://chainstack.com/evm-nodes-a-dive-into-the-full-vs-archive-mode). > [!NOTE] > > curl needs a `HEX String` starting with `0x` to identify the block if you want to use a hex integer block number as a parameter.\ > For example, block number `14000000` will be `0xD59F80`. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getstorage = async (address, slot, block) => { const storage = await provider.send("eth_getStorageAt", [address, slot, block]); console.log(storage); }; getstorage("0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83", "0x7", "latest" ) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" storage = web3.eth.get_storage_at("0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83", 7, "latest") print(web3.to_hex(storage)) ``` ## Use case One use case for `eth_getStorageAt` is analyzing how the value stored at a specific storage slot of a smart contract changes over time. For example, a developer may want to track the change in a specific variable of a smart contract, such as the total supply of a token, and perform some analysis on it. Here's an example implementation of this use case using ethers.js. The following program analyzes the [Matic token](https://gnosisscan.io/token/0x7122d7661c4564b7c6cd4878b06766489a6028a2#readProxyContract) smart contract on the Gnosis Chain to track how the total supply changes over time. Here's an example implementation of this use case using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // You need an archive node is going more than 127 blocks in the past. async function getStorageValueOverTime() { // The address of the APE smart contract const address = '0x7122d7661c4564b7C6Cd4878B06766489a6028A2'; // The index of the storage slots where the total supply and name data are stored const totalSupplySlot = 4; const nameSlot = 0; // The range of blocks to analyze const startBlock = 16000000; const endBlock = await provider.getBlockNumber(); for (let blockNumber = startBlock; blockNumber <= endBlock; blockNumber++) { // Retrieve the name and total supply from the smart contract storage at each block const [name, supply] = await Promise.all([ provider.getStorage(address, nameSlot, blockNumber), provider.getStorage(address, totalSupplySlot, blockNumber) ]); // Decode the name and total supply from hexadecimal format to their original values const decodedName = ethers.toUtf8String(name.replace(/(00)+$/, "")); const decodedSupply = BigInt(supply); // Convert the total supply from wei to ether and round to 4 decimal places const convertedSupply = ethers.formatEther(decodedSupply); const adjustedSupply = Number(convertedSupply).toFixed(4); // Output the block number, name, and total supply for each block console.log(`Block ${blockNumber}: Name = ${decodedName}, Total supply = ${adjustedSupply}`); } } async function main() { try { await getStorageValueOverTime(); } catch (error) { console.error(error); } } main(); ``` The bulk of the program is the `getStorageValueOverTime` function. The `getStorageValueOverTime` function retrieves and outputs the name and total supply of the Matic smart contract at each block height between a specified start block and the current block height. The function begins by declaring constants for the Matic contract address, the storage slot indices for the name and total supply data, the starting block height, and the current block height. Next, the function enters a `for` loop that iterates over every block between the start block and the current block height. Within the loop, the function retrieves the name and total supply from the smart contract storage at the current block height using the `provider.getStorage` method. The `Promise.all` method is used to retrieve the name and total supply in parallel for improved efficiency. The `ethers.toUtf8String` method is then used to convert the name from hexadecimal format to its original string value. The `BigInt` function is used to convert the total supply from hexadecimal format to a native `bigint` value, which can be used for mathematical operations. The `ethers.formatEther` method is then used to convert the total supply from wei to ether. Finally, the `Number` and `toFixed` methods are used to round the total supply to four decimal places and output the block number, name, and total supply for each block; note that this is for readability, and you should use the raw wei data to make calculations. Overall, the `getStorageValueOverTime` function provides a way to monitor changes in the state of a smart contract over time, which can be useful for understanding the contract's behavior and performance. # Gnosis Chain API reference: xDai sidechain JSON-RPC Source: https://docs.chainstack.com/reference/gnosis-getting-started Use the Gnosis Chain JSON-RPC and Beacon APIs on Chainstack to query the xDai EVM sidechain, send transactions, and access consensus layer data. ## What is the Gnosis Chain API The Gnosis Chain, formerly known as xDai Chain, is a sidechain that aims to address the scaling challenges of the Ethereum blockchain. It is an EVM-based blockchain network that uses the xDAI stablecoin for transactions, which maintains a 1:1 value ratio with the DAI stablecoin. Gnosis Chain leverages faster block and epoch times for efficient transaction processing, minimizing network congestion. Gnosis Chain provides a stable, lean, and high-performance network with cross-chain bridges for smooth interactions. It is designed for Web3 builders looking to develop scalable solutions and offers a familiar developer experience for those accustomed to Ethereum. Its EVM compatibility and cross-chain connections make it an ideal environment for building various applications. ## What is the Gnosis Beacon Chain API The Gnosis Beacon Chain API allows developers to communicate with the Gnosis Chain [consensus layer](https://beacon.gnosischain.com/). ## How to start using the Gnosis Beacon Chain API To use the Gnosis Chain API, you need access to a Gnosis Chain RPC node. Follow these steps to sign up on Chainstack, deploy a Gnosis Chain RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the Gnosis Chain and use the Gnosis Chain API to build. > [!NOTE] > > **See also** > > [Build better with Gnosis Chain](https://chainstack.com/build-better-with-gnosis/) # eth_getTransactionByBlockHashAndIndex | Gnosis Source: https://docs.chainstack.com/reference/gnosis-gettransactionbyblockhashandindex openapi/gnosis_node_api/transaction_info/eth_getTransactionByBlockHashAndIndex.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. Gnosis Chain API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on Gnosis Chain. ## Parameters * `hash` — the hash of the block * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal ## Response * `object` — a transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blockHash, index) => { const transaction = await provider.send("eth_getTransactionByBlockHashAndIndex", [blockHash, index]); console.log(transaction); } getTransaction("0x6ae9bd64f2bdacc7cee93ca2d6b7b9c44ca6265367550bd7ffa81c616f27fe4c", "0x3"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block("0x6ae9bd64f2bdacc7cee93ca2d6b7b9c44ca6265367550bd7ffa81c616f27fe4c", 2)) ``` ## Use case The `eth_getTransactionByBlockHashAndIndex` can be used to retrieve transaction details from a block. In the following example the `getSendersInBlock` checks how many transactions exist in a block, then receives the sender of each (`from` field) and displays it. > [!NOTE] > > Note that there are simpler ways to get this information, and this is only a practical example of how to use `eth_getTransactionByBlockHashAndIndex`, if the number of transactions in the block is already known, the first step can be skipped. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getSendersInBlock(blockHash) { const block = await provider.getBlock(blockHash); const transactions = block.transactions; console.log(`Block ${blockHash} contains ${transactions.length} transactions`); for (let i = 0; i < transactions.length; i++) { const transaction = await provider.send("eth_getTransactionByBlockHashAndIndex", [blockHash, ethers.toBeHex(i)]); console.log(`Sender of transaction at index ${i}:`, transaction.from); } } getSendersInBlock("0x6ae9bd64f2bdacc7cee93ca2d6b7b9c44ca6265367550bd7ffa81c616f27fe4c"); ``` The number of transactions in the block is determined using `transactions.length` and the total number of transactions logged to the console. A `for` loop is then used to retrieve information about each transaction in the block. For each iteration of the loop, the code calls `provider.send` with the `eth_getTransactionByBlockHashAndIndex` method to retrieve information about the transaction at the current index. The `from` property of the returned transaction object is logged to the console to show the sender of the transaction. # eth_getTransactionByBlockNumberAndIndex | Gnosis Source: https://docs.chainstack.com/reference/gnosis-gettransactionbyblocknumberandindex openapi/gnosis_node_api/transaction_info/eth_getTransactionByBlockNumberAndIndex.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. Gnosis Chain API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Gnosis Chain. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal. ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blocNumber, index) => { const transaction = await provider.send("eth_getTransactionByBlockNumberAndIndex", [blocNumber, index]); console.log(transaction); } getTransaction("latest", "0x1"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block(16819758, 2)) # Hex encoded parameters starting with "0x" are accepted as well. ``` ## Use case The `eth_getTransactionByBlockNumberAndIndex` can be used to retrieve transaction details from a block. In the following example the `getTypeInBlock` function checks how many transactions exist in a block, then retrieves the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of each (`type` field), and displays it. > [!NOTE] > > Note that there are simpler ways to get this information, and this is only a practical example of how to use `eth_getTransactionByBlockNumberAndIndex`, if the number of transactions in the block is already known, the first step can be skipped. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTypeInBlock() { const latestBlock = await provider.getBlockNumber(); const block = await provider.getBlock(latestBlock); const transactions = block.transactions; console.log(`The latest block is ${latestBlock} and contains ${transactions.length} transactions`); for (let i = 0; i < transactions.length; i++) { const transaction = await provider.send("eth_getTransactionByBlockNumberAndIndex", [ethers.toBeHex(latestBlock), ethers.toBeHex(i)]); console.log(`The type of transaction at index ${i}:`, transaction.type); } } getTypeInBlock(); ``` The number of transactions in the block is determined using `transactions.length` and the total number of transactions logged to the console. A `for` loop is then used to retrieve information about each transaction in the block. For each iteration of the loop, the code calls the `eth_getTransactionByBlockNumberAndIndex` method through `provider.send` to retrieve information about the transaction at the current index. The `type` property of the returned transaction object is logged to the console to show the sender of the transaction. # eth_getTransactionByHash | Gnosis Source: https://docs.chainstack.com/reference/gnosis-gettransactionbyhash openapi/gnosis_node_api/transaction_info/eth_getTransactionByHash.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the information about a transaction from the transaction hash. Use it on Gnosis via Chainstack. Gnosis Chain API method that returns the information about a transaction from the transaction hash. It is used to retrieve information about a specific transaction by its hash. The method returns an object with details about the transaction, such as the sender, recipient, gas used, transaction value, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction., usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet; it essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. This field is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (transactionHash) => { const transaction = await provider.send("eth_getTransactionByHash", [transactionHash]); console.log(transaction); }; getTransaction("0xb30747ec4485c3ddb0ff846475e35a67486e82ca0f5abff9302d4ba4606d28ab") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction("0xb30747ec4485c3ddb0ff846475e35a67486e82ca0f5abff9302d4ba4606d28ab")) ``` ## Use case One use case for `eth_getTransactionByHash` is to inspect a specific transaction and display information about it, such as the address that was sent, the block it was included in, and the number of transactions the sender sent so far (nonce). This information can be useful for monitoring and analyzing transactions. Here's an example of how you can achieve this in ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTransaction(transactionHash) { // Get the transaction using the provided transaction hash const transaction = await provider.getTransaction(transactionHash) // Store the desired parameters in constants const sender = transaction.from; const block = transaction.blockNumber const transactionsCount = transaction.nonce // Log the transaction information to the console console.log(`Transaction ${transactionHash} was sent from address ${sender}`); console.log(`It was included in block ${block}`); console.log(`The sender has sent ${transactionsCount} transactions so far\n`); } getTransaction("0xb30747ec4485c3ddb0ff846475e35a67486e82ca0f5abff9302d4ba4606d28ab") ``` In this example, the program starts by connecting to a node endpoint. Then, call the `eth_getTransactionByHash` method and pass in the transaction hash as an argument. The method returns a Promise that resolves to a transaction object, which is used to retrieve the sender address, the block number the transaction was included in, and the address nonce. The last step is to log the information to the console. > [!NOTE] > > Note that the desired parameters are stored in constants, so you can use them for further processing. # eth_getTransactionCount | Gnosis Source: https://docs.chainstack.com/reference/gnosis-gettransactioncount openapi/gnosis_node_api/accounts_info/eth_getTransactionCount.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the number of transactions sent from an address at the selected block. Gnosis via Chainstack. Gnosis Chain API method that returns the number of transactions sent from an address at the selected block. This value is also called `nonce`; it is an important piece of information, especially to ensure that a transaction is not sent twice. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`— the address to retrieve the transaction count. * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## Response * `quantity` — an integer value identifying the number of transactions sent from an address at the specified block. ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getNonce = async (address, blockId) => { const nonce = await provider.send("eth_getTransactionCount", [address, blockId]); console.log(nonce); }; getNonce("0xc448b51CF665210419543C5A53F13B8cE87834B2", "latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_count("0xc448b51CF665210419543C5A53F13B8cE87834B2", "latest")) ``` ## Use case One of the most common use cases for `eth_getTransactionCount`is to create the transaction object built in a script designed to send a transaction. The nonce field is required, and it is retrieved using the `eth_getTransactionCount` method. The following code shows how to build a `rawTransaction` using ethers.js. > [!WARNING] > > **Security notice** > > You need your private key to sign the transaction; never share your private key with anyone. > > On a side note, the private key in this case must be imported without '0x' at the beginning of the string. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Initialize the wallet with your private key const PRIVATE_KEY = "PRIVATE_KEY"; const wallet = new ethers.Wallet(PRIVATE_KEY, provider); // Validate input parameters and return an error message if invalid const validateInputs = (fromAddress, toAddress) => { if (!ethers.isAddress(fromAddress)) { throw new Error(`Invalid fromAddress: ${fromAddress}`); } if (!ethers.isAddress(toAddress)) { throw new Error(`Invalid toAddress: ${toAddress}`); } }; // Async function to create a raw transaction async function createRawTransaction(fromAddress, toAddress, value) { validateInputs(fromAddress, toAddress); const nonce = await provider.getTransactionCount(fromAddress); const gasPrice = (await provider.getFeeData()).gasPrice; const gasLimit = await provider.estimateGas({ from: fromAddress, to: toAddress, }); // Build the transaction object const transactionObject = { to: toAddress, gasPrice: gasPrice, gasLimit: gasLimit, nonce: nonce, value: value, }; // Sign the transaction using the wallet and return the raw transaction const rawTransaction = await wallet.signTransaction(transactionObject); return rawTransaction; } async function main() { const rawTransaction = await createRawTransaction('0x6f46cf5569aefa1acc1009290c8e043747172d89', "0xe341b2f448eb190495ed4a89c01f20078b26b0f6" , "100000000000000") console.log(`Raw transaction: ${rawTransaction}`) } main() ``` In summary, the code creates a valid raw transaction that can be broadcasted to the network. During the process, the script retrieves important values such as the `nonce`, `gasPrice`, and `gasLimit`, builds a transaction object, and signs it using a private key. First, the `createRawTransaction` function calls `validateInputs` to ensure that the `fromAddress` and `toAddress` parameters are valid addresses. If either of these addresses is invalid, the function throws an error with a descriptive message. Next, the function makes a call to `provider.getTransactionCount` with the `fromAddress` as a parameter. This method returns the number of transactions sent from the `fromAddress`, and is used as the `nonce` for the transaction. The function retrieves the `gasPrice` and `gasLimit` for the transaction using `provider.getFeeData` and `provider.estimateGas`, respectively. The [eth\_gasPrice](/reference/gasprice) is the amount of gas that the transaction sender is willing to pay per unit of gas consumed by the transaction, while the [eth\_estimateGas](/reference/gnosis-estimategas) is an estimate of the maximum amount of gas that the transaction will consume. With the `nonce`, `gasPrice`, and `gasLimit` values, the function builds a transaction object, which includes the `toAddress`, `gasPrice`, `gasLimit`, `nonce`, and `value` of the transaction. The value is the amount transferred from the `fromAddress` to the `toAddress`. The transaction object is then passed to `wallet.signTransaction`, which signs it using the wallet's private key and returns the serialized signed transaction as a `rawTransaction`. This raw transaction can be broadcasted to the network to execute the transaction using [eth\_sendRawTransaction](/reference/gnosis-sendrawtransaction). # eth_getTransactionReceipt | Gnosis Source: https://docs.chainstack.com/reference/gnosis-gettransactionreceipt openapi/gnosis_node_api/transaction_info/eth_getTransactionReceipt.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that retrieves the receipt of a transaction from a transaction hash. eth_getTransactionReceipt on Gnosis via Chainstack. > [!NOTE] > > **No response in the interactive API** > > The response to the call might be too big for the web app to handle, so if you are not getting it here, copy the CURL example and run in your terminal—this will work. Gnosis Chain API method that retrieves the receipt of a transaction from a transaction hash. It can be used to verify the success or failure of a transaction, check the execution status of a smart contract, retrieve logs, check if a transaction deployed a smart contract, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `Transaction receipt` — the receipt object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the address of the contract created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects generated by this transaction, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionReceipt = async (transactionHash) => { const receipt = await provider.send("eth_getTransactionReceipt", [transactionHash]); console.log(receipt); }; getTransactionReceipt('0xb30747ec4485c3ddb0ff846475e35a67486e82ca0f5abff9302d4ba4606d28ab') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_receipt("0xb30747ec4485c3ddb0ff846475e35a67486e82ca0f5abff9302d4ba4606d28ab")) ``` ## Use case A practical use case for `eth_getTransactionReceipt` is to verify a transaction's status and whether it was successful or not. This can be especially useful when deploying a contract, as you can use the receipt to confirm that the contract was deployed successfully and has a valid contract address. Here's an example in ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function verifyTransaction(transactionHash) { // Get the transaction receipt const receipt = await provider.getTransactionReceipt(transactionHash); // Check if the receipt is not yet available if (!receipt) { console.log(`Transaction ${transactionHash} is still pending...`); return; } // Check if the transaction was successful if (receipt.status) { console.log(`✅The transaction was successful! \nHash: ${transactionHash}`); // Check if the transaction deployed a smart contract if (receipt.contractAddress != null) { console.log(`📑The smart contract was deployed at address: ${receipt.contractAddress}`); } else { console.log(`❗This transaction did not deploy a smart contract`); } } else { console.log(`💥The transaction failed. \nHash: ${transactionHash}`); } } verifyTransaction('0xb30747ec4485c3ddb0ff846475e35a67486e82ca0f5abff9302d4ba4606d28ab') ``` This script holds an async function called `verifyTransaction` that takes a transaction hash as input. The function uses the ethers.js library to retrieve the transaction receipt of the specified transaction using the `eth_getTransactionReceipt` method. The function first checks if the transaction receipt is not yet available, and if so, it logs a message to the console indicating that the transaction is still pending. If the receipt is available, the function checks the status property of the receipt to determine if the transaction was successful. If the transaction was successful, the function checks if a smart contract was deployed as part of the transaction. The function logs the smart contract's address to the console if a smart contract is deployed. If not, the function logs a message indicating that no smart contract was deployed. If the transaction was not successful, the function logs a message indicating that the transaction failed, along with the transaction hash. # net_listening | Gnosis Source: https://docs.chainstack.com/reference/gnosis-listening openapi/gnosis_node_api/client_info/net_listening.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns a boolean value indicating whether the client is currently listening for network connections or not. Gnosis Chain API method that returns a boolean value indicating whether the client is currently listening for network connections or not. The `net_listening` method can be useful in many scenarios where it is important to monitor the status of a Gnosis Chain client's network connection to ensure that it is working properly. ## Parameters * `none` ## Response * `boolean` — a boolean value that indicates whether or not a node is currently actively seeking peer connections. `true` if the client is actively listening for connections, `false` if not. ## `net_listening` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const isListening = async () => { const listen = await provider.send("net_listening"); console.log(listen); } isListening() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) is_listening = web3.provider.make_request('net_listening', []) print(is_listening) ``` ## Use case A method like `net_listening` can be used to monitor the network status of the Gnosis Chain node a developer is connecting to. Imagine a blockchain explorer where users can search for transactions, addresses, and other information on the Gnosis Chain. In order to provide accurate information to its users, the website needs to be able to monitor the current status of its connection. Here is an example of this implementation using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Create a function to monitor the node status and display information to users async function monitorNetworkStatus() { try { // Check if the client is currently listening for network connections const isListening = await provider.send("net_listening", []); // Get the number of peers currently connected to the client const peerCount = Number(await provider.send("net_peerCount", [])); // Log the network status information to the console console.log(`Network status: ${isListening ? 'Listening' : 'Not listening'} | Peers: ${peerCount}`); } catch (error) { // Handle any errors that might occur console.error(`Error monitoring network status: ${error}`); } } // Call the monitorNetworkStatus function at regular intervals to continuously monitor the network status setInterval(monitorNetworkStatus, 5000); // Monitor every 5 seconds ``` The `monitorNetworkStatus` function defined in this code uses the `net_listening` method to check whether the Gnosis Chain client is currently listening for network connections, and the [net\_peerCount](/reference/gnosis-peercount) method to get the number of peers currently connected to the client. The function logs this information to the console, which could be displayed to the user in a user-friendly format on the blockchain explorer website. By calling this function at regular intervals using the `setInterval` function, the website can continuously monitor the node's status and provide users with up-to-date information about the overall health of the node. # Logs and events | Gnosis Source: https://docs.chainstack.com/reference/gnosis-logs-rpc-methods Gnosis Chain event log RPC methods for retrieving smart contract events, transaction logs, and filter-based data from your Chainstack node endpoints. Retrieving logs information from the Gnosis Chain can be done by using the following methods: > [card] [eth\_getLogs](/reference/gnosis-getlogs) > [card] [eth\_newFilter](/reference/gnosis-newfilter) > [!NOTE] > > Find logs limitations in [Understanding eth\_getLogs limitations](/docs/understanding-eth-getlogs-limitations) # eth_subscribe ("logs") | Gnosis Source: https://docs.chainstack.com/reference/gnosis-native-subscribe-logs Subscribe to real-time Gnosis Chain event logs via WebSocket. Receive notifications whenever new logs matching your specified filter criteria are emitted. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) the event filter options: * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about topics and event logs. ## Response * `subscription` — the subscription ID. * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_subscribe("logs")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_subscribe", "params": ["logs", {"address":"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewLogs() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['logs', {"address":"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewLogs(); ``` This will generate a continuous stream of data displaying new event logs matching the filter. Use [eth\_unsubscribe](/reference/gnosis-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("logs")` method on the Gnosis Chain is used to subscribe to the logs generated by smart contracts on the blockchain. These logs contain information about events that have occurred on the blockchain, such as details of a smart contract function and outputs of transactions. A practical and real-world scenario for using the `eth_subscribe("logs")` method could be in the context of a decentralized exchange (DEX). In the context of a DEX, trades are executed using smart contracts that run on the Gnosis Chain. When a trade is executed on a DEX, a smart contract is called to process the transaction. This smart contract generates a log that contains information about the trade, such as the amount of cryptocurrency being traded, the parties involved, and so on. By using the `eth_subscribe("logs")` method, a user or application can subscribe to the logs generated by the DEX's smart contracts. This can be useful for tracking trades in real time, monitoring the performance of the DEX, and generating reports on trading activity. For example, a trader might use the `eth_subscribe("logs")` method to monitor the logs generated by a DEX in order to identify trends in trading activity. They might look for patterns in the volume and price of trades, which could help them make more informed trading decisions. Similarly, an analytics platform might use the `eth_subscribe("logs")` method to collect data on trading activity across multiple DEXs. By analyzing this data, they could generate insights into the performance of different DEXs, which could be useful for investors and traders looking to make informed decisions about where to trade. # eth_subscribe ("newHeads") | Gnosis Source: https://docs.chainstack.com/reference/gnosis-native-subscribe-newheads Subscribe to new Gnosis Chain block headers via WebSocket. Receive real-time notifications whenever a new block is added to the Gnosis blockchain. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `newHeads` in this case. ## Response * `subscription` — the subscription ID. * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Gnosis nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Gnosis Chain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Gnosis Chain network since the inception of the network. It measures the overall security and integrity of the network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/gnosis-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `eth_subscribe("newHeads")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewBlocks() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewBlocks(); ``` This will generate a continuous stream of data displaying new block headers as they are added to the chain. Use [eth\_unsubscribe](/reference/gnosis-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newHeads")` method can be useful in a variety of real-world scenarios, such as: * Real-time monitoring of blockchain activity. Companies or individuals who rely on the Gnosis Chain network for their business can use `eth_subscribe("newHeads")` to receive real-time notifications about new blocks being added to the chain. This can help them keep track of important transactions, such as high-value trades, in real time and respond quickly to any issues that may arise. * Decentralized Finance (DeFi) applications. DeFi applications, such as lending platforms, can use `eth_subscribe("newHeads")` to monitor the blockchain for new blocks that might contain important data, such as changes in interest rates or new lending opportunities. * Smart contract automation. Developers can use `eth_subscribe("newHeads")` to trigger automated smart contract actions whenever a new block is added to the chain. For example, a smart contract could automatically execute an action order whenever a new block is added that meets certain criteria. # eth_subscribe ("newPendingTransactions") | Gnosis Source: https://docs.chainstack.com/reference/gnosis-native-subscribe-newpendingtransactions Subscribe to pending Gnosis Chain transactions via WebSocket. Receive real-time notifications when new unconfirmed transactions enter the mempool. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `newPendingTransactions` in this case. ## Response * `subscription` — the subscription ID. * `string `— the hash of a pending transaction. ## `eth_subscribe("newPendingTransactions")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newPendingTransactions", true]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewPendingTransactions() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newPendingTransactions', true], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewPendingTransactions(); ``` This will generate a continuous stream of data displaying new pending transactions as they are added to the mempool. Use [eth\_unsubscribe](/reference/gnosis-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newPendingTransactions")` method on Gnosis Chain is used to subscribe to the network and receive real-time updates on all new pending transactions on the network. This can be useful in a variety of real-world scenarios, including: * Trading applications. A trading application can use this method to keep track of any new transactions that are being made on the network. This can be particularly useful for high-frequency trading, where speed is crucial. By subscribing to this method, the trading application can receive real-time updates on new trades, allowing it to quickly respond and execute trades. * Monitoring tools. Monitoring tools can use this method to keep track of any suspicious activity on the network. By subscribing to this method, monitoring tools can receive real-time updates on any new pending transactions and quickly take action if necessary. * Payment processors. Payment processors can use the `eth_subscribe("newPendingTransactions")` method to monitor incoming transactions from their customers. This can be particularly useful for businesses that rely on blockchain technology to process payments, as it allows them to track incoming payments in real time. By subscribing to this method, payment processors can receive instant notifications when new payments are received, which can help streamline their payment processing workflows. # eth_unsubscribe | Gnosis Source: https://docs.chainstack.com/reference/gnosis-native-unsubscribe Cancel any active Gnosis Chain WebSocket subscription with eth_unsubscribe. Pass the subscription ID to stop receiving real-time event notifications. When a client subscribes to a particular event, the Gnosis Chain node will send updates as soon as they occur. However, in some cases, the client may no longer be interested in receiving updates for a particular event. The `eth_unsubscribe` method can be used to cancel the following subscriptions: > [card] [eth\_subscribe('newHeads')](/reference/gnosis-native-subscribe-newheads) > [card] [eth\_subscribe('newPendingTransactions')](/reference/gnosis-native-subscribe-newpendingtransactions) > [card] [eth\_subscribe('logs')](/reference/gnosis-native-subscribe-logs) ## Parameters * `string` — the ID of the subscription that you want to cancel ## Response * `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `eth_unsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_unsubscribe", "params": ["0x7529626735859f32962b10b19ee2e3eb"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function removeSubscription() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_unsubscribe', params: ['0x9de3135dd3dd6c6b99bd104061a5af4f'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } removeSubscription(); ``` This will remove the subscription matching the ID passed as a parameter. # eth_newBlockFilter | Gnosis Source: https://docs.chainstack.com/reference/gnosis-newblockfilter openapi/gnosis_node_api/blocks_info/eth_newBlockFilter.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that creates a filter that watches for new blocks on the blockchain. eth_newBlockFilter on Gnosis via Chainstack. Gnosis Chain API method that creates a filter that watches for new blocks on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/gnosis-getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real-time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter The filters created are stored on the blockchain client instance. The filter fill is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/gnosis-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/gnosis-uninstallfilter) to remove the filter ## `eth_newBlockFilter` code examples > [!NOTE] > > **web3.eth.filterdeprecation** > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/gnosis-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newBlockFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') return blocks_filter except Exception as e: print(e) blocks = get_new_blocks() filter_id = blocks.filter_id print(filter_id) ``` ## Use case One use case for `eth_newBlockFilter` in a simple DApp is to listen for new blocks and update the user interface with the latest block information. When the DApp starts, it creates a new filter using `eth_newBlockFilter` to listen for new blocks. When a new block is added to the blockchain, the filter is triggered, and the DApp retrieves the latest block information using a Web3 library. Here is an implementation of this use case using web3.py: ```python main.py theme={"system"} import time from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Set up a new blocks filter def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) def watch_for_blocks(new_filter): # Wait for new blocks to arrive while True: # Retrieve the changes that match the filter changes = web3.eth.get_filter_changes(new_filter) # If there are no changes, wait for new blocks if len(changes) == 0: time.sleep(1) # Wait for 1 second continue # Process the new blocks for change in changes: block = web3.eth.get_block(change) print('New block:', block) def main(): # Create a new filter to watch for new blocks new_filter = get_new_blocks() print(new_filter) # Watch for new blocks watch_for_blocks(new_filter) if __name__ == '__main__': main() ``` The code is divided into three main functions: `get_new_blocks`, `watch_for_blocks`, and `main`. Let's take a closer look at each of them: `get_new_blocks` is responsible for setting up a new filter to watch for new blocks. The `try` block sets up the filter by calling the `eth.filter` method of the web3.py library and passing it the string `latest`. The `split` method is then used to split the resulting string at each space character, and the second part of the resulting list is extracted as the filter ID. The filter ID is returned from the function. > [!NOTE] > > The `eth.filter` method of the web3.py library does not only return the filter ID but it returns a sentence saying `Filter for 0x3c3f5a5e637d29c033e4e7a3f7e870cd`; this is the reason for the string manipulation within the `get_new_blocks` function. `watch_for_blocks` is the function that watches for new blocks. It does this by first entering an infinite loop while `True`. Within this loop, the [eth\_getFilterChanges](/reference/gnosis-getfilterchanges) method is used to retrieve any changes that match the filter ID passed to the function. If there are no changes, the code waits for 1 second using the `time.sleep` method and then continues with the loop. If there are changes, the new blocks are processed by looping through them and calling the `eth.getBlock` method of the web3 library to retrieve the details of each block. Finally, the details of the new blocks are printed on the console. `main` is the function that ties everything together. It first calls `get_new_blocks` to set up a new filter to watch for new blocks, and then passes the resulting filter ID to `watch_for_blocks` to start watching for new blocks. The filter ID is also printed to the console to confirm that the filter has been set up correctly. # eth_newFilter | Gnosis Source: https://docs.chainstack.com/reference/gnosis-newfilter openapi/gnosis_node_api/logs_and_events/eth_newFilter.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that generates a filter object based on the filter parameters. Available on Gnosis via Chainstack JSON-RPC nodes. Gnosis Chain API method that generates a filter object based on the filter parameters. It returns a filter ID, which can be used to retrieve the results of the filter using the [eth\_getFilterChanges](/reference/gnosis-getfilterchanges) method. By creating a filter for specific events, developers can receive notifications when those events occur and use them to trigger actions in their applications. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block > * `earliest` — the earliest available or genesis block > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `result` — a hexadecimal string representing the ID of the newly created filter > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/gnosis-getfilterchanges) to retrieve updated logs * [eth\_uninstallFilter](/reference/gnosis-uninstallfilter) to remove the filter ## `eth_newFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/gnosis-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x7122d7661c4564b7C6Cd4878B06766489a6028A2', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const createFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) filter = { 'toBlock': 'latest', 'address': '0x7122d7661c4564b7C6Cd4878B06766489a6028A2', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } def create_logs_filter(filter_object): try: logs_filter = web3.eth.filter(filter_object) return logs_filter except Exception as e: print(e) logs_filter = create_logs_filter(filter) filter_id = logs_filter.filter_id print(f'New filter ID: {filter_id}') ``` ## Use case You can use `eth_newFilter` to create a filter for a specific action on a smart contract, for example, to monitor the transfer transactions from the [Wrapped ETH token](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2). The idea is to create a filter using the `eth_newFilter` method to monitor an ERC-20 smart contract, WETH in this case. The following filter parameters are selected: ```javascript filter_params theme={"system"} const filter = { // Poll the latest block available toBlock: 'latest', // WETH smart contract address address: '0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1', // Signature of the Transfer event. topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], }; ``` The program is then polling the filter every 2 seconds to check for any new changes using the `eth_getFilterChanges` method. Here is the implementation using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], }; const createAndPollFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(`The filter ID is: ${filterId}`); // the filter ID returned by eth_newFilter // Poll the filter every 30 seconds setInterval(async () => { const filterChanges = await provider.send('eth_getFilterChanges', [filterId]); if (filterChanges.length === 0) { console.log('No new logs since the last poll.'); } else { console.log(filterChanges); // the filter changes returned by eth_getFilterChanges } }, 2000); // 2 seconds in milliseconds return filterId; } catch (error) { console.log(error); } }; createAndPollFilter(); ``` Here's a breakdown of how the code works: The `createAndPollFilter` function is an asynchronous function that creates and polls a filter for the WETH smart contract on Gnosis. The `eth_newFilter` method creates a new filter with the specified parameters. In this case, it passes in the filter object as the filter parameter. The method returns the ID of the newly created filter, which is then logged into the console. The `setInterval` method is used to poll the filter every 2 seconds. It takes a function as the first argument and an interval in milliseconds as the second argument. The function passed to `setInterval` is an asynchronous function that calls `eth_getFilterChanges` with the filter ID returned by `eth_newFilter`. This method returns an array of all the changes to the filter since the last time it was polled. If there are no changes, it logs "No new logs since the last poll." to the console. If there are changes, it logs the filter changes to the console. The `createAndPollFilter` function returns the filter ID, and if there are any errors thrown while creating or polling the filter, the catch block logs the error to the console. # eth_newPendingTransactionFilter | Gnosis Source: https://docs.chainstack.com/reference/gnosis-newpendingtransactionfilter openapi/gnosis_node_api/transaction_info/eth_newPendingTransactionFilter.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that creates a filter that listens for new pending transactions on the blockchain. Chainstack Gnosis reference. Gnosis Chain API method that creates a filter that listens for new pending transactions on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/gnosis-getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter ## Code examples > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/gnosis-getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/gnosis-uninstallfilter) to remove the filter ## `eth_newPendingTransactionFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/gnosis-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newPendingTransactionFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_pending_transactions(): try: blocks_filter = web3.eth.filter('pending') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) new_filter = get_new_pending_transactions() print(new_filter) ``` ## Use case One way to use the `eth_newPendingTransactionFilter` method is to listen for new pending transactions at predefined intervals and extract specific data from them. For instance, a decentralized application might check for pending transactions every second and identify those that transfer a value greater than a certain amount of the ETH token. This could be useful for monitoring high-value transactions or detecting potential fraud or security threats in real time. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Create a filter using eth_newPendingTransactionFilter const createFilter = async () => { try { const filterId = await provider.send('eth_newPendingTransactionFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; // Use the filter to extract the value from each transaction async function getValue(filter) { try { // Retrieve the list of new pending transactions const transactions = await provider.send('eth_getFilterChanges', [filter]); // Loop through the list of transactions and process each one for (const hash of transactions) { const receipt = await provider.send("eth_getTransactionByHash", [hash]); // Check that the receipt is not null and has a non-null value field if (receipt && receipt.value != null) { const value = receipt.value; const decimalValue = BigInt(value).toString(); const convertedValue = ethers.utils.formatEther(decimalValue); // Check if the transferred value is greater than or equal to 100 ETH if (convertedValue >= 100) { console.log(`This transaction is sending more than 100 ETH`); console.log(`Transaction Hash: ${hash}`); console.log(`Value transferred: ${convertedValue} \n`); } } } } catch (error) { console.error(error); // Handle errors that may occur } } // Main program setting an interval to call the `getValue` function at regular intervals async function main() { const filterId = await createFilter(); setInterval(getValue, 1000, filterId); } main() ``` This code sets up an Gnosis Chain filter to listen for new pending transactions and extract specific data from them. The code consists of three functions: `createFilter`, `getValue`, and `main`. Here's an overview of how each function works: The `createFilter` function sets up a new filter using the `eth_newPendingTransactionFilter` method. It returns the ID of the filter, which can be used to retrieve data from the filter later. The `getValue` function retrieves the list of new pending transactions using the [eth\_getFilterChanges](/reference/gnosis-getfilterchanges) method and loops through each transaction. For each transaction, it retrieves the transaction data using the [eth\_getTransactionByHash](/reference/gnosis-gettransactionbyhash) method and checks if the transaction value is greater than or equal to 100 ETH. If the value is above this threshold, the function logs information about the transaction to the console. The `main` function sets up the program by first calling the `createFilter` function to create a new filter and retrieve its ID. It then sets an interval to call the `getValue` function every 1 second, passing the filter ID as an argument to retrieve the latest transaction data at regular intervals. # net_peerCount | Gnosis Source: https://docs.chainstack.com/reference/gnosis-peercount openapi/gnosis_node_api/client_info/net_peerCount.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns the number of peers connected to the node. Available on Gnosis via Chainstack JSON-RPC nodes. Gnosis Chain API method that returns the number of peers connected to the node. This method can be useful for developers who want to monitor the connectivity of their node and ensure that it is functioning as expected. By checking the number of connected peers, developers can verify that their node is receiving updates and is properly synchronized with the network. ## Parameters * `none` ## Response * `quantity` — an integer value indicating the current number of peers connected to the node ## `net_peerCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const peersCount = async () => { const peers = await provider.send("net_peerCount"); console.log(`Peers connected: ${peers}`); } peersCount() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) peers = web3.provider.make_request('net_peerCount', []) print(peers) ``` ## Use case One practical use case for the `net_peerCount` method is to monitor the number of peers connected to a node and take some action if the number drops below a certain threshold. For example, a developer might want to send a notification to check the node if the peer count is low for more than 10 minutes. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const MIN_PEER_COUNT = 5; // Set the minimum number of peers. let lowPeerCountTime = null; // Track the time when peer count first dropped below the threshold. /** * Checks the number of connected peers and sends a notification if the peer count has been low for more than 10 minutes. */ function checkPeerCount() { provider.send("net_peerCount").then((result) => { const peerCount = Number(result); console.log(`Number of connected peers: ${peerCount}`); if (peerCount < MIN_PEER_COUNT) { // Peer count has dropped below the threshold. if (lowPeerCountTime === null) { // This is the first time we've seen a low peer count. lowPeerCountTime = Date.now(); console.log(`Low peer count detected at ${new Date(lowPeerCountTime)}.`); } else if (Date.now() - lowPeerCountTime > 10 * 60 * 1000) { // Peer count has been low for more than 10 minutes, send a notification. console.log('Sending notification to check the node.'); // TODO: Add code here to send a notification, e.g. via email or SMS. } } else { // Peer count is above the threshold. lowPeerCountTime = null; } }); } // Check the peer count once when the script starts. checkPeerCount(); // Check the peer count every minute. setInterval(checkPeerCount, 60 * 1000); ``` This code defines a function called `checkPeerCount` that retrieves the current number of connected peers by sending the `net_peerCount` request through the provider and converting the returned value to a number. The function then checks whether the peer count is below a minimum threshold (defined as `MIN_PEER_COUNT`), and takes appropriate action based on the result. If the peer count is below the threshold and this is the first time the count has dropped below the threshold, the function records the current time and logs a message to the console. If the peer count remains below the threshold for more than 10 minutes, the function logs a message to the console indicating that a notification should be sent and optionally sends a notification via email or SMS. If the peer count is above the threshold, the function resets the `lowPeerCountTime` variable to null. By running the `checkPeerCount` function periodically, we can ensure that we are notified if the peer count drops below a certain threshold and stays low for an extended period of time, which can indicate issues with the node's connectivity. # eth_sendRawTransaction | Gnosis Source: https://docs.chainstack.com/reference/gnosis-sendrawtransaction openapi/gnosis_node_api/execute_transactions/eth_sendRawTransaction.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that allows submitting a signed transaction to the network. eth_sendRawTransaction on Gnosis via Chainstack. Gnosis Chain API method that allows submitting a signed transaction to the network. Once a transaction is signed, you can use the `eth_sendRawTransaction` method to submit the signed transaction to the Gnosis Chain network for processing. > [!WARNING] > > Note that the interactive example in this page will not work, due to the fact that `eth_sendRawTransaction` is used to modify the state of the blockchain, it is not possible to duplicate the same request. ## Parameters * `data` — the signed transaction. The serialized transaction data, which includes the transaction metadata such as the nonce, gas price, gas limit, recipient address, and data payload, as well as the digital signature generated using the private key associated with the address that is sending the transaction. Typically, signed with a library such as web3.py, web3.js, or ethers.js using the sender's private key. ## Response * `result` — the unique hash identifying the transaction or the zero hash if the transaction is not available yet. The zero hash is a special value that represents an invalid or null hash. ## `eth_sendRawTransaction` code examples The following examples demonstrate how to use Web3 libraries to make an ETH transfer on the Sepolia testnet. > [!NOTE] > > **Additional libraries requirement** > > Note that web3.js requires installing two extra libraries: > > * [ethereumjs-tx](https://www.npmjs.com/package/@ethereumjs/tx) > * [ethereumjs-common](https://www.npmjs.com/package/@ethereumjs/common) ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function sendEth(value) { // Define the sender and receiver addresses, and the private key const sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a'; const receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114'; const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Define the gas limit const gasLimit = await provider.estimateGas({ from: sender, to: receiver, value: ethers.parseEther(value), }); // Get the transaction count for the sender address const nonce = await provider.getTransactionCount(sender); // Define the transaction object const transactionObject = { to: receiver, gasPrice: ethers.parseUnits('50', 'gwei'), gasLimit: gasLimit.toString(), nonce: nonce, value: ethers.parseEther(value), }; // Sign the transaction using the private key const wallet = new ethers.Wallet(privateKey, provider); console.log(`Sending transaction...`) const transaction = await wallet.sendTransaction(transactionObject); console.log('Transaction hash:', transaction.hash); } sendEth("2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the sender and receiver addresses, and the private key sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a' receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114' private_key = '1111111111111111111111111111111111111111111111111111111111111111' # Estimate gas limit gas_limit = web3.eth.estimate_gas({ 'from': sender, 'to': receiver }) # Build the transaction object transaction = { 'nonce': web3.eth.get_transaction_count(sender), 'to': receiver, 'value': web3.to_wei(1, 'ether'), # value to send 'gas': gas_limit, 'gasPrice': web3.eth.gas_price, 'chainId': web3.eth.chain_id } # Sign the transaction signed_tx = web3.eth.account.sign_transaction(transaction, private_key) print(f'Signed transaction: {signed_tx}') # Send the transaction print('Sending transaction...') tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) print(f'Transaction hash: {web3.toHex(tx_hash)}') ``` > [!NOTE] > > Note that you will need to edit the `sender`, `receiver`, and `privateKey` fields to be able to use this code. ## Use case One potential use case for `eth_sendRawTransaction` is to automatically swap ETH for a token when its price reaches a certain level. `eth_sendRawTransaction` will allow specifying custom transaction parameters, such as gas price or gas limit, to optimize the speed and cost of the transaction. For instance, you could build a program monitoring the price of a specific token and execute a `swapExactETHForTokens` transaction when the token reaches a certain price level, it could use `eth_sendRawTransaction` to submit a raw transaction with a custom gas price and limit that are optimized for the current network conditions. # subscribe "logs" | Gnosis Source: https://docs.chainstack.com/reference/gnosis-subscribelogs Reference for the subscribe "logs" JSON-RPC method on the Gnosis blockchain via Chainstack nodes. Parameters, response, and examples included. Gnosis Chain subscription equivalent to [eth\_newFilter](/reference/gnosis-newfilter). `subscribe("logs")` allows developers to subscribe to real-time updates about new event logs on the Gnosis Chain; the application will receive notifications whenever new logs matching the filter are emitted. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) The subscription options: * `fromBlock` — an integer that specifies the starting block number from which the logs should be fetched. `null` by default. * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `subscribe("logs")` code example > [!NOTE] > > Note that subscriptions require a WebSocket connection. Use the ethers.js `WebSocketProvider` to attach event listeners to a log filter: * `provider.on(filter, listener)` — registers a listener that activates for each new log matching the filter. * `provider.on("error", listener)` — activates if an error is detected during the subscription. * `provider.off(filter)` — removes the listener and stops receiving updates for the filter. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); // Only log transfer events from the given smart contract. const logsFilter = { address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } async function subscribeToLogs() { try { // Create a new subscription to logs matching the filter provider.on(logsFilter, handleLogs); // Attach an event listener for subscription errors provider.on("error", handleError); console.log("Subscribed to logs"); } catch (error) { console.error(`Error subscribing to logs: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs the filtered events async function handleLogs(log) { console.log(log) } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new logs: ${error}`); } subscribeToLogs(); ``` ## Use case A practical use case for `subscribe("logs")` is a DApp that continuously listens for new specific event logs and isolates certain fields for analytics purposes. This is useful, for example, to track events about a specific ERC-721 token. # subscribe ("newBlockHeaders") | Gnosis Source: https://docs.chainstack.com/reference/gnosis-subscribenewblockheaders Reference docs for the subscribe ("newBlockHeaders") JSON-RPC method on the Gnosis blockchain, available via Chainstack JSON-RPC nodes. Gnosis Chain ethers.js subscription equivalent to [eth\_newBlockFilter](/reference/gnosis-newblockfilter). `subscribe("newBlockHeaders")` allows developers to subscribe to real-time updates about new block headers on the Gnosis Chain; the application will receive notifications whenever a new block is added to the blockchain. The notification will include information about the new block, such as its block number, hash, and timestamp. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `newBlockHeaders` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Gnosis Chain nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Gnosis Chain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Gnosis Chain network since the inception of the network. It measures the overall security and integrity of the network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/gnosis-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `subscribe("newBlockHeaders")` code example > [!NOTE] > > Note that ethers.js subscriptions require a WebSocket connection. With ethers.js you attach a listener to the provider's `"block"` event, which fires every time a new block is added to the chain. The listener receives the new block number; use `provider.getBlock` to fetch the full block header. You can also register a one-off `"error"` listener on the provider, and call `provider.off("block")` to stop receiving updates: * `"block"` — fires for each new block and returns the block number. * `"error"` — fires if an error is detected during the subscription. * `provider.off("block")` — removes the listener and stops the subscription. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToNewBlocks() { try { // Create a new subscription to the 'block' event provider.on("block", handleNewBlock); // Attach an event listener for any errors provider.on("error", handleError); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs the received block header data async function handleNewBlock(blockNumber) { const blockHeader = await provider.getBlock(blockNumber); console.log(blockHeader); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToNewBlocks(); ``` ## Use case A practical use case for `subscribe("newBlockHeaders")` is a DApp that continuously listens for new block headers, then isolates the block number and `baseFeePerGas` for analytics purposes. The following is an implementation of this concept using ethers.js subscriptions: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); let blockCount = 0; async function subscribeToNewBlocks() { try { // Create a new subscription to the 'block' event provider.on("block", handleNewBlock); // Attach an event listener for any errors provider.on("error", handleError); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } function unsubscribeFromNewBlocks() { provider.off("block"); console.log("Successfully unsubscribed!"); return process.exit(1); } /* Fallback functions to react to the different events */ // Event listener that extracts block number and base fee per gas from the received block header data async function handleNewBlock(blockNumber) { const blockHeader = await provider.getBlock(blockNumber); const baseFeeGwei = ethers.formatUnits(blockHeader.baseFeePerGas, "gwei"); blockCount++; console.log(`Block number: ${blockNumber} \n Base Fee per Gas: ${baseFeeGwei} Gwei \n`); if (blockCount === 100) { unsubscribeFromNewBlocks(); } } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToNewBlocks(); ``` This code creates a new subscription to the `block` event using the `provider.on` method. Registering a listener on this event lets the application react to every new block as it is added to the chain. The code defines two event listener functions that are attached to the provider: `handleNewBlock` and `handleError`. The `handleNewBlock` function is called when a new block is received; it fetches the full block header with `provider.getBlock` and extracts the block number and base fee per gas, formatting the fee with `ethers.formatUnits`. The `handleError` function is called when an error occurs, and it logs an error message. This code includes an `unsubscribeFromNewBlocks` function which removes the `block` listener with `provider.off("block")` and quits the program using `return process.exit(1)` after it received 100 new blocks. This is to demonstrate its use. Finally, the code calls the `subscribeToNewBlocks` function, which creates the subscription and attaches the event listeners. When a new block header is received, the `handleNewBlock` function is called to extract the block data and log it to the console. # subscribe ("pendingTransactions") | Gnosis Source: https://docs.chainstack.com/reference/gnosis-subscribependingtransactions Reference docs for the subscribe ("pendingTransactions") JSON-RPC method on the Gnosis blockchain, available via Chainstack JSON-RPC nodes. Gnosis Chain ethers.js subscription equivalent to [eth\_newPendingTransactionFilter](/reference/gnosis-newpendingtransactionfilter). `subscribe("pendingTransactions")` allows developers to subscribe to real-time updates about new block headers on the gnosis blockchain; the application will receive notifications whenever a new block is added to the blockchain. The notification will include information about the new block, such as its block number, hash, and timestamp. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `pendingTransactions` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `string` — the hash identifying the pending transaction. ## `subscribe("pendingTransactions")` code example > [!NOTE] > > Note that ethers.js subscriptions require a WebSocket connection. Use the `WebSocketProvider` event listeners to react to subscription events: * `"pending"` — activates for each new pending transaction hash received. * `provider.on("error", ...)` — activates if an error is detected during the subscription. * `provider.removeAllListeners("pending")` — unsubscribes from the pending transactions event. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToPendingTransactions() { try { // Subscribe to the 'pending' event to receive new pending transaction hashes provider.on("pending", handleNewPending); // Attach an error listener to the provider provider.on("error", handleError); console.log("Subscribed to pending transactions"); } catch (error) { console.error(`Error subscribing to new pending transactions: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs the received pending transaction hashes function handleNewPending(transactionHash) { console.log(transactionHash); } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToPendingTransactions(); ``` ## Use case A practical use case for `subscribe("pendingTransactions")` is a DApp that continuously listens for new pending transactions, then isolates the `from`, `to`, and `value` fields for analytics purposes. This is useful, for example, to only track transactions that move at least a certain amount of xDAI. The following is an implementation of this concept using ethers.js subscriptions: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeTopendingTransactions() { try { // Subscribe to the 'pending' event to receive new pending transaction hashes provider.on("pending", handleNewPending); // Attach an error listener to the provider provider.on("error", handleError); console.log("Subscribed to pending transactions"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } // Use this function to unsubscribe and exit the program function unsubscribe() { provider.removeAllListeners("pending"); console.log("Successfully unsubscribed!"); return process.exit(1); } /* Fallback functions to react to the different events */ // Event listener that logs the received pending transactions and extracts from, to, and value fields async function handleNewPending(transactionHash) { try { const transaction = await provider.getTransaction(transactionHash); if (!transaction) return; // The transaction may no longer be in the mempool const from = transaction.from; const to = transaction.to; const value = transaction.value; if (value >= 100000000000000000000n) { // 100 xDAI in wei console.log(`----- New pending transaction ------`); console.log(`From: ${from}`) console.log(`To: ${to}`) console.log(`Value: ${Number(ethers.formatEther(value)).toFixed(2)} xDAI\n`) } } catch (error) { console.error(`Error getting transaction: ${error}`); } } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeTopendingTransactions(); ``` This code creates a new subscription to the `pending` event using the `provider.on` method on a `WebSocketProvider`. This method registers event listeners that are called whenever a new pending transaction hash is received. The code defines two event listener functions that are attached to the provider: `handleNewPending` and `handleError`. The `handleNewPending` function is called when a new pending transaction is received; it runs the [eth\_getTransactionByHash](/reference/gnosis-gettransactionbyhash) method via `provider.getTransaction` and extracts the `from`, `to`, and `value` fields. If the value transferred is above 100 xDAI, the data is logged. The code includes the `unsubscribe` function that can be implemented in the logic to unsubscribe and exit the program when a condition is met. The `handleError` function is called when an error occurs, and it logs an error message. Finally, the code calls the `subscribeTopendingTransactions` function, which creates the subscription and attaches the event listeners. When a new pending transaction is received, the `handleNewPending` function is called to extract the data and log it to the console. # subscribe ("syncing") | Gnosis Source: https://docs.chainstack.com/reference/gnosis-subscribesyncing Monitor Gnosis Chain node sync status in real time with web3.js. Subscribe to syncing updates to track block download and chain synchronization progress. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `logs` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `object` — the following sync object when the node is currently syncing: * `startingBlock` — the block number from which the node began syncing. * `currentBlock` — the latest block number the node has synced to. * `highestBlock` — the estimated highest block number that needs to be synced. * `pulledStates` — the number of state entries that have already been downloaded. * `knownStates` — the estimated number of state entries to be downloaded during the sync. * `boolean` — returns `False` when the node is already in sync. ## `subscribe("syncing")` code example > [!NOTE] > > Note that subscriptions require a WebSocket connection. Use a `WebSocketProvider` with ethers.js. ethers.js does not expose a dedicated `syncing` event, so use the WebSocketProvider to send the raw `eth_subscribe` request and listen for `eth_subscription` notifications on the underlying socket: * `eth_subscription` notification — fires for each new syncing event. * `error` — fires if an error is detected during the subscription. * the subscription ID — returned by `eth_subscribe` after the subscription is successfully created. * `eth_unsubscribe` — removes the subscription and returns `true` if successful. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToSync() { try { // Create a new subscription to the 'syncing' event const subscriptionId = await provider.send("eth_subscribe", ["syncing"]); handleConnected(subscriptionId); // Listen for subscription notifications on the underlying WebSocket provider.websocket.onmessage = (message) => { const payload = JSON.parse(message.data); if (payload.method === "eth_subscription" && payload.params.subscription === subscriptionId) { handleSync(payload.params.result); } }; provider.websocket.onerror = handleError; } catch (error) { console.error(`Error subscribing to sync: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs a message when there is a new syncing event function handleConnected(subscriptionId) { console.log(`New subscription: ${subscriptionId}`); } // Event listener that logs the filtered events async function handleSync(sync) { console.log(sync) } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error: ${error}`); } subscribeToSync(); ``` ## Use case A practical use case for `subscribe("syncing")` is a DApp that continuously listens for the status of a node and notifies the developer if the node falls behind a certain number of blocks. The following is an implementation of this concept using ethers.js subscriptions, this program will leave a notification in the console if the node falls more than 100 blocks behind. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToSync() { try { // Create a new subscription to the 'syncing' event const subscriptionId = await provider.send("eth_subscribe", ["syncing"]); handleConnected(subscriptionId); // Listen for subscription notifications on the underlying WebSocket provider.websocket.onmessage = (message) => { const payload = JSON.parse(message.data); if (payload.method === "eth_subscription" && payload.params.subscription === subscriptionId) { handleSync(payload.params.result); } }; provider.websocket.onerror = handleError; } catch (error) { console.error(`Error: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs a message when there is a new syncing event function handleConnected(subscriptionId) { console.log(`New subscription: ${subscriptionId}`); } // Event listener that logs the filtered events async function handleSync(sync) { let currentBlock; let highestBlock; const status = sync.status; for (const property in status) { if (property === 'CurrentBlock') { currentBlock = status[property]; } else if (property === 'HighestBlock') { highestBlock = status[property]; } } if (currentBlock !== undefined && highestBlock !== undefined) { const blocksBehind = highestBlock - currentBlock; console.log(`The node is ${blocksBehind} blocks behind the network.`); if (blocksBehind > 1000) { alert(`The node is ${blocksBehind} blocks behind the network. Please check your connection.`); } } } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToSync(); ``` This code creates a new subscription to the `syncing` event using the `eth_subscribe` request sent through the `WebSocketProvider`. This returns a subscription ID that is used to match incoming notifications on the underlying WebSocket. The code defines three event listener functions: `handleConnected`, `handleSync`, and `handleError`. The `handleConnected` function is called when the subscription is created, and it logs a message with the subscription ID. The `handleSync` function is called when a new syncing notification arrives, extracts the `CurrentBlock` and the `HighestBlock` field, and then compares them. If the node is more than 1,000 blocks behind, the user will receive an alert. The `handleError` function is called when an error occurs, and it logs an error message. Finally, the code calls the `subscribeToSync` function, which creates the subscription and attaches the event listeners. When a new event is received, the `handleSync` function is called to extract the data and log it to the console. # eth_syncing | Gnosis Source: https://docs.chainstack.com/reference/gnosis-syncing openapi/gnosis_node_api/chain_info/eth_syncing.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method that returns an object with information about the current synchronization status of the node, or false if the node is fully synced. Gnosis Chain API method that returns an object with information about the current synchronization status of the node, or `false` if the node is fully synced. This method can track the progress of a node's synchronization with the Ethereum blockchain. The returned object contains data such as the starting block, current block, and highest block of the node, allowing developers to monitor and estimate the time remaining for the node to sync fully. ## Parameters * `none` ## Response * `result` — the boolean value `false` when not syncing or a JSON object when syncing: * `startingBlock` — the block number where the synchronization process started, encoded as hexadecimal. * `currentBlock` — the block number that the node has currently processed, same as `eth_blockNumber`, encoded as hexadecimal. * `highestBlock` — the block number of the latest block in the blockchain known to the node. ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const syncStatus = async () => { const status = await provider.send("eth_syncing", []); console.log(status); }; syncStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" print (web3.eth.syncing) ``` ## Use case The `eth_syncing` method on Gnosis can be useful for developers building applications that interact with the Gnosis Chain. For example, you may want to provide feedback to your application's user about the status of the Gnosis Chain node the application is connecting to. Here is a code example using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function syncstatus() { try { // Query the sync status const syncing = await provider.send("eth_syncing", []); if (syncing) { console.log(`Node is syncing. Current block: ${syncing.currentBlock}. Highest block: ${syncing.highestBlock}`); } else { console.log('Node is fully synced.'); } } catch (error) { console.error(error); } }; syncstatus() ``` In this example, the program creates a new `provider` instance and connects to the node. Then, it calls `eth_syncing` to get the current synchronization status of the node. If the node is fully synced, the function logs a message to the console indicating that the node is fully synced. If the node is still syncing, the function logs the current block number and the highest block number known to the node. # Transactions info | Gnosis Source: https://docs.chainstack.com/reference/gnosis-transactions-rpc-methods Gnosis Chain transaction info JSON-RPC methods — retrieve transaction data, receipts, and status for monitoring and analyzing on-chain network activity. Retrieving transaction information from the Gnosis Chain can be done by using the following methods: > [card] [eth\_getTransactionByHash](/reference/gnosis-gettransactionbyhash) > [card] [eth\_getTransactionReceipt](/reference/gnosis-gettransactionreceipt) > [card] [eth\_getTransactionByBlockHashAndIndex](/reference/gnosis-gettransactionbyblockhashandindex) > [card] [eth\_newPendingTransactionFilter](/reference/gnosis-newpendingtransactionfilter) # eth_uninstallFilter | Gnosis Source: https://docs.chainstack.com/reference/gnosis-uninstallfilter openapi/gnosis_node_api/filter_handling/eth_uninstallFilter.json POST /5ecc1173d57f331622b75affcca0c9d8 Gnosis Chain API method used to remove a filter previously created using one of the following methods:. Chainstack Gnosis reference. Gnosis Chain API method used to remove a filter previously created using one of the following methods: * [eth\_newFilter](/reference/gnosis-newfilter) * [eth\_newPendingTransactionFilter](/reference/gnosis-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/gnosis-newblockfilter) Upon successful execution, the filter is removed and will no longer emit events. This method can be useful for optimizing resource usage when a developer no longer needs to monitor certain events or blocks. It can also be used to clean up unused filters in order to reduce clutter in the client-side filter list. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/gnosis-newfilter) > * [eth\_newPendingTransactionFilter](/reference/gnosis-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/gnosis-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `string` — the filter ID that you want to uninstall ## Response * `boolean` — a boolean value indicating whether the filter was successfully uninstalled. `true` if successfully removed, and `false` if not. ## `eth_uninstallFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/gnosis-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function removeFilter(filter) { try { const removed = await provider.send('eth_uninstallFilter', [filter]); console.log(removed); // Log whether the filter is removed } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' removeFilter(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def remove_filter(filter_id): try: removed = web3.eth.uninstall_filter(filter_id) return removed except Exception as e: print(e) filter_id = '0x0' print(remove_filter(filter_id)) ``` ## Use case One use case for `eth_uninstallFilter` is to optimize resource usage in a DApp. When a DApp needs to monitor events on the blockchain, it can create a filter using [eth\_newFilter](/reference/gnosis-newfilter),[eth\_newPendingTransactionFilter](/reference/gnosis-newpendingtransactionfilter), or [eth\_newBlockFilter](/reference/gnosis-newblockfilter) to listen for specific events or blocks. However, once the DApp no longer needs to monitor these events or blocks, it can use `eth_uninstallFilter` to stop watching for them. By doing so, the DApp can reduce the number of active filters and free up resources, such as network bandwidth and computational power, that would otherwise be used to maintain the filter. For example, let's say that a DApp is monitoring incoming blocks, logs batches of 50 blocks, and then uses `uninstallFilter` to remove the filter using. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const BLOCKS_TO_FETCH = 50; const BLOCK_FETCH_INTERVAL_MS = 200; async function getNewBlocks() { const filterId = await provider.send("eth_newBlockFilter", []); let blocks = []; while (blocks.length < BLOCKS_TO_FETCH) { const changes = await provider.send("eth_getFilterChanges", [filterId]); //console.log(changes) // show the changes if (changes.length === 0) { await new Promise(resolve => setTimeout(resolve, BLOCK_FETCH_INTERVAL_MS)); // wait for new blocks } else { blocks = blocks.concat(changes); } } const removeFilter = await provider.send("eth_uninstallFilter", [filterId]); // clean up filter if (removeFilter) { console.log(`We got 50 blocks, filter ${filterId} was removed.`) return blocks } } async function main() { const fiftyBlocks = await getNewBlocks(); console.log(fiftyBlocks) } main() ``` This code fetches the latest 50 blocks using a filter and creates an array. The `BLOCKS_TO_FETCH` constant is set to 50, which represents the number of blocks to fetch. The `BLOCK_FETCH_INTERVAL_MS` constant is set to 200, which represents the time interval (in milliseconds) to wait for new blocks to arrive. The `getNewBlocks` function is defined to fetch the blocks. This function uses a loop to retrieve new blocks until it has fetched the desired number of blocks. The loop waits for new blocks to arrive for a certain amount of time before trying again. Inside the `getNewBlocks` function, the [eth\_newBlockFilter](/reference/gnosis-newblockfilter) JSON-RPC method is called to create a new filter to watch for new blocks. The filter ID is returned and stored in the `filterId` variable. An array stores the blocks fetched. Inside the loop, the [eth\_getFilterChanges](/reference/gnosis-getfilterchanges) JSON-RPC method is called to retrieve any new blocks that have arrived since the filter was created. If there are no new blocks, the code waits a certain amount of time before trying again. If there are new blocks, they are stored in the `blocks` array. Once the `blocks` array has reached the `BLOCKS_TO_FETCH` limit, the filter is uninstalled using the `eth_uninstallFilter` JSON-RPC method. The `removeFilter` variable is set to true if the filter is successfully removed. If the filter is removed successfully, a message is logged to the console indicating that `BLOCKS_TO_FETCH` blocks have been fetched, and the filter was removed. The blocks array is returned as the result of the `getNewBlocks` function. The main function is defined to call the `getNewBlocks` function and store the result in the `fiftyBlocks` variable. The `fiftyBlocks` variable is then logged to the console. # Web3.js subscriptions | Gnosis Source: https://docs.chainstack.com/reference/gnosis-web3js-subscriptions-methods Gnosis Chain web3.js subscription methods — subscribe to new block headers, contract events, and other real-time blockchain updates via WebSocket. > [!NOTE] > > Note that all subscriptions require a WebSocket connection. ## Native EVM subscriptions Developers can use the following native methods to subscribe to events: > [card] [eth\_subscribe('newHeads')](/reference/gnosis-native-subscribe-newheads) > [card] [eth\_subscribe('newPendingTransactions')](/reference/gnosis-native-subscribe-newpendingtransactions) > [card] [eth\_subscribe('logs')](/reference/gnosis-native-subscribe-logs) The subscriptions created can be cleared using the following method: > [card] [eth\_unsubscribe](/reference/gnosis-native-unsubscribe) ## web3.js subscriptions The [web3.js](https://web3js.readthedocs.io/en/v1.2.11/getting-started.html) library also offers subscription methods. web3.js subscriptions are the successors of the deprecated `web3.eth.filter` methods and provide a way for developers to receive real-time updates about events on the blockchain without constantly polling the blockchain for new data. Subscriptions can be created using one of the following methods: > [card] [subscribe('newBlockHeaders')](/reference/gnosis-subscribenewblockheaders) > [card] [subscribe('pendingTransactions')](/reference/gnosis-subscribependingtransactions) > [card] [subscribe('logs')](/reference/gnosis-subscribelogs) > [card] [subscribe('syncing')](/reference/gnosis-subscribesyncing) The subscriptions created can be cleared using the following method: > [card] [clearSubscriptions](/reference/gnosis-clearsubscriptions) # debug_getRawBlock | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-debug-get-raw-block openapi/hyperliquid_node_api/hyperevm/evm_debug_get_raw_block.json post /evm The debug_getRawBlock JSON-RPC method returns the complete raw block data for a specific block. Chainstack Hyperliquid EVM reference. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `debug_getRawBlock` JSON-RPC method returns the complete raw block data for a specific block. This method provides the entire block in its raw encoded format, including header and all transactions, useful for low-level blockchain analysis and custom block processing applications. ## Parameters 1. **Block identifier** (string, required): Block number, hash, or "latest"/"earliest"/"pending" ## Response The method returns the complete raw block data in hexadecimal format for the specified block. ### Response structure **Raw block data:** * `result` — Complete raw block data encoded in hexadecimal format, including header and all transactions ## Usage example ### Basic implementation ```javascript theme={"system"} // Get raw block data const getRawBlock = async (blockIdentifier = 'latest') => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'debug_getRawBlock', params: [blockIdentifier], id: 1 }) }); const data = await response.json(); return data.result; }; // Usage getRawBlock('latest').then(blockData => { console.log('Raw block data:', blockData); console.log('Block size:', blockData.length / 2 - 1, 'bytes'); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_getRawBlock", "params": [ "latest" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # debug_getRawBlock has no convenience wrapper, so call it through the provider response = w3.provider.make_request("debug_getRawBlock", ["latest"]) print(response["result"]) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // debug_getRawBlock has no convenience wrapper, so use the generic send method const rawBlock = await provider.send("debug_getRawBlock", ["latest"]); console.log(rawBlock); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // debug_getRawBlock is non-standard, so call it through the request method const rawBlock = await client.request({ method: "debug_getRawBlock", params: ["latest"], }); console.log(rawBlock); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `debug_getRawBlock` method is essential for applications that need to: * **Block analysis**: Perform detailed analysis of complete block structures * **Data archival**: Archive complete blockchain data in raw format * **Custom parsing**: Implement custom block parsing and validation logic * **Protocol research**: Research blockchain protocol implementation details * **Development tools**: Build advanced blockchain development and debugging tools * **Cross-chain bridges**: Process blocks for cross-chain verification * **Academic research**: Support detailed academic blockchain research * **Forensic investigation**: Perform comprehensive forensic analysis of blocks * **Performance testing**: Test block processing performance and efficiency * **Compliance auditing**: Generate detailed compliance reports from raw data This method provides complete raw block access for comprehensive blockchain analysis on the Hyperliquid EVM platform. # debug_getRawHeader | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-debug-get-raw-header openapi/hyperliquid_node_api/hyperevm/evm_debug_get_raw_header.json post /evm The debug_getRawHeader JSON-RPC method returns the raw header data for a specific block. Use it on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `debug_getRawHeader` JSON-RPC method returns the raw header data for a specific block. This method provides the complete block header in its raw encoded format, useful for low-level blockchain analysis, debugging, and applications that need direct access to the block header's binary representation. ## Parameters 1. **Block identifier** (string, required): Block number, hash, or "latest"/"earliest"/"pending" ## Response The method returns the raw header data in hexadecimal format for the specified block. ### Response structure **Raw header data:** * `result` — Complete raw header data encoded in hexadecimal format ## Usage example ### Basic implementation ```javascript theme={"system"} // Get raw header for a specific block const getRawHeader = async (blockIdentifier = 'latest') => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'debug_getRawHeader', params: [blockIdentifier], id: 1 }) }); const data = await response.json(); return data.result; }; // Usage getRawHeader('latest').then(header => { console.log('Raw header:', header); console.log('Header size:', header.length / 2 - 1, 'bytes'); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_getRawHeader", "params": [ "latest" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # debug_getRawHeader has no first-class web3.py wrapper, so call it through the provider response = w3.provider.make_request("debug_getRawHeader", ["latest"]) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // send() reaches methods that have no high-level API in the Provider const rawHeader = await provider.send("debug_getRawHeader", ["latest"]); console.log(rawHeader); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // request() issues a raw JSON-RPC call for methods without a built-in action const rawHeader = await client.request({ method: "debug_getRawHeader", params: ["latest"], }); console.log(rawHeader); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `debug_getRawHeader` method is essential for applications that need to: * **Low-level analysis**: Perform direct analysis of block header binary data * **Protocol research**: Research blockchain protocol details and header structure * **Custom parsing**: Implement custom header parsing and validation logic * **Development tools**: Build blockchain development and debugging tools * **Data archival**: Archive raw blockchain data for historical analysis * **Cross-chain verification**: Verify block headers across different blockchain systems * **Academic research**: Support academic blockchain research projects * **Forensic analysis**: Perform forensic investigation of blockchain data * **Compliance tools**: Build regulatory compliance and auditing tools * **Testing frameworks**: Create comprehensive blockchain testing suites This method provides direct access to raw block header data, enabling low-level blockchain analysis and custom tooling on the Hyperliquid EVM platform. # debug_getRawReceipts | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-debug-get-raw-receipts openapi/hyperliquid_node_api/hyperevm/evm_debug_get_raw_receipts.json post /evm The debug_getRawReceipts JSON-RPC method returns the raw receipt data for all transactions in a specific block. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `debug_getRawReceipts` JSON-RPC method returns the raw receipt data for all transactions in a specific block. This method provides complete receipt information in raw encoded format for all transactions in a block, useful for bulk receipt processing and low-level blockchain analysis. ## Parameters 1. **Block identifier** (string, required): Block number, hash, or "latest"/"earliest"/"pending" ## Response The method returns an array of raw receipt data for all transactions in the specified block. ### Response structure **Raw receipts array:** * `result` — Array of raw receipt data, one for each transaction in the block ## Usage example ### Basic implementation ```javascript theme={"system"} // Get raw receipts for all transactions in a block const getRawReceipts = async (blockIdentifier = 'latest') => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'debug_getRawReceipts', params: [blockIdentifier], id: 1 }) }); const data = await response.json(); return data.result; }; // Usage getRawReceipts('latest').then(receipts => { console.log(`Found ${receipts.length} receipts in block`); receipts.forEach((receipt, index) => { console.log(`Receipt ${index + 1} size: ${receipt.length / 2 - 1} bytes`); }); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_getRawReceipts", "params": [ "latest" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # debug_getRawReceipts has no typed helper, so call it via the raw provider request response = w3.provider.make_request("debug_getRawReceipts", ["latest"]) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // debug_getRawReceipts has no typed helper, so call it via the generic send method const receipts = await provider.send("debug_getRawReceipts", ["latest"]); console.log(receipts); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // debug_getRawReceipts has no typed action, so call it via the generic request method const receipts = await client.request({ method: "debug_getRawReceipts", params: ["latest"], } as any); console.log(receipts); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `debug_getRawReceipts` method is essential for applications that need to: * **Bulk receipt processing**: Process large numbers of transaction receipts efficiently * **Data archival**: Archive complete receipt data in raw format * **Analytics platforms**: Build comprehensive blockchain analytics tools * **Compliance reporting**: Generate detailed compliance and audit reports * **Performance analysis**: Analyze transaction execution performance across blocks * **Custom parsing**: Implement custom receipt parsing and validation logic * **Development tools**: Build advanced blockchain development and debugging tools * **Academic research**: Support detailed academic blockchain research * **Event log analysis**: Analyze contract event logs across entire blocks * **Gas analysis**: Perform detailed gas usage analysis across transactions This method provides efficient bulk access to raw receipt data for comprehensive blockchain analysis on the Hyperliquid EVM platform. # debug_getRawTransaction | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-debug-get-raw-transaction openapi/hyperliquid_node_api/hyperevm/evm_debug_get_raw_transaction.json post /evm The debug_getRawTransaction JSON-RPC method returns the raw transaction data for a specific transaction. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `debug_getRawTransaction` JSON-RPC method returns the raw transaction data for a specific transaction. This method provides the complete transaction in its raw encoded format, useful for low-level transaction analysis, custom parsing, and applications that need direct access to the transaction's binary representation. ## Parameters 1. **Transaction hash** (string, required): The hash of the transaction to retrieve ## Response The method returns the raw transaction data in hexadecimal format. ### Response structure **Raw transaction data:** * `result` — Complete raw transaction data encoded in hexadecimal format ## Usage example ### Basic implementation ```javascript theme={"system"} // Get raw transaction data const getRawTransaction = async (txHash) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'debug_getRawTransaction', params: [txHash], id: 1 }) }); const data = await response.json(); return data.result; }; // Usage const txHash = '0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31'; getRawTransaction(txHash).then(rawTx => { console.log('Raw transaction:', rawTx); console.log('Transaction size:', rawTx.length / 2 - 1, 'bytes'); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_getRawTransaction", "params": [ "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) tx_hash = "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31" # debug_getRawTransaction has no typed wrapper, so call it directly response = w3.provider.make_request("debug_getRawTransaction", [tx_hash]) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const txHash = "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31"; // debug_getRawTransaction has no typed wrapper, so use the generic send const rawTx = await provider.send("debug_getRawTransaction", [txHash]); console.log(rawTx); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const txHash = "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31"; // debug_getRawTransaction has no typed action, so use the raw request method const rawTx = await client.request({ method: "debug_getRawTransaction", params: [txHash], }); console.log(rawTx); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `debug_getRawTransaction` method is essential for applications that need to: * **Transaction analysis**: Perform low-level analysis of transaction structures * **Custom parsing**: Implement custom transaction parsing and validation * **Development tools**: Build transaction debugging and analysis tools * **Data archival**: Archive complete transaction data in raw format * **Forensic investigation**: Perform detailed forensic analysis of transactions * **Protocol research**: Research transaction format and encoding details * **Cross-chain verification**: Verify transactions across different systems * **Security analysis**: Analyze transaction security and validation * **Academic research**: Support academic research on blockchain transactions * **Compliance tools**: Build regulatory compliance and auditing systems This method provides direct access to raw transaction data for comprehensive analysis on the Hyperliquid EVM platform. # debug_traceBlockByHash | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-debug-trace-block openapi/hyperliquid_node_api/hyperevm/evm_debug_trace_block.json post /evm The debug_traceBlockByHash JSON-RPC method returns detailed trace information for all transactions in a specific block. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `debug_traceBlockByHash` JSON-RPC method returns detailed trace information for all transactions in a specific block. This method provides comprehensive debugging information for an entire block, including call traces, gas usage, and execution details for each transaction, making it essential for block-level analysis, forensic investigations, and bulk transaction debugging. ## Parameters 1. **Block hash** (string, required): The hash of the block to trace 2. **Tracer configuration** (object, required): Configuration options for the tracer ### Tracer configuration options * `tracer` (string): The type of tracer to use * `"callTracer"`: Provides detailed call trace information for each transaction * `"prestateTracer"`: Shows state before each transaction execution * `"4byteTracer"`: Tracks function selector usage across all transactions ## Response The method returns an array of detailed trace information for all transactions in the specified block. ### Response structure **Block trace data:** * Array of transaction traces, each containing: * `type` — The type of call (CALL, DELEGATECALL, STATICCALL, CREATE, etc.) * `from` — The address that initiated the call * `to` — The address that received the call * `value` — The value transferred in the call * `gas` — The amount of gas allocated for the call * `gasUsed` — The amount of gas actually consumed * `input` — The input data for the call * `output` — The output data returned by the call * `calls` — Array of sub-calls made during execution ## Usage example ### Basic implementation ```javascript theme={"system"} // Trace all transactions in a block const traceBlock = async (blockHash) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'debug_traceBlockByHash', params: [ blockHash, { tracer: 'callTracer' } ], id: 1 }) }); const data = await response.json(); return data.result; }; // Analyze block execution patterns const analyzeBlockExecution = async (blockHash) => { try { const traces = await traceBlock(blockHash); let totalGasUsed = 0; let successfulTxs = 0; let failedTxs = 0; let totalSubCalls = 0; console.log(`Block Analysis for ${blockHash}:`); console.log(`Total Transactions: ${traces.length}`); traces.forEach((trace, index) => { const gasUsed = parseInt(trace.gasUsed, 16); totalGasUsed += gasUsed; if (trace.output && trace.output !== '0x') { successfulTxs++; } else { failedTxs++; } if (trace.calls) { totalSubCalls += trace.calls.length; } console.log(` TX ${index + 1}: ${trace.from} -> ${trace.to}, Gas: ${gasUsed}`); }); console.log(`\\nSummary:`); console.log(` Successful: ${successfulTxs}`); console.log(` Failed: ${failedTxs}`); console.log(` Total Gas Used: ${totalGasUsed.toLocaleString()}`); console.log(` Average Gas per TX: ${Math.round(totalGasUsed / traces.length).toLocaleString()}`); console.log(` Total Sub-calls: ${totalSubCalls}`); return { totalTransactions: traces.length, successfulTxs, failedTxs, totalGasUsed, averageGasPerTx: Math.round(totalGasUsed / traces.length), totalSubCalls, traces }; } catch (error) { console.error('Error tracing block:', error); throw error; } }; // Find high gas usage transactions in a block const findHighGasTransactions = async (blockHash, gasThreshold = 100000) => { const traces = await traceBlock(blockHash); const highGasTransactions = traces .map((trace, index) => ({ index, from: trace.from, to: trace.to, gasUsed: parseInt(trace.gasUsed, 16), type: trace.type, hasSubCalls: trace.calls && trace.calls.length > 0, subCallCount: trace.calls ? trace.calls.length : 0 })) .filter(tx => tx.gasUsed > gasThreshold) .sort((a, b) => b.gasUsed - a.gasUsed); console.log(`High Gas Transactions (>${gasThreshold.toLocaleString()} gas):`); highGasTransactions.forEach(tx => { console.log(` TX ${tx.index + 1}: ${tx.gasUsed.toLocaleString()} gas, ${tx.subCallCount} sub-calls`); console.log(` ${tx.from} -> ${tx.to}`); }); return highGasTransactions; }; // Detect patterns in block transactions const detectBlockPatterns = async (blockHash) => { const traces = await traceBlock(blockHash); const patterns = { simpleTransfers: 0, contractInteractions: 0, contractDeployments: 0, multiCallTransactions: 0, uniqueFromAddresses: new Set(), uniqueToAddresses: new Set(), methodSignatures: new Map() }; traces.forEach(trace => { patterns.uniqueFromAddresses.add(trace.from); patterns.uniqueToAddresses.add(trace.to); if (trace.type === 'CREATE' || trace.type === 'CREATE2') { patterns.contractDeployments++; } else if (trace.input === '0x' || trace.input === '0x0') { patterns.simpleTransfers++; } else { patterns.contractInteractions++; // Extract method signature (first 4 bytes of input) if (trace.input && trace.input.length >= 10) { const methodSig = trace.input.substring(0, 10); patterns.methodSignatures.set( methodSig, (patterns.methodSignatures.get(methodSig) || 0) + 1 ); } } if (trace.calls && trace.calls.length > 0) { patterns.multiCallTransactions++; } }); console.log('Block Pattern Analysis:'); console.log(` Simple Transfers: ${patterns.simpleTransfers}`); console.log(` Contract Interactions: ${patterns.contractInteractions}`); console.log(` Contract Deployments: ${patterns.contractDeployments}`); console.log(` Multi-call Transactions: ${patterns.multiCallTransactions}`); console.log(` Unique From Addresses: ${patterns.uniqueFromAddresses.size}`); console.log(` Unique To Addresses: ${patterns.uniqueToAddresses.size}`); return patterns; }; // Usage const blockHash = '0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e'; analyzeBlockExecution(blockHash).then(analysis => { console.log('Block analysis completed'); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceBlockByHash", "params": [ "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e", { "tracer": "callTracer" } ], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block_hash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e" # debug_traceBlockByHash is not wrapped by web3.py, so call it directly. traces = w3.provider.make_request( "debug_traceBlockByHash", [block_hash, {"tracer": "callTracer"}], ) print(traces["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blockHash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"; // debug_traceBlockByHash has no dedicated helper, so use the raw send method. const traces = await provider.send("debug_traceBlockByHash", [ blockHash, { tracer: "callTracer" }, ]); console.log(traces); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blockHash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"; // debug_traceBlockByHash is not a first-class action, so use the request method. const traces = await client.request({ method: "debug_traceBlockByHash", params: [blockHash, { tracer: "callTracer" }], } as any); console.log(traces); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `debug_traceBlockByHash` method is essential for applications that need to: * **Block analysis**: Analyze entire blocks for patterns, gas usage, and execution details * **Forensic investigation**: Investigate suspicious blocks and transaction patterns * **Performance monitoring**: Monitor block execution performance and gas efficiency * **MEV analysis**: Analyze Maximum Extractable Value opportunities across entire blocks * **Security auditing**: Audit blocks for security issues and attack patterns * **Compliance monitoring**: Monitor blocks for regulatory compliance and reporting * **Analytics platforms**: Build comprehensive blockchain analytics and reporting tools * **Research tools**: Support academic and commercial blockchain research * **Debugging tools**: Debug complex multi-transaction scenarios and dependencies * **Gas optimization**: Analyze gas usage patterns across multiple transactions * **Network monitoring**: Monitor network health and transaction execution patterns * **Arbitrage detection**: Identify arbitrage opportunities across block transactions * **Front-running analysis**: Detect and analyze front-running patterns in blocks * **Protocol analysis**: Analyze protocol behavior across multiple transactions * **Risk assessment**: Assess risks and patterns in block execution * **Educational tools**: Create educational content about blockchain execution patterns This method provides comprehensive block-level transaction analysis capabilities, enabling detailed investigation and monitoring of blockchain execution patterns on the Hyperliquid EVM platform. # debug_traceBlockByNumber | Hyperliquid Source: https://docs.chainstack.com/reference/hyperliquid-evm-debug-trace-block-by-number openapi/hyperliquid_node_api/hyperevm/evm_debug_trace_block_by_number.json post /evm The debug_traceBlockByNumber method returns detailed trace information for all transactions in a specific block identified by block number. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `debug_traceBlockByNumber` method returns detailed trace information for all transactions in a specific block identified by block number. This method provides comprehensive debugging information including call traces, gas usage, and execution details for each transaction. ## Parameters * `blockNumber` (string, required) — The block number in hexadecimal format (e.g., `"0x1"`), or one of the tags: `"latest"`, `"earliest"`, `"pending"`. * `tracerConfig` (object, optional) — Tracer configuration: * `tracer` (string) — Tracer type (`"callTracer"` or `"prestateTracer"`). ## Response The response is an array of trace objects, one per transaction in the block. With `callTracer`, each trace contains: * `type` (string) — Call type (e.g., `"CALL"`, `"DELEGATECALL"`). * `from` (string) — Sender address. * `to` (string) — Recipient address. * `value` (string) — Value transferred in wei (hex). * `gas` (string) — Gas provided (hex). * `gasUsed` (string) — Gas used (hex). * `input` (string) — Call input data. * `output` (string) — Call output data. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_traceBlockByNumber", "params": ["0x1", {"tracer": "callTracer"}], "id": 1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) traces = w3.manager.request_blocking( "debug_traceBlockByNumber", ["0x1", {"tracer": "callTracer"}], ) print(traces) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const traces = await provider.send("debug_traceBlockByNumber", [ "0x1", { tracer: "callTracer" }, ]); console.log(traces); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const traces = await client.request({ method: "debug_traceBlockByNumber", params: ["0x1", { tracer: "callTracer" }], }); console.log(traces); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": [] } ``` ## Use case The `debug_traceBlockByNumber` method is useful for: * Debugging all transactions in a specific block by number * Analyzing gas usage patterns across block transactions * Tracing internal calls and value transfers within a block * Building block explorers with detailed execution traces # debug_traceCall | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-debug-trace-call openapi/hyperliquid_node_api/hyperevm/evm_debug_trace_call.json post /evm The debug_traceCall JSON-RPC method executes a call and returns detailed trace information without creating a transaction. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `debug_traceCall` JSON-RPC method executes a call and returns detailed trace information without creating a transaction. This method simulates transaction execution and provides comprehensive debugging information including call traces, gas usage, and state changes, making it essential for testing and debugging smart contract interactions before committing transactions to the blockchain. ## Parameters 1. **Call object** (object, required): Transaction call details 2. **Block parameter** (string, required): Block number, hash, or "latest"/"earliest"/"pending" 3. **Tracer configuration** (object, required): Configuration options for the tracer ### Call object structure * `from` (string, optional): Address the transaction is sent from * `to` (string, required): Address the transaction is directed to * `gas` (string, optional): Gas provided for the transaction execution * `gasPrice` (string, optional): Gas price for the transaction * `value` (string, optional): Value sent with the transaction * `data` (string, optional): Hash of the method signature and encoded parameters ### Tracer configuration options * `tracer` (string): The type of tracer to use * `"callTracer"`: Provides detailed call trace information * `"prestateTracer"`: Shows state before execution * `"4byteTracer"`: Tracks function selector usage ## Response The method returns detailed trace information for the simulated call, including execution details, gas usage, and state changes. ### Response structure **Trace data:** * `type` — The type of call (CALL, DELEGATECALL, STATICCALL, CREATE, etc.) * `from` — The address that initiated the call * `to` — The address that received the call * `value` — The value transferred in the call * `gas` — The amount of gas allocated for the call * `gasUsed` — The amount of gas actually consumed * `input` — The input data for the call * `output` — The output data returned by the call * `calls` — Array of sub-calls made during execution ## Usage example ### Basic implementation ```javascript theme={"system"} // Trace a simulated call const traceCall = async (callObject, blockParameter = 'latest') => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'debug_traceCall', params: [ callObject, blockParameter, { tracer: 'callTracer' } ], id: 1 }) }); const data = await response.json(); return data.result; }; // Simulate and analyze a contract interaction const simulateContractCall = async () => { const callObject = { from: '0x69835D480110e4919B7899f465aAB101e21c8A87', to: '0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5', gas: '0x76c0', gasPrice: '0x9184e72a000', value: '0xde0b6b3a7640000', data: '0x' // Simple transfer, no data }; try { const trace = await traceCall(callObject); console.log('Call Simulation Results:'); console.log(`Type: ${trace.type}`); console.log(`From: ${trace.from}`); console.log(`To: ${trace.to}`); console.log(`Value: ${parseInt(trace.value, 16)} wei`); console.log(`Gas Used: ${parseInt(trace.gasUsed, 16)}`); return trace; } catch (error) { console.error('Error simulating call:', error); throw error; } }; // Test contract function before execution const testContractFunction = async (contractAddress, functionData, fromAddress) => { const callObject = { from: fromAddress, to: contractAddress, data: functionData, gas: '0x100000' // High gas limit for testing }; const trace = await traceCall(callObject); // Analyze the simulation const analysis = { willSucceed: trace.output !== '0x', gasEstimate: parseInt(trace.gasUsed, 16), hasSubCalls: trace.calls && trace.calls.length > 0, subCallCount: trace.calls ? trace.calls.length : 0 }; console.log('Function Test Analysis:'); console.log(`Will Succeed: ${analysis.willSucceed}`); console.log(`Estimated Gas: ${analysis.gasEstimate}`); console.log(`Sub-calls: ${analysis.subCallCount}`); return { trace, analysis }; }; // Compare gas usage across different scenarios const compareGasUsage = async (scenarios) => { const results = []; for (const [name, callObject] of scenarios) { try { const trace = await traceCall(callObject); results.push({ scenario: name, gasUsed: parseInt(trace.gasUsed, 16), success: trace.output !== '0x' }); } catch (error) { results.push({ scenario: name, error: error.message, success: false }); } } console.log('Gas Usage Comparison:'); results.forEach(result => { if (result.success) { console.log(`${result.scenario}: ${result.gasUsed.toLocaleString()} gas`); } else { console.log(`${result.scenario}: Failed - ${result.error}`); } }); return results; }; // Usage simulateContractCall().then(trace => { console.log('Simulation completed successfully'); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceCall", "params": [ { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", "gas": "0x76c0", "gasPrice": "0x9184e72a000", "value": "0xde0b6b3a7640000", "data": "0x" }, "latest", { "tracer": "callTracer" } ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) call_object = { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", "gas": "0x76c0", "gasPrice": "0x9184e72a000", "value": "0xde0b6b3a7640000", "data": "0x", } trace = w3.manager.request_blocking( "debug_traceCall", [call_object, "latest", {"tracer": "callTracer"}], ) print(trace) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const callObject = { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", gas: "0x76c0", gasPrice: "0x9184e72a000", value: "0xde0b6b3a7640000", data: "0x", }; const trace = await provider.send("debug_traceCall", [ callObject, "latest", { tracer: "callTracer" }, ]); console.log(trace); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const callObject = { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", gas: "0x76c0", gasPrice: "0x9184e72a000", value: "0xde0b6b3a7640000", data: "0x", }; const trace = await client.request({ method: "debug_traceCall", params: [callObject, "latest", { tracer: "callTracer" }], }); console.log(trace); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `debug_traceCall` method is essential for applications that need to: * **Pre-execution testing**: Test transactions before committing them to the blockchain * **Gas estimation**: Accurately estimate gas requirements for complex transactions * **Smart contract debugging**: Debug contract interactions and identify execution issues * **Transaction simulation**: Simulate complex DeFi transactions and multi-step operations * **Error prediction**: Predict and prevent transaction failures before execution * **Cost optimization**: Optimize transaction parameters for better gas efficiency * **Integration testing**: Test smart contract integrations and compatibility * **Security testing**: Test for potential vulnerabilities and edge cases * **Development workflows**: Integrate simulation into development and testing pipelines * **User experience**: Provide users with transaction previews and success predictions * **MEV analysis**: Analyze Maximum Extractable Value opportunities safely * **Arbitrage testing**: Test arbitrage strategies without risking capital * **Flash loan simulation**: Simulate complex flash loan strategies * **Protocol testing**: Test new protocol features and upgrades * **Compliance checking**: Verify regulatory compliance before transaction execution * **Risk assessment**: Assess transaction risks and potential outcomes This method provides safe transaction simulation capabilities, enabling thorough testing and analysis without blockchain state changes on the Hyperliquid EVM platform. # debug_traceTransaction | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-debug-trace-transaction openapi/hyperliquid_node_api/hyperevm/evm_debug_trace_transaction.json post /evm The debug_traceTransaction JSON-RPC method returns detailed trace information for a specific transaction. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `debug_traceTransaction` JSON-RPC method returns detailed trace information for a specific transaction. This method provides comprehensive debugging information including call traces, gas usage, state changes, and execution details, making it essential for transaction analysis, debugging, and forensic investigations. ## Parameters 1. **Transaction hash** (string, required): The hash of the transaction to trace 2. **Tracer configuration** (object, required): Configuration options for the tracer ### Tracer configuration options * `tracer` (string): The type of tracer to use. Common options include: * `"callTracer"`: Provides detailed call trace information * `"prestateTracer"`: Shows state before transaction execution * `"4byteTracer"`: Tracks function selector usage ## Response The method returns detailed trace information for the specified transaction, including call hierarchy, gas usage, and state changes. ### Response structure **Trace data:** * `type` — The type of call (CALL, DELEGATECALL, STATICCALL, CREATE, etc.) * `from` — The address that initiated the call * `to` — The address that received the call * `value` — The value transferred in the call * `gas` — The amount of gas allocated for the call * `gasUsed` — The amount of gas actually consumed * `input` — The input data for the call * `output` — The output data returned by the call * `calls` — Array of sub-calls made during execution ## Usage example ### Basic implementation ```javascript theme={"system"} // Trace a specific transaction const traceTransaction = async (txHash) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'debug_traceTransaction', params: [ txHash, { tracer: 'callTracer' } ], id: 1 }) }); const data = await response.json(); return data.result; }; // Analyze transaction execution const analyzeTransaction = async (txHash) => { try { const trace = await traceTransaction(txHash); console.log('Transaction Trace Analysis:'); console.log(`Type: ${trace.type}`); console.log(`From: ${trace.from}`); console.log(`To: ${trace.to}`); console.log(`Value: ${parseInt(trace.value, 16)} wei`); console.log(`Gas Used: ${parseInt(trace.gasUsed, 16)}`); // Analyze sub-calls if (trace.calls && trace.calls.length > 0) { console.log(`\nSub-calls (${trace.calls.length}):`); trace.calls.forEach((call, index) => { console.log(` ${index + 1}. ${call.type}: ${call.from} -> ${call.to}`); console.log(` Gas Used: ${parseInt(call.gasUsed, 16)}`); }); } return trace; } catch (error) { console.error('Error tracing transaction:', error); throw error; } }; // Gas usage analysis const analyzeGasUsage = (trace) => { const totalGas = parseInt(trace.gas, 16); const gasUsed = parseInt(trace.gasUsed, 16); const efficiency = ((gasUsed / totalGas) * 100).toFixed(2); console.log('Gas Analysis:'); console.log(`Total Gas Limit: ${totalGas.toLocaleString()}`); console.log(`Gas Used: ${gasUsed.toLocaleString()}`); console.log(`Efficiency: ${efficiency}%`); return { totalGas, gasUsed, efficiency: parseFloat(efficiency) }; }; // Usage const txHash = '0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31'; analyzeTransaction(txHash).then(trace => { analyzeGasUsage(trace); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceTransaction", "params": [ "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31", { "tracer": "callTracer" } ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) tx_hash = "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31" # debug_traceTransaction is not a standard eth_* method, so call it via the provider trace = w3.provider.make_request( "debug_traceTransaction", [tx_hash, {"tracer": "callTracer"}], ) print(trace["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const txHash = "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31"; // debug_traceTransaction is not exposed as a typed method, so use send() const trace = await provider.send("debug_traceTransaction", [ txHash, { tracer: "callTracer" }, ]); console.log(trace); ``` ```typescript viem theme={"system"} import { createPublicClient, http, type Hash } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const txHash: Hash = "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31"; // debug_traceTransaction is not a standard action, so type the request with a custom RPC schema const trace = await client.request<{ Method: "debug_traceTransaction"; Parameters: [Hash, { tracer: string }]; ReturnType: unknown; }>({ method: "debug_traceTransaction", params: [txHash, { tracer: "callTracer" }], }); console.log(trace); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `debug_traceTransaction` method is essential for applications that need to: * **Transaction debugging**: Debug failed transactions and identify execution issues * **Gas optimization**: Analyze gas usage patterns and optimize contract efficiency * **Security analysis**: Perform security audits and vulnerability assessments * **Forensic investigation**: Investigate suspicious transactions and trace fund flows * **Smart contract testing**: Test contract behavior and verify execution paths * **Development tools**: Build debugging tools and transaction analyzers * **Performance monitoring**: Monitor transaction performance and execution metrics * **Error diagnosis**: Diagnose and resolve transaction execution errors * **Compliance tracking**: Track regulatory compliance and audit trails * **MEV analysis**: Analyze Maximum Extractable Value opportunities and patterns * **DeFi protocol analysis**: Understand complex DeFi transaction flows * **Arbitrage detection**: Identify and analyze arbitrage opportunities * **Front-running detection**: Detect and analyze front-running activities * **Sandwich attack analysis**: Identify and study sandwich attack patterns * **Educational tools**: Create educational content about blockchain execution * **Research platforms**: Support academic and commercial blockchain research This method provides comprehensive transaction execution details, enabling deep analysis and debugging capabilities on the Hyperliquid EVM platform. # erigon_getHeaderByNumber | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-erigon-get-header-by-number openapi/hyperliquid_node_api/hyperevm/evm_erigon_get_header_by_number.json post /evm The erigon_getHeaderByNumber JSON-RPC method retrieves block header information by block number on the Hyperliquid EVM blockchain. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `erigon_getHeaderByNumber` JSON-RPC method retrieves block header information by block number on the Hyperliquid EVM blockchain. This method is an alias for `ots_getHeaderByNumber` and exists for compatibility with tools expecting Erigon-specific endpoints. ## Parameters 1. **block number** (integer, required): The block number to retrieve the header for ## Response The method returns a block header object with the following fields: ### Response structure * `number` — the block number * `hash` — the block hash * `parentHash` — the parent block hash * `nonce` — proof-of-work nonce (always 0x0000000000000000 for PoS) * `sha3Uncles` — SHA3 hash of the uncles data * `logsBloom` — bloom filter for the logs * `transactionsRoot` — root of the transaction trie * `stateRoot` — root of the final state trie * `receiptsRoot` — root of the receipts trie * `miner` — address of the beneficiary * `difficulty` — difficulty for this block * `totalDifficulty` — total difficulty of the chain until this block * `extraData` — extra data field of this block * `size` — size of this block in bytes * `gasLimit` — maximum gas allowed in this block * `gasUsed` — total gas used by all transactions * `timestamp` — Unix timestamp of the block * `mixHash` — mix hash for the block * `baseFeePerGas` — base fee per gas (post-EIP-1559) ## Usage example `erigon_getHeaderByNumber` is a non-standard JSON-RPC method, so the EVM client libraries below call it through their raw request interface rather than a named helper. ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "erigon_getHeaderByNumber", "params": [1000], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) response = w3.provider.make_request("erigon_getHeaderByNumber", [1000]) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const header = await provider.send("erigon_getHeaderByNumber", [1000]); console.log(header); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const header = await client.request({ method: "erigon_getHeaderByNumber", params: [1000], } as any); console.log(header); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": { "number": "0x3e8", "hash": "0xe8399bf41516f5ca663c79bc30db02b4989385c2f3a96f42958dea5753cbf4a9", "parentHash": "0x7c8f6a0e4d3b2a1c9e5f8b7d4a6c3e9f2b8d5a7c1e4f9b3a6d8c2e5f7a9b4c6d", "nonce": "0x0000000000000000", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "stateRoot": "0x9f3c6b0d8e5a2d1f7c9e4b3a8d6c5e2f9a7b4c8d1e6f3a5b7d9c2e4f6a8b3c5d", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "miner": "0x0000000000000000000000000000000000000000", "difficulty": "0x0", "totalDifficulty": "0x0", "extraData": "0x", "size": "0x220", "gasLimit": "0x1c9c380", "gasUsed": "0x0", "timestamp": "0x6734e8d0", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "baseFeePerGas": "0x7" } } ``` ## Compatibility note This method is functionally identical to `ots_getHeaderByNumber`. The `erigon_` prefix exists for compatibility with: * Otterscan block explorer (which checks for Erigon nodes) * Tools expecting Erigon-specific endpoints * Migration paths from Erigon to other node implementations ## Use cases The `erigon_getHeaderByNumber` method is essential for: * **Otterscan compatibility**: Allow Otterscan to detect node type * **Tool migration**: Support tools moving from Erigon nodes * **Block header retrieval**: Efficiently fetch header data * **Cross-client compatibility**: Work with tools supporting multiple clients * **Legacy support**: Maintain compatibility with older integrations * **Node detection**: Identify nodes supporting Erigon-like APIs * **Development tools**: Support development frameworks expecting Erigon * **Block verification**: Validate block headers across different clients * **API standardization**: Provide familiar endpoints for developers * **Infrastructure flexibility**: Allow switching between node types This alias method ensures maximum compatibility with existing Ethereum tooling and block explorers. # eth_bigBlockGasPrice | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-big-block-gas-price openapi/hyperliquid_node_api/hyperevm/evm_eth_big_block_gas_price.json post /evm The eth_bigBlockGasPrice JSON-RPC method returns the base fee for the next big block on Hyperliquid EVM. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_bigBlockGasPrice` JSON-RPC method returns the base fee for the next big block on Hyperliquid EVM. This is a Hyperliquid-specific method that provides gas pricing for addresses using big blocks. ## Parameters This method takes no parameters. ## Response The method returns the big block gas price as a hexadecimal string representing wei. ### Response structure **Big block gas price:** * `result` — The base fee for the next big block in wei as a hexadecimal string ### Data interpretation **Gas price format:** * Returned as hexadecimal string with `0x` prefix * Value represents wei (smallest unit of ether) * Convert to gwei by dividing by 10^9 * Convert to ether by dividing by 10^18 **Price calculation:** ```javascript theme={"system"} // Convert big block gas price to different units const bigBlockGasPriceWei = parseInt("0x3b9aca00", 16); // 1000000000 wei const bigBlockGasPriceGwei = bigBlockGasPriceWei / 1e9; // 1 gwei const bigBlockGasPriceEther = bigBlockGasPriceWei / 1e18; // 0.000000001 ether ``` ## Big blocks on Hyperliquid ### Overview Big blocks are a Hyperliquid-specific feature that allows certain addresses to use larger block space. The `eth_bigBlockGasPrice` method returns the base fee specifically for these big blocks. **Key differences:** * Big block gas price may differ from standard gas price (`eth_gasPrice`) * Only addresses using big blocks need this pricing information * Use `eth_usingBigBlocks` to check if an address is using big blocks ### Related methods **Check big block usage:** * `eth_usingBigBlocks` — returns whether an address is using big blocks * `eth_gasPrice` — returns the base fee for standard (small) blocks ## Usage example ### Basic implementation ```javascript theme={"system"} // Get big block gas price const getBigBlockGasPrice = async () => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_bigBlockGasPrice', params: [], id: 1 }) }); const data = await response.json(); return data.result; }; // Compare with standard gas price const compareGasPrices = async () => { const [standardPrice, bigBlockPrice] = await Promise.all([ // Standard gas price call fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 1 }) }).then(r => r.json()), // Big block gas price call getBigBlockGasPrice() ]); return { standard: standardPrice.result, bigBlock: bigBlockPrice, standardGwei: parseInt(standardPrice.result, 16) / 1e9, bigBlockGwei: parseInt(bigBlockPrice, 16) / 1e9 }; }; ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_bigBlockGasPrice","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # eth_bigBlockGasPrice is a Hyperliquid-specific method, so call it directly. big_block_gas_price = w3.manager.request_blocking("eth_bigBlockGasPrice", []) print(big_block_gas_price) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // eth_bigBlockGasPrice is a Hyperliquid-specific method, so send it directly. const bigBlockGasPrice = await provider.send("eth_bigBlockGasPrice", []); console.log(bigBlockGasPrice); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // eth_bigBlockGasPrice is a Hyperliquid-specific method, so request it directly. const bigBlockGasPrice = await client.request({ method: "eth_bigBlockGasPrice", }); console.log(bigBlockGasPrice); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_bigBlockGasPrice` method is useful for: * **Big block users**: Get appropriate gas pricing when your address is using big blocks * **Gas price comparison**: Compare big block pricing with standard block pricing * **Transaction cost optimization**: Choose the most cost-effective gas price option * **Wallet applications**: Display accurate gas pricing for addresses using big blocks * **DeFi applications**: Optimize transaction costs based on block type usage * **Analytics tools**: Monitor and compare different gas pricing options on Hyperliquid * **Development tools**: Test applications with different gas pricing scenarios > [!NOTE] > > This is a Hyperliquid-specific method that returns the base fee for the next big block. Use `eth_usingBigBlocks` to check if an address is using big blocks, and compare with `eth_gasPrice` for standard block pricing. # eth_blobBaseFee | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-blob-base-fee openapi/hyperliquid_node_api/hyperevm/evm_eth_blob_base_fee.json post /evm The eth_blobBaseFee JSON-RPC method returns the current base fee for blob transactions on the Hyperliquid EVM blockchain. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_blobBaseFee` JSON-RPC method returns the current base fee for blob transactions on the Hyperliquid EVM blockchain. This method is essential for applications working with EIP-4844 blob transactions, providing the current pricing information needed for blob data submission and cost estimation. ## Parameters This method takes no parameters. The `params` field should be an empty array. ## Response The method returns the current blob base fee as a hexadecimal string. ### Response structure **Blob fee information:** * `result` — The current blob base fee as a hexadecimal string (in wei units) ### Blob base fee interpretation **Fee calculation:** * Blob base fees are returned in wei as hexadecimal strings * Convert to decimal for calculations and human-readable amounts * Multiply by the number of blobs to get total blob fee cost * Add to regular transaction fees for complete cost estimation **Usage patterns:** * Calculate blob transaction costs before submission * Monitor blob fee fluctuations for cost optimization * Implement dynamic blob pricing strategies * Optimize blob transaction timing based on fee levels ## Usage example ### Basic implementation ```javascript theme={"system"} // Get current blob base fee const getBlobBaseFee = async () => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_blobBaseFee', params: [], id: 1 }) }); const data = await response.json(); const feeHex = data.result; const feeWei = BigInt(feeHex); const feeGwei = Number(feeWei) / 1e9; return { hex: feeHex, wei: feeWei.toString(), gwei: feeGwei }; }; // Calculate blob transaction cost const calculateBlobCost = async (blobCount) => { try { const { wei: baseFee } = await getBlobBaseFee(); const totalCost = BigInt(baseFee) * BigInt(blobCount); return { baseFeeWei: baseFee, blobCount: blobCount, totalCostWei: totalCost.toString(), totalCostGwei: Number(totalCost) / 1e9 }; } catch (error) { console.error('Error calculating blob cost:', error); return null; } }; // Monitor blob fee changes const monitorBlobFees = async (callback) => { let lastFee = null; const checkFeeChange = async () => { try { const { gwei: currentFee } = await getBlobBaseFee(); if (lastFee !== null && currentFee !== lastFee) { const change = currentFee - lastFee; const percentChange = ((change / lastFee) * 100).toFixed(2); callback({ current: currentFee, previous: lastFee, change: change, percentChange: percentChange }); } lastFee = currentFee; } catch (error) { console.error('Error monitoring blob fees:', error); } }; // Check every 12 seconds (approximate block time) setInterval(checkFeeChange, 12000); }; // Usage calculateBlobCost(4).then(cost => { console.log(`Cost for 4 blobs: ${cost.totalCostGwei} Gwei`); }); monitorBlobFees((feeData) => { console.log(`Blob fee changed: ${feeData.current} Gwei (${feeData.percentChange}%)`); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_blobBaseFee","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) blob_base_fee = w3.eth.get_blob_base_fee() print(blob_base_fee) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blobBaseFee = await provider.send("eth_blobBaseFee", []); console.log(blobBaseFee); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blobBaseFee = await client.getBlobBaseFee(); console.log(blobBaseFee); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_blobBaseFee` method is essential for applications that need to: * **Blob transaction cost estimation**: Calculate the cost of blob transactions before submission * **Layer 2 scaling solutions**: Implement efficient data availability solutions using blobs * **Rollup optimizations**: Optimize rollup data submission costs and timing * **Fee market analysis**: Analyze blob fee market dynamics and pricing trends * **Cost optimization**: Implement strategies to minimize blob transaction costs * **DeFi protocols**: Build DeFi applications that utilize blob data for cost-effective operations * **Data availability services**: Provide data availability services with accurate pricing * **MEV strategies**: Implement MEV strategies that account for blob costs and timing * **Cross-chain bridges**: Optimize cross-chain data transfer costs using blob transactions * **Oracle services**: Provide cost-effective oracle data submission using blobs * **Batch processing**: Optimize batch transaction processing with blob cost considerations * **Gaming applications**: Implement blockchain gaming with cost-effective blob data storage * **NFT platforms**: Optimize NFT metadata and asset storage using blob transactions * **Social media platforms**: Build decentralized social platforms with efficient blob storage * **File storage systems**: Implement decentralized file storage with blob cost optimization * **Analytics platforms**: Track blob fee trends and provide market insights * **Automated trading**: Implement trading strategies that consider blob fee fluctuations * **Infrastructure monitoring**: Monitor blob fee changes for infrastructure cost planning * **Wallet applications**: Display accurate blob transaction costs to users * **Developer tools**: Build development tools that account for blob transaction costs * **Testing frameworks**: Create testing frameworks that simulate blob fee scenarios * **Arbitrage opportunities**: Identify arbitrage opportunities based on blob fee differentials * **Liquidity provision**: Optimize liquidity provision strategies considering blob costs * **Governance systems**: Implement governance systems with cost-effective blob data storage This method provides essential blob fee information, enabling cost-effective and efficient blob transaction management on the Hyperliquid EVM platform. # eth_blockNumber | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-block-number openapi/hyperliquid_node_api/hyperevm/evm_eth_block_number.json post /evm The eth_blockNumber JSON-RPC method returns the number of the most recent block on the Hyperliquid EVM blockchain. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_blockNumber` JSON-RPC method returns the number of the most recent block on the Hyperliquid EVM blockchain. This method is fundamental for tracking blockchain state, monitoring synchronization status, and implementing block-based logic in applications. ## Parameters This method takes no parameters. The `params` field should be an empty array. ## Response The method returns the current block number as a hexadecimal string. ### Response structure **Block information:** * `result` — The current block number as a hexadecimal string (e.g., "0x1b4" which equals 436 in decimal) ### Block number interpretation **Hexadecimal format:** * Block numbers are returned in hexadecimal format with "0x" prefix * Convert to decimal for human-readable block height * Example: "0x1b4" = 436 in decimal **Usage patterns:** * Track blockchain progression and new block creation * Implement block-based polling and event detection * Verify synchronization status with other nodes * Calculate block intervals and timing ## Usage example ### Basic implementation ```javascript theme={"system"} // Get current block number const getCurrentBlockNumber = async () => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 }) }); const data = await response.json(); const blockNumber = parseInt(data.result, 16); return { hex: data.result, decimal: blockNumber }; }; // Poll for new blocks const pollForNewBlocks = async (callback) => { let lastBlockNumber = 0; const checkForNewBlock = async () => { try { const { decimal: currentBlock } = await getCurrentBlockNumber(); if (currentBlock > lastBlockNumber) { lastBlockNumber = currentBlock; callback(currentBlock); } } catch (error) { console.error('Error checking block number:', error); } }; // Poll every 2 seconds setInterval(checkForNewBlock, 2000); }; // Usage pollForNewBlocks((blockNumber) => { console.log(`New block: ${blockNumber}`); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block_number = w3.eth.block_number print(block_number) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blockNumber = await provider.getBlockNumber(); console.log(blockNumber); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blockNumber = await client.getBlockNumber(); console.log(blockNumber); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_blockNumber` method is essential for applications that need to: * **Blockchain monitoring**: Monitor blockchain health, synchronization, and block progression * **Event detection**: Implement efficient event detection and monitoring systems * **Transaction tracking**: Track transaction confirmations and finality status * **Synchronization verification**: Verify node synchronization and detect lag issues * **Block-based logic**: Implement application logic that depends on block heights * **Performance monitoring**: Monitor network performance and block creation times * **Data consistency**: Ensure data consistency across blockchain state changes * **Polling optimization**: Implement efficient polling mechanisms for blockchain data * **Cache management**: Manage cache invalidation and data freshness using block numbers * **Analytics platforms**: Build analytics tools that track blockchain metrics over time * **Trading applications**: Implement trading logic that depends on block timing * **DeFi protocols**: Build DeFi applications with block-aware functionality * **NFT platforms**: Track NFT transactions and state changes across blocks * **Gaming applications**: Implement blockchain-based gaming with block-dependent mechanics * **Oracle services**: Provide block-aware oracle data and timing services * **Cross-chain bridges**: Coordinate cross-chain operations using block numbers * **Governance systems**: Implement governance mechanisms with block-based voting periods * **Staking protocols**: Track staking rewards and penalties across block intervals * **Audit tools**: Build audit tools that analyze blockchain state across blocks * **Development frameworks**: Create development tools with block-aware debugging * **Testing suites**: Implement blockchain testing with block progression simulation * **Load balancers**: Distribute load based on node synchronization status * **Health checks**: Monitor node health and blockchain connectivity * **Alerting systems**: Create alerts based on block progression and timing anomalies This method provides fundamental blockchain state information, enabling robust and block-aware applications on the Hyperliquid EVM platform. # eth_call | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-call openapi/hyperliquid_node_api/hyperevm/evm_eth_call.json post /evm The eth_call JSON-RPC method executes a new message call immediately without creating a transaction on the blockchain. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_call` JSON-RPC method executes a new message call immediately without creating a transaction on the blockchain. This method is essential for reading data from smart contracts, calling view functions, and retrieving blockchain state without modifying it or paying gas fees. ## Parameters The method takes two parameters and an optional third: 1. **Transaction call object** - Contains the call details 2. **Block parameter** - Specifies which block state to use 3. **State override** (optional) - Changes account state for this call only ### Transaction call object * `to` (string, required) — The contract address to call * `data` (string, optional) — The hash of the method signature and encoded parameters * `from` (string, optional) — The address the call is made from * `gas` (string, optional) — Gas limit for the call execution * `gasPrice` (string, optional) — Gas price for the call * `value` (string, optional) — Value sent with the call ### Block parameter * Block identifier: `"latest"` (only the latest block is supported on Hyperliquid) ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url YOUR_CHAINSTACK_ENDPOINT/evm \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response The method returns the result of the contract call as a hexadecimal string. ### Response structure **Call result:** * `result` — The return value of the executed contract method as a hexadecimal string ### Data interpretation **Return value format:** * Results are returned as hexadecimal strings * Decode according to the contract function's return type * Use ABI decoding libraries for complex return types * Simple values like uint256 can be converted directly from hex **Common patterns:** * `0x` prefix indicates hexadecimal format * Leading zeros are included in the response * Empty results return `0x` * Failed calls may return error data ## Smart contract interaction ### Function calls **Method signature encoding:** * Function selector: First 4 bytes of keccak256 hash of function signature * Parameters: ABI-encoded according to Ethereum standards * Example: `totalSupply()` → `0x18160ddd` **Parameter encoding:** * Use ABI encoding for function parameters * Combine function selector with encoded parameters * Tools like web3.js, ethers.js handle encoding automatically ### Common use cases **ERC-20 token calls:** * `totalSupply()` → `0x18160ddd` * `balanceOf(address)` → `0x70a08231` + encoded address * `allowance(address,address)` → `0xdd62ed3e` + encoded addresses **Contract state queries:** * Read public variables and mappings * Call view and pure functions * Check contract configuration and parameters * Validate contract state before transactions ## Development patterns ### Error handling **Call failures:** * Calls can fail due to reverts, out-of-gas, or invalid data * Check for error responses and handle appropriately * Use try-catch patterns for robust error handling * Validate contract addresses and function signatures **Gas considerations:** * Specify gas limit to prevent out-of-gas errors * Use reasonable gas limits for complex calls * Monitor gas usage for optimization opportunities * Consider gas costs for view function complexity ### Optimization strategies **Batch calls:** * Group multiple calls to reduce network overhead * Use multicall contracts for efficient batch operations * Cache results when appropriate to reduce API calls * Implement intelligent caching based on latest state **State consistency:** * Only the latest block is supported on Hyperliquid * All calls are made against the current blockchain state * Handle potential state changes between calls * Implement retry logic for temporary failures ## Example request This an example call with wrapped HYPE (wHYPE) on the Hyperliquid mainnet deployed at [0x5555555555555555555555555555555555555555](https://hyperevmscan.io/token/0x5555555555555555555555555555555555555555). ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x5555555555555555555555555555555555555555","data":"0x18160ddd"},"latest"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) result = w3.eth.call({ "to": "0x5555555555555555555555555555555555555555", "data": "0x18160ddd", }, "latest") print(result.hex()) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const result = await provider.call({ to: "0x5555555555555555555555555555555555555555", data: "0x18160ddd", }); console.log(result); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const result = await client.call({ to: "0x5555555555555555555555555555555555555555", data: "0x18160ddd", }); console.log(result.data); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_call` method is essential for applications that need to: * **Smart contract interaction**: Read data from smart contracts without modifying state * **Token information**: Query ERC-20/ERC-721 token balances, supplies, and metadata * **DeFi protocols**: Interact with DeFi contracts to read prices, liquidity, and positions * **Contract validation**: Validate contract state and parameters before transactions * **Data aggregation**: Aggregate data from multiple contracts efficiently * **Portfolio tracking**: Track user balances and positions across multiple contracts * **Price oracles**: Read price data from oracle contracts and price feeds * **Governance systems**: Query governance proposals, voting power, and delegation status * **NFT platforms**: Read NFT metadata, ownership, and marketplace information * **Gaming applications**: Query game state, player stats, and in-game assets * **Analytics platforms**: Build analytics tools that read blockchain state data * **Wallet applications**: Display user balances, allowances, and contract interactions * **Trading interfaces**: Read market data, order books, and trading parameters * **Lending protocols**: Query lending rates, collateral ratios, and liquidation data * **Staking platforms**: Read staking rewards, validator information, and delegation data * **Cross-chain bridges**: Verify bridge state and cross-chain transaction status * **Audit tools**: Analyze contract state for security audits and compliance * **Development tools**: Build debugging and testing tools for smart contract development * **Monitoring systems**: Monitor contract state changes and trigger alerts * **Integration services**: Integrate blockchain data with external systems and databases * **Educational platforms**: Create educational tools that demonstrate blockchain concepts * **Research tools**: Conduct blockchain research and analysis using contract data * **Compliance tools**: Ensure regulatory compliance by monitoring contract interactions * **Risk management**: Assess risks by analyzing contract state and parameters * **Arbitrage bots**: Identify arbitrage opportunities by reading market data from contracts This method provides fundamental smart contract interaction capabilities, enabling robust and efficient blockchain applications on the Hyperliquid EVM platform. > [!NOTE] > > On Hyperliquid, `eth_call` only supports the latest block. Historical state queries are not supported in the default RPC implementation. # eth_callMany | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-call-many openapi/hyperliquid_node_api/hyperevm/evm_eth_call_many.json post /evm The eth_callMany JSON-RPC method executes multiple call transactions in sequence without creating transactions on the blockchain. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_callMany` JSON-RPC method executes multiple call transactions in sequence without creating transactions on the blockchain. This method is useful for batch reading contract states and simulating multiple contract interactions efficiently. ## Parameters The method takes two parameters: 1. **Array of call context objects** — Contains transactions to execute 2. **Block state object** — The block state for execution ### Parameter details **Call context array:** Each object in the array contains: * `transactions` (array, required) — Array of transaction objects to execute * `to` (string, required) — The address to call * `data` (string, optional) — The data to send with the call * `from` (string, optional) — The address the call is made from * `gas` (string, optional) — The gas limit for the call * `gasPrice` (string, optional) — The gas price for the call * `value` (string, optional) — The value to send with the call **Block state object:** * `blockNumber` (string, required) — Block identifier (e.g., "latest", or specific block number) ## Response The method returns an array of results corresponding to each call. ### Response structure **Results array:** * Array of hexadecimal strings, each representing the return data from the corresponding call * Results maintain the same order as the input calls * Failed calls return `0x` or an error ## Example request ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_callMany", "params": [ [ { "transactions": [ { "to": "0x5555555555555555555555555555555555555555", "data": "0x70a082310000000000000000000000008D25Fb438C6efCD08679ffA82766869B50E24608" }, { "to": "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb", "data": "0x70a082310000000000000000000000008D25Fb438C6efCD08679ffA82766869B50E24608" } ] } ], { "blockNumber": "latest" } ], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # eth_callMany is a non-standard method, so call it through the raw JSON-RPC interface. call_context = [ { "transactions": [ { "to": "0x5555555555555555555555555555555555555555", "data": "0x70a082310000000000000000000000008D25Fb438C6efCD08679ffA82766869B50E24608", }, { "to": "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb", "data": "0x70a082310000000000000000000000008D25Fb438C6efCD08679ffA82766869B50E24608", }, ] } ] block_state = {"blockNumber": "latest"} result = w3.provider.make_request("eth_callMany", [call_context, block_state]) print(result["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // eth_callMany is a non-standard method, so use the generic send() interface. const callContext = [ { transactions: [ { to: "0x5555555555555555555555555555555555555555", data: "0x70a082310000000000000000000000008D25Fb438C6efCD08679ffA82766869B50E24608", }, { to: "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb", data: "0x70a082310000000000000000000000008D25Fb438C6efCD08679ffA82766869B50E24608", }, ], }, ]; const blockState = { blockNumber: "latest" }; const result = await provider.send("eth_callMany", [callContext, blockState]); console.log(result); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // eth_callMany is a non-standard method, so use the low-level request() interface. const callContext = [ { transactions: [ { to: "0x5555555555555555555555555555555555555555", data: "0x70a082310000000000000000000000008D25Fb438C6efCD08679ffA82766869B50E24608", }, { to: "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb", data: "0x70a082310000000000000000000000008D25Fb438C6efCD08679ffA82766869B50E24608", }, ], }, ]; const blockState = { blockNumber: "latest" }; const result = await client.request({ method: "eth_callMany" as any, params: [callContext, blockState] as any, }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_callMany` method is essential for applications that need to: * **Batch read operations**: Read multiple contract states in a single request * **DeFi dashboards**: Fetch multiple token balances and positions efficiently * **Portfolio trackers**: Query multiple asset positions simultaneously * **Smart contract testing**: Simulate multiple contract interactions * **Data aggregation**: Collect data from multiple sources in one call * **Price oracles**: Fetch prices from multiple oracle contracts * **Multi-token operations**: Check balances across multiple tokens * **Protocol monitoring**: Monitor multiple protocol states * **Wallet applications**: Display multiple asset balances * **Analytics platforms**: Gather multiple metrics efficiently * **DEX interfaces**: Query multiple pool states * **Lending protocols**: Check multiple collateral positions * **Yield aggregators**: Compare yields across protocols * **NFT marketplaces**: Query multiple NFT metadata * **Governance systems**: Check voting power across proposals * **Cross-protocol queries**: Interact with multiple protocols * **Risk assessment**: Evaluate multiple risk parameters * **Audit tools**: Verify multiple contract states * **Development tools**: Debug multiple contract calls * **Integration services**: Provide batched API endpoints This method provides efficient batch execution capabilities for read-only operations on the Hyperliquid EVM platform. > [!NOTE] > > The `eth_callMany` method executes calls sequentially in the order provided. Each call can see the state changes from previous calls in the batch, making it useful for dependent operations. # eth_chainId | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-chain-id openapi/hyperliquid_node_api/hyperevm/evm_eth_chain_id.json post /evm The eth_chainId JSON-RPC method returns the chain ID of the currently connected blockchain network. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_chainId` JSON-RPC method returns the chain ID of the currently connected blockchain network. This method is fundamental for identifying the specific blockchain network, ensuring transaction compatibility, and implementing network-specific logic in applications. ## Parameters This method takes no parameters. The `params` field should be an empty array. ## Response The method returns the chain ID as a hexadecimal string. ### Response structure **Chain identification:** * `result` — The chain ID as a hexadecimal string (e.g., "0x3e7" which equals 999 in decimal for Hyperliquid mainnet) ### Chain ID interpretation **Hexadecimal format:** * Chain IDs are returned in hexadecimal format with "0x" prefix * Convert to decimal for human-readable chain identification * Examples: "0x3e7" = 999 in decimal (Hyperliquid mainnet); "0x3e6" = 998 (Hyperliquid testnet) **Network identification:** * Each blockchain network has a unique chain ID * Essential for preventing replay attacks across different networks * Used by wallets and applications for network validation ## Usage example ### Basic implementation ```javascript theme={"system"} // Get Hyperliquid chain ID const getChainId = async () => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 1 }) }); const data = await response.json(); const chainId = parseInt(data.result, 16); return { hex: data.result, decimal: chainId, network: chainId === 999 ? 'Hyperliquid Mainnet' : chainId === 998 ? 'Hyperliquid Testnet' : 'Unknown' }; }; // Verify network connection const verifyNetwork = async () => { const { decimal: chainId, network } = await getChainId(); if (chainId !== 999) { throw new Error(`Expected Hyperliquid Mainnet (999), got ${network} (${chainId})`); } console.log(`Connected to ${network}`); return true; }; // Usage verifyNetwork() .then(() => console.log('Network verified')) .catch(error => console.error('Network verification failed:', error)); ``` ## Network identification ### Hyperliquid chain IDs **Chain details:** * Mainnet: 999 (0x3e7 in hexadecimal) * Testnet: 998 (0x3e6 in hexadecimal) * Used for transaction signing and replay protection **Security considerations:** * Always verify chain ID matches expected network (998) * Chain ID prevents transaction replay across different networks * Essential for secure transaction processing on Hyperliquid ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) chain_id = w3.eth.chain_id print(chain_id) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const network = await provider.getNetwork(); console.log(network.chainId); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const chainId = await client.getChainId(); console.log(chainId); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_chainId` method is essential for applications that need to: * **Network verification**: Verify connection to Hyperliquid Mainnet (chain ID 998) * **Transaction security**: Ensure transactions are signed with correct chain ID for replay protection * **Wallet configuration**: Configure wallets to recognize and connect to Hyperliquid * **Application initialization**: Verify network connection during application startup * **Error handling**: Provide meaningful error messages for network mismatches * **Development tools**: Build tools that automatically detect the Hyperliquid network * **Multi-network applications**: Support Hyperliquid alongside other EVM networks * **Security validation**: Validate network before executing sensitive operations # eth_createAccessList | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-create-access-list openapi/hyperliquid_node_api/hyperevm/evm_eth_create_access_list.json post /evm The eth_createAccessList JSON-RPC method generates an access list for a transaction. eth_createAccessList on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_createAccessList` JSON-RPC method generates an access list for a transaction. This method simulates a transaction execution to determine which storage slots and addresses will be accessed, enabling more efficient gas usage through EIP-2930 access lists. ## Parameters The method takes two parameters: 1. **Transaction object** — The transaction call object 2. **Block parameter** — The block at which to simulate the transaction ### Parameter details **Transaction object:** * `from` (string, optional) — The address the transaction is sent from * `to` (string, required) — The address the transaction is directed to * `gas` (string, optional) — The gas provided for the transaction execution * `gasPrice` (string, optional) — The gas price for the transaction * `value` (string, optional) — The value sent with this transaction * `data` (string, optional) — The data sent along with the transaction * `maxFeePerGas` (string, optional) — Maximum fee per gas for EIP-1559 transactions * `maxPriorityFeePerGas` (string, optional) — Maximum priority fee per gas for EIP-1559 transactions **Block parameter:** * `block` (string, required) — Block identifier: `"latest"` (only the latest block is supported on Hyperliquid) ## Response The method returns an access list and estimated gas for the transaction. ### Response structure **Access list result:** * `accessList` — Array of access list entries * `address` — The accessed contract address * `storageKeys` — Array of accessed storage keys for this address * `gasUsed` — The estimated gas used by the transaction with the access list * `error` — Error message if the transaction would fail ### Access list benefits **Gas optimization:** * Reduces gas costs for cross-contract calls * Pre-declares state access for more predictable pricing * Enables gas savings of up to 2400 gas per address * Saves 2100 gas per storage key on first access **Transaction efficiency:** * Prevents unexpected out-of-gas errors * Improves transaction predictability * Reduces risk of failed transactions * Optimizes complex DeFi interactions ## Example request ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_createAccessList", "params": [ { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", "gas": "0x76c0", "gasPrice": "0x9184e72a000", "value": "0xde0b6b3a7640000", "data": "0x" }, "latest" ], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) access_list = w3.eth.create_access_list( { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", "gas": "0x76c0", "gasPrice": "0x9184e72a000", "value": "0xde0b6b3a7640000", "data": "0x", }, "latest", ) print(access_list) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const accessList = await provider.send("eth_createAccessList", [ { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", gas: "0x76c0", gasPrice: "0x9184e72a000", value: "0xde0b6b3a7640000", data: "0x", }, "latest", ]); console.log(accessList); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const accessList = await client.createAccessList({ account: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", value: 1000000000000000000n, data: "0x", blockTag: "latest", }); console.log(accessList); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_createAccessList` method is essential for applications that need to: * **Gas optimization**: Generate access lists to reduce transaction gas costs * **DeFi protocols**: Optimize complex multi-contract interactions * **Smart wallets**: Provide users with gas-efficient transaction options * **MEV protection**: Create more predictable transaction execution paths * **Transaction builders**: Construct optimized transactions for users * **Aggregators**: Optimize routing through multiple protocols * **Arbitrage bots**: Minimize gas costs for profitable trades * **Liquidation systems**: Ensure efficient liquidation transactions * **Cross-contract calls**: Optimize interactions between multiple contracts * **Token swaps**: Reduce gas costs for swap transactions * **Yield farming**: Optimize complex farming strategies * **Governance systems**: Reduce voting transaction costs * **Batch operations**: Optimize batched transaction execution * **Bridge protocols**: Minimize cross-chain transaction costs * **Oracle systems**: Optimize oracle update transactions * **NFT marketplaces**: Reduce gas for complex NFT operations * **Lending protocols**: Optimize lending and borrowing transactions * **Insurance platforms**: Reduce claim processing costs * **Payment systems**: Optimize payment routing * **Staking operations**: Minimize staking transaction costs * **Development tools**: Test and optimize contract interactions * **Analytics platforms**: Analyze access patterns and costs * **Security tools**: Identify potential attack vectors * **Testing frameworks**: Simulate transactions efficiently * **Integration services**: Provide optimized transaction APIs This method provides crucial gas optimization capabilities, enabling more efficient and cost-effective transactions on the Hyperliquid EVM platform. > [!NOTE] > > Access lists are particularly beneficial for transactions that interact with multiple contracts or access many storage slots. The gas savings can be significant for complex DeFi operations, but may not be worth it for simple transfers. Always test both with and without access lists to determine the most cost-effective approach. # eth_estimateGas | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-estimate-gas openapi/hyperliquid_node_api/hyperevm/evm_eth_estimate_gas.json post /evm The eth_estimateGas JSON-RPC method estimates the amount of gas required to execute a transaction without actually executing it on the blockchain. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_estimateGas` JSON-RPC method estimates the amount of gas required to execute a transaction without actually executing it on the blockchain. This method is essential for determining gas costs before sending transactions, helping users avoid failed transactions due to insufficient gas. On Hyperliquid, gas estimation is performed against the latest block state only. ## Parameters The method takes one parameter: a transaction object containing the transaction details. ### Transaction object * `from` (string, optional) — The address the transaction is sent from * `to` (string, optional) — The address the transaction is directed to * `gas` (string, optional) — Integer of the gas provided for the transaction execution * `gasPrice` (string, optional) — Integer of the gasPrice used for each paid gas * `value` (string, optional) — Integer of the value sent with this transaction * `data` (string, optional) — The compiled code of a contract OR the hash of the invoked method signature and encoded parameters ## Response The method returns the estimated gas amount as a hexadecimal string. ### Response structure **Gas estimate:** * `result` — The estimated gas amount as a hexadecimal string ### Gas estimation accuracy **Estimation considerations:** * Estimates are based on the latest block state only (Hyperliquid limitation) * Actual gas usage may vary if state changes before execution * Complex transactions may have less accurate estimates * Include a buffer (10-20%) when using estimates for actual transactions **Factors affecting accuracy:** * Contract complexity and execution paths * State changes between estimation and execution * Gas price fluctuations and network congestion * Dynamic contract behavior based on inputs **Hyperliquid considerations:** * Estimates are based on latest block state only * Monitor actual vs estimated gas usage for optimization * Implement fallback gas limits for critical transactions ## Example request This an example call with wrapped HYPE (wHYPE) on the Hyperliquid mainnet deployed at [0x5555555555555555555555555555555555555555](https://hyperevmscan.io/token/0x5555555555555555555555555555555555555555). ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_estimateGas","params":[{"from":"0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA","to":"0x5555555555555555555555555555555555555555","data":"0xa9059cbb000000000000000000000000fefefefefefefefefefefefefefefefefefefefe0000000000000000000000000000000000000000000000000de0b6b3a7640000"}],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) gas = w3.eth.estimate_gas({ "from": "0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", "to": "0x5555555555555555555555555555555555555555", "data": "0xa9059cbb000000000000000000000000fefefefefefefefefefefefefefefefefefefefe0000000000000000000000000000000000000000000000000de0b6b3a7640000", }) print(gas) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const gas = await provider.estimateGas({ from: "0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", to: "0x5555555555555555555555555555555555555555", data: "0xa9059cbb000000000000000000000000fefefefefefefefefefefefefefefefefefefefe0000000000000000000000000000000000000000000000000de0b6b3a7640000", }); console.log(gas); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const gas = await client.estimateGas({ account: "0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", to: "0x5555555555555555555555555555555555555555", data: "0xa9059cbb000000000000000000000000fefefefefefefefefefefefefefefefefefefefe0000000000000000000000000000000000000000000000000de0b6b3a7640000", }); console.log(gas); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_estimateGas` method is essential for applications that need to: * **Transaction cost calculation**: Estimate gas costs before sending transactions * **Wallet applications**: Display transaction fees to users before confirmation * **DeFi protocols**: Calculate gas costs for complex multi-step transactions * **Smart contract deployment**: Estimate deployment costs for contracts * **Batch operations**: Plan gas usage for batch transactions and operations * **Trading platforms**: Estimate gas costs for trading operations and swaps * **NFT marketplaces**: Calculate minting and trading gas costs * **Gaming applications**: Estimate gas for in-game transactions and operations * **Automated systems**: Set appropriate gas limits for automated transactions * **Cost optimization**: Compare gas costs across different transaction methods * **User experience**: Prevent transaction failures due to insufficient gas * **Analytics platforms**: Analyze gas usage patterns and optimization opportunities * **Development tools**: Test and optimize smart contract gas efficiency * **Portfolio management**: Calculate total transaction costs for portfolio operations * **Cross-chain bridges**: Estimate gas costs for bridge operations * **Governance systems**: Calculate voting and proposal submission costs * **Staking platforms**: Estimate gas costs for staking and delegation operations * **Lending protocols**: Calculate gas costs for lending and borrowing operations * **Insurance platforms**: Estimate gas costs for claim processing and payouts * **Supply chain**: Calculate gas costs for supply chain tracking operations * **Identity systems**: Estimate gas costs for identity verification transactions * **Subscription services**: Calculate gas costs for recurring payment operations * **Audit tools**: Analyze gas usage for security and efficiency audits * **Educational platforms**: Demonstrate gas concepts and optimization techniques * **Research tools**: Study gas usage patterns and blockchain economics This method provides essential gas estimation capabilities, enabling cost-effective and reliable transaction execution on the Hyperliquid EVM platform. > [!NOTE] > > On Hyperliquid, `eth_estimateGas` only supports the latest block state. Gas estimates may not be accurate for complex transactions or when the blockchain state changes between the estimate and actual execution. Always include a reasonable buffer when using estimates for actual transactions. # eth_feeHistory | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-fee-history openapi/hyperliquid_node_api/hyperevm/evm_eth_fee_history.json post /evm The eth_feeHistory JSON-RPC method returns historical gas fee data for a range of blocks. Use it on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_feeHistory` JSON-RPC method returns historical gas fee data for a range of blocks. This method provides essential information for gas price estimation, including base fees, gas usage ratios, and priority fee percentiles. ## Parameters The method takes three parameters: 1. **Block count** - Number of blocks to retrieve fee history for 2. **Newest block** - The newest block to include in the range 3. **Reward percentiles** - Array of percentiles for priority fee rewards ### Parameter details * `blockCount` (string, required) — Number of blocks in the requested range as a hexadecimal string (maximum 1024) * `newestBlock` (string, required) — Block identifier: `"latest"`, `"earliest"`, `"pending"`, or a specific block number in hexadecimal * `rewardPercentiles` (array, optional) — Array of percentile values (0-100) for priority fee rewards ## Response The method returns historical fee data for the specified block range. ### Response structure **Fee history object:** * `oldestBlock` — The oldest block number in the range as a hexadecimal string * `baseFeePerGas` — Array of base fees per gas for each block (including one extra for the next block) * `gasUsedRatio` — Array of gas used ratios for each block (0.0 to 1.0) * `reward` — Array of priority fee rewards at specified percentiles for each block ### Data interpretation **Base fee analysis:** * Base fees adjust automatically based on network congestion * Higher base fees indicate network congestion * Use base fee trends for gas price prediction * Base fee array includes one extra element for the next block **Gas usage patterns:** * Gas used ratio indicates block fullness (0.0 = empty, 1.0 = full) * Higher ratios suggest network congestion * Consistent high ratios may indicate sustained demand * Use for capacity planning and congestion analysis **Priority fee insights:** * On Hyperliquid, priority fees are currently always zero * Percentile data will reflect this zero priority fee structure * Transaction ordering is not based on priority fees * Focus on base fee analysis for gas price optimization ## Usage example ### Basic implementation ```javascript theme={"system"} // Get fee history from Hyperliquid const getFeeHistory = async (blockCount = 4, newestBlock = 'latest', percentiles = [25, 50, 75]) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_feeHistory', params: [`0x${blockCount.toString(16)}`, newestBlock, percentiles], id: 1 }) }); const data = await response.json(); return data.result; }; // Analyze fee trends const analyzeFeeHistory = async () => { try { const feeHistory = await getFeeHistory(); // Convert base fees from hex to decimal (in gwei) const baseFees = feeHistory.baseFeePerGas.map(fee => parseInt(fee, 16) / 1e9 ); // Analyze gas usage ratios const avgGasUsage = feeHistory.gasUsedRatio.reduce((a, b) => a + b, 0) / feeHistory.gasUsedRatio.length; // Note: Priority fees are always zero on Hyperliquid const priorityFees = feeHistory.reward.map(block => block.map(fee => parseInt(fee, 16) / 1e9) ); return { oldestBlock: parseInt(feeHistory.oldestBlock, 16), baseFees: baseFees, averageGasUsage: avgGasUsage, priorityFees: priorityFees, // Will be all zeros on Hyperliquid recommendation: { baseFee: Math.max(...baseFees) * 1.1, // Add 10% buffer priorityFee: 0 // Always zero on Hyperliquid } }; } catch (error) { console.error('Fee history analysis failed:', error); throw error; } }; // Usage analyzeFeeHistory() .then(analysis => { console.log('Fee Analysis:', analysis); console.log(`Recommended base fee: ${analysis.recommendation.baseFee.toFixed(2)} gwei`); console.log(`Priority fee: ${analysis.recommendation.priorityFee} gwei (always zero on Hyperliquid)`); }) .catch(error => console.error('Error:', error)); ``` ## Hyperliquid-specific considerations ### Priority fees **Important limitation:** * Priority fees are always zero on Hyperliquid * `eth_maxPriorityFeePerGas` always returns zero * Transaction ordering is not based on priority fees * Focus on base fee optimization for cost management ### Fee optimization **Base fee analysis:** * Monitor base fee trends for optimal transaction timing * Use gas usage ratios to identify network congestion * Plan batch operations during low base fee periods * Implement base fee-based cost optimization strategies ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_feeHistory","params":["0x4","latest",[25,50,75]],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) fee_history = w3.eth.fee_history(4, "latest", [25, 50, 75]) print(fee_history) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const feeHistory = await provider.send("eth_feeHistory", [ "0x4", "latest", [25, 50, 75], ]); console.log(feeHistory); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const feeHistory = await client.getFeeHistory({ blockCount: 4, rewardPercentiles: [25, 50, 75], blockTag: "latest", }); console.log(feeHistory); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_feeHistory` method is essential for applications that need to: * **Gas price estimation**: Calculate optimal gas prices for transactions * **Wallet applications**: Provide users with fee estimation and recommendations * **DeFi protocols**: Optimize transaction costs for automated operations * **Trading platforms**: Minimize transaction costs for high-frequency operations * **Analytics platforms**: Analyze network congestion and fee trends * **Cost optimization**: Schedule transactions during low-fee periods * **Fee prediction**: Build predictive models for future gas prices * **Network monitoring**: Track network health and congestion patterns * **Arbitrage bots**: Optimize transaction timing for profitability * **Portfolio management**: Calculate transaction costs for portfolio operations * **Automated systems**: Implement dynamic fee adjustment strategies * **Research tools**: Study network economics and fee market dynamics * **Development tools**: Test applications under various fee conditions * **Infrastructure services**: Provide fee estimation APIs to other applications * **Compliance tools**: Monitor transaction costs for regulatory reporting * **Risk management**: Assess fee volatility risks for operations * **Educational platforms**: Demonstrate gas market concepts and dynamics * **Optimization services**: Provide fee optimization recommendations * **Monitoring systems**: Alert on significant fee changes or anomalies * **Integration services**: Connect fee data with external systems * **Planning tools**: Plan transaction batches based on fee forecasts * **Cost accounting**: Track and analyze transaction cost patterns * **Performance optimization**: Optimize application performance based on fees * **User experience**: Provide transparent fee information to users * **Economic analysis**: Study the economic impact of fee structures This method provides comprehensive fee market data, enabling cost-effective and efficient transaction management on the Hyperliquid EVM platform. > [!NOTE] > > On Hyperliquid, priority fees are always zero (`eth_maxPriorityFeePerGas` always returns zero). Fee history data reflects past network conditions and may not predict future fees accurately during periods of high volatility. Focus on base fee analysis for gas price optimization. # eth_gasPrice | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-gas-price openapi/hyperliquid_node_api/hyperevm/evm_eth_gas_price.json post /evm The eth_gasPrice JSON-RPC method returns the base fee for the next small block on Hyperliquid EVM. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_gasPrice` JSON-RPC method returns the base fee for the next small block on Hyperliquid EVM. This method provides the gas price for standard (small) block transactions. ## Parameters This method takes no parameters. ## Response The method returns the current gas price as a hexadecimal string representing wei. ### Response structure **Gas price:** * `result` — The base fee for the next small block in wei as a hexadecimal string ### Data interpretation **Gas price format:** * Returned as hexadecimal string with `0x` prefix * Value represents wei (smallest unit of ether) * Convert to gwei by dividing by 10^9 * Convert to ether by dividing by 10^18 **Price calculation:** * 1 gwei = 10^9 wei * 1 ether = 10^18 wei * Example: `0x3b9aca00` = 1,000,000,000 wei = 1 gwei ## Usage example ### Basic implementation ```javascript theme={"system"} // Get gas price for small blocks on Hyperliquid const getGasPrice = async () => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 1 }) }); const data = await response.json(); const gasPriceWei = parseInt(data.result, 16); return { hex: data.result, wei: gasPriceWei, gwei: gasPriceWei / 1e9, ether: gasPriceWei / 1e18 }; }; // Compare small block vs big block gas prices const compareGasPrices = async () => { const [smallBlockPrice, bigBlockPrice] = await Promise.all([ // Small block gas price fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 1 }) }).then(r => r.json()), // Big block gas price fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_bigBlockGasPrice', params: [], id: 2 }) }).then(r => r.json()) ]); return { smallBlock: { hex: smallBlockPrice.result, gwei: parseInt(smallBlockPrice.result, 16) / 1e9 }, bigBlock: { hex: bigBlockPrice.result, gwei: parseInt(bigBlockPrice.result, 16) / 1e9 } }; }; // Usage getGasPrice() .then(price => console.log(`Small block gas price: ${price.gwei} gwei`)) .catch(error => console.error('Error:', error)); ``` ## Hyperliquid gas pricing ### Small vs big blocks **Small block pricing:** * `eth_gasPrice` returns the base fee for the next small block * Used for standard transactions * Default pricing for most operations **Big block pricing:** * `eth_bigBlockGasPrice` returns the base fee for the next big block * Used for addresses that are using big blocks * Check with `eth_usingBigBlocks` to determine if an address uses big blocks ### Transaction fee calculation **Basic calculation:** * Transaction cost = gas price × gas limit * Use `eth_gasPrice` for standard transactions * Use `eth_bigBlockGasPrice` if the address uses big blocks **Unit conversions:** * 1 gwei = 10^9 wei * 1 ether = 10^18 wei * Example: `0x3b9aca00` = 1,000,000,000 wei = 1 gwei ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) gas_price = w3.eth.gas_price print(gas_price) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const { gasPrice } = await provider.getFeeData(); console.log(gasPrice); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const gasPrice = await client.getGasPrice(); console.log(gasPrice); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_gasPrice` method is essential for applications that need to: * **Standard transaction pricing**: Get gas price for small block transactions * **Fee estimation**: Calculate transaction costs for standard operations * **Wallet applications**: Display gas prices for regular transactions * **Cost comparison**: Compare small block vs big block pricing * **Transaction planning**: Determine appropriate gas pricing strategy * **Development tools**: Test applications with current small block pricing * **Analytics platforms**: Monitor small block gas price trends * **Automated systems**: Set gas prices for standard automated transactions > [!NOTE] > > On Hyperliquid, `eth_gasPrice` returns the base fee for the next small block. For addresses using big blocks, use `eth_bigBlockGasPrice` instead. Check if an address uses big blocks with `eth_usingBigBlocks`. # eth_getBalance | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-balance openapi/hyperliquid_node_api/hyperevm/evm_eth_get_balance.json post /evm The eth_getBalance JSON-RPC method returns the balance of an account at a given block number. Chainstack Hyperliquid EVM reference. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getBalance` JSON-RPC method returns the balance of an account at a given block number. This method is fundamental for checking account balances and is essential for wallet applications, portfolio tracking, and balance verification. ## Parameters The method takes two parameters: 1. **Address** - The account address to check the balance for 2. **Block parameter** - The block at which to check the balance ### Parameter details * `address` (string, required) — The 20-byte account address to check the balance for * `block` (string, required) — Block identifier: `"latest"` (only the latest block is supported on Hyperliquid) ## Response The method returns the account balance in wei as a hexadecimal string. ### Response structure **Account balance:** * `result` — The account balance in wei as a hexadecimal string ### Data interpretation **Balance format:** * Returned as hexadecimal string with `0x` prefix * Value represents wei (smallest unit of HYPE) * Convert to HYPE by dividing by 10^18 * Convert to gwei by dividing by 10^9 **Unit conversions:** * 1 HYPE = 10^18 wei * 1 gwei = 10^9 wei * Example: `0xde0b6b3a7640000` = 1,000,000,000,000,000,000 wei = 1 HYPE ## Hyperliquid-specific considerations ### Block limitations **Latest block only:** * Only the `"latest"` block parameter is supported * Historical balance queries are not available in the default implementation * All balance checks return the current state * For historical data, consider using archive node implementations ### Balance monitoring **Real-time tracking:** * Check balances at regular intervals for portfolio tracking * Monitor balance changes for transaction confirmation * Set up alerts for significant balance changes * Use current balance for all transaction validations ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA","latest"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) balance = w3.eth.get_balance("0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", "latest") print(balance) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const balance = await provider.getBalance("0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", "latest"); console.log(balance); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const balance = await client.getBalance({ address: "0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", blockTag: "latest", }); console.log(balance); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getBalance` method is essential for applications that need to: * **Wallet applications**: Display current account balances to users * **Portfolio tracking**: Monitor balances across multiple accounts * **Transaction validation**: Verify sufficient balance before transactions * **DeFi protocols**: Check user balances for protocol interactions * **Analytics platforms**: Track account balance changes over time * **Audit systems**: Generate balance reports for compliance * **Trading platforms**: Verify account balances for trading operations * **Payment systems**: Check sender balances before processing payments * **Gaming applications**: Monitor in-game currency balances * **Staking platforms**: Verify balances for staking operations * **Lending protocols**: Check collateral balances and ratios * **Insurance platforms**: Monitor account balances for coverage * **Cross-chain bridges**: Verify balances before bridge operations * **Governance systems**: Check voting power based on token balances * **NFT marketplaces**: Verify buyer balances for purchases * **Subscription services**: Monitor account balances for recurring payments * **Risk management**: Track account balances for liquidation monitoring * **Compliance tools**: Generate balance reports for regulatory requirements * **Educational platforms**: Demonstrate balance concepts and calculations * **Research tools**: Analyze account balance distributions and patterns * **Monitoring systems**: Alert on significant balance changes * **Integration services**: Provide balance data to external systems * **Development tools**: Test applications with various balance scenarios * **User experience**: Provide real-time balance feedback * **Security systems**: Monitor for unusual balance changes or activities This method provides fundamental account balance information, enabling comprehensive account management and financial tracking on the Hyperliquid EVM platform. > [!NOTE] > > On Hyperliquid, `eth_getBalance` only supports the latest block. Historical balance queries are not supported in the default RPC implementation. All balance queries return the current account state. # eth_getBlockByHash | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-block-by-hash openapi/hyperliquid_node_api/hyperevm/evm_eth_get_block_by_hash.json post /evm The eth_getBlockByHash JSON-RPC method returns information about a block by its hash. Available on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getBlockByHash` JSON-RPC method returns information about a block by its hash. This method is essential for retrieving detailed block information when you have the block hash, commonly used for block exploration, transaction verification, and blockchain analysis. ## Parameters The method takes two parameters: 1. **Block hash** - The hash of the block to retrieve 2. **Full transaction objects** - Whether to return full transaction objects or just hashes ### Parameter details * `blockHash` (string, required) — The 32-byte hash of the block to retrieve * `fullTransactionObjects` (boolean, required) — If `true`, returns full transaction objects; if `false`, returns only transaction hashes ## Response The method returns detailed information about the specified block, or `null` if the block is not found. ### Response structure **Block object:** * `number` — Block number as a hexadecimal string * `hash` — The 32-byte hash of the block * `parentHash` — Hash of the parent block * `timestamp` — Block timestamp as a Unix timestamp in hexadecimal * `gasLimit` — Maximum gas allowed in this block * `gasUsed` — Total gas used by all transactions in the block * `transactions` — Array of transaction hashes or full transaction objects * `miner` — Address of the block miner/validator * `difficulty` — Block difficulty (may be 0 for some consensus mechanisms) * `totalDifficulty` — Total difficulty of the chain until this block * `size` — Block size in bytes as a hexadecimal string * `extraData` — Extra data field of the block * `nonce` — Block nonce (if applicable) * `receiptsRoot` — Root hash of the receipts trie * `stateRoot` — Root hash of the state trie * `transactionsRoot` — Root hash of the transactions trie ### Transaction data format **With fullTransactionObjects = false:** * Returns array of transaction hashes as strings * Minimal data transfer for basic block information * Suitable for block overview and transaction counting **With fullTransactionObjects = true:** * Returns array of complete transaction objects * Includes all transaction details (from, to, value, gas, etc.) * Larger response but comprehensive transaction data ## Hyperliquid-specific considerations ### System transactions **HyperCore transactions:** * Blocks may contain system transactions that originate from HyperCore * Use `eth_getSystemTxsByBlockHash` to retrieve system transactions specifically * System transactions are separate from regular user transactions * Important for complete block analysis and understanding network activity ### Block data interpretation **Transaction types:** * Regular transactions: Standard user-initiated transactions * System transactions: Transactions originating from HyperCore * Both types contribute to block gas usage and transaction count **Data analysis:** * Consider both regular and system transactions for complete analysis * System transactions may have different patterns and characteristics * Use appropriate methods to retrieve the specific transaction type needed ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getBlockByHash","params":["0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a",false],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block = w3.eth.get_block( "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a", full_transactions=False, ) print(block) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const block = await provider.getBlock( "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a" ); console.log(block); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const block = await client.getBlock({ blockHash: "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a", includeTransactions: false, }); console.log(block); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getBlockByHash` method is essential for applications that need to: * **Block explorers**: Display detailed block information and navigation * **Transaction verification**: Confirm transaction inclusion in specific blocks * **Blockchain analytics**: Analyze block data for network insights * **Audit systems**: Verify block integrity and transaction history * **Development tools**: Debug and test blockchain applications * **Monitoring systems**: Track block production and network health * **DeFi protocols**: Verify block-based events and state changes * **System transaction analysis**: Analyze both regular and HyperCore system transactions * **Network diagnostics**: Troubleshoot blockchain synchronization issues * **Integration services**: Provide block data to external systems > [!NOTE] > > Block hashes are unique identifiers. If a block hash doesn't exist or is invalid, the method returns `null`. On Hyperliquid, blocks may contain system transactions from HyperCore - use `eth_getSystemTxsByBlockHash` to retrieve these separately. # eth_getBlockByNumber | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-block-by-number openapi/hyperliquid_node_api/hyperevm/evm_eth_get_block_by_number.json post /evm The eth_getBlockByNumber JSON-RPC method returns information about a block by its number. Use it on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getBlockByNumber` JSON-RPC method returns information about a block by its number. This method is essential for retrieving block data when you know the block number, commonly used for sequential block processing, blockchain synchronization, and historical data analysis. ## Parameters The method takes two parameters: 1. **Block number** - The number of the block to retrieve 2. **Full transaction objects** - Whether to return full transaction objects or just hashes ### Parameter details * `blockNumber` (string, required) — Block identifier: `"latest"`, `"earliest"`, `"pending"`, or a specific block number in hexadecimal * `fullTransactionObjects` (boolean, required) — If `true`, returns full transaction objects; if `false`, returns only transaction hashes ## Response The method returns detailed information about the specified block, or `null` if the block is not found. ### Response structure **Block object:** * `number` — Block number as a hexadecimal string * `hash` — The 32-byte hash of the block * `parentHash` — Hash of the parent block * `timestamp` — Block timestamp as a Unix timestamp in hexadecimal * `gasLimit` — Maximum gas allowed in this block * `gasUsed` — Total gas used by all transactions in the block * `transactions` — Array of transaction hashes or full transaction objects * `miner` — Address of the block miner/validator * `difficulty` — Block difficulty (may be 0 for some consensus mechanisms) * `totalDifficulty` — Total difficulty of the chain until this block * `size` — Block size in bytes as a hexadecimal string * `extraData` — Extra data field of the block * `nonce` — Block nonce (if applicable) * `receiptsRoot` — Root hash of the receipts trie * `stateRoot` — Root hash of the state trie * `transactionsRoot` — Root hash of the transactions trie ### Block number formats **Special identifiers:** * `"latest"` — Most recent block * `"earliest"` — Genesis block (block 0) * `"pending"` — Pending block being mined **Hexadecimal numbers:** * `"0x0"` — Genesis block * `"0x1"` — Block 1 * `"0x9d0c37"` — Block 10,291,255 (decimal) ## Hyperliquid-specific considerations ### System transactions **HyperCore transactions:** * Blocks may contain system transactions that originate from HyperCore * Use `eth_getSystemTxsByBlockNumber` to retrieve system transactions specifically * System transactions are separate from regular user transactions * Important for complete block analysis and understanding network activity ### Block processing **Sequential access:** * Process blocks sequentially for complete chain analysis * Handle missing blocks gracefully in case of gaps * Use block numbers for efficient sequential processing * Track last processed block for incremental updates **Block number formats:** * `"latest"` — Most recent block * `"earliest"` — Genesis block (block 0) * `"0x1a4"` — Specific block number in hexadecimal (420 in decimal) ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x9d0c37",false],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block = w3.eth.get_block(10291255, full_transactions=False) print(block) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const block = await provider.getBlock(10291255, false); console.log(block); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const block = await client.getBlock({ blockNumber: 10291255n, includeTransactions: false, }); console.log(block); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getBlockByNumber` method is essential for applications that need to: * **Blockchain synchronization**: Download and process blocks sequentially * **Block explorers**: Display block information and enable navigation * **Analytics platforms**: Collect block data for network analysis * **Transaction monitoring**: Track transactions within specific blocks * **Historical analysis**: Study past blockchain states and events * **Audit systems**: Verify blockchain integrity and transaction history * **Development tools**: Debug and test blockchain applications * **System transaction analysis**: Analyze both regular and HyperCore system transactions * **Network monitoring**: Track block production and network health * **Integration services**: Provide block data to external systems > [!NOTE] > > Block numbers are sequential. If a block number doesn't exist (e.g., future block), the method returns `null`. On Hyperliquid, blocks may contain system transactions from HyperCore - use `eth_getSystemTxsByBlockNumber` to retrieve these separately. # eth_getBlockReceipts | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-block-receipts openapi/hyperliquid_node_api/hyperevm/evm_eth_get_block_receipts.json post /evm The eth_getBlockReceipts JSON-RPC method returns all transaction receipts for a given block. Chainstack Hyperliquid EVM reference. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getBlockReceipts` JSON-RPC method returns all transaction receipts for a given block. This method is highly efficient for retrieving receipt data for all transactions in a block with a single API call, making it ideal for block processing, event extraction, and comprehensive transaction analysis. ## Parameters The method takes one parameter: 1. **Block number** - The block for which to retrieve all transaction receipts ### Parameter details * `blockNumber` (string, required) — Block identifier: `"latest"`, `"earliest"`, `"pending"`, or a specific block number in hexadecimal ## Response The method returns an array of transaction receipt objects for all transactions in the specified block. ### Response structure **Receipt array:** * Array of transaction receipt objects, one for each transaction in the block * Empty array if the block contains no transactions * `null` if the block doesn't exist **Transaction receipt object:** * `transactionHash` — Hash of the transaction * `transactionIndex` — Index of the transaction in the block (0-based) * `blockHash` — Hash of the block containing the transaction * `blockNumber` — Number of the block containing the transaction * `from` — Address of the transaction sender * `to` — Address of the transaction receiver (null for contract creation) * `gasUsed` — Amount of gas used by this specific transaction * `cumulativeGasUsed` — Total gas used in the block up to and including this transaction * `contractAddress` — Address of the created contract (null if not a contract creation) * `logs` — Array of log objects generated by the transaction * `status` — Transaction status (`0x0` for failure, `0x1` for success) * `effectiveGasPrice` — Actual gas price paid by the transaction * `type` — Transaction type (0x0 for legacy, 0x1 for EIP-2930, 0x2 for EIP-1559) ## Usage example ### Basic implementation ```javascript theme={"system"} // Get all transaction receipts for a block on Hyperliquid const getBlockReceipts = async (blockNumber) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getBlockReceipts', params: [blockNumber], id: 1 }) }); const data = await response.json(); return data.result; }; // Analyze block transaction outcomes const analyzeBlockTransactions = async (blockNumber) => { const receipts = await getBlockReceipts(blockNumber); if (!receipts || receipts.length === 0) { return { message: 'No transactions in block' }; } const analysis = { totalTransactions: receipts.length, successfulTransactions: 0, failedTransactions: 0, totalGasUsed: 0, contractCreations: 0, totalLogs: 0 }; receipts.forEach(receipt => { // Count successful vs failed transactions if (receipt.status === '0x1') { analysis.successfulTransactions++; } else { analysis.failedTransactions++; } // Sum gas usage analysis.totalGasUsed += parseInt(receipt.gasUsed, 16); // Count contract creations if (receipt.contractAddress) { analysis.contractCreations++; } // Count logs/events analysis.totalLogs += receipt.logs.length; }); analysis.successRate = (analysis.successfulTransactions / analysis.totalTransactions) * 100; return analysis; }; // Extract all events from a block const extractBlockEvents = async (blockNumber, contractAddress = null) => { const receipts = await getBlockReceipts(blockNumber); if (!receipts) { return []; } // Extract all logs from all receipts const allLogs = receipts.flatMap(receipt => receipt.logs.map(log => ({ ...log, transactionHash: receipt.transactionHash, transactionIndex: receipt.transactionIndex, blockNumber: receipt.blockNumber })) ); // Filter by contract address if specified if (contractAddress) { return allLogs.filter(log => log.address.toLowerCase() === contractAddress.toLowerCase() ); } return allLogs; }; // Usage examples analyzeBlockTransactions('latest') .then(analysis => console.log('Block Analysis:', analysis)) .catch(error => console.error('Error:', error)); // Extract events from a specific block extractBlockEvents('0x9d0c37') .then(events => console.log(`Found ${events.length} events in block`)) .catch(error => console.error('Error:', error)); ``` ## Hyperliquid-specific considerations ### System transactions **HyperCore transactions:** * Block receipts include receipts for both regular and system transactions * System transactions originate from HyperCore and may have different patterns * All transactions (regular and system) are included in the receipt array * System transactions contribute to block gas usage and event generation ### Efficiency benefits **Batch processing:** * Retrieve all receipts for a block in one request * More efficient than individual `eth_getTransactionReceipt` calls * Reduces API call overhead and network latency * Ideal for processing entire blocks atomically ### Receipt data interpretation **Transaction status:** * `0x1` — Transaction succeeded * `0x0` — Transaction failed (reverted) * Failed transactions still consume gas * Check status before processing transaction effects **Gas analysis:** * `gasUsed` — Gas consumed by individual transaction * `cumulativeGasUsed` — Total gas used up to this transaction in the block * Use for calculating gas efficiency and block utilization ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getBlockReceipts","params":["0x9d0c37"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # Get all transaction receipts for a block receipts = w3.eth.get_block_receipts(0x9d0c37) print(f"Receipts in block: {len(receipts)}") for receipt in receipts: print(receipt["transactionHash"].hex(), receipt["status"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // eth_getBlockReceipts is not a typed provider method in ethers v6, // so call it directly with send(). const receipts = await provider.send("eth_getBlockReceipts", ["0x9d0c37"]); console.log(`Receipts in block: ${receipts.length}`); for (const receipt of receipts) { console.log(receipt.transactionHash, receipt.status); } ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // Get all transaction receipts for a block const receipts = await client.getBlockReceipts({ blockNumber: 0x9d0c37n, }); console.log(`Receipts in block: ${receipts.length}`); for (const receipt of receipts) { console.log(receipt.transactionHash, receipt.status); } ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getBlockReceipts` method is essential for applications that need to: * **Block processing**: Efficiently process all transactions in a block with a single API call * **Event indexing**: Build comprehensive event indexes from receipt data * **Analytics platforms**: Collect transaction and event data for analysis * **DeFi monitoring**: Track protocol events and state changes * **Audit systems**: Verify transaction outcomes and contract interactions * **Block explorers**: Display comprehensive block transaction information * **Transaction monitoring**: Track transaction success rates and patterns * **Gas analysis**: Analyze gas usage patterns across block transactions * **Smart contract monitoring**: Track contract interactions and events * **System transaction analysis**: Analyze both regular and HyperCore system transaction receipts * **Data synchronization**: Maintain synchronized blockchain data * **Integration services**: Provide receipt data to external systems > [!NOTE] > > This method returns receipts for all transactions in a block, including both regular transactions and system transactions from HyperCore. This can be a large amount of data for blocks with many transactions. Consider filtering strategies for high-volume blocks. # eth_getBlockTransactionCountByHash | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-block-transaction-count-by-hash openapi/hyperliquid_node_api/hyperevm/evm_eth_get_block_transaction_count_by_hash.json post /evm The eth_getBlockTransactionCountByHash JSON-RPC method returns the number of transactions in a block by its hash. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getBlockTransactionCountByHash` JSON-RPC method returns the number of transactions in a block by its hash. This method provides a lightweight way to get transaction count information without retrieving the full block data, making it efficient for block analysis and statistics. ## Parameters The method takes one parameter: 1. **Block hash** - The hash of the block to get the transaction count for ### Parameter details * `blockHash` (string, required) — The 32-byte hash of the block ## Response The method returns the number of transactions in the specified block as a hexadecimal string, or `null` if the block is not found. ### Response structure **Transaction count:** * `result` — The number of transactions in the block as a hexadecimal string ### Data interpretation **Count format:** * Returned as hexadecimal string with `0x` prefix * Convert to decimal for numerical operations * `0x0` indicates an empty block (no transactions) * `null` indicates the block hash doesn't exist **Conversion example:** ```javascript theme={"system"} // Convert hex count to decimal const txCount = parseInt("0x5", 16); // 5 transactions ``` ## Usage example ### Basic implementation ```javascript theme={"system"} // Get transaction count for a block by hash on Hyperliquid const getBlockTransactionCountByHash = async (blockHash) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getBlockTransactionCountByHash', params: [blockHash], id: 1 }) }); const data = await response.json(); if (data.result === null) { throw new Error('Block not found'); } return { hex: data.result, decimal: parseInt(data.result, 16) }; }; // Analyze multiple blocks for activity patterns const analyzeBlockActivity = async (blockHashes) => { const results = []; for (const hash of blockHashes) { try { const count = await getBlockTransactionCountByHash(hash); results.push({ blockHash: hash, transactionCount: count.decimal }); } catch (error) { console.error(`Error processing block ${hash}:`, error); results.push({ blockHash: hash, transactionCount: null, error: error.message }); } } // Calculate statistics const validCounts = results .filter(r => r.transactionCount !== null) .map(r => r.transactionCount); if (validCounts.length === 0) { return { results, statistics: null }; } const statistics = { totalBlocks: validCounts.length, totalTransactions: validCounts.reduce((sum, count) => sum + count, 0), averageTransactions: validCounts.reduce((sum, count) => sum + count, 0) / validCounts.length, maxTransactions: Math.max(...validCounts), minTransactions: Math.min(...validCounts), emptyBlocks: validCounts.filter(count => count === 0).length }; return { results, statistics }; }; // Usage examples const blockHash = "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a"; getBlockTransactionCountByHash(blockHash) .then(count => console.log(`Block has ${count.decimal} transactions`)) .catch(error => console.error('Error:', error)); // Analyze multiple blocks const blockHashes = [ "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a", "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" ]; analyzeBlockActivity(blockHashes) .then(analysis => console.log('Block Activity Analysis:', analysis)) .catch(error => console.error('Error:', error)); ``` ## Efficiency benefits ### Lightweight queries **Minimal data transfer:** * Returns only the transaction count, not full block data * Significantly smaller response than `eth_getBlockByHash` * Faster response times for count-only queries * Ideal for preliminary block analysis and statistics ### Performance optimization **Conditional processing:** * Check transaction count before deciding to process full block * Skip empty blocks in analysis workflows * Optimize processing based on block activity levels * Implement efficient batch processing strategies ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getBlockTransactionCountByHash","params":["0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block_hash = "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a" # Passing a block hash routes to eth_getBlockTransactionCountByHash count = w3.eth.get_block_transaction_count(block_hash) print(count) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blockHash = "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a"; // ethers v6 has no dedicated helper, so call the method directly const count = await provider.send("eth_getBlockTransactionCountByHash", [ blockHash, ]); console.log(parseInt(count, 16)); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blockHash = "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a"; // Passing blockHash routes to eth_getBlockTransactionCountByHash const count = await client.getBlockTransactionCount({ blockHash }); console.log(count); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getBlockTransactionCountByHash` method is essential for applications that need to: * **Block statistics**: Generate transaction count statistics and metrics * **Network analysis**: Analyze blockchain activity patterns and trends * **Performance optimization**: Optimize block processing based on transaction counts * **Block explorers**: Display transaction counts in block listings efficiently * **Analytics platforms**: Collect block activity data for analysis * **Monitoring systems**: Track network activity and usage patterns * **Development tools**: Optimize applications based on block activity levels * **Data visualization**: Create charts and graphs of block activity over time > [!NOTE] > > This method returns only the transaction count, including both regular transactions and system transactions from HyperCore. The count represents the total number of all transactions in the block. Use `eth_getBlockByHash` if you need the full block data including transaction details. # eth_getBlockTransactionCountByNumber | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-block-transaction-count-by-number openapi/hyperliquid_node_api/hyperevm/evm_eth_get_block_transaction_count_by_number.json post /evm The eth_getBlockTransactionCountByNumber JSON-RPC method returns the number of transactions in a block by its number. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getBlockTransactionCountByNumber` JSON-RPC method returns the number of transactions in a block by its number. This method provides a lightweight way to get transaction count information for sequential block analysis, making it efficient for building statistics and monitoring network activity over time. ## Parameters The method takes one parameter: 1. **Block number** - The number of the block to get the transaction count for ### Parameter details * `blockNumber` (string, required) — Block identifier: `"latest"`, `"earliest"`, `"pending"`, or a specific block number in hexadecimal ## Response The method returns the number of transactions in the specified block as a hexadecimal string, or `null` if the block is not found. ### Response structure **Transaction count:** * `result` — The number of transactions in the block as a hexadecimal string ### Data interpretation **Count format:** * Returned as hexadecimal string with `0x` prefix * Convert to decimal for numerical operations * `0x0` indicates an empty block (no transactions) * `null` indicates the block number doesn't exist **Block number formats:** * `"latest"` — Most recent block transaction count * `"earliest"` — Genesis block transaction count (usually 0) * `"pending"` — Pending block transaction count * `"0x9d0c37"` — Specific block number (10,291,255 in decimal) ## Usage example ### Basic implementation ```javascript theme={"system"} // Get transaction count for a block by number on Hyperliquid const getBlockTransactionCountByNumber = async (blockNumber) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getBlockTransactionCountByNumber', params: [blockNumber], id: 1 }) }); const data = await response.json(); if (data.result === null) { throw new Error('Block not found'); } return { hex: data.result, decimal: parseInt(data.result, 16) }; }; // Analyze a range of blocks for activity patterns const analyzeBlockRange = async (startBlock, endBlock) => { const results = []; for (let i = startBlock; i <= endBlock; i++) { const hexBlock = "0x" + i.toString(16); try { const count = await getBlockTransactionCountByNumber(hexBlock); results.push({ blockNumber: i, hexBlockNumber: hexBlock, transactionCount: count.decimal }); } catch (error) { console.error(`Error processing block ${i}:`, error); results.push({ blockNumber: i, hexBlockNumber: hexBlock, transactionCount: null, error: error.message }); } } // Calculate statistics const validCounts = results .filter(r => r.transactionCount !== null) .map(r => r.transactionCount); if (validCounts.length === 0) { return { results, statistics: null }; } const statistics = { totalBlocks: validCounts.length, totalTransactions: validCounts.reduce((sum, count) => sum + count, 0), averageTransactions: validCounts.reduce((sum, count) => sum + count, 0) / validCounts.length, maxTransactions: Math.max(...validCounts), minTransactions: Math.min(...validCounts), emptyBlocks: validCounts.filter(count => count === 0).length }; return { results, statistics }; }; // Get latest block transaction count const getLatestBlockTransactionCount = async () => { return await getBlockTransactionCountByNumber('latest'); }; // Usage examples getLatestBlockTransactionCount() .then(count => console.log(`Latest block has ${count.decimal} transactions`)) .catch(error => console.error('Error:', error)); // Analyze blocks 100-110 analyzeBlockRange(100, 110) .then(analysis => { console.log('Block Range Analysis:', analysis.statistics); console.log('Detailed Results:', analysis.results); }) .catch(error => console.error('Error:', error)); ``` ## Sequential analysis benefits ### Range processing **Efficient bulk analysis:** * Query transaction counts across block ranges efficiently * Build activity timelines and trend analysis * Generate network utilization statistics * Identify periods of high and low activity ### Block number formats **Special identifiers:** * `"latest"` — Most recent block transaction count * `"earliest"` — Genesis block transaction count (usually 0) * `"0x9d0c37"` — Specific block number (10,291,255 in decimal) ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getBlockTransactionCountByNumber","params":["0x9d0c37"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # Block 0x9d0c37 (10,291,255). Pass an int for by-number, or "latest" for the tip. count = w3.eth.get_block_transaction_count(10291255) print(count) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // ethers v6 has no high-level wrapper for this RPC, so call it directly. const count = await provider.send("eth_getBlockTransactionCountByNumber", ["0x9d0c37"]); console.log(parseInt(count, 16)); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // blockNumber is a bigint; omit it (or use blockTag: "latest") for the tip. const count = await client.getBlockTransactionCount({ blockNumber: 10291255n }); console.log(count); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getBlockTransactionCountByNumber` method is essential for applications that need to: * **Sequential analysis**: Analyze transaction counts across block ranges efficiently * **Network monitoring**: Track blockchain activity and usage patterns over time * **Statistics generation**: Build comprehensive network activity statistics * **Block explorers**: Display transaction counts in sequential block listings * **Analytics platforms**: Collect activity data for network analysis and trends * **Real-time monitoring**: Track current network activity using "latest" block * **Development tools**: Optimize applications based on network activity levels * **Data visualization**: Create time-series charts of block activity patterns > [!NOTE] > > This method supports special block identifiers like `"latest"` and `"earliest"`, making it suitable for both historical analysis and real-time monitoring. The count includes both regular transactions and system transactions from HyperCore. # eth_getCode | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-code openapi/hyperliquid_node_api/hyperevm/evm_eth_get_code.json post /evm The eth_getCode JSON-RPC method returns the bytecode of a smart contract at a given address and block. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getCode` JSON-RPC method returns the bytecode of a smart contract at a given address and block. This method is essential for contract verification, analysis, and determining whether an address contains a smart contract or is an externally owned account (EOA). ## Parameters The method takes two parameters: 1. **Address** - The contract address to retrieve bytecode from 2. **Block parameter** - The block at which to retrieve the code ### Parameter details * `address` (string, required) — The 20-byte address to retrieve code from * `block` (string, required) — Block identifier: `"latest"` (only the latest block is supported on Hyperliquid) ## Response The method returns the contract bytecode as a hexadecimal string, or `"0x"` if no code exists at the address. ## Hyperliquid-specific considerations ### Block limitations **Latest block only:** * Only the `"latest"` block parameter is supported * Historical contract code queries are not available in the default implementation * All code retrieval is performed against the current state * For historical analysis, consider using archive node implementations ### Contract analysis **Address classification:** * Distinguish between contracts and EOAs efficiently * `"0x"` indicates an externally owned account (EOA) * Non-empty bytecode indicates a smart contract * Use for implementing different handling logic for different address types ## Example request This an example call with wrapped HYPE (wHYPE) on the Hyperliquid mainnet deployed at [0x5555555555555555555555555555555555555555](https://hyperevmscan.io/token/0x5555555555555555555555555555555555555555). ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getCode","params":["0x5555555555555555555555555555555555555555","latest"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) code = w3.eth.get_code( Web3.to_checksum_address("0x5555555555555555555555555555555555555555"), "latest", ) print(code.hex()) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const code = await provider.getCode( "0x5555555555555555555555555555555555555555", "latest" ); console.log(code); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const code = await client.getBytecode({ address: "0x5555555555555555555555555555555555555555", blockTag: "latest", }); console.log(code); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getCode` method is essential for applications that need to: * **Address classification**: Distinguish between smart contracts and EOAs * **Contract verification**: Verify deployed contracts against expected bytecode * **Security analysis**: Analyze contract bytecode for security patterns * **Block explorers**: Display contract information and verification status * **Development tools**: Build contract analysis and debugging tools * **Wallet applications**: Verify contract authenticity before interactions * **DeFi protocols**: Analyze and verify protocol contract implementations * **Integration services**: Provide contract information to external systems > [!NOTE] > > On Hyperliquid, `eth_getCode` only supports the latest block. The returned bytecode is the runtime code deployed on the blockchain, not the constructor code. For EOAs (externally owned accounts), this method returns `"0x"`. # eth_getFilterChanges | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-filter-changes openapi/hyperliquid_node_api/hyperevm/evm_eth_get_filter_changes.json post /evm The eth_getFilterChanges JSON-RPC method returns an array of logs or block hashes that have occurred since the last poll for the specified filter. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getFilterChanges` JSON-RPC method returns an array of logs or block hashes that have occurred since the last poll for the specified filter. This method is used to retrieve new results from filters created with `eth_newFilter` or `eth_newBlockFilter`, providing an efficient polling mechanism for monitoring blockchain events. ## Parameters 1. **filter\_id** (string) — The filter ID returned by `eth_newFilter` or `eth_newBlockFilter` ## Response The method returns an array of results based on the filter type: * **Log filters**: Array of log objects matching the filter criteria * **Block filters**: Array of block hashes for new blocks ### Response structure **For log filters:** * Array of log objects with properties like `address`, `topics`, `data`, `blockNumber`, `transactionHash`, etc. * Empty array if no new matching logs since last poll **For block filters:** * Array of block hash strings * Empty array if no new blocks since last poll ### Polling behavior **State tracking:** * Each call returns only new results since the last `eth_getFilterChanges` call * Filter maintains internal state of last polled position * Subsequent calls continue from where the previous call left off * Returns empty array if no new changes ## Usage example ### Basic implementation ```javascript theme={"system"} // Get filter changes const getFilterChanges = async (filterId) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getFilterChanges', params: [filterId], id: 1 }) }); const data = await response.json(); return data.result; }; // Poll for log changes const pollLogFilter = async (filterId, callback, intervalMs = 2000) => { const poll = async () => { try { const changes = await getFilterChanges(filterId); if (changes && changes.length > 0) { for (const log of changes) { callback(log); } } } catch (error) { console.error('Error polling filter changes:', error); } }; const intervalId = setInterval(poll, intervalMs); return { stop: () => clearInterval(intervalId), poll: poll // Manual poll function }; }; // Poll for block changes const pollBlockFilter = async (filterId, callback, intervalMs = 2000) => { const poll = async () => { try { const blockHashes = await getFilterChanges(filterId); if (blockHashes && blockHashes.length > 0) { for (const blockHash of blockHashes) { callback(blockHash); } } } catch (error) { console.error('Error polling block changes:', error); } }; const intervalId = setInterval(poll, intervalMs); return { stop: () => clearInterval(intervalId), poll: poll }; }; // Advanced event processor class EventProcessor { constructor() { this.filters = new Map(); } async addLogFilter(filterId, eventHandler) { const poller = await pollLogFilter(filterId, (log) => { eventHandler({ type: 'log', filterId, data: log, timestamp: new Date() }); }); this.filters.set(filterId, { type: 'log', poller, handler: eventHandler }); return filterId; } async addBlockFilter(filterId, blockHandler) { const poller = await pollBlockFilter(filterId, (blockHash) => { blockHandler({ type: 'block', filterId, blockHash, timestamp: new Date() }); }); this.filters.set(filterId, { type: 'block', poller, handler: blockHandler }); return filterId; } stopFilter(filterId) { const filter = this.filters.get(filterId); if (filter) { filter.poller.stop(); this.filters.delete(filterId); return true; } return false; } stopAll() { for (const [filterId, filter] of this.filters) { filter.poller.stop(); } this.filters.clear(); } async pollAllOnce() { const promises = []; for (const [filterId, filter] of this.filters) { promises.push(filter.poller.poll()); } await Promise.all(promises); } } // Real-time event dashboard const createEventDashboard = async () => { const processor = new EventProcessor(); const stats = { totalLogs: 0, totalBlocks: 0, lastActivity: null }; // Generic event handler const handleEvent = (event) => { if (event.type === 'log') { stats.totalLogs++; console.log(`Log Event from filter ${event.filterId}:`, event.data); } else if (event.type === 'block') { stats.totalBlocks++; console.log(`New Block from filter ${event.filterId}: ${event.blockHash}`); } stats.lastActivity = event.timestamp; }; return { processor, stats, addLogFilter: (filterId) => processor.addLogFilter(filterId, handleEvent), addBlockFilter: (filterId) => processor.addBlockFilter(filterId, handleEvent), stop: () => processor.stopAll() }; }; // Usage examples const filterId = '0x1'; // Simple polling pollLogFilter(filterId, (log) => { console.log('New log:', log); }).then(poller => { console.log('Started polling for logs'); // Stop after 5 minutes setTimeout(() => { poller.stop(); console.log('Stopped polling'); }, 5 * 60 * 1000); }); // Advanced event processing createEventDashboard().then(dashboard => { // Add filters to dashboard dashboard.addLogFilter('0x1'); dashboard.addBlockFilter('0x2'); // Display stats every 30 seconds setInterval(() => { console.log('Dashboard Stats:', dashboard.stats); }, 30000); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": [ "0x1" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) changes = w3.eth.get_filter_changes("0x1") print(changes) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const changes = await provider.send("eth_getFilterChanges", ["0x1"]); console.log(changes); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const changes = await client.request({ method: "eth_getFilterChanges", params: ["0x1"], }); console.log(changes); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getFilterChanges` method is essential for applications that need to: * **Real-time event monitoring**: Monitor smart contract events as they happen * **Block synchronization**: Stay synchronized with new blocks on the blockchain * **DeFi applications**: React to DeFi events like swaps, transfers, and liquidations * **Trading bots**: Execute trading strategies based on blockchain events * **Analytics platforms**: Collect real-time data for analysis and reporting * **Notification systems**: Send alerts based on specific blockchain events * **Audit tools**: Monitor contracts for compliance and security events * **DEX monitoring**: Track decentralized exchange activities in real-time * **NFT platforms**: Monitor NFT transfers, sales, and minting events * **Bridge monitoring**: Track cross-chain bridge events and transfers * **Governance tracking**: Monitor DAO governance events and proposals * **Oracle monitoring**: Track oracle price feeds and data updates * **Gaming applications**: Monitor in-game transactions and events * **Supply chain tracking**: Track product transfers and supply chain events * **Insurance monitoring**: Monitor insurance claims and policy events This method provides efficient polling capabilities for real-time blockchain monitoring, enabling responsive event-driven applications on the Hyperliquid EVM platform. # eth_getFilterLogs | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-filter-logs openapi/hyperliquid_node_api/hyperevm/evm_eth_get_filter_logs.json post /evm The eth_getFilterLogs JSON-RPC method returns an array of all logs matching the filter with the given ID. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getFilterLogs` JSON-RPC method returns an array of all logs matching the filter with the given ID. Unlike `eth_getFilterChanges`, this method returns all matching logs from the entire filter range, not just changes since the last poll. This method is useful for retrieving the complete history of events matching a filter. ## Parameters 1. **filter\_id** (string) — The filter ID returned by `eth_newFilter` (not compatible with block filters created by `eth_newBlockFilter`) ## Response The method returns an array of log objects matching the filter criteria from the entire filter range. ### Response structure **Log objects contain:** * `address` — The address from which this log originated * `topics` — Array of 0 to 4 indexed log arguments (32-byte hex strings) * `data` — Contains non-indexed arguments of the log * `blockNumber` — The block number where this log was in (hex string) * `transactionHash` — Hash of the transaction this log was created from * `transactionIndex` — Integer of the transaction index position log was created from * `blockHash` — Hash of the block where this log was in * `logIndex` — Integer of the log index position in the block * `removed` — True when the log was removed due to a chain reorganization ### Difference from eth\_getFilterChanges **Complete vs incremental:** * `eth_getFilterLogs`: Returns ALL logs matching the filter criteria * `eth_getFilterChanges`: Returns only NEW logs since the last poll **Use cases:** * Use `eth_getFilterLogs` for complete historical analysis * Use `eth_getFilterChanges` for real-time monitoring ## Usage example ### Basic implementation ```javascript theme={"system"} // Get all filter logs const getFilterLogs = async (filterId) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getFilterLogs', params: [filterId], id: 1 }) }); const data = await response.json(); return data.result; }; // Create filter and get all matching logs const createFilterAndGetLogs = async (filterOptions) => { // Create filter const createResponse = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_newFilter', params: [filterOptions], id: 1 }) }); const createData = await createResponse.json(); const filterId = createData.result; // Get all matching logs const logs = await getFilterLogs(filterId); return { filterId, logs, logCount: logs.length }; }; // Analyze event history const analyzeEventHistory = async (contractAddress, eventSignature, fromBlock, toBlock) => { const filterOptions = { fromBlock: `0x${fromBlock.toString(16)}`, toBlock: `0x${toBlock.toString(16)}`, address: contractAddress, topics: [eventSignature] }; const result = await createFilterAndGetLogs(filterOptions); // Analyze the logs const analysis = { totalEvents: result.logs.length, blockRange: { from: fromBlock, to: toBlock }, eventsByBlock: {}, uniqueTransactions: new Set(), timeAnalysis: { firstEvent: null, lastEvent: null, blockSpread: 0 } }; result.logs.forEach(log => { const blockNum = parseInt(log.blockNumber, 16); // Count events by block analysis.eventsByBlock[blockNum] = (analysis.eventsByBlock[blockNum] || 0) + 1; // Track unique transactions analysis.uniqueTransactions.add(log.transactionHash); // Time analysis if (!analysis.timeAnalysis.firstEvent || blockNum < analysis.timeAnalysis.firstEvent) { analysis.timeAnalysis.firstEvent = blockNum; } if (!analysis.timeAnalysis.lastEvent || blockNum > analysis.timeAnalysis.lastEvent) { analysis.timeAnalysis.lastEvent = blockNum; } }); analysis.timeAnalysis.blockSpread = analysis.timeAnalysis.lastEvent - analysis.timeAnalysis.firstEvent; analysis.uniqueTransactions = analysis.uniqueTransactions.size; return { ...result, analysis }; }; // Compare filter states const compareFilterStates = async (filterId) => { // Get all logs const allLogs = await getFilterLogs(filterId); // Get current changes (this might return empty if no new changes) const changesResponse = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getFilterChanges', params: [filterId], id: 1 }) }); const changesData = await changesResponse.json(); const changes = changesData.result; return { allLogsCount: allLogs.length, newChangesCount: changes.length, hasNewChanges: changes.length > 0, allLogs, newChanges: changes }; }; // Export logs to CSV format const exportLogsToCSV = async (filterId) => { const logs = await getFilterLogs(filterId); if (logs.length === 0) { return 'No logs found for this filter'; } // CSV header const headers = [ 'blockNumber', 'blockHash', 'transactionHash', 'transactionIndex', 'logIndex', 'address', 'data', 'topics', 'removed' ]; const csvData = [ headers.join(','), ...logs.map(log => [ parseInt(log.blockNumber, 16), log.blockHash, log.transactionHash, parseInt(log.transactionIndex, 16), parseInt(log.logIndex, 16), log.address, log.data, `"${log.topics.join(';')}"`, // Semicolon-separated topics log.removed || false ].join(',')) ]; return csvData.join('\n'); }; // Batch process multiple filters const batchProcessFilters = async (filterIds) => { const results = {}; const promises = filterIds.map(async (filterId) => { try { const logs = await getFilterLogs(filterId); results[filterId] = { success: true, logCount: logs.length, logs }; } catch (error) { results[filterId] = { success: false, error: error.message }; } }); await Promise.all(promises); return results; }; // Usage examples const filterId = '0x1'; // Get all logs from a filter getFilterLogs(filterId).then(logs => { console.log(`Found ${logs.length} logs for filter ${filterId}`); logs.forEach((log, index) => { console.log(`Log ${index + 1}:`, { block: parseInt(log.blockNumber, 16), transaction: log.transactionHash, address: log.address }); }); }); // Analyze event history for ERC-20 transfers const tokenAddress = '0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5'; const transferSignature = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; analyzeEventHistory(tokenAddress, transferSignature, 1000, 2000).then(result => { console.log('Event Analysis:', result.analysis); console.log(`Found ${result.logCount} transfer events`); }); // Compare filter states compareFilterStates(filterId).then(comparison => { console.log(`Total logs: ${comparison.allLogsCount}`); console.log(`New changes: ${comparison.newChangesCount}`); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getFilterLogs", "params": [ "0x1" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) logs = w3.eth.get_filter_logs("0x1") print(logs) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const logs = await provider.send("eth_getFilterLogs", ["0x1"]); console.log(logs); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const logs = await client.request({ method: "eth_getFilterLogs", params: ["0x1"], }); console.log(logs); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getFilterLogs` method is useful for applications that need to: * **Historical analysis**: Analyze complete event history for a specific filter * **Data export**: Export all matching logs for external analysis * **Compliance reporting**: Generate complete reports of contract activities * **Audit trails**: Create comprehensive audit trails for specific events * **Research tools**: Support blockchain research with complete event datasets * **Analytics platforms**: Perform comprehensive analytics on event data * **Backup and archival**: Create backups of event data for specific filters * **Data migration**: Migrate event data between systems * **Forensic analysis**: Investigate blockchain activities with complete event logs * **Statistical analysis**: Generate statistics from complete event datasets * **Reconciliation**: Reconcile event data with external systems * **Event replay**: Replay complete sequences of events for testing * **Data validation**: Validate event data integrity and completeness * **Batch processing**: Process large batches of events efficiently * **Report generation**: Generate comprehensive reports from event data This method provides complete access to all logs matching a filter, enabling comprehensive blockchain data analysis and processing on the Hyperliquid EVM platform. # eth_getHeaderByHash | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-header-by-hash openapi/hyperliquid_node_api/hyperevm/evm_eth_get_header_by_hash.json post /evm The eth_getHeaderByHash JSON-RPC method returns the block header information for a given block hash. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getHeaderByHash` JSON-RPC method returns the block header information for a given block hash. This method provides header data without the transaction list, offering a lightweight way to access block metadata when you have the specific block hash. ## Parameters 1. **block\_hash** (string) — The hash of the block as a hexadecimal string ## Response The method returns block header information or `null` if the block doesn't exist. ### Response structure **Block header fields:** * `hash` — The block hash (matches the input parameter) * `parentHash` — Hash of the parent block * `number` — The block number * `timestamp` — The unix timestamp when the block was collated * `gasLimit` — The maximum gas allowed in this block * `gasUsed` — The total gas used by all transactions in this block * `difficulty` — The difficulty for this block * `totalDifficulty` — The total difficulty of the chain until this block * `miner` — The address of the beneficiary to whom the mining rewards were given * `nonce` — The nonce used to generate this block * `sha3Uncles` — SHA3 of the uncles data in the block * `logsBloom` — The bloom filter for the logs of the block * `transactionsRoot` — The root of the transaction trie of the block * `stateRoot` — The root of the final state trie of the block * `receiptsRoot` — The root of the receipts trie of the block ### Hash validation **Block hash format:** * Must be a valid 32-byte hexadecimal string with "0x" prefix * Example: "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e" * Returns `null` if the hash doesn't correspond to any existing block ## Usage example ### Basic implementation ```javascript theme={"system"} // Get block header by hash const getHeaderByHash = async (blockHash) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getHeaderByHash', params: [blockHash], id: 1 }) }); const data = await response.json(); return data.result; }; // Validate block hash and get header info const validateBlockHash = async (blockHash) => { try { const header = await getHeaderByHash(blockHash); if (header === null) { return { valid: false, error: 'Block not found' }; } return { valid: true, header, blockNumber: parseInt(header.number, 16), timestamp: new Date(parseInt(header.timestamp, 16) * 1000), gasUtilization: (parseInt(header.gasUsed, 16) / parseInt(header.gasLimit, 16) * 100).toFixed(2) }; } catch (error) { return { valid: false, error: error.message }; } }; // Compare two block headers const compareBlockHeaders = async (hash1, hash2) => { const [header1, header2] = await Promise.all([ getHeaderByHash(hash1), getHeaderByHash(hash2) ]); if (!header1 || !header2) { return { error: 'One or both blocks not found' }; } return { block1: { hash: header1.hash, number: parseInt(header1.number, 16), timestamp: parseInt(header1.timestamp, 16), gasUsed: parseInt(header1.gasUsed, 16) }, block2: { hash: header2.hash, number: parseInt(header2.number, 16), timestamp: parseInt(header2.timestamp, 16), gasUsed: parseInt(header2.gasUsed, 16) }, comparison: { blockDifference: parseInt(header2.number, 16) - parseInt(header1.number, 16), timeDifference: parseInt(header2.timestamp, 16) - parseInt(header1.timestamp, 16), gasUsedDifference: parseInt(header2.gasUsed, 16) - parseInt(header1.gasUsed, 16) } }; }; // Track block ancestry const getBlockAncestry = async (blockHash, generations = 5) => { const ancestry = []; let currentHash = blockHash; for (let i = 0; i < generations; i++) { const header = await getHeaderByHash(currentHash); if (!header) break; ancestry.push({ generation: i, hash: header.hash, number: parseInt(header.number, 16), parentHash: header.parentHash, timestamp: parseInt(header.timestamp, 16) }); currentHash = header.parentHash; // Stop if we reach the genesis block if (currentHash === '0x0000000000000000000000000000000000000000000000000000000000000000') { break; } } return ancestry; }; // Usage const blockHash = '0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e'; validateBlockHash(blockHash).then(result => { if (result.valid) { console.log(`Block ${result.blockNumber} found:`, result.header); } else { console.log('Block validation failed:', result.error); } }); getBlockAncestry(blockHash, 3).then(ancestry => { console.log('Block ancestry:', ancestry); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getHeaderByHash", "params": [ "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block_hash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e" # eth_getHeaderByHash has no high-level wrapper, so call it via the provider header = w3.provider.make_request("eth_getHeaderByHash", [block_hash]) print(header["result"]) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blockHash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"; // eth_getHeaderByHash has no high-level wrapper, so send the raw JSON-RPC call const header = await provider.send("eth_getHeaderByHash", [blockHash]); console.log(header); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blockHash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"; // eth_getHeaderByHash has no high-level action, so use the EIP-1193 request method const header = await client.request({ method: "eth_getHeaderByHash" as any, params: [blockHash] as any, }); console.log(header); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getHeaderByHash` method is useful for applications that need to: * **Block validation**: Validate specific blocks by hash without downloading transaction data * **Chain analysis**: Analyze blockchain structure and block relationships * **Block verification**: Verify block integrity and metadata * **Ancestry tracking**: Track parent-child relationships between blocks * **Fork detection**: Detect and analyze blockchain forks * **Block comparison**: Compare metadata between different blocks * **Historical analysis**: Analyze historical block data efficiently * **Mining analytics**: Analyze mining patterns and block characteristics * **Network forensics**: Investigate specific blocks in network analysis * **Block explorers**: Provide detailed block information by hash * **Audit tools**: Build blockchain audit tools with block verification * **Chain synchronization**: Implement selective chain synchronization * **Performance analysis**: Analyze block timing and gas usage patterns * **Security analysis**: Analyze suspicious or specific blocks * **Research tools**: Support blockchain research with block-level data This method provides direct access to block header information using block hashes, enabling precise blockchain analysis and verification on the Hyperliquid EVM platform. # eth_getHeaderByNumber | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-header-by-number openapi/hyperliquid_node_api/hyperevm/evm_eth_get_header_by_number.json post /evm The eth_getHeaderByNumber JSON-RPC method returns the block header information for a given block number. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getHeaderByNumber` JSON-RPC method returns the block header information for a given block number. This method provides header data without the transaction list, offering a lightweight way to access block metadata and is more efficient than fetching the full block when only header information is needed. ## Parameters 1. **block\_number** (string) — The block number as a hexadecimal string, or one of the string tags: "latest", "earliest", or "pending" ## Response The method returns block header information or `null` if the block doesn't exist. ### Response structure **Block header fields:** * `hash` — The block hash * `parentHash` — Hash of the parent block * `number` — The block number * `timestamp` — The unix timestamp when the block was collated * `gasLimit` — The maximum gas allowed in this block * `gasUsed` — The total gas used by all transactions in this block * `difficulty` — The difficulty for this block * `totalDifficulty` — The total difficulty of the chain until this block * `miner` — The address of the beneficiary to whom the mining rewards were given * `nonce` — The nonce used to generate this block * `sha3Uncles` — SHA3 of the uncles data in the block * `logsBloom` — The bloom filter for the logs of the block * `transactionsRoot` — The root of the transaction trie of the block * `stateRoot` — The root of the final state trie of the block * `receiptsRoot` — The root of the receipts trie of the block ### Block number tags **Available tags:** * `"latest"` — The most recent block in the chain * `"earliest"` — The genesis block (block 0) * `"pending"` — The pending state/transactions ## Usage example ### Basic implementation ```javascript theme={"system"} // Get block header by number const getHeaderByNumber = async (blockNumber) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getHeaderByNumber', params: [blockNumber], id: 1 }) }); const data = await response.json(); return data.result; }; // Monitor block headers efficiently const monitorBlockHeaders = async (callback) => { let lastBlockNumber = 0; const checkLatestHeader = async () => { try { const header = await getHeaderByNumber('latest'); const currentBlock = parseInt(header.number, 16); if (currentBlock > lastBlockNumber) { lastBlockNumber = currentBlock; callback(header); } } catch (error) { console.error('Error fetching header:', error); } }; // Poll every 2 seconds setInterval(checkLatestHeader, 2000); }; // Analyze block timing const analyzeBlockTiming = async (fromBlock, toBlock) => { const analysis = { blocks: [], averageBlockTime: 0, minBlockTime: Infinity, maxBlockTime: 0 }; for (let i = fromBlock; i <= toBlock; i++) { const header = await getHeaderByNumber(`0x${i.toString(16)}`); if (header) { const blockTime = parseInt(header.timestamp, 16); analysis.blocks.push({ number: i, timestamp: blockTime, gasUsed: parseInt(header.gasUsed, 16), gasLimit: parseInt(header.gasLimit, 16) }); } } // Calculate timing statistics for (let i = 1; i < analysis.blocks.length; i++) { const timeDiff = analysis.blocks[i].timestamp - analysis.blocks[i-1].timestamp; analysis.minBlockTime = Math.min(analysis.minBlockTime, timeDiff); analysis.maxBlockTime = Math.max(analysis.maxBlockTime, timeDiff); } const totalTime = analysis.blocks[analysis.blocks.length - 1].timestamp - analysis.blocks[0].timestamp; analysis.averageBlockTime = totalTime / (analysis.blocks.length - 1); return analysis; }; // Usage getHeaderByNumber('latest').then(header => { console.log('Latest block header:', header); }); monitorBlockHeaders((header) => { console.log(`New block ${parseInt(header.number, 16)}: ${header.hash}`); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getHeaderByNumber", "params": [ "latest" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # eth_getHeaderByNumber is not a standard web3.py method, so call it directly. response = w3.provider.make_request("eth_getHeaderByNumber", ["latest"]) header = response["result"] print(header) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // eth_getHeaderByNumber is not a standard ethers method, so send the raw request. const header = await provider.send("eth_getHeaderByNumber", ["latest"]); console.log(header); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http, rpcSchema } from "viem"; // eth_getHeaderByNumber is not a standard JSON-RPC method, so declare its schema. type CustomRpcSchema = [ { Method: "eth_getHeaderByNumber"; Parameters: [blockNumber: string]; ReturnType: object | null; }, ]; const client = createPublicClient({ rpcSchema: rpcSchema(), transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const header = await client.request({ method: "eth_getHeaderByNumber", params: ["latest"], }); console.log(header); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getHeaderByNumber` method is useful for applications that need to: * **Efficient block monitoring**: Monitor new blocks without downloading full transaction data * **Chain synchronization**: Implement lightweight chain synchronization mechanisms * **Block analytics**: Analyze block metadata, timing, and gas usage patterns * **Network monitoring**: Monitor network health and block production rates * **Mining analytics**: Track mining difficulty, block times, and miner statistics * **Chain validation**: Validate blockchain integrity and block relationships * **Performance optimization**: Reduce bandwidth usage when only header data is needed * **Block explorers**: Build efficient block explorer interfaces * **DeFi applications**: Monitor block timing for time-sensitive operations * **Gas analysis**: Analyze gas usage patterns across blocks * **Network statistics**: Generate blockchain statistics and metrics * **Timestamp tracking**: Track block timestamps for time-based operations * **Parent-child relationships**: Analyze block relationships and chain structure * **Difficulty analysis**: Monitor mining difficulty adjustments * **State root tracking**: Track state changes across blocks without transaction details This method provides efficient access to block header information, enabling lightweight blockchain monitoring and analysis on the Hyperliquid EVM platform. # eth_getLogs | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-logs openapi/hyperliquid_node_api/hyperevm/evm_eth_get_logs.json post /evm The eth_getLogs JSON-RPC method returns event logs that match specified filter criteria. Use it on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getLogs` JSON-RPC method returns event logs that match specified filter criteria. This method is essential for retrieving smart contract events, monitoring contract activity, and building event-driven applications on the blockchain. ## Parameters The method takes one parameter: a filter object that specifies the criteria for log retrieval. ### Filter object * `address` (string or array, optional) — Contract address(es) to filter logs from * `fromBlock` (string, optional) — Starting block number in hexadecimal (default: `"latest"`) * `toBlock` (string, optional) — Ending block number in hexadecimal (default: `"latest"`) * `topics` (array, optional) — Array of topic filters for event matching * `blockhash` (string, optional) — Block hash to filter logs from (alternative to block range) ### Parameter details **Address filtering:** * Single address: `"0x1234..."` * Multiple addresses: `["0x1234...", "0x5678..."]` * Omit to get logs from all contracts **Block range:** * `fromBlock` and `toBlock` define the range * Use `"latest"`, `"earliest"`, `"pending"`, or hex block numbers * Maximum range is 50 blocks on Hyperliquid **Topic filtering:** * Array of up to 4 topic filters (indexed event parameters) - Hyperliquid limitation * Each topic can be `null` (any value), string (exact match), or array (multiple options) * Topics correspond to event signature and indexed parameters ## Response The method returns an array of log objects matching the filter criteria. ### Response structure **Log object:** * `address` — Contract address that emitted the log * `topics` — Array of indexed event parameters (including event signature) * `data` — Non-indexed event parameters as hexadecimal string * `blockNumber` — Block number containing the log * `blockHash` — Hash of the block containing the log * `transactionHash` — Hash of the transaction that generated the log * `transactionIndex` — Index of the transaction in the block * `logIndex` — Index of the log in the transaction * `removed` — Boolean indicating if log was removed due to chain reorganization ### Event decoding **Topic structure:** * `topics[0]` — Event signature hash (keccak256 of event signature) * `topics[1-3]` — Indexed event parameters (if any) * `data` — Non-indexed parameters (ABI-encoded) **Event signature:** ```javascript theme={"system"} // Example: Transfer(address indexed from, address indexed to, uint256 value) // topics[0] = keccak256("Transfer(address,address,uint256)") // topics[1] = from address (indexed) // topics[2] = to address (indexed) // data = value (non-indexed) ``` ## Event filtering ### Address filtering **Single contract:** ```json theme={"system"} { "address": "0x5555555555555555555555555555555555555555", "fromBlock": "0x9d0c37", "toBlock": "0x9d0c42" } ``` **Multiple contracts:** ```json theme={"system"} { "address": ["0x1111...", "0x2222...", "0x3333..."], "fromBlock": "0x9d0c37", "toBlock": "0x9d0c42" } ``` ### Topic filtering **Specific event:** ```json theme={"system"} { "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"] } ``` **Event with indexed parameters:** ```json theme={"system"} { "topics": [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x000000000000000000000000fc1286eeddf81d6955edad5c8d99b8aa32f3d2aa", null ] } ``` **Multiple event options:** ```json theme={"system"} { "topics": [ ["0xevent1hash", "0xevent2hash"], null, "0xspecificvalue" ] } ``` ## Hyperliquid-specific limitations ### Block range constraint **50-block limit:** * Maximum query range is 50 blocks on Hyperliquid * Use chunking for larger ranges * Process blocks in batches to respect this limit * Plan queries accordingly for historical analysis ### Topic filtering **4-topic maximum:** * Up to 4 topic filters are supported * Each topic can be null, string, or array of strings * Topics\[0] is typically the event signature hash * Topics\[1-3] are indexed event parameters ## Filter examples ### Token transfers **All transfers:** ```json theme={"system"} { "address": "0x5555555555555555555555555555555555555555", "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"], "fromBlock": "0x9d0c37", "toBlock": "0x9d0c42" } ``` **Transfers from specific address:** ```json theme={"system"} { "topics": [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x000000000000000000000000fc1286eeddf81d6955edad5c8d99b8aa32f3d2aa" ] } ``` ### Multiple event types **Transfers and approvals:** ```json theme={"system"} { "topics": [ [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" ] ] } ``` ## Example request This an example call with wrapped HYPE (wHYPE) on the Hyperliquid mainnet deployed at [0x5555555555555555555555555555555555555555](https://hyperevmscan.io/token/0x5555555555555555555555555555555555555555). ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"address":"0x5555555555555555555555555555555555555555","fromBlock":"0x9d0c37","toBlock":"0x9d0c42"}],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) logs = w3.eth.get_logs({ "address": "0x5555555555555555555555555555555555555555", "fromBlock": "0x9d0c37", "toBlock": "0x9d0c42", }) print(logs) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const logs = await provider.getLogs({ address: "0x5555555555555555555555555555555555555555", fromBlock: "0x9d0c37", toBlock: "0x9d0c42", }); console.log(logs); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const logs = await client.getLogs({ address: "0x5555555555555555555555555555555555555555", fromBlock: 10292279n, toBlock: 10292290n, }); console.log(logs); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getLogs` method is essential for applications that need to: * **Event monitoring**: Monitor smart contract events within 50-block ranges * **DeFi analytics**: Track token transfers, swaps, and protocol events * **Transaction analysis**: Analyze contract interactions and state changes * **Audit systems**: Track contract activity for compliance and security * **Portfolio tracking**: Monitor user transactions and balance changes * **Market analysis**: Analyze trading activity and market events * **Notification systems**: Generate alerts based on specific events * **Data indexing**: Build event databases with chunked processing * **Development tools**: Debug and test smart contract event emissions * **Integration services**: Provide event data to external systems > [!NOTE] > > On Hyperliquid, `eth_getLogs` supports up to 4 topics and a maximum query range of 50 blocks. For larger ranges, use chunking to process blocks in batches of 50 or fewer. # eth_getProof | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-proof openapi/hyperliquid_node_api/hyperevm/evm_eth_get_proof.json post /evm The eth_getProof JSON-RPC method returns the Merkle proof for an account and optionally some storage keys. Hyperliquid EVM via Chainstack. > [!WARNING] > > HyperEVM does not implement `eth_getProof`. Chainstack nodes and the official Hyperliquid RPC both return `-32601` `Method not found`. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getProof` JSON-RPC method returns the Merkle proof for an account and optionally some storage keys. This method is essential for verifying account states and storage values without trusting the node, enabling light clients and cross-chain verification systems. ## Parameters The method takes three parameters: 1. **Address** — The account address to get the proof for 2. **Storage keys** — Array of storage keys to get proofs for (optional) 3. **Block parameter** — The block at which to get the proof ### Parameter details * `address` (string, required) — The 20-byte account address to get the proof for * `storageKeys` (array, optional) — Array of 32-byte storage keys to include in the proof * `block` (string, required) — Block identifier: `"latest"` (only the latest block is supported on Hyperliquid) ## Response The method returns the account proof and storage proofs if requested. ### Response structure **Account proof object:** * `address` — The account address * `accountProof` — Array of RLP-encoded Merkle tree nodes for the account proof * `balance` — The account balance in wei as a hexadecimal string * `codeHash` — The hash of the account code * `nonce` — The account nonce as a hexadecimal string * `storageHash` — The hash of the storage trie root * `storageProof` — Array of storage proof objects for requested keys **Storage proof object:** * `key` — The storage key * `value` — The storage value at the key * `proof` — Array of RLP-encoded Merkle tree nodes for the storage proof ### Proof verification **Merkle proof structure:** * Account proofs verify against the state root * Storage proofs verify against the storage hash * Each proof consists of RLP-encoded nodes * Verification requires reconstructing the Merkle path **Use in light clients:** * Enables stateless verification of account data * Reduces trust requirements for off-chain systems * Supports cross-chain state verification * Essential for fraud proofs and optimistic rollups ## Hyperliquid-specific considerations ### Proof limitations **Latest block only:** * Only the `"latest"` block parameter is supported * Historical proofs are not available * All proofs reflect the current state * For historical data, consider using archive node implementations ### Storage key handling **Efficient proof requests:** * Request only necessary storage keys to minimize response size * Empty storage keys array returns only account proof * Large storage key arrays may impact performance * Consider batching proof requests for multiple accounts ## Example request ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getProof", "params": [ "0x69835D480110e4919B7899f465aAB101e21c8A87", [], "latest" ], "id": 1 }' ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) proof = w3.eth.get_proof( "0x69835D480110e4919B7899f465aAB101e21c8A87", [], "latest", ) print(proof) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const proof = await provider.send("eth_getProof", [ "0x69835D480110e4919B7899f465aAB101e21c8A87", [], "latest", ]); console.log(proof); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const proof = await client.getProof({ address: "0x69835D480110e4919B7899f465aAB101e21c8A87", storageKeys: [], blockTag: "latest", }); console.log(proof); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getProof` method is essential for applications that need to: * **Light clients**: Verify account states without full node synchronization * **Cross-chain bridges**: Prove account states across different chains * **State verification**: Validate account balances and storage values * **Fraud proofs**: Generate proofs for optimistic rollup challenges * **Merkle proof systems**: Build trustless verification systems * **Audit tools**: Verify on-chain data independently * **Decentralized oracles**: Prove on-chain states to off-chain systems * **Layer 2 solutions**: Generate state proofs for settlement layers * **Snapshot verification**: Validate state snapshots cryptographically * **Smart contract verification**: Prove contract storage values * **Compliance systems**: Generate cryptographic proofs for regulatory requirements * **Security monitoring**: Verify account states for security analysis * **Wallet applications**: Verify balances without trusting nodes * **DeFi protocols**: Prove collateral positions and liquidation states * **State channels**: Generate proofs for channel dispute resolution * **Governance systems**: Prove voting power and delegation states * **Insurance protocols**: Verify claim conditions cryptographically * **Data availability**: Prove data existence on-chain * **Research tools**: Analyze state tree structures and proofs * **Testing frameworks**: Verify state transitions in test environments * **Backup systems**: Validate state backups cryptographically * **Migration tools**: Prove account states during chain migrations * **Analytics platforms**: Verify on-chain metrics independently * **Integration services**: Provide proof-based APIs for external systems * **Educational tools**: Demonstrate Merkle proof concepts * **Development tools**: Debug state verification issues This method provides cryptographic proof of account and storage states, enabling trustless verification and cross-chain interoperability on the Hyperliquid EVM platform. > [!NOTE] > > On Hyperliquid, `eth_getProof` only supports the latest block. The proof returned can be used to cryptographically verify the account state and any requested storage values against the current state root. # eth_getRawTransactionByBlockHashAndIndex | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-raw-transaction-by-block-hash-and-index openapi/hyperliquid_node_api/hyperevm/evm_eth_get_raw_transaction_by_block_hash_and_index.json post /evm Returns the raw transaction data by block hash and transaction index. eth_getRawTransactionByBlockHashAndIndex on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the raw transaction data by block hash and transaction index. Use this method to retrieve the RLP-encoded raw transaction from a specific position within a known block. ## Parameters * `blockHash` (string, required) — The 32-byte hash of the block * `transactionIndex` (string, required) — The index position of the transaction in the block (hex string) ## Returns Returns the raw transaction data as a hex-encoded string, or `null` if the block or transaction is not found. The raw transaction data is RLP-encoded and includes all transaction fields in their original binary format. > [!NOTE] > > Transaction indices start from 0x0. Use hex format for the index parameter (e.g., "0x0" for the first transaction, "0x1" for the second). ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getRawTransactionByBlockHashAndIndex", "params": [ "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e", "0x0" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block_hash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e" tx_index = "0x0" # eth_getRawTransactionByBlockHashAndIndex has no typed wrapper, so call it directly response = w3.provider.make_request( "eth_getRawTransactionByBlockHashAndIndex", [block_hash, tx_index] ) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blockHash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"; const txIndex = "0x0"; // eth_getRawTransactionByBlockHashAndIndex has no typed wrapper, so use the generic send const rawTx = await provider.send("eth_getRawTransactionByBlockHashAndIndex", [ blockHash, txIndex, ]); console.log(rawTx); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blockHash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"; const txIndex = "0x0"; // eth_getRawTransactionByBlockHashAndIndex has no typed action, so use the raw request method const rawTx = await client.request({ method: "eth_getRawTransactionByBlockHashAndIndex", params: [blockHash, txIndex], }); console.log(rawTx); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases * **Block analysis** — Extract raw transactions from specific blocks * **Transaction reconstruction** — Rebuild transactions from block data * **Cross-chain verification** — Verify transaction encoding across networks * **Archive tools** — Efficiently process transactions by block position * **Forensic investigation** — Analyze transaction ordering within blocks * **Data migration** — Transfer transaction data between systems # eth_getRawTransactionByBlockNumberAndIndex | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-raw-transaction-by-block-number-and-index openapi/hyperliquid_node_api/hyperevm/evm_eth_get_raw_transaction_by_block_number_and_index.json post /evm Returns the raw transaction data by block number and transaction index. eth_getRawTransactionByBlockNumberAndIndex on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the raw transaction data by block number and transaction index. Use this method to retrieve the RLP-encoded raw transaction from a specific position within a block identified by its number. ## Parameters * `blockNumber` (string, required) — Block number in hex format, or one of the special tags: "earliest", "latest", "pending", or "finalized" * `transactionIndex` (string, required) — The index position of the transaction in the block (hex string) ## Returns Returns the raw transaction data as a hex-encoded string, or `null` if the block or transaction is not found. The raw transaction data is RLP-encoded and includes all transaction fields in their original binary format. > [!NOTE] > > Transaction indices start from 0x0. Use hex format for the index parameter. Block number can be a hex string (e.g., "0x64") or special tags like "latest" for the most recent block. ## Use cases * **Latest block analysis** — Extract raw transactions from the newest blocks * **Historical data processing** — Process transactions from specific block heights * **Streaming transaction data** — Monitor new transactions as blocks are mined * **Data pipeline integration** — Feed raw transaction data to processing systems * **Backup and recovery** — Create transaction backups in original format * **Performance testing** — Replay transactions for load testing ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getRawTransactionByBlockNumberAndIndex","params":["0x9d0c37","0x0"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # This RPC returns RLP-encoded bytes with no high-level wrapper, so call it directly. # Block 0x9d0c37 (10,291,255), transaction index 0x0; use "latest" for the tip. raw_tx = w3.manager.request_blocking( "eth_getRawTransactionByBlockNumberAndIndex", ["0x9d0c37", "0x0"] ) print(raw_tx) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // ethers v6 has no high-level wrapper for this RPC, so call it directly. // Block 0x9d0c37 (10,291,255), transaction index 0x0; use "latest" for the tip. const rawTx = await provider.send( "eth_getRawTransactionByBlockNumberAndIndex", ["0x9d0c37", "0x0"] ); console.log(rawTx); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // viem has no high-level action for this RPC, so issue a raw request. // Block 0x9d0c37 (10,291,255), transaction index 0x0; use "latest" for the tip. const rawTx = await client.request({ method: "eth_getRawTransactionByBlockNumberAndIndex", params: ["0x9d0c37", "0x0"], }); console.log(rawTx); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. # eth_getRawTransactionByHash | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-raw-transaction-by-hash openapi/hyperliquid_node_api/hyperevm/evm_eth_get_raw_transaction_by_hash.json post /evm Returns the raw transaction data by its hash. Use this method to retrieve the RLP-encoded raw transaction when you have the transaction hash. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the raw transaction data by its hash. Use this method to retrieve the RLP-encoded raw transaction when you have the transaction hash. ## Parameters * `transactionHash` (string, required) — The 32-byte hash of the transaction ## Returns Returns the raw transaction data as a hex-encoded string, or `null` if the transaction is not found. The raw transaction data is RLP-encoded and includes all transaction fields in their original binary format. > [!NOTE] > > Raw transaction data can be decoded using RLP decoding libraries to extract individual transaction fields like nonce, gas price, gas limit, recipient, value, and signature components. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getRawTransactionByHash","params":["0x66112ff0438f84686c8003f3a855057694c9018f07905c0e15f2f20f4dd0791a"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # eth_getRawTransactionByHash has no high-level helper, so call it directly response = w3.provider.make_request( "eth_getRawTransactionByHash", ["0x66112ff0438f84686c8003f3a855057694c9018f07905c0e15f2f20f4dd0791a"], ) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // eth_getRawTransactionByHash has no high-level helper, so use send() const rawTx = await provider.send("eth_getRawTransactionByHash", [ "0x66112ff0438f84686c8003f3a855057694c9018f07905c0e15f2f20f4dd0791a", ]); console.log(rawTx); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // eth_getRawTransactionByHash has no high-level action, so use request() const rawTx = await client.request({ method: "eth_getRawTransactionByHash", params: ["0x66112ff0438f84686c8003f3a855057694c9018f07905c0e15f2f20f4dd0791a"], }); console.log(rawTx); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases * **Transaction broadcasting** — Re-broadcast transactions to other networks * **Forensic analysis** — Analyze transaction structure and encoding * **Cross-chain bridges** — Verify transaction format across different networks * **Development tools** — Debug transaction encoding and decoding * **Archive services** — Store transactions in their original format * **Signature verification** — Extract and verify transaction signatures # eth_getStorageAt | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-storage-at openapi/hyperliquid_node_api/hyperevm/evm_eth_get_storage_at.json post /evm The eth_getStorageAt JSON-RPC method returns the value stored at a specific storage position in a smart contract. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getStorageAt` JSON-RPC method returns the value stored at a specific storage position in a smart contract. This method provides direct access to contract storage, enabling contract state inspection, debugging, and advanced contract analysis. ## Parameters The method takes three parameters: 1. **Address** - The contract address to read storage from 2. **Storage position** - The storage slot position to read 3. **Block parameter** - The block at which to read the storage ### Parameter details * `address` (string, required) — The 20-byte contract address * `position` (string, required) — The storage position as a hexadecimal string * `block` (string, required) — Block identifier: `"latest"` (only the latest block is supported on Hyperliquid) ## Response The method returns the 32-byte value stored at the specified position as a hexadecimal string. ### Response structure **Storage value:** * `result` — The 32-byte storage value as a hexadecimal string with `0x` prefix ### Data interpretation **Storage format:** * Always returns 32 bytes (64 hex characters + `0x` prefix) * Values are zero-padded to 32 bytes * `0x0000000000000000000000000000000000000000000000000000000000000000` indicates empty/zero storage * Actual data may be packed or encoded depending on variable type **Value decoding:** ```javascript theme={"system"} // Decode different data types const decodeUint256 = (hex) => parseInt(hex, 16); const decodeAddress = (hex) => "0x" + hex.slice(-40); // Last 20 bytes const decodeBool = (hex) => parseInt(hex, 16) !== 0; const decodeBytes32 = (hex) => hex; // Already in correct format ``` ## Storage layout ### Solidity storage rules **Basic types:** * `uint256`, `int256`, `bytes32` — Occupy full 32-byte slot * `uint128`, `uint64`, etc. — Packed into slots when possible * `address` — 20 bytes, often packed with other data * `bool` — 1 byte, packed with other data **Complex types:** * Arrays — Length at base slot, elements at calculated positions * Mappings — Values at `keccak256(key, slot)` positions * Structs — Members packed sequentially starting from base slot ### Storage slot calculation **Simple variables:** ```solidity theme={"system"} contract Example { uint256 public value1; // Slot 0 address public owner; // Slot 1 (packed with next if possible) bool public active; // Slot 1 (packed with owner) uint256 public value2; // Slot 2 } ``` **Arrays:** ```solidity theme={"system"} uint256[] public items; // Length at slot 0 // items[i] at keccak256(0) + i ``` **Mappings:** ```solidity theme={"system"} mapping(address => uint256) public balances; // Slot 0 // balances[addr] at keccak256(addr, 0) ``` ## Contract state inspection ### Variable reading **Direct variable access:** * Read public variables directly from their storage slots * Decode packed variables using appropriate offsets * Handle different data types and encodings * Verify variable values against expected ranges **State verification:** * Compare storage values with expected contract state * Verify contract initialization and configuration * Check for state corruption or unexpected changes * Validate contract upgrade states ### Current state analysis **State inspection:** * Read current contract storage values * Analyze current contract state and configuration * Verify current variable values and settings * Inspect current contract initialization state ## Usage example ### Basic implementation ```javascript theme={"system"} // Get storage value from a contract on Hyperliquid const getStorageAt = async (contractAddress, position) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getStorageAt', params: [contractAddress, position, 'latest'], // Only 'latest' is supported id: 1 }) }); const data = await response.json(); return data.result; }; // Read different data types from storage const analyzeContractStorage = async (contractAddress) => { // Read first few storage slots const slot0 = await getStorageAt(contractAddress, '0x0'); const slot1 = await getStorageAt(contractAddress, '0x1'); const slot2 = await getStorageAt(contractAddress, '0x2'); return { slot0: { raw: slot0, asUint256: parseInt(slot0, 16), asAddress: "0x" + slot0.slice(-40), // Last 20 bytes asBool: parseInt(slot0, 16) !== 0 }, slot1: { raw: slot1, asUint256: parseInt(slot1, 16), asAddress: "0x" + slot1.slice(-40), asBool: parseInt(slot1, 16) !== 0 }, slot2: { raw: slot2, asUint256: parseInt(slot2, 16), asAddress: "0x" + slot2.slice(-40), asBool: parseInt(slot2, 16) !== 0 } }; }; // Calculate mapping storage position const getMappingValueSlot = (key, baseSlot) => { // This requires a library like ethers.js or web3.js for keccak256 // Example: mapping(address => uint256) at slot 0 const encodedKey = ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [key, baseSlot]); return ethers.utils.keccak256(encodedKey); }; // Read mapping value const readMappingValue = async (contractAddress, key, baseSlot) => { const slot = getMappingValueSlot(key, baseSlot); const value = await getStorageAt(contractAddress, slot); return parseInt(value, 16); }; // Calculate array element position const getArrayElementSlot = (baseSlot, index) => { // Array length is at baseSlot, elements start at keccak256(baseSlot) const arrayStartSlot = ethers.utils.keccak256( ethers.utils.defaultAbiCoder.encode(['uint256'], [baseSlot]) ); return (BigInt(arrayStartSlot) + BigInt(index)).toString(16); }; // Usage examples const contractAddress = "0x5555555555555555555555555555555555555555"; analyzeContractStorage(contractAddress) .then(analysis => console.log('Storage Analysis:', analysis)) .catch(error => console.error('Error:', error)); // Read a specific storage slot getStorageAt(contractAddress, '0x0') .then(value => console.log('Slot 0 value:', value)) .catch(error => console.error('Error:', error)); ``` ## Hyperliquid-specific considerations ### Block limitations **Latest block only:** * Only the `"latest"` block parameter is supported * Historical storage queries are not available in the default implementation * All storage reads are performed against the current state * For historical analysis, consider using archive node implementations ### Storage analysis **Current state inspection:** * Read contract storage values at current block * Analyze current contract configuration and state * Verify current variable values and settings * Debug current contract behavior and issues **Data interpretation:** * Storage values are always 32 bytes (64 hex characters + "0x") * Values are zero-padded to 32 bytes * Different data types require different decoding approaches * Packed variables share storage slots for gas efficiency ## Example request This an example call with wrapped HYPE (wHYPE) on the Hyperliquid mainnet deployed at [0x5555555555555555555555555555555555555555](https://hyperevmscan.io/token/0x5555555555555555555555555555555555555555). ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["0x5555555555555555555555555555555555555555","0x0","latest"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) value = w3.eth.get_storage_at( "0x5555555555555555555555555555555555555555", 0, "latest", # Only "latest" is supported on Hyperliquid ) print(value.hex()) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // Only "latest" is supported on Hyperliquid const value = await provider.getStorage( "0x5555555555555555555555555555555555555555", "0x0", "latest", ); console.log(value); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const value = await client.getStorageAt({ address: "0x5555555555555555555555555555555555555555", slot: "0x0", blockTag: "latest", // Only "latest" is supported on Hyperliquid }); console.log(value); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getStorageAt` method is essential for applications that need to: * **Contract debugging**: Inspect current contract state during development and testing * **State verification**: Verify current contract variables and configuration * **Security analysis**: Analyze current contract storage for security vulnerabilities * **Contract auditing**: Examine current contract state for audit and compliance * **Development tools**: Build contract inspection and debugging tools * **Reverse engineering**: Analyze unknown contracts by examining current storage * **Testing frameworks**: Verify current contract state in automated tests * **Block explorers**: Display current contract storage information * **Integration services**: Provide current contract state data to external systems > [!NOTE] > > On Hyperliquid, `eth_getStorageAt` only supports the latest block. Storage positions are calculated based on Solidity's storage layout rules. Complex types like arrays and mappings require specific calculations to determine the correct storage positions. # eth_getSystemTxsByBlockHash | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-system-txs-by-block-hash openapi/hyperliquid_node_api/hyperevm/evm_eth_get_system_txs_by_block_hash.json post /evm The eth_getSystemTxsByBlockHash JSON-RPC method returns system transactions for a given block hash on Hyperliquid EVM. On Hyperliquid EVM. > [!NOTE] > > Available on both the `/evm` (default) and `/nanoreth` (with system tx) paths of any Chainstack Hyperliquid node. To also see system transactions inside `eth_getBlockByHash` and `eth_getBlockReceipts`, use `/nanoreth`. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) and [Hyperliquid methods](/docs/hyperliquid-methods). The `eth_getSystemTxsByBlockHash` JSON-RPC method returns system transactions for a given block hash on Hyperliquid EVM. System transactions are protocol-generated pseudo-transactions that record HyperCore↔HyperEVM bridging events (HYPE deposits and withdrawals, spot token transfers, and similar operations) — they always carry `gasPrice: 0x0` and originate from system addresses such as `0x2222…2222` (HYPE bridge) or `0x2000…00XX` (per-token bridge). ## Parameters The method takes one parameter: 1. **Block hash** - The hash of the block to retrieve system transactions from ### Parameter details * `blockHash` (string, required) — The 32-byte hash of the block ## Response The method returns an array of system transaction objects for the specified block, or `null` if the block is not found. ### Response structure **System transactions array:** * Array of system transaction objects * Empty array if no system transactions exist in the block * `null` if the block hash doesn't exist **System transaction object:** * `hash` — System transaction hash * `type` — System transaction type identifier * `blockNumber` — Block number containing the system transaction * `blockHash` — Hash of the block containing the system transaction * `transactionIndex` — Index of the system transaction in the block * `from` — System address that initiated the transaction * `to` — Target address of the system transaction * `value` — Value transferred in the system transaction * `gasUsed` — Gas used by the system transaction * `gasPrice` — Gas price for the system transaction (often 0 for system txs) * `input` — Input data for the system transaction * `status` — System transaction status (0x0 for failure, 0x1 for success) ### Data interpretation **Hash-based identification:** * Block hashes provide unique, immutable block identification * Useful during chain reorganizations when block numbers may change * Ensures retrieval from specific block versions * Maintains data consistency across different chain states **System transaction analysis:** ```javascript theme={"system"} // Process system transactions from specific block hash const processSystemTxsByHash = async (blockHash) => { const systemTxs = await eth_getSystemTxsByBlockHash(blockHash); if (!systemTxs) { throw new Error(`Block not found: ${blockHash}`); } return systemTxs.map(tx => ({ hash: tx.hash, type: parseInt(tx.type, 16), blockHash: tx.blockHash, blockNumber: parseInt(tx.blockNumber, 16), gasUsed: parseInt(tx.gasUsed, 16), status: tx.status === "0x1" ? "success" : "failed", purpose: determineSystemTxPurpose(tx.type) })); }; ``` ## Usage example ### Basic implementation ```javascript theme={"system"} // Get system transactions by block hash on Hyperliquid const getSystemTxsByBlockHash = async (blockHash) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getSystemTxsByBlockHash', params: [blockHash], id: 1 }) }); const data = await response.json(); if (data.result === null) { throw new Error(`Block not found: ${blockHash}`); } return data.result; }; // Analyze system transactions from a specific block const analyzeSystemTransactions = async (blockHash) => { try { const systemTxs = await getSystemTxsByBlockHash(blockHash); if (systemTxs.length === 0) { return { blockHash, message: 'No system transactions in this block' }; } const analysis = { blockHash, blockNumber: parseInt(systemTxs[0].blockNumber, 16), totalSystemTxs: systemTxs.length, transactions: systemTxs.map(tx => ({ hash: tx.hash, type: parseInt(tx.type, 16), from: tx.from, to: tx.to, value: parseInt(tx.value, 16), gasUsed: parseInt(tx.gasUsed, 16), gasPrice: parseInt(tx.gasPrice, 16), status: tx.status === "0x1" ? "Success" : "Failed", index: parseInt(tx.transactionIndex, 16) })), summary: { successfulTxs: systemTxs.filter(tx => tx.status === "0x1").length, failedTxs: systemTxs.filter(tx => tx.status === "0x0").length, totalGasUsed: systemTxs.reduce((sum, tx) => sum + parseInt(tx.gasUsed, 16), 0), uniqueTypes: [...new Set(systemTxs.map(tx => parseInt(tx.type, 16)))] } }; return analysis; } catch (error) { return { blockHash, error: error.message, suggestion: 'Block may not exist or may have been reorganized' }; } }; // Compare system transactions between two blocks const compareSystemTxsBetweenBlocks = async (blockHash1, blockHash2) => { const [block1Txs, block2Txs] = await Promise.all([ getSystemTxsByBlockHash(blockHash1).catch(() => null), getSystemTxsByBlockHash(blockHash2).catch(() => null) ]); return { block1: { hash: blockHash1, exists: block1Txs !== null, systemTxCount: block1Txs ? block1Txs.length : 0, types: block1Txs ? [...new Set(block1Txs.map(tx => parseInt(tx.type, 16)))] : [] }, block2: { hash: blockHash2, exists: block2Txs !== null, systemTxCount: block2Txs ? block2Txs.length : 0, types: block2Txs ? [...new Set(block2Txs.map(tx => parseInt(tx.type, 16)))] : [] }, comparison: { bothExist: block1Txs !== null && block2Txs !== null, countDifference: block1Txs && block2Txs ? block1Txs.length - block2Txs.length : null } }; }; // Usage examples const blockHash = "0x1970d9c7ce6f00a982b421610ad400a79522c102a26db24ef3ec1f2bd621c399"; analyzeSystemTransactions(blockHash) .then(analysis => console.log('System Transaction Analysis:', analysis)) .catch(error => console.error('Error:', error)); ``` ## Hash-based identification benefits ### Immutable block references **Precise identification:** * Block hashes provide cryptographically unique identifiers * Remain constant even during chain reorganizations * Enable precise block version identification * Useful for audit trails and data consistency ### Chain reorganization handling **Robust data retrieval:** * Hash-based queries work even during chain reorganizations * Provides access to specific block versions * Maintains data integrity across different chain states * Essential for forensic analysis and debugging ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getSystemTxsByBlockHash","params":["0x1970d9c7ce6f00a982b421610ad400a79522c102a26db24ef3ec1f2bd621c399"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block_hash = "0x1970d9c7ce6f00a982b421610ad400a79522c102a26db24ef3ec1f2bd621c399" # eth_getSystemTxsByBlockHash is a HyperEVM extension, so call it via the raw request manager system_txs = w3.manager.request_blocking("eth_getSystemTxsByBlockHash", [block_hash]) print(system_txs) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blockHash = "0x1970d9c7ce6f00a982b421610ad400a79522c102a26db24ef3ec1f2bd621c399"; // eth_getSystemTxsByBlockHash is a HyperEVM extension, so send the raw JSON-RPC call const systemTxs = await provider.send("eth_getSystemTxsByBlockHash", [ blockHash, ]); console.log(systemTxs); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blockHash = "0x1970d9c7ce6f00a982b421610ad400a79522c102a26db24ef3ec1f2bd621c399"; // eth_getSystemTxsByBlockHash is a HyperEVM extension, so issue the raw JSON-RPC request const systemTxs = await client.request({ method: "eth_getSystemTxsByBlockHash" as any, params: [blockHash], }); console.log(systemTxs); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getSystemTxsByBlockHash` method is essential for applications that need to: * **Precise block identification**: Access system transactions from specific block versions using immutable hashes * **Chain reorganization handling**: Maintain data consistency during blockchain reorganizations * **Audit systems**: Create immutable audit trails using cryptographic block identifiers * **Block explorers**: Provide precise block navigation and system transaction display * **Analytics platforms**: Analyze system transactions with consistent block references * **Development tools**: Debug system operations using specific block versions * **Protocol monitoring**: Monitor HyperCore system operations with precise identification * **Integration services**: Provide system transaction data with immutable identifiers > [!NOTE] > > Block hashes provide immutable identification and are essential for maintaining data consistency during chain reorganizations. If a block hash is not found, it may indicate the block was reorganized out of the canonical chain. # eth_getSystemTxsByBlockNumber | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-system-txs-by-block-number openapi/hyperliquid_node_api/hyperevm/evm_eth_get_system_txs_by_block_number.json post /evm The eth_getSystemTxsByBlockNumber JSON-RPC method returns system transactions for a given block number on Hyperliquid EVM. On Hyperliquid EVM. > [!NOTE] > > Available on both the `/evm` (default) and `/nanoreth` (with system tx) paths of any Chainstack Hyperliquid node. To also see system transactions inside `eth_getBlockByNumber` and `eth_getBlockReceipts`, use `/nanoreth`. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) and [Hyperliquid methods](/docs/hyperliquid-methods). The `eth_getSystemTxsByBlockNumber` JSON-RPC method returns system transactions for a given block number on Hyperliquid EVM. System transactions are protocol-generated pseudo-transactions that record HyperCore↔HyperEVM bridging events (HYPE deposits and withdrawals, spot token transfers, and similar operations) — they always carry `gasPrice: 0x0` and originate from system addresses such as `0x2222…2222` (HYPE bridge) or `0x2000…00XX` (per-token bridge). ## Parameters The method takes one parameter: 1. **Block number** - The block number to retrieve system transactions from ### Parameter details * `blockNumber` (string, required) — Block identifier: `"latest"`, `"earliest"`, `"pending"`, or a specific block number in hexadecimal ## Response The method returns an array of system transaction objects for the specified block. ### Response structure **System transactions array:** * Array of system transaction objects * Empty array if no system transactions exist in the block * Each transaction contains system-specific information **System transaction object:** * `hash` — System transaction hash * `type` — System transaction type identifier * `blockNumber` — Block number containing the system transaction * `blockHash` — Hash of the block containing the system transaction * `transactionIndex` — Index of the system transaction in the block * `from` — System address that initiated the transaction * `to` — Target address of the system transaction * `value` — Value transferred in the system transaction * `gasUsed` — Gas used by the system transaction * `gasPrice` — Gas price for the system transaction (often 0 for system txs) * `input` — Input data for the system transaction * `status` — System transaction status (0x0 for failure, 0x1 for success) ### Data interpretation **System transaction types:** * System transactions have special type identifiers * Different types handle various protocol operations * Types may include validator operations, protocol updates, or network maintenance * System transactions often have zero gas price **Transaction characteristics:** ```javascript theme={"system"} // Identify and process system transactions const processSystemTransactions = (systemTxs) => { return systemTxs.map(tx => ({ hash: tx.hash, type: parseInt(tx.type, 16), isSystemTx: true, gasUsed: parseInt(tx.gasUsed, 16), gasPrice: parseInt(tx.gasPrice, 16), status: tx.status === "0x1" ? "success" : "failed", purpose: determineSystemTxPurpose(tx.type) })); }; ``` ## Usage example ### Basic implementation ```javascript theme={"system"} // Get system transactions by block number on Hyperliquid const getSystemTxsByBlockNumber = async (blockNumber) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getSystemTxsByBlockNumber', params: [blockNumber], id: 1 }) }); const data = await response.json(); return data.result; }; // Analyze system transactions from a specific block const analyzeSystemTransactions = async (blockNumber) => { const systemTxs = await getSystemTxsByBlockNumber(blockNumber); if (systemTxs.length === 0) { return { blockNumber: parseInt(blockNumber, 16), message: 'No system transactions in this block' }; } const analysis = { blockNumber: parseInt(blockNumber, 16), totalSystemTxs: systemTxs.length, transactions: systemTxs.map(tx => ({ hash: tx.hash, type: parseInt(tx.type, 16), from: tx.from, to: tx.to, value: parseInt(tx.value, 16), gasUsed: parseInt(tx.gasUsed, 16), gasPrice: parseInt(tx.gasPrice, 16), status: tx.status === "0x1" ? "Success" : "Failed", index: parseInt(tx.transactionIndex, 16) })), summary: { successfulTxs: systemTxs.filter(tx => tx.status === "0x1").length, failedTxs: systemTxs.filter(tx => tx.status === "0x0").length, totalGasUsed: systemTxs.reduce((sum, tx) => sum + parseInt(tx.gasUsed, 16), 0), uniqueTypes: [...new Set(systemTxs.map(tx => parseInt(tx.type, 16)))] } }; return analysis; }; // Monitor system transactions across a range of blocks const monitorSystemTxsRange = async (startBlock, endBlock) => { const results = []; for (let i = startBlock; i <= endBlock; i++) { const hexBlock = "0x" + i.toString(16); try { const systemTxs = await getSystemTxsByBlockNumber(hexBlock); results.push({ blockNumber: i, systemTxCount: systemTxs.length, hasSystemTxs: systemTxs.length > 0, types: [...new Set(systemTxs.map(tx => parseInt(tx.type, 16)))] }); } catch (error) { console.error(`Error processing block ${i}:`, error); results.push({ blockNumber: i, error: error.message }); } } return { range: { start: startBlock, end: endBlock }, results, summary: { totalBlocks: results.length, blocksWithSystemTxs: results.filter(r => r.hasSystemTxs).length, totalSystemTxs: results.reduce((sum, r) => sum + (r.systemTxCount || 0), 0) } }; }; // Get latest system transactions const getLatestSystemTransactions = async () => { return await getSystemTxsByBlockNumber('latest'); }; // Usage examples analyzeSystemTransactions('0x9d16cf') .then(analysis => console.log('System Transaction Analysis:', analysis)) .catch(error => console.error('Error:', error)); // Monitor recent blocks for system activity monitorSystemTxsRange(10291248, 10291258) .then(monitoring => console.log('System Transaction Monitoring:', monitoring)) .catch(error => console.error('Error:', error)); ``` ## HyperCore system transactions ### System transaction characteristics **Origin and purpose:** * System transactions originate from HyperCore * Handle protocol operations and network maintenance * Often have zero gas price (system-funded) * Different transaction types for different operations ### Sequential block analysis **Block-by-block monitoring:** * Use block numbers for sequential analysis * Track system transaction patterns over time * Monitor protocol operations and network health * Analyze system transaction frequency and types ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getSystemTxsByBlockNumber","params":["0x9d16cf"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # eth_getSystemTxsByBlockNumber is a HyperEVM-specific method, so call it # directly through the request manager rather than a typed helper. system_txs = w3.manager.request_blocking("eth_getSystemTxsByBlockNumber", ["0x9d16cf"]) print(system_txs) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // eth_getSystemTxsByBlockNumber is a HyperEVM-specific method, so use the // generic send() to issue the raw JSON-RPC request. const systemTxs = await provider.send("eth_getSystemTxsByBlockNumber", ["0x9d16cf"]); console.log(systemTxs); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // eth_getSystemTxsByBlockNumber is a HyperEVM-specific method, so use the // low-level request() to issue the raw JSON-RPC request. const systemTxs = await client.request({ method: "eth_getSystemTxsByBlockNumber", params: ["0x9d16cf"], }); console.log(systemTxs); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getSystemTxsByBlockNumber` method is essential for applications that need to: * **Protocol monitoring**: Monitor HyperCore system operations and protocol activities * **Network analysis**: Analyze network health and system transaction patterns * **Block explorers**: Display comprehensive block information including system transactions * **Analytics platforms**: Study protocol behavior and system operation trends * **Development tools**: Debug and understand system-level blockchain operations * **Sequential analysis**: Track system transactions across block ranges * **Audit systems**: Verify system operations and protocol compliance * **Integration services**: Provide system transaction data to external applications > [!NOTE] > > System transactions are internal protocol operations that may not appear in regular transaction lists. They provide insight into network maintenance, protocol operations, and system-level activities on Hyperliquid EVM. # eth_getTransactionByBlockHashAndIndex | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-transaction-by-block-hash-and-index openapi/hyperliquid_node_api/hyperevm/evm_eth_get_transaction_by_block_hash_and_index.json post /evm The eth_getTransactionByBlockHashAndIndex JSON-RPC method returns transaction information by block hash and transaction index. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getTransactionByBlockHashAndIndex` JSON-RPC method returns transaction information by block hash and transaction index. This method is useful for retrieving specific transactions when you know the block hash and the transaction's position within that block. ## Parameters The method takes two parameters: 1. **Block hash** - The hash of the block containing the transaction 2. **Transaction index** - The index position of the transaction within the block ### Parameter details * `blockHash` (string, required) — The 32-byte hash of the block containing the transaction * `transactionIndex` (string, required) — The index position of the transaction in the block as a hexadecimal string (0-based) ## Response The method returns detailed transaction information, or `null` if the transaction is not found. ### Response structure **Transaction object:** * `hash` — The 32-byte transaction hash * `nonce` — Transaction nonce (number of transactions sent by sender) * `blockHash` — Hash of the block containing the transaction * `blockNumber` — Number of the block containing the transaction * `transactionIndex` — Index of the transaction in the block * `from` — Address of the transaction sender * `to` — Address of the transaction receiver (null for contract creation) * `value` — Value transferred in wei as a hexadecimal string * `gas` — Gas limit provided by the sender * `gasPrice` — Gas price provided by the sender in wei * `input` — Data sent along with the transaction * `v`, `r`, `s` — ECDSA signature components * `type` — Transaction type (0x0 for legacy, 0x1 for EIP-2930, 0x2 for EIP-1559) ### Transaction types **Legacy transactions (type 0x0):** * Traditional Ethereum transactions * Use `gasPrice` for fee calculation * Simple fee structure **EIP-2930 transactions (type 0x1):** * Include access lists for gas optimization * Still use `gasPrice` for fees * Reduce gas costs for certain operations **EIP-1559 transactions (type 0x2):** * Use `maxFeePerGas` and `maxPriorityFeePerGas` * Dynamic fee structure with base fee * More predictable fee estimation ## Hash-based transaction retrieval ### Precise block identification **Immutable block references:** * Block hashes provide unique, immutable block identification * Useful during chain reorganizations when block numbers may change * Ensures retrieval from specific block versions * Maintains data consistency across different chain states ### Index-based access **Sequential processing:** * Process transactions in block order using indices (0-based) * Iterate through all transactions in a block systematically * Maintain transaction execution order for analysis * Access specific transactions when index is known ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0","method": "eth_getTransactionByBlockHashAndIndex","params": ["0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a","0x0"],"id": 1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block_hash = "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a" tx = w3.eth.get_transaction_by_block(block_hash, 0) print(tx) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blockHash = "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a"; const tx = await provider.getTransaction( // ethers resolves the transaction hash from the block hash and index (await provider.getBlock(blockHash)).transactions[0] ); console.log(tx); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const tx = await client.getTransaction({ blockHash: "0x53e84f299e6893680383c6a53329574122a7292e5bb9397bb6a0b51b4db5957a", index: 0, }); console.log(tx); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getTransactionByBlockHashAndIndex` method is essential for applications that need to: * **Block transaction processing**: Process all transactions in a specific block sequentially * **Transaction ordering analysis**: Analyze transaction execution order within blocks * **Block explorers**: Display transactions in block context with proper indexing * **Analytics platforms**: Collect transaction data for comprehensive analysis * **Audit systems**: Verify transaction ordering and block composition * **Development tools**: Debug and analyze transaction execution within blocks * **Historical analysis**: Study transaction patterns in historical blocks using immutable block hashes * **Integration services**: Provide transaction data to external systems with precise block identification > [!NOTE] > > Transaction indices are 0-based and sequential within each block. If the specified index exceeds the number of transactions in the block, the method returns `null`. Block hashes provide immutable identification, useful during chain reorganizations. # eth_getTransactionByBlockNumberAndIndex | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-transaction-by-block-number-and-index openapi/hyperliquid_node_api/hyperevm/evm_eth_get_transaction_by_block_number_and_index.json post /evm The eth_getTransactionByBlockNumberAndIndex JSON-RPC method returns transaction information by block number and transaction index. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getTransactionByBlockNumberAndIndex` JSON-RPC method returns transaction information by block number and transaction index. This method is ideal for sequential transaction processing and building comprehensive blockchain datasets when working with block numbers. ## Parameters The method takes two parameters: 1. **Block number** - The number of the block containing the transaction 2. **Transaction index** - The index position of the transaction within the block ### Parameter details * `blockNumber` (string, required) — Block identifier: `"latest"`, `"earliest"`, `"pending"`, or a specific block number in hexadecimal * `transactionIndex` (string, required) — The index position of the transaction in the block as a hexadecimal string (0-based) ## Response The method returns detailed transaction information, or `null` if the transaction is not found. ### Response structure **Transaction object:** * `hash` — The 32-byte transaction hash * `nonce` — Transaction nonce (number of transactions sent by sender) * `blockHash` — Hash of the block containing the transaction * `blockNumber` — Number of the block containing the transaction * `transactionIndex` — Index of the transaction in the block * `from` — Address of the transaction sender * `to` — Address of the transaction receiver (null for contract creation) * `value` — Value transferred in wei as a hexadecimal string * `gas` — Gas limit provided by the sender * `gasPrice` — Gas price provided by the sender in wei * `input` — Data sent along with the transaction * `v`, `r`, `s` — ECDSA signature components * `type` — Transaction type (0x0 for legacy, 0x1 for EIP-2930, 0x2 for EIP-1559) ### Block number formats **Special identifiers:** * `"latest"` — Most recent block's transactions * `"earliest"` — Genesis block transactions (usually none) * `"pending"` — Pending block transactions **Hexadecimal numbers:** * `"0x0"` — Genesis block * `"0x9d0c37"` — Block 10,291,255 (decimal) ## Sequential processing ### Range-based analysis **Block range processing:** * Process transactions across multiple consecutive blocks * Build comprehensive transaction datasets systematically * Analyze transaction patterns over time periods * Generate time-series transaction analytics **Chronological analysis:** * Process transactions in chronological order * Track transaction evolution and patterns over time * Build historical transaction databases * Analyze network activity trends and cycles ### Efficient iteration **Block-by-block processing:** * Iterate through blocks sequentially using block numbers * Process all transactions within each block systematically * Maintain transaction order and block context * Build complete blockchain transaction datasets **Parallel processing:** * Process multiple blocks concurrently for efficiency * Distribute transaction processing across workers * Optimize throughput for large-scale analysis * Balance load across processing resources ## Development patterns ### Systematic data collection **Complete blockchain analysis:** * Process all transactions from genesis to latest block * Build comprehensive transaction databases * Generate complete network activity statistics * Create historical blockchain datasets **Incremental processing:** * Process new blocks as they are mined * Maintain up-to-date transaction datasets * Implement efficient incremental updates * Handle real-time transaction processing ### Transaction indexing **Index-based retrieval:** * Build transaction indexes based on block position * Enable efficient transaction lookup by position * Maintain transaction ordering within blocks * Optimize for sequential access patterns **Batch processing:** * Process multiple transactions from the same block * Optimize API usage and network efficiency * Implement efficient batch processing workflows * Reduce overhead for bulk operations ## Integration strategies ### Analytics platforms **Time-series analysis:** * Analyze transaction patterns over time using block numbers * Build time-based transaction metrics and KPIs * Generate historical transaction reports * Create transaction trend analysis systems **Network monitoring:** * Monitor transaction activity in real-time using latest blocks * Track network utilization and capacity metrics * Generate network health indicators * Build transaction-based alerting systems ### Block explorers **Sequential navigation:** * Enable navigation through blocks using block numbers * Display transactions in chronological order * Provide block-based transaction browsing * Show transaction context within block sequences **Historical exploration:** * Browse historical transactions using block numbers * Provide time-based transaction search capabilities * Enable exploration of transaction history * Show transaction evolution over time ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getTransactionByBlockNumberAndIndex","params":["0x9d0c37","0x0"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # Block 10,291,255 (0x9d0c37), transaction at index 0 tx = w3.eth.get_transaction_by_block(10_291_255, 0) print(tx) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // Block 0x9d0c37, transaction at index 0x0 const tx = await provider.send("eth_getTransactionByBlockNumberAndIndex", [ "0x9d0c37", "0x0", ]); console.log(tx); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // Block 10,291,255 (0x9d0c37), transaction at index 0 const tx = await client.getTransaction({ blockNumber: 10_291_255n, index: 0, }); console.log(tx); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getTransactionByBlockNumberAndIndex` method is essential for applications that need to: * **Sequential processing**: Process transactions across block ranges systematically * **Time-series analysis**: Analyze transaction patterns over time using block numbers * **Blockchain synchronization**: Build and maintain synchronized blockchain datasets * **Analytics platforms**: Collect comprehensive transaction data for analysis * **Block explorers**: Display transactions in chronological block order * **Historical analysis**: Study transaction evolution and network growth patterns * **Data archival**: Archive transaction data with proper temporal context * **Network monitoring**: Track transaction activity and network utilization over time * **Research platforms**: Study blockchain behavior and transaction patterns * **Audit systems**: Verify transaction sequences and blockchain integrity * **Development tools**: Debug and test applications with historical transaction data * **Compliance monitoring**: Track transactions for regulatory compliance over time * **Performance analysis**: Analyze network performance and transaction throughput * **Integration services**: Provide chronological transaction data to external systems * **Educational platforms**: Demonstrate blockchain concepts using real transaction data * **Forensic analysis**: Investigate transaction patterns and blockchain activities * **Risk management**: Analyze transaction risks and patterns over time periods * **Portfolio tracking**: Track user transactions across multiple blocks chronologically * **DeFi analytics**: Analyze DeFi protocol activity and usage patterns over time * **Market analysis**: Study trading activity and market behavior using transaction data * **MEV research**: Analyze maximal extractable value patterns across blocks * **Network economics**: Study transaction fees and network economics over time * **Chain analysis**: Analyze blockchain structure and transaction relationships * **Security monitoring**: Monitor suspicious activities across block ranges * **Data visualization**: Create time-based charts and graphs of transaction activity This method provides efficient sequential transaction access by block number, enabling comprehensive time-based analysis on the Hyperliquid EVM platform. > [!NOTE] > > This method supports special block identifiers like `"latest"` and `"pending"`, making it suitable for both historical analysis and real-time transaction monitoring. # eth_getTransactionByHash | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-transaction-by-hash openapi/hyperliquid_node_api/hyperevm/evm_eth_get_transaction_by_hash.json post /evm Returns transaction information by its hash. Use this method to retrieve specific transaction details when you have the transaction hash. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns transaction information by its hash. Use this method to retrieve specific transaction details when you have the transaction hash. ## Parameters * `transactionHash` (string, required) — The 32-byte hash of the transaction ## Returns Returns a transaction object, or `null` if the transaction is not found. **Transaction object:** * `hash` — Transaction hash * `nonce` — Transaction nonce (hex string) * `blockHash` — Hash of containing block (null if pending) * `blockNumber` — Number of containing block (null if pending) * `transactionIndex` — Index in block (null if pending) * `from` — Sender address * `to` — Receiver address (null for contract creation) * `value` — Value transferred in wei (hex string) * `gas` — Gas limit (hex string) * `gasPrice` — Gas price in wei (hex string) * `input` — Transaction data * `v`, `r`, `s` — ECDSA signature components * `type` — Transaction type (0x0 for legacy, 0x2 for EIP-1559) > [!NOTE] > > On Hyperliquid, priority fees are always zero. EIP-1559 transactions use only the base fee. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getTransactionByHash","params":["0x33c3321b162edac1fdbb53af2962b2940c07e334a4f5ff758f1d5ef1235e55d0"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) tx = w3.eth.get_transaction("0x33c3321b162edac1fdbb53af2962b2940c07e334a4f5ff758f1d5ef1235e55d0") print(tx) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const tx = await provider.getTransaction("0x33c3321b162edac1fdbb53af2962b2940c07e334a4f5ff758f1d5ef1235e55d0"); console.log(tx); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const tx = await client.getTransaction({ hash: "0x33c3321b162edac1fdbb53af2962b2940c07e334a4f5ff758f1d5ef1235e55d0", }); console.log(tx); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases * **Transaction verification** — Confirm transaction details and status * **Wallet applications** — Display transaction history to users * **Payment confirmation** — Verify payment transactions * **Block explorers** — Show detailed transaction information * **DeFi protocols** — Track protocol interactions * **Analytics** — Analyze transaction patterns and data # eth_getTransactionBySenderAndNonce | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-transaction-by-sender-and-nonce openapi/hyperliquid_node_api/hyperevm/evm_eth_get_transaction_by_sender_and_nonce.json post /evm Reference docs for the eth_getTransactionBySenderAndNonce JSON-RPC method on the Hyperliquid EVM blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getTransactionBySenderAndNonce` JSON-RPC method retrieves transaction information by specifying the sender address and transaction nonce on the Hyperliquid EVM blockchain. This method is particularly useful for tracking specific transactions when you know the sender and the sequence number of their transaction. ## Parameters The method accepts two parameters: 1. **Sender address** (string, required) — The address of the transaction sender 2. **Nonce** (string, required) — The transaction nonce as a hexadecimal string ### Parameter details * `address` — The Ethereum address that sent the transaction * `nonce` — The transaction sequence number for the sender, in hexadecimal format with "0x" prefix ## Response The method returns detailed transaction information if found, or `null` if no transaction exists for the specified sender and nonce. ### Response structure **Transaction object (if found):** * `blockHash` — Hash of the block containing the transaction * `blockNumber` — Block number containing the transaction (hexadecimal) * `from` — Address of the transaction sender * `gas` — Gas limit provided for the transaction (hexadecimal) * `gasPrice` — Gas price used for the transaction (hexadecimal) * `hash` — Transaction hash * `input` — Transaction data payload * `nonce` — Transaction nonce (hexadecimal) * `to` — Address of the transaction recipient * `transactionIndex` — Position of transaction in the block (hexadecimal) * `value` — Value transferred in the transaction (hexadecimal, in wei) * `type` — Transaction type (hexadecimal) * `v`, `r`, `s` — Transaction signature components **Null response:** * Returns `null` if no transaction is found for the specified sender and nonce ## Usage example ### Basic transaction lookup ```javascript theme={"system"} // Get transaction by sender and nonce const getTransactionBySenderAndNonce = async (sender, nonce) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getTransactionBySenderAndNonce', params: [sender, nonce], id: 1 }) }); const data = await response.json(); return data.result; }; // Track user transactions by nonce const trackUserTransactions = async (userAddress, startNonce = 0, count = 10) => { const transactions = []; for (let i = 0; i < count; i++) { const nonce = `0x${(startNonce + i).toString(16)}`; try { const tx = await getTransactionBySenderAndNonce(userAddress, nonce); if (tx) { transactions.push({ nonce: parseInt(tx.nonce, 16), hash: tx.hash, blockNumber: parseInt(tx.blockNumber, 16), value: BigInt(tx.value).toString(), gasUsed: BigInt(tx.gas).toString(), to: tx.to }); } else { // No more transactions found break; } } catch (error) { console.error(`Error fetching transaction for nonce ${nonce}:`, error); break; } } return transactions; }; // Find pending transaction by nonce const findPendingTransaction = async (sender, nonce) => { const tx = await getTransactionBySenderAndNonce(sender, nonce); if (!tx) { return { status: 'not_found', message: 'Transaction not found' }; } if (!tx.blockNumber) { return { status: 'pending', transaction: tx }; } return { status: 'confirmed', transaction: tx }; }; // Usage examples const sender = "0x69835D480110e4919B7899f465aAB101e21c8A87"; const nonce = "0x0"; getTransactionBySenderAndNonce(sender, nonce).then(tx => { if (tx) { console.log('Transaction found:', tx.hash); console.log('Block number:', parseInt(tx.blockNumber, 16)); console.log('Value:', BigInt(tx.value).toString(), 'wei'); } else { console.log('No transaction found for sender and nonce'); } }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getTransactionBySenderAndNonce", "params": [ "0x69835D480110e4919B7899f465aAB101e21c8A87", "0x0" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # eth_getTransactionBySenderAndNonce is a Hyperliquid-specific method with no # dedicated web3.py call, so send it through the raw provider request. response = w3.provider.make_request( "eth_getTransactionBySenderAndNonce", ["0x69835D480110e4919B7899f465aAB101e21c8A87", "0x0"], ) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // send() is the escape hatch for methods without a high-level provider API. const tx = await provider.send("eth_getTransactionBySenderAndNonce", [ "0x69835D480110e4919B7899f465aAB101e21c8A87", "0x0", ]); console.log(tx); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // Type the custom method through request(), since it is not in viem's schema. const tx = await client.request<{ method: "eth_getTransactionBySenderAndNonce"; Parameters: [string, string]; ReturnType: unknown; }>({ method: "eth_getTransactionBySenderAndNonce", params: ["0x69835D480110e4919B7899f465aAB101e21c8A87", "0x0"], }); console.log(tx); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getTransactionBySenderAndNonce` method is essential for applications that need to: * **Transaction tracking**: Track specific transactions by sender and sequence number * **Nonce management**: Implement robust nonce management and transaction sequencing * **Wallet applications**: Build wallet interfaces that track user transaction history * **Pending transaction monitoring**: Monitor pending transactions and detect stuck transactions * **Transaction replacement**: Implement transaction replacement and speed-up mechanisms * **Account analysis**: Analyze account transaction patterns and behavior * **Debugging tools**: Debug transaction issues and investigate failed transactions * **DeFi protocols**: Track protocol interactions and user transaction sequences * **MEV protection**: Detect and prevent MEV attacks by monitoring transaction sequences * **Audit tools**: Build audit tools that trace transaction flows and account activity * **Analytics platforms**: Create analytics dashboards that track user transaction patterns * **Risk management**: Implement risk management systems that monitor transaction behavior * **Compliance monitoring**: Monitor transactions for compliance and regulatory requirements * **Trading bots**: Implement trading bots that track their own transaction sequences * **Arbitrage strategies**: Monitor arbitrage transaction sequences and timing * **Cross-chain bridges**: Track cross-chain transaction sequences and confirmations * **Oracle services**: Monitor oracle transaction sequences and data submission patterns * **Gaming applications**: Track gaming transaction sequences and player interactions * **NFT platforms**: Monitor NFT transaction sequences and trading patterns * **Social recovery**: Implement social recovery mechanisms based on transaction patterns * **Security monitoring**: Detect suspicious transaction patterns and potential attacks * **Performance optimization**: Optimize transaction submission and nonce management * **Development frameworks**: Build development tools with transaction tracking capabilities * **Testing suites**: Create comprehensive testing suites for transaction scenarios This method provides precise transaction lookup capabilities, enabling efficient transaction management and monitoring on the Hyperliquid EVM platform. # eth_getTransactionCount | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-transaction-count openapi/hyperliquid_node_api/hyperevm/evm_eth_get_transaction_count.json post /evm Returns the number of transactions sent from an address (the nonce). This is essential for creating new transactions with the correct nonce value. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the number of transactions sent from an address (the nonce). This is essential for creating new transactions with the correct nonce value. ## Parameters * `address` (string, required) — The 20-byte account address * `block` (string, required) — Block identifier: `"latest"` (only the latest block is supported on Hyperliquid) ## Returns Returns the transaction count (nonce) as a hexadecimal string. > [!NOTE] > > On Hyperliquid, only the `"latest"` block parameter is supported for this method. The `"pending"` state is not supported on the default RPC. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA","latest"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) nonce = w3.eth.get_transaction_count("0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", "latest") print(nonce) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const nonce = await provider.getTransactionCount("0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", "latest"); console.log(nonce); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const nonce = await client.getTransactionCount({ address: "0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", blockTag: "latest", }); console.log(nonce); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases * **Transaction creation** — Get the correct nonce for new transactions * **Wallet applications** — Manage transaction ordering and nonce assignment * **Account monitoring** — Track account activity and transaction frequency * **Batch operations** — Create multiple transactions with sequential nonces * **Transaction replacement** — Replace stuck transactions using the same nonce * **Smart contract deployment** — Calculate contract addresses using deployer nonce # eth_getTransactionReceipt | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-transaction-receipt openapi/hyperliquid_node_api/hyperevm/evm_eth_get_transaction_receipt.json post /evm Returns the receipt of a transaction by its hash, containing execution results, gas usage, event logs, and transaction status. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the receipt of a transaction by its hash, containing execution results, gas usage, event logs, and transaction status. Use this to verify transaction execution and extract contract events. ## Parameters * `transactionHash` (string, required) — The 32-byte hash of the transaction ## Returns Returns a transaction receipt object, or `null` if the transaction is not found or still pending. **Transaction receipt:** * `transactionHash` — Hash of the transaction * `transactionIndex` — Index in the block (hex string) * `blockHash` — Hash of containing block * `blockNumber` — Number of containing block (hex string) * `from` — Sender address * `to` — Receiver address (null for contract creation) * `gasUsed` — Actual gas used (hex string) * `cumulativeGasUsed` — Total gas used in block up to this transaction (hex string) * `contractAddress` — Created contract address (null if not contract creation) * `logs` — Array of event logs * `status` — Transaction status (`0x0` for failure, `0x1` for success) * `effectiveGasPrice` — Actual gas price paid (hex string) * `type` — Transaction type (hex string) **Log object:** * `address` — Contract address that emitted the log * `topics` — Array of indexed event parameters * `data` — Non-indexed event parameters > [!NOTE] > > Transaction receipts are only available for mined transactions. Pending transactions return `null`. ## Example request ```shell cURL theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H 'Content-Type: application/json' \ --data '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0x33c3321b162edac1fdbb53af2962b2940c07e334a4f5ff758f1d5ef1235e55d0"],"id":1}' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) receipt = w3.eth.get_transaction_receipt( "0x33c3321b162edac1fdbb53af2962b2940c07e334a4f5ff758f1d5ef1235e55d0" ) print(receipt) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const receipt = await provider.getTransactionReceipt( "0x33c3321b162edac1fdbb53af2962b2940c07e334a4f5ff758f1d5ef1235e55d0" ); console.log(receipt); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const receipt = await client.getTransactionReceipt({ hash: "0x33c3321b162edac1fdbb53af2962b2940c07e334a4f5ff758f1d5ef1235e55d0", }); console.log(receipt); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases * **Transaction verification** — Confirm transaction execution and success status * **Event extraction** — Process smart contract events and logs * **Gas analysis** — Analyze transaction costs and gas usage * **Contract deployment** — Verify contract creation and get addresses * **Wallet applications** — Display transaction results to users * **DeFi protocols** — Monitor protocol events and state changes # eth_getUncleByBlockHashAndIndex | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-uncle-by-block-hash-and-index openapi/hyperliquid_node_api/hyperevm/evm_eth_get_uncle_by_block_hash_and_index.json post /evm The eth_getUncleByBlockHashAndIndex JSON-RPC method returns information about an uncle block by block hash and uncle index position. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getUncleByBlockHashAndIndex` JSON-RPC method returns information about an uncle block by block hash and uncle index position. Uncle blocks are valid blocks that were not included in the main chain but are referenced by the main chain blocks for network security. ## Parameters 1. **block\_hash** (string) — The hash of the block containing the uncle 2. **uncle\_index** (string) — The uncle index position as a hexadecimal string (e.g., "0x0" for the first uncle) ## Response The method returns uncle block information or `null` if no uncle exists at the specified index. ### Response structure **Uncle block information:** * Returns uncle block object with header information if found * Returns `null` if no uncle exists at the specified index * Uncle blocks contain header information but no transaction data ### Uncle block interpretation **Block structure:** * Uncle blocks are valid blocks that were not included in the main chain * They are referenced by main chain blocks to maintain network security * Uncle blocks earn reduced rewards compared to main chain blocks **Index positioning:** * Uncle index starts from 0 for the first uncle in the block * Multiple uncles can exist in a single block * Index must be provided as hexadecimal string with "0x" prefix ## Usage example ### Basic implementation ```javascript theme={"system"} // Get uncle block by hash and index const getUncleByBlockHashAndIndex = async (blockHash, uncleIndex) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getUncleByBlockHashAndIndex', params: [blockHash, uncleIndex], id: 1 }) }); const data = await response.json(); return data.result; }; // Check for uncles in a block const checkBlockUncles = async (blockHash) => { const uncles = []; let index = 0; while (true) { const uncle = await getUncleByBlockHashAndIndex( blockHash, `0x${index.toString(16)}` ); if (uncle === null) { break; } uncles.push(uncle); index++; } return uncles; }; // Usage const blockHash = '0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e'; checkBlockUncles(blockHash).then(uncles => { console.log(`Found ${uncles.length} uncles in block`); uncles.forEach((uncle, i) => { console.log(`Uncle ${i}:`, uncle); }); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getUncleByBlockHashAndIndex", "params": [ "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e", "0x0" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block_hash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e" uncle = w3.eth.get_uncle_by_block(block_hash, 0) print(uncle) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blockHash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"; const uncle = await provider.send("eth_getUncleByBlockHashAndIndex", [ blockHash, "0x0", ]); console.log(uncle); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blockHash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"; const uncle = await client.request({ method: "eth_getUncleByBlockHashAndIndex", params: [blockHash, "0x0"], }); console.log(uncle); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getUncleByBlockHashAndIndex` method is useful for applications that need to: * **Network analysis**: Analyze network health and uncle block rates * **Mining pool monitoring**: Monitor mining pool performance and uncle rates * **Blockchain research**: Study blockchain consensus and network behavior * **Security analysis**: Analyze network security and decentralization metrics * **Uncle block tracking**: Track uncle blocks for reward calculations * **Network statistics**: Generate statistics about blockchain efficiency * **Mining analytics**: Analyze mining patterns and uncle block occurrences * **Block validation**: Validate block structure and uncle references * **Chain analysis**: Perform detailed blockchain analysis and metrics * **Educational tools**: Build tools for blockchain education and visualization * **Academic research**: Support academic research on blockchain consensus * **Mining optimization**: Optimize mining strategies based on uncle patterns * **Network monitoring**: Monitor network consensus and fork behavior * **Reward calculation**: Calculate accurate mining rewards including uncles * **Historical analysis**: Analyze historical uncle block patterns and trends This method provides detailed access to uncle block information, enabling comprehensive blockchain analysis and network monitoring on the Hyperliquid EVM platform. # eth_getUncleByBlockNumberAndIndex | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-uncle-by-block-number-and-index openapi/hyperliquid_node_api/hyperevm/evm_eth_get_uncle_by_block_number_and_index.json post /evm The eth_getUncleByBlockNumberAndIndex JSON-RPC method returns information about an uncle block by block number and uncle index position. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_getUncleByBlockNumberAndIndex` JSON-RPC method returns information about an uncle block by block number and uncle index position. Uncle blocks are valid blocks that were not included in the main chain but are referenced by the main chain blocks for network security. ## Parameters 1. **block\_number** (string) — The block number as a hexadecimal string, or one of the string tags: "latest", "earliest", or "pending" 2. **uncle\_index** (string) — The uncle index position as a hexadecimal string (e.g., "0x0" for the first uncle) ## Response The method returns uncle block information or `null` if no uncle exists at the specified index. ### Response structure **Uncle block information:** * Returns uncle block object with header information if found * Returns `null` if no uncle exists at the specified index * Uncle blocks contain header information but no transaction data ### Block number tags **Available tags:** * `"latest"` — The most recent block in the chain * `"earliest"` — The genesis block (block 0) * `"pending"` — The pending state/transactions **Hexadecimal format:** * Block numbers can be specified as hexadecimal strings with "0x" prefix * Example: "0x1b4" represents block 436 in decimal ## Usage example ### Basic implementation ```javascript theme={"system"} // Get uncle block by number and index const getUncleByBlockNumberAndIndex = async (blockNumber, uncleIndex) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getUncleByBlockNumberAndIndex', params: [blockNumber, uncleIndex], id: 1 }) }); const data = await response.json(); return data.result; }; // Monitor latest block for uncles const monitorLatestBlockUncles = async () => { const uncles = []; let index = 0; while (true) { const uncle = await getUncleByBlockNumberAndIndex( 'latest', `0x${index.toString(16)}` ); if (uncle === null) { break; } uncles.push(uncle); index++; } return uncles; }; // Check uncles in a specific block const checkBlockUncles = async (blockNumber) => { const result = { blockNumber, uncles: [], uncleCount: 0 }; let index = 0; while (true) { const uncle = await getUncleByBlockNumberAndIndex( blockNumber, `0x${index.toString(16)}` ); if (uncle === null) { break; } result.uncles.push(uncle); index++; } result.uncleCount = result.uncles.length; return result; }; // Usage monitorLatestBlockUncles().then(uncles => { console.log(`Latest block has ${uncles.length} uncles`); }); checkBlockUncles('0x100').then(result => { console.log(`Block ${result.blockNumber} has ${result.uncleCount} uncles`); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getUncleByBlockNumberAndIndex", "params": [ "latest", "0x0" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) uncle = w3.eth.get_uncle_by_block("latest", 0) print(uncle) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const uncle = await provider.send("eth_getUncleByBlockNumberAndIndex", [ "latest", "0x0", ]); console.log(uncle); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const uncle = await client.request({ method: "eth_getUncleByBlockNumberAndIndex", params: ["latest", "0x0"], }); console.log(uncle); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_getUncleByBlockNumberAndIndex` method is useful for applications that need to: * **Real-time monitoring**: Monitor uncle blocks in the latest blocks * **Mining analytics**: Analyze mining performance and uncle rates * **Network health monitoring**: Track network consensus and chain quality * **Block explorer development**: Build block explorers with uncle block support * **Mining pool operations**: Monitor mining pool uncle block statistics * **Blockchain research**: Study consensus mechanisms and network behavior * **Network statistics**: Generate uncle block statistics and trends * **Performance analysis**: Analyze blockchain performance and efficiency * **Educational platforms**: Create educational content about blockchain consensus * **Mining reward calculation**: Calculate mining rewards including uncle rewards * **Chain analysis tools**: Build tools for detailed blockchain analysis * **Monitoring dashboards**: Create dashboards for blockchain metrics * **Academic research**: Support research on blockchain consensus algorithms * **Network optimization**: Optimize network parameters based on uncle patterns * **Historical data analysis**: Analyze uncle block trends over time This method provides efficient access to uncle block information using block numbers, enabling comprehensive blockchain monitoring and analysis on the Hyperliquid EVM platform. # eth_getUncleCountByBlockHash | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-uncle-count-by-block-hash openapi/hyperliquid_node_api/hyperevm/evm_eth_get_uncle_count_by_block_hash.json post /evm Returns the number of uncle blocks for a given block hash. Use this method to get the count of uncle (ommer) blocks associated with a specific block. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the number of uncle blocks for a given block hash. Use this method to get the count of uncle (ommer) blocks associated with a specific block. ## Parameters * `blockHash` (string, required) — The 32-byte hash of the block ## Returns Returns the number of uncle blocks as a hex-encoded integer, or `null` if the block is not found. > [!NOTE] > > On Hyperliquid, uncle blocks are not part of the consensus mechanism. This method is included for Ethereum compatibility but will typically return "0x0" as Hyperliquid doesn't produce uncle blocks. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getUncleCountByBlockHash","params":["0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block_hash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e" # Passing a block hash routes to eth_getUncleCountByBlockHash count = w3.eth.get_uncle_count(block_hash) print(count) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blockHash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"; // ethers v6 has no dedicated helper, so call the method directly const count = await provider.send("eth_getUncleCountByBlockHash", [blockHash]); console.log(parseInt(count, 16)); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blockHash = "0x2ce91ae0ed242b4b78b432a45b982fb81a414d6b04167762ed3c7446710a4b8e"; // viem v2 has no dedicated action, so call the method directly const count = await client.request({ method: "eth_getUncleCountByBlockHash", params: [blockHash], }); console.log(parseInt(count, 16)); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases * **Ethereum compatibility** — Maintain compatibility with Ethereum RPC interfaces * **Block analysis tools** — Complete block metadata collection * **Network statistics** — Gather comprehensive blockchain metrics * **Protocol research** — Study consensus mechanisms and block production * **Migration tools** — Ensure compatibility when porting Ethereum applications * **Testing frameworks** — Validate RPC method implementations # eth_getUncleCountByBlockNumber | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-get-uncle-count-by-block-number openapi/hyperliquid_node_api/hyperevm/evm_eth_get_uncle_count_by_block_number.json post /evm Returns the number of uncle blocks for a given block number. Reference for eth_getUncleCountByBlockNumber on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the number of uncle blocks for a given block number. Use this method to get the count of uncle (ommer) blocks associated with a block identified by its number. ## Parameters * `blockNumber` (string, required) — Block number in hex format, or one of the special tags: "earliest", "latest", "pending", or "finalized" ## Returns Returns the number of uncle blocks as a hex-encoded integer, or `null` if the block is not found. > [!NOTE] > > On Hyperliquid, uncle blocks are not part of the consensus mechanism. This method is included for Ethereum compatibility but will typically return "0x0" as Hyperliquid doesn't produce uncle blocks. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getUncleCountByBlockNumber","params":["latest"],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) uncle_count = w3.eth.get_uncle_count("latest") print(uncle_count) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const uncleCount = await provider.send("eth_getUncleCountByBlockNumber", ["latest"]); console.log(uncleCount); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const uncleCount = await client.request({ method: "eth_getUncleCountByBlockNumber", params: ["latest"], }); console.log(uncleCount); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases * **Ethereum compatibility** — Maintain compatibility with Ethereum RPC interfaces * **Network monitoring** — Track uncle block production across different block heights * **Performance analysis** — Analyze network efficiency and block production patterns * **Compatibility testing** — Verify application behavior when migrating from Ethereum * **Protocol comparison** — Compare consensus mechanisms between networks * **API completeness** — Implement full Ethereum RPC compatibility # eth_maxPriorityFeePerGas | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-max-priority-fee-per-gas openapi/hyperliquid_node_api/hyperevm/evm_eth_max_priority_fee_per_gas.json post /evm Returns the current maximum priority fee per gas for EIP-1559 transactions. On Hyperliquid, this method always returns zero as priority fees are not used. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the current maximum priority fee per gas for EIP-1559 transactions. On Hyperliquid, this method always returns zero as priority fees are not used. ## Parameters This method takes no parameters. ## Returns Returns the maximum priority fee per gas as a hexadecimal string. On Hyperliquid, this is always `"0x0"`. > [!WARNING] > > On Hyperliquid, priority fees are always zero. The network uses only base fees for transaction pricing, so setting a priority fee will not affect transaction inclusion speed or priority. ## Example request ```bash Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_maxPriorityFeePerGas","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) max_priority_fee = w3.eth.max_priority_fee print(max_priority_fee) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const maxPriorityFee = await provider.send("eth_maxPriorityFeePerGas", []); console.log(maxPriorityFee); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const maxPriorityFee = await client.estimateMaxPriorityFeePerGas(); console.log(maxPriorityFee); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. Example response: ```json theme={"system"} {"jsonrpc":"2.0","id":1,"result":"0x0"} ``` ## Use cases * **EIP-1559 compatibility** — Maintain compatibility with EIP-1559 transaction format * **Wallet applications** — Handle priority fee queries for cross-chain compatibility * **Fee estimation** — Calculate transaction costs knowing priority fees are zero * **Protocol integration** — Integrate with protocols that query priority fees # eth_newBlockFilter | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-new-block-filter openapi/hyperliquid_node_api/hyperevm/evm_eth_new_block_filter.json post /evm The eth_newBlockFilter JSON-RPC method creates a filter object to notify when new blocks arrive on the blockchain. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_newBlockFilter` JSON-RPC method creates a filter object to notify when new blocks arrive on the blockchain. This method returns a filter ID that can be used with `eth_getFilterChanges` to retrieve new block hashes as they are mined, providing an efficient way to monitor blockchain progression. ## Parameters This method takes no parameters. The `params` field should be an empty array. ## Response The method returns a filter ID as a hexadecimal string that can be used to retrieve new block hashes. ### Response structure **Filter ID:** * Returns a unique filter identifier as a hexadecimal string * Use this ID with `eth_getFilterChanges` to get new block hashes * Each call to `eth_getFilterChanges` returns only new blocks since the last call * Filters have a limited lifetime and may expire if not used ### Block filter behavior **Monitoring:** * The filter starts monitoring from the time it's created * Only new blocks mined after filter creation are returned * Block hashes are returned in chronological order * The filter automatically tracks the last retrieved block ## Usage example ### Basic implementation ```javascript theme={"system"} // Create a new block filter const createBlockFilter = async () => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_newBlockFilter', params: [], id: 1 }) }); const data = await response.json(); return data.result; }; // Get new blocks from filter const getFilterChanges = async (filterId) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getFilterChanges', params: [filterId], id: 1 }) }); const data = await response.json(); return data.result; }; // Monitor new blocks with callback const monitorNewBlocks = async (callback) => { const filterId = await createBlockFilter(); console.log(`Created block filter: ${filterId}`); const pollForNewBlocks = async () => { try { const newBlocks = await getFilterChanges(filterId); if (newBlocks && newBlocks.length > 0) { for (const blockHash of newBlocks) { callback(blockHash); } } } catch (error) { console.error('Error polling for new blocks:', error); } }; // Poll every 2 seconds const intervalId = setInterval(pollForNewBlocks, 2000); return { filterId, stop: () => clearInterval(intervalId) }; }; // Block statistics tracker const trackBlockStatistics = async () => { const stats = { blocksReceived: 0, startTime: Date.now(), blockHashes: [], averageBlockTime: 0 }; const monitor = await monitorNewBlocks((blockHash) => { stats.blocksReceived++; stats.blockHashes.push({ hash: blockHash, timestamp: Date.now() }); // Calculate average block time if (stats.blockHashes.length > 1) { const timeSpan = stats.blockHashes[stats.blockHashes.length - 1].timestamp - stats.blockHashes[0].timestamp; stats.averageBlockTime = timeSpan / (stats.blockHashes.length - 1); } console.log(`New block: ${blockHash}`); console.log(`Total blocks: ${stats.blocksReceived}, Avg time: ${stats.averageBlockTime.toFixed(0)}ms`); }); return { stats, monitor }; }; // Block notification system const createBlockNotifier = async (webhookUrl) => { const monitor = await monitorNewBlocks(async (blockHash) => { try { await fetch(webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'new_block', blockHash, timestamp: new Date().toISOString() }) }); } catch (error) { console.error('Failed to send block notification:', error); } }); return monitor; }; // Usage examples monitorNewBlocks((blockHash) => { console.log(`New block mined: ${blockHash}`); }).then(monitor => { console.log('Block monitoring started'); // Stop monitoring after 5 minutes setTimeout(() => { monitor.stop(); console.log('Block monitoring stopped'); }, 5 * 60 * 1000); }); // Track block statistics trackBlockStatistics().then(({ stats, monitor }) => { console.log('Block statistics tracking started'); // Display stats every 30 seconds setInterval(() => { console.log('Block Stats:', stats); }, 30000); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_newBlockFilter", "params": [], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # Create a filter that notifies on every new block. block_filter = w3.eth.filter("latest") print(block_filter.filter_id) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // eth_newBlockFilter has no high-level wrapper, so send the raw call. const filterId = await provider.send("eth_newBlockFilter", []); console.log(filterId); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // createBlockFilter maps to eth_newBlockFilter. const filter = await client.createBlockFilter(); console.log(filter.id); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_newBlockFilter` method is essential for applications that need to: * **Real-time monitoring**: Monitor blockchain progression in real-time * **Block notifications**: Create notification systems for new blocks * **Chain synchronization**: Implement efficient chain synchronization mechanisms * **Mining analytics**: Track block mining rates and patterns * **Network health monitoring**: Monitor network performance and block times * **DeFi applications**: React to new blocks for time-sensitive operations * **Block explorers**: Update block explorer data as new blocks arrive * **Transaction monitoring**: Detect new blocks to check for transaction confirmations * **Performance analytics**: Analyze blockchain performance metrics * **Automated trading**: Trigger trading logic based on new block arrivals * **Consensus monitoring**: Monitor blockchain consensus and chain progression * **Alert systems**: Create alerts based on block timing and frequency * **Data synchronization**: Synchronize application data with blockchain state * **Event triggering**: Trigger application events when new blocks are mined * **Statistics collection**: Collect blockchain statistics and metrics This method provides efficient real-time block monitoring capabilities, enabling responsive blockchain applications and monitoring systems on the Hyperliquid EVM platform. # eth_newFilter | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-new-filter openapi/hyperliquid_node_api/hyperevm/evm_eth_new_filter.json post /evm The eth_newFilter JSON-RPC method creates a filter object to notify when logs match the specified criteria. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_newFilter` JSON-RPC method creates a filter object to notify when logs match the specified criteria. This method returns a filter ID that can be used with `eth_getFilterChanges` and `eth_getFilterLogs` to retrieve matching logs. Filters are essential for monitoring specific events on the blockchain. ## Parameters 1. **filter\_object** (object) — The filter criteria object with the following properties: * `fromBlock` (string, optional) — Block number (hexadecimal) or tag ("latest", "earliest", "pending") * `toBlock` (string, optional) — Block number (hexadecimal) or tag ("latest", "earliest", "pending") * `address` (string or array, optional) — Contract address or array of addresses to filter * `topics` (array, optional) — Array of topic hashes to match (supports null wildcards) ## Response The method returns a filter ID as a hexadecimal string that can be used to retrieve filter results. ### Response structure **Filter ID:** * Returns a unique filter identifier as a hexadecimal string * Use this ID with `eth_getFilterChanges` to get new matching logs * Use this ID with `eth_getFilterLogs` to get all matching logs * Filters have a limited lifetime and may expire ### Filter object options **Block range:** * `fromBlock` and `toBlock` define the block range to monitor * Use "latest" to monitor from the current block * Use specific block numbers in hexadecimal format **Address filtering:** * Single address: `"0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5"` * Multiple addresses: `["0xAddr1", "0xAddr2"]` * Omit to match logs from any address **Topic filtering:** * Topics are indexed event parameters * Array position corresponds to topic index (topic0, topic1, etc.) * Use `null` as wildcard for any value at that position * Empty array `[]` matches all topics ## Usage example ### Basic implementation ```javascript theme={"system"} // Create a new filter const createFilter = async (filterOptions) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_newFilter', params: [filterOptions], id: 1 }) }); const data = await response.json(); return data.result; }; // Monitor specific contract events const monitorContractEvents = async (contractAddress, eventSignature) => { const filter = await createFilter({ fromBlock: 'latest', toBlock: 'latest', address: contractAddress, topics: [eventSignature] // topic0 is the event signature hash }); console.log(`Created filter ${filter} for contract ${contractAddress}`); return filter; }; // Create filter for ERC-20 Transfer events const monitorTokenTransfers = async (tokenAddress, fromAddress = null, toAddress = null) => { // Transfer event signature: Transfer(address,address,uint256) const transferSignature = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; const topics = [transferSignature]; // Add indexed parameters (from and to addresses) if (fromAddress) { topics.push('0x000000000000000000000000' + fromAddress.slice(2).toLowerCase()); } else { topics.push(null); // Wildcard for any from address } if (toAddress) { topics.push('0x000000000000000000000000' + toAddress.slice(2).toLowerCase()); } const filter = await createFilter({ fromBlock: 'latest', toBlock: 'latest', address: tokenAddress, topics: topics }); return filter; }; // Monitor multiple contracts const monitorMultipleContracts = async (addresses, topics = []) => { const filter = await createFilter({ fromBlock: 'latest', toBlock: 'latest', address: addresses, topics: topics }); console.log(`Created multi-contract filter: ${filter}`); return filter; }; // Historical event filter const createHistoricalFilter = async (fromBlock, toBlock, contractAddress) => { const filter = await createFilter({ fromBlock: `0x${fromBlock.toString(16)}`, toBlock: `0x${toBlock.toString(16)}`, address: contractAddress, topics: [] }); console.log(`Created historical filter from block ${fromBlock} to ${toBlock}`); return filter; }; // Usage examples const contractAddress = '0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5'; // Monitor all events from a contract monitorContractEvents(contractAddress, null).then(filterId => { console.log('Monitor filter created:', filterId); }); // Monitor token transfers monitorTokenTransfers(contractAddress).then(filterId => { console.log('Transfer filter created:', filterId); }); // Monitor multiple contracts const addresses = [ '0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5', '0xAnother...Address' ]; monitorMultipleContracts(addresses).then(filterId => { console.log('Multi-contract filter created:', filterId); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_newFilter", "params": [ { "fromBlock": "latest", "toBlock": "latest", "address": "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", "topics": [] } ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # Create a filter for logs from a contract over the latest block log_filter = w3.eth.filter({ "fromBlock": "latest", "toBlock": "latest", "address": "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", "topics": [], }) # filter_id holds the eth_newFilter result print(log_filter.filter_id) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // eth_newFilter has no high-level helper, so call it directly const filterId = await provider.send("eth_newFilter", [ { fromBlock: "latest", toBlock: "latest", address: "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", topics: [], }, ]); console.log(filterId); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // createEventFilter maps to eth_newFilter and returns a filter whose id is the result const filter = await client.createEventFilter({ address: "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", fromBlock: "latest", toBlock: "latest", }); console.log(filter.id); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_newFilter` method is essential for applications that need to: * **Event monitoring**: Monitor smart contract events in real-time * **DeFi applications**: Track token transfers, swaps, and other DeFi operations * **NFT platforms**: Monitor NFT minting, transfers, and marketplace activities * **Trading bots**: React to specific blockchain events for automated trading * **Analytics platforms**: Collect event data for analysis and reporting * **Notification systems**: Create alerts based on specific blockchain events * **Audit tools**: Monitor specific contracts for compliance and security * **DEX monitoring**: Track decentralized exchange activities and liquidity events * **Governance tracking**: Monitor DAO governance events and voting * **Bridge monitoring**: Track cross-chain bridge events and transfers * **Staking applications**: Monitor staking rewards and delegation events * **Oracle monitoring**: Track oracle price updates and data feeds * **Gaming platforms**: Monitor in-game transactions and asset transfers * **Supply chain tracking**: Monitor supply chain events and product transfers * **Insurance platforms**: Track insurance claims and policy events This method provides flexible and efficient event filtering capabilities, enabling real-time blockchain monitoring and event-driven applications on the Hyperliquid EVM platform. # eth_protocolVersion | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-protocol-version openapi/hyperliquid_node_api/hyperevm/evm_eth_protocol_version.json post /evm The eth_protocolVersion JSON-RPC method returns the current Ethereum protocol version supported by the Hyperliquid EVM node. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_protocolVersion` JSON-RPC method returns the current Ethereum protocol version supported by the Hyperliquid EVM node. This method is essential for ensuring compatibility between client applications and the blockchain network, helping developers verify protocol support and maintain version compatibility. ## Parameters This method takes no parameters. The `params` field should be an empty array. ## Response The method returns the current protocol version as a hexadecimal string. ### Response structure **Protocol information:** * `result` — The current protocol version as a hexadecimal string representing the supported Ethereum protocol version ### Protocol version interpretation **Hexadecimal format:** * Protocol versions are returned in hexadecimal format with "0x" prefix * Convert to decimal for human-readable version numbers * Higher numbers indicate more recent protocol versions **Compatibility checking:** * Use this method to verify client-server protocol compatibility * Ensure your application supports the returned protocol version * Check for protocol updates and compatibility requirements ## Usage example ### Basic implementation ```javascript theme={"system"} // Get current protocol version const getProtocolVersion = async () => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_protocolVersion', params: [], id: 1 }) }); const data = await response.json(); const versionHex = data.result; const versionDecimal = parseInt(versionHex, 16); return { hex: versionHex, decimal: versionDecimal }; }; // Check protocol compatibility const checkCompatibility = async (requiredVersion) => { try { const { decimal: currentVersion } = await getProtocolVersion(); if (currentVersion >= requiredVersion) { console.log(`Compatible: Node supports protocol version ${currentVersion}`); return true; } else { console.warn(`Incompatible: Node version ${currentVersion} < required ${requiredVersion}`); return false; } } catch (error) { console.error('Error checking protocol version:', error); return false; } }; // Usage checkCompatibility(65).then(isCompatible => { if (isCompatible) { console.log('Proceeding with application initialization'); } else { console.log('Application may not function properly'); } }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_protocolVersion","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) protocol_version = w3.eth.protocol_version print(protocol_version) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const protocolVersion = await provider.send("eth_protocolVersion", []); console.log(protocolVersion); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const protocolVersion = await client.request({ method: "eth_protocolVersion" }); console.log(protocolVersion); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_protocolVersion` method is essential for applications that need to: * **Compatibility verification**: Ensure client applications are compatible with the node's protocol version * **Version management**: Track protocol versions across different environments and deployments * **Network diagnostics**: Diagnose network connectivity and protocol support issues * **Development tools**: Build development tools that adapt to different protocol versions * **Client initialization**: Initialize client applications with appropriate protocol settings * **Migration planning**: Plan migrations and upgrades based on protocol version support * **Integration testing**: Test applications against different protocol versions * **API validation**: Validate API compatibility and feature support across versions * **Multi-network support**: Build applications that work across different EVM networks * **Protocol monitoring**: Monitor protocol version changes and network updates * **Smart contract deployment**: Ensure smart contracts are compatible with the network protocol * **DApp compatibility**: Verify DApp compatibility with the underlying protocol version * **Wallet integration**: Ensure wallet applications support the network's protocol version * **Bridge operations**: Validate protocol compatibility for cross-chain bridge operations * **Node management**: Monitor and manage node protocol versions in infrastructure * **Debugging tools**: Build debugging tools that account for protocol version differences * **Analytics platforms**: Track protocol adoption and version distribution across networks * **Testing frameworks**: Create testing frameworks that validate protocol compatibility * **Development environments**: Set up development environments with appropriate protocol support * **Quality assurance**: Implement QA processes that verify protocol version compatibility * **Documentation systems**: Generate documentation that accounts for protocol version differences * **Monitoring dashboards**: Build monitoring dashboards that track protocol version metrics * **Automated deployments**: Implement deployment scripts that verify protocol compatibility * **Security audits**: Conduct security audits that account for protocol version vulnerabilities This method provides fundamental protocol information, enabling robust version management and compatibility validation for applications on the Hyperliquid EVM platform. # eth_sendRawTransaction | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-send-raw-transaction openapi/hyperliquid_node_api/hyperevm/evm_eth_send_raw_transaction.json post /evm The eth_sendRawTransaction JSON-RPC method submits a pre-signed transaction to the network for broadcasting and inclusion in a block. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_sendRawTransaction` JSON-RPC method submits a pre-signed transaction to the network for broadcasting and inclusion in a block. This method is essential for sending transactions that have been signed offline or by external wallets, enabling secure transaction submission without exposing private keys. ## Parameters The method takes one parameter: 1. **Signed transaction data** — The signed raw transaction data ### Parameter details * `signedTransactionData` (string, required) — The signed transaction data in hexadecimal format with `0x` prefix ## Response The method returns the transaction hash if the transaction was successfully submitted to the network. ### Response structure **Transaction hash:** * `result` — The 32-byte transaction hash as a hexadecimal string ### Transaction status **Submission outcomes:** * Success returns a transaction hash immediately * The hash does not guarantee inclusion in a block * Use `eth_getTransactionReceipt` to confirm inclusion * Failed transactions may still consume gas **Common errors:** * `nonce too low` — Transaction nonce already used * `insufficient funds` — Account lacks funds for gas + value * `gas limit exceeded` — Transaction gas limit too high * `transaction underpriced` — Gas price below network minimum ## Hyperliquid-specific considerations ### Transaction requirements **Gas and fees:** * Transactions require HYPE for gas fees * Gas prices fluctuate based on network demand * Use `eth_gasPrice` to get current gas price * Consider priority fees for faster inclusion ### Transaction monitoring **Confirmation tracking:** * Monitor transaction status with `eth_getTransactionReceipt` * Check for transaction replacement by monitoring nonce * Verify block confirmations for finality * Handle reorg scenarios appropriately ## Example request ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": [ "f86d80843b9aca0082520894b7c609cffa0e47db2467ea03ff3e598bf59361a5880de0b6b3a7640000808207f2a02d013b9980176ca8674f77797ac04e928b8de3be92dd452501ec26acbb2b1abca054041f43109eeadbb4d6b712a20dc71e5dbe1225e549644be3de803b55041a89" ], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) signed_transaction_data = "0xf86d80843b9aca0082520894b7c609cffa0e47db2467ea03ff3e598bf59361a5880de0b6b3a7640000808207f2a02d013b9980176ca8674f77797ac04e928b8de3be92dd452501ec26acbb2b1abca054041f43109eeadbb4d6b712a20dc71e5dbe1225e549644be3de803b55041a89" tx_hash = w3.eth.send_raw_transaction(signed_transaction_data) print(tx_hash.hex()) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const signedTransactionData = "0xf86d80843b9aca0082520894b7c609cffa0e47db2467ea03ff3e598bf59361a5880de0b6b3a7640000808207f2a02d013b9980176ca8674f77797ac04e928b8de3be92dd452501ec26acbb2b1abca054041f43109eeadbb4d6b712a20dc71e5dbe1225e549644be3de803b55041a89"; const txResponse = await provider.broadcastTransaction(signedTransactionData); console.log(txResponse.hash); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const signedTransactionData = "0xf86d80843b9aca0082520894b7c609cffa0e47db2467ea03ff3e598bf59361a5880de0b6b3a7640000808207f2a02d013b9980176ca8674f77797ac04e928b8de3be92dd452501ec26acbb2b1abca054041f43109eeadbb4d6b712a20dc71e5dbe1225e549644be3de803b55041a89"; const txHash = await client.sendRawTransaction({ serializedTransaction: signedTransactionData, }); console.log(txHash); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_sendRawTransaction` method is essential for applications that need to: * **Wallet applications**: Submit user-signed transactions to the network * **DeFi protocols**: Execute pre-signed transactions for swaps and liquidity operations * **Trading bots**: Submit high-frequency trading transactions * **Bridge operations**: Process cross-chain transfer transactions * **Smart contract interactions**: Deploy contracts and execute contract methods * **Batch operations**: Submit multiple pre-signed transactions sequentially * **Multisig wallets**: Submit transactions signed by multiple parties * **Hardware wallets**: Broadcast transactions signed offline * **Payment systems**: Process payment transactions securely * **Token transfers**: Send ERC-20, ERC-721, and ERC-1155 token transfers * **Staking operations**: Submit staking and delegation transactions * **Governance voting**: Cast votes in on-chain governance systems * **NFT minting**: Submit minting transactions for NFT collections * **Liquidation bots**: Execute time-sensitive liquidation transactions * **Arbitrage systems**: Submit profitable arbitrage transactions quickly * **Meta-transactions**: Process gasless transactions via relayers * **Scheduled transactions**: Submit pre-signed future transactions * **Recovery operations**: Broadcast recovery transactions for compromised accounts * **Testing frameworks**: Submit test transactions in development environments * **Analytics tools**: Monitor transaction submission patterns * **Security systems**: Submit security-related transactions like pausing contracts * **Compliance tools**: Process regulatory-required transactions * **Integration services**: Bridge web2 and web3 transaction systems * **Mobile applications**: Submit transactions from mobile wallets * **Game transactions**: Process in-game asset transfers and purchases This method provides the fundamental capability to submit transactions to the Hyperliquid EVM network, enabling all transaction-based operations on the platform. > [!NOTE] > > Ensure your transaction is properly signed and includes the correct nonce, gas price, and gas limit before submission. Failed transactions will still consume gas fees. Always verify the transaction hash returned and monitor its status using `eth_getTransactionReceipt`. # eth_simulateV1 | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-simulate-v1 openapi/hyperliquid_node_api/hyperevm/evm_eth_simulate_v1.json post /evm The eth_simulateV1 JSON-RPC method provides transaction simulation capabilities on the Hyperliquid EVM blockchain. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_simulateV1` JSON-RPC method provides transaction simulation capabilities on the Hyperliquid EVM blockchain. This method allows developers to execute transactions in a controlled environment without actually submitting them to the blockchain, enabling testing, gas estimation, and outcome prediction. ## Parameters The method accepts a single parameter object containing simulation configuration: ### Simulation parameters * `blockStateCalls` (array, required) — Array of block state call objects * `calls` (array, required) — Array of transaction calls to simulate * `from` (string, optional) — Transaction sender address * `to` (string, optional) — Transaction recipient address * `gas` (string, optional) — Gas limit as hexadecimal string * `gasPrice` (string, optional) — Gas price as hexadecimal string * `value` (string, optional) — Transaction value as hexadecimal string * `data` (string, optional) — Transaction data as hexadecimal string * `blockOverride` (object, optional) — Block state override options * `number` (string) — Block number to simulate against (e.g., "latest") * `traceTransfers` (boolean, optional) — Whether to trace token transfers during simulation * `validation` (boolean, optional) — Whether to perform validation during simulation ## Response The method returns detailed simulation results including transaction outcomes, gas usage, and state changes. ### Response structure **Simulation results:** * `result` — Object containing simulation outcome data including gas estimates, transaction success status, and any error messages **Transaction simulation details:** * Gas consumption estimates and limits * Transaction success or failure status * State changes and side effects * Error messages if simulation fails * Transfer traces if enabled ## Usage example ### Basic transaction simulation ```javascript theme={"system"} // Simulate a simple value transfer const simulateTransaction = async () => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_simulateV1', params: [{ blockStateCalls: [{ calls: [{ from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", gas: "0x5208", gasPrice: "0x9184e72a000", value: "0xde0b6b3a7640000", data: "0x" }], blockOverride: { number: "latest" } }], traceTransfers: false, validation: true }], id: 1 }) }); const data = await response.json(); return data.result; }; // Test transaction before sending simulateTransaction().then(result => { console.log('Simulation result:', result); // Check if transaction would succeed before actually sending it }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_simulateV1", "params": [ { "blockStateCalls": [ { "calls": [ { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", "gas": "0x5208", "gasPrice": "0x9184e72a000", "value": "0xde0b6b3a7640000", "data": "0x" } ], "blockOverride": { "number": "latest" } } ], "traceTransfers": false, "validation": true } ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # eth_simulateV1 has no high-level web3.py wrapper, so send it as a raw JSON-RPC request. params = [ { "blockStateCalls": [ { "calls": [ { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", "gas": "0x5208", "gasPrice": "0x9184e72a000", "value": "0xde0b6b3a7640000", "data": "0x", } ], "blockOverride": {"number": "latest"}, } ], "traceTransfers": False, "validation": True, } ] response = w3.provider.make_request("eth_simulateV1", params) print(response["result"]) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // eth_simulateV1 has no dedicated ethers method, so send it via provider.send. const result = await provider.send("eth_simulateV1", [ { blockStateCalls: [ { calls: [ { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", gas: "0x5208", gasPrice: "0x9184e72a000", value: "0xde0b6b3a7640000", data: "0x", }, ], blockOverride: { number: "latest" }, }, ], traceTransfers: false, validation: true, }, ]); console.log(result); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // viem wraps eth_simulateV1 with the simulateBlocks action. const result = await client.simulateBlocks({ blocks: [ { calls: [ { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", gas: 21000n, gasPrice: 10000000000000n, value: 1000000000000000000n, data: "0x", }, ], }, ], traceTransfers: false, validation: true, }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_simulateV1` method is essential for applications that need to: * **Transaction testing**: Test transactions before submitting them to avoid failed transactions and wasted gas * **Gas estimation**: Accurately estimate gas requirements for complex transactions and smart contract interactions * **Smart contract debugging**: Debug smart contract calls and identify potential issues before deployment * **DeFi protocol integration**: Test complex DeFi transactions involving multiple protocols and token swaps * **Transaction optimization**: Optimize transaction parameters and gas settings for better execution * **Risk management**: Assess transaction risks and potential failures in trading and financial applications * **MEV protection**: Simulate transactions to detect and prevent MEV (Maximum Extractable Value) attacks * **Batch transaction planning**: Plan and validate batch transactions before execution * **State change prediction**: Predict state changes and side effects of complex transactions * **Cross-protocol interactions**: Test interactions between different protocols and smart contracts * **Governance proposals**: Simulate governance proposals and their effects before voting * **Arbitrage strategies**: Test arbitrage opportunities and validate profitability before execution * **Liquidation simulations**: Test liquidation scenarios and validate liquidation bot strategies * **Oracle integration**: Test oracle data integration and price feed reliability * **Bridge operations**: Simulate cross-chain bridge operations and validate transfer logic * **Staking mechanisms**: Test staking and unstaking operations with various parameters * **Token distribution**: Simulate token distribution scenarios and validate allocation logic * **Flash loan strategies**: Test flash loan strategies and validate repayment scenarios * **AMM interactions**: Simulate automated market maker interactions and liquidity provision * **NFT marketplace**: Test NFT trading scenarios and marketplace integrations This method provides comprehensive transaction simulation capabilities, enabling developers to build robust and reliable applications on the Hyperliquid EVM platform with confidence in transaction outcomes. # eth_subscribe ("logs") | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-subscribe-logs Subscribe to real-time updates about new event logs on Hyperliquid EVM blockchain. Receive notifications whenever new logs matching the filter are emitted. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_subscribe("logs")` JSON-RPC method allows developers to subscribe to real-time updates about new event logs on the Hyperliquid EVM blockchain. The application will receive notifications whenever new logs matching the filter are emitted, making it essential for monitoring smart contract events and building reactive dApps. ## Parameters 1. **subscription type** (string, required): Keyword identifying the type of event to subscribe to, `logs` in this case 2. **filter object** (object, optional): The event filter options * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses * `topics` — an array of DATA topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics ## Response The method returns a subscription ID that can be used to identify and manage the subscription. ### Response structure **Initial subscription response:** * `subscription` — the subscription ID **Log notification structure:** * `address` — the contract address from which the event originated * `topics` — an array of 32-byte data fields containing indexed event parameters * `data` — the non-indexed data that was emitted along with the event * `blockNumber` — the block number in which the event was included * `transactionHash` — the hash of the transaction that triggered the event * `transactionIndex` — the integer index of the transaction within the block's list of transactions * `blockHash` — the hash of the block in which the event was included * `logIndex` — the integer identifying the index of the event within the block's list of events * `removed` — boolean value indicating if the event was removed due to a chain reorganization ## Usage example ### Basic implementation > [!NOTE] > > Note that subscriptions require a WebSocket connection. Install WebSocket cat for testing: > > ```bash theme={"system"} > npm install -g wscat > ``` ```shell wscat theme={"system"} $ wscat -c wss://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm # Wait for the connection to be established Connected (press CTRL+C to quit) # Subscribe to all logs from a specific contract > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["logs",{"address":"0x5555555555555555555555555555555555555555"}]} < {"jsonrpc":"2.0","id":1,"result":"0x1234567890abcdef"} # Log notifications will stream in as events are emitted: < {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1234567890abcdef","result":{"address":"0x5555555555555555555555555555555555555555","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"data":"0x00000000000000000000000000000000000000000000000000000000000186a0","blockNumber":"0x1234","transactionHash":"0x...","transactionIndex":"0x0","blockHash":"0x...","logIndex":"0x0","removed":false}}} ``` ### JavaScript implementation - Monitor ERC-20 transfers ```javascript theme={"system"} const WebSocket = require('ws'); const CHAINSTACK_WSS_URL = 'wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm'; // ERC-20 Transfer event signature const TRANSFER_EVENT_SIGNATURE = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; async function monitorTokenTransfers(tokenAddress) { const ws = new WebSocket(CHAINSTACK_WSS_URL); ws.on('open', () => { console.log('Connected to Hyperliquid EVM WebSocket'); // Subscribe to Transfer events from specific token const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: [ 'logs', { address: tokenAddress, topics: [TRANSFER_EVENT_SIGNATURE] } ] }; ws.send(JSON.stringify(request)); }); ws.on('message', (data) => { const response = JSON.parse(data); // Handle subscription confirmation if (response.id === 1) { console.log(`Subscribed with ID: ${response.result}`); } // Handle log notifications if (response.method === 'eth_subscription') { const log = response.params.result; // Decode Transfer event (topics[1] = from, topics[2] = to, data = amount) const from = '0x' + log.topics[1].slice(26); const to = '0x' + log.topics[2].slice(26); const amount = BigInt(log.data); console.log('Transfer detected:'); console.log(` From: ${from}`); console.log(` To: ${to}`); console.log(` Amount: ${amount.toString()}`); console.log(` Block: ${parseInt(log.blockNumber, 16)}`); console.log(` TX: ${log.transactionHash}`); } }); ws.on('error', (error) => { console.error('WebSocket error:', error); }); } // Monitor a specific token const tokenAddress = '0x5555555555555555555555555555555555555555'; monitorTokenTransfers(tokenAddress); ``` ### Python implementation - Monitor multiple events ```python theme={"system"} import json import asyncio import websockets CHAINSTACK_WSS_URL = 'wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm' # Common event signatures TRANSFER_EVENT = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' APPROVAL_EVENT = '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925' async def monitor_multiple_events(contract_addresses): async with websockets.connect(CHAINSTACK_WSS_URL) as websocket: # Subscribe to Transfer and Approval events from multiple contracts subscribe_request = { "id": 1, "jsonrpc": "2.0", "method": "eth_subscribe", "params": [ "logs", { "address": contract_addresses, "topics": [[TRANSFER_EVENT, APPROVAL_EVENT]] # OR condition } ] } await websocket.send(json.dumps(subscribe_request)) while True: response = json.loads(await websocket.recv()) # Handle subscription confirmation if response.get('id') == 1: print(f"Subscribed with ID: {response['result']}") # Handle log notifications elif response.get('method') == 'eth_subscription': log = response['params']['result'] event_sig = log['topics'][0] print(f"\nEvent detected in block {int(log['blockNumber'], 16)}:") print(f" Contract: {log['address']}") if event_sig == TRANSFER_EVENT: print(" Event: Transfer") from_addr = '0x' + log['topics'][1][26:] to_addr = '0x' + log['topics'][2][26:] print(f" From: {from_addr}") print(f" To: {to_addr}") elif event_sig == APPROVAL_EVENT: print(" Event: Approval") owner = '0x' + log['topics'][1][26:] spender = '0x' + log['topics'][2][26:] print(f" Owner: {owner}") print(f" Spender: {spender}") # Monitor multiple contracts contracts = [ '0x5555555555555555555555555555555555555555', '0x6666666666666666666666666666666666666666' ] asyncio.run(monitor_multiple_events(contracts)) ``` ### Advanced filtering with topic combinations ```javascript theme={"system"} // Monitor specific NFT transfers (ERC-721) const monitorNFTTransfers = async (nftContract, fromAddress = null, toAddress = null) => { const ws = new WebSocket(CHAINSTACK_WSS_URL); ws.on('open', () => { // Build topics array for filtering const topics = [ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // Transfer event fromAddress ? fromAddress.toLowerCase().padStart(64, '0') : null, // from address (optional) toAddress ? toAddress.toLowerCase().padStart(64, '0') : null // to address (optional) ]; const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: [ 'logs', { address: nftContract, topics: topics.filter(t => t !== null) } ] }; ws.send(JSON.stringify(request)); console.log('Monitoring NFT transfers...'); }); ws.on('message', (data) => { const response = JSON.parse(data); if (response.method === 'eth_subscription') { const log = response.params.result; const tokenId = BigInt(log.topics[3]); // NFT token ID is in topics[3] console.log(`NFT Transfer - Token ID: ${tokenId}`); console.log(` Transaction: ${log.transactionHash}`); } }); }; ``` ## Example request `eth_subscribe` is a WebSocket-only method, so every example connects over a WebSocket endpoint. ```shell wscat theme={"system"} $ wscat -c wss://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm # Wait for the connection to be established Connected (press CTRL+C to quit) # Subscribe to all logs from a specific contract > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["logs",{"address":"0x5555555555555555555555555555555555555555"}]} < {"jsonrpc":"2.0","id":1,"result":"0x1234567890abcdef"} # Log notifications will stream in as events are emitted: < {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1234567890abcdef","result":{"address":"0x5555555555555555555555555555555555555555","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"data":"0x00000000000000000000000000000000000000000000000000000000000186a0","blockNumber":"0x1234","transactionHash":"0x...","transactionIndex":"0x0","blockHash":"0x...","logIndex":"0x0","removed":false}}} ``` ```python Python (web3.py) theme={"system"} import asyncio from web3 import AsyncWeb3 from web3.providers.persistent import WebSocketProvider # eth_subscribe requires a WebSocket connection. async def main(): async with AsyncWeb3(WebSocketProvider("YOUR_CHAINSTACK_ENDPOINT")) as w3: # subscribe("logs", ...) maps to eth_subscribe("logs", ...). sub_id = await w3.eth.subscribe("logs", { "address": "0x5555555555555555555555555555555555555555", }) print(sub_id) async for message in w3.socket.process_subscriptions(): print(message) asyncio.run(main()) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { WebSocketProvider } from "ethers"; // eth_subscribe requires a WebSocket connection. const provider = new WebSocketProvider("YOUR_CHAINSTACK_ENDPOINT"); // A log filter subscription maps to eth_subscribe("logs", ...). const filter = { address: "0x5555555555555555555555555555555555555555" }; provider.on(filter, (log) => { console.log(log); }); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, webSocket } from "viem"; // Over a WebSocket transport, watchEvent uses eth_subscribe("logs", ...). const client = createPublicClient({ transport: webSocket("YOUR_CHAINSTACK_ENDPOINT"), }); const unwatch = client.watchEvent({ address: "0x5555555555555555555555555555555555555555", onLogs: (logs) => console.log(logs), }); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_subscribe("logs")` method is essential for applications that need to: * **Monitor token transfers**: Track ERC-20/ERC-721 transfers in real-time * **DEX activity tracking**: Monitor swaps, liquidity additions, and removals on decentralized exchanges * **Smart contract events**: React to specific contract events as they occur * **DeFi protocol monitoring**: Track lending, borrowing, and liquidation events * **Wallet activity alerts**: Notify users of incoming/outgoing transactions * **NFT marketplace activity**: Monitor mints, sales, and transfers of NFTs * **Bridge monitoring**: Track cross-chain bridge deposits and withdrawals * **Governance tracking**: Monitor voting and proposal events in DAOs * **Price oracle updates**: Track price feed updates from oracle contracts * **Security monitoring**: Detect suspicious contract interactions in real-time This WebSocket subscription method provides efficient, real-time event monitoring with lower latency than polling-based approaches, making it ideal for building responsive dApps and monitoring systems. # eth_subscribe ("newHeads") | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-subscribe-newheads Subscribe to real-time notifications for new block headers on Hyperliquid EVM blockchain. Sends notifications whenever a new block is added. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_subscribe("newHeads")` JSON-RPC method allows developers to receive real-time notifications regarding new block headers on the Hyperliquid EVM blockchain. It sends notifications whenever a new block is added, making it essential for applications that need to track blockchain state changes in real-time. ## Parameters 1. **subscription type** (string, required): Keyword identifying the type of event to subscribe to, `newHeads` in this case ## Response The method returns a subscription ID that can be used to identify and manage the subscription. ### Response structure **Initial subscription response:** * `subscription` — the subscription ID **Notification structure:** * `number` — block number of the requested block, encoded as hexadecimal * `hash` — block hash of the requested block * `parentHash` — hash of the previous block * `nonce` — proof-of-work hash (0x0000000000000000 for proof-of-stake) * `sha3Uncles` — hash of the list of uncles included in the block * `logsBloom` — bloom filter for the logs of the block * `transactionsRoot` — root of the transaction trie of the block * `stateRoot` — root of the final state trie of the block * `receiptsRoot` — root of the receipts trie of the block * `miner` — address of the miner receiving the reward * `difficulty` — block difficulty (legacy field, typically 0x0 for PoS) * `totalDifficulty` — null (obsolete after merge to PoS) * `extraData` — extra data included by the miner * `size` — size of this block in bytes * `gasLimit` — maximum gas allowed in this block * `gasUsed` — total used gas by all transactions * `timestamp` — Unix timestamp for when the block was collated ## Usage example ### Basic implementation > [!NOTE] > > Note that subscriptions require a WebSocket connection. Install WebSocket cat for testing: > > ```bash theme={"system"} > npm install -g wscat > ``` ```shell wscat theme={"system"} $ wscat -c wss://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} < {"jsonrpc":"2.0","id":1,"result":"0x1234567890abcdef"} # New block notifications will stream in: < {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1234567890abcdef","result":{"difficulty":"0x0","extraData":"0x","gasLimit":"0x1c9c380","gasUsed":"0x5208","hash":"0x...","logsBloom":"0x...","miner":"0x...","nonce":"0x0000000000000000","number":"0x1234","parentHash":"0x...","receiptsRoot":"0x...","sha3Uncles":"0x...","size":"0x220","stateRoot":"0x...","timestamp":"0x65abc123","transactionsRoot":"0x..."}}} ``` ### JavaScript implementation ```javascript theme={"system"} const WebSocket = require('ws'); const CHAINSTACK_WSS_URL = 'wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm'; async function subscribeToNewBlocks() { const ws = new WebSocket(CHAINSTACK_WSS_URL); ws.on('open', () => { console.log('Connected to Hyperliquid EVM WebSocket'); // Subscribe to new block headers const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'] }; ws.send(JSON.stringify(request)); }); ws.on('message', (data) => { const response = JSON.parse(data); // Handle subscription confirmation if (response.id === 1) { console.log(`Subscribed with ID: ${response.result}`); } // Handle new block notifications if (response.method === 'eth_subscription') { const block = response.params.result; console.log('New block received:'); console.log(` Number: ${parseInt(block.number, 16)}`); console.log(` Hash: ${block.hash}`); console.log(` Timestamp: ${new Date(parseInt(block.timestamp, 16) * 1000).toISOString()}`); console.log(` Gas Used: ${parseInt(block.gasUsed, 16).toLocaleString()}`); } }); ws.on('error', (error) => { console.error('WebSocket error:', error); }); ws.on('close', () => { console.log('WebSocket connection closed'); }); } // Run the subscription subscribeToNewBlocks(); ``` ### Python implementation ```python theme={"system"} import json import asyncio import websockets from datetime import datetime CHAINSTACK_WSS_URL = 'wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm' async def subscribe_to_new_blocks(): async with websockets.connect(CHAINSTACK_WSS_URL) as websocket: # Subscribe to new block headers subscribe_request = { "id": 1, "jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newHeads"] } await websocket.send(json.dumps(subscribe_request)) while True: response = json.loads(await websocket.recv()) # Handle subscription confirmation if response.get('id') == 1: print(f"Subscribed with ID: {response['result']}") # Handle new block notifications elif response.get('method') == 'eth_subscription': block = response['params']['result'] print('New block received:') print(f" Number: {int(block['number'], 16)}") print(f" Hash: {block['hash']}") timestamp = datetime.fromtimestamp(int(block['timestamp'], 16)) print(f" Timestamp: {timestamp.isoformat()}") print(f" Gas Used: {int(block['gasUsed'], 16):,}") # Run the async function asyncio.run(subscribe_to_new_blocks()) ``` ## Example request `eth_subscribe("newHeads")` runs over a WebSocket connection, so each example below opens a WebSocket to the endpoint and streams new block headers. The Shell tab uses `wscat` for a quick check; the Python, JavaScript, and TypeScript tabs use the WebSocket transport of each library. ```shell wscat theme={"system"} $ wscat -c wss://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} < {"jsonrpc":"2.0","id":1,"result":"0x1234567890abcdef"} # New block notifications will stream in: < {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1234567890abcdef","result":{"difficulty":"0x0","extraData":"0x","gasLimit":"0x1c9c380","gasUsed":"0x5208","hash":"0x...","logsBloom":"0x...","miner":"0x...","nonce":"0x0000000000000000","number":"0x1234","parentHash":"0x...","receiptsRoot":"0x...","sha3Uncles":"0x...","size":"0x220","stateRoot":"0x...","timestamp":"0x65abc123","transactionsRoot":"0x..."}}} ``` ```python web3.py theme={"system"} import asyncio from web3 import AsyncWeb3, WebSocketProvider async def subscribe_to_new_heads(): async with AsyncWeb3(WebSocketProvider("YOUR_CHAINSTACK_ENDPOINT")) as w3: subscription_id = await w3.eth.subscribe("newHeads") print(f"Subscribed with ID: {subscription_id}") async for message in w3.socket.process_subscriptions(): header = message["result"] print(f"New block: {header['number']} {header['hash'].hex()}") asyncio.run(subscribe_to_new_heads()) ``` ```javascript ethers.js theme={"system"} import { WebSocketProvider } from "ethers"; const provider = new WebSocketProvider("YOUR_CHAINSTACK_ENDPOINT"); provider.on("block", async (blockNumber) => { const header = await provider.getBlock(blockNumber); console.log(`New block: ${header.number} ${header.hash}`); }); ``` ```typescript viem theme={"system"} import { createPublicClient, webSocket } from "viem"; const client = createPublicClient({ transport: webSocket("YOUR_CHAINSTACK_ENDPOINT"), }); const unwatch = await client.watchBlocks({ onBlock: (block) => { console.log(`New block: ${block.number} ${block.hash}`); }, }); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_subscribe("newHeads")` method is essential for applications that need to: * **Monitor blockchain state**: Track new blocks as they are added to the chain * **Calculate confirmations**: Count confirmations for transactions by tracking subsequent blocks * **Build real-time dashboards**: Display current block height, gas prices, and network activity * **Trigger automated actions**: Execute smart contract interactions when new blocks arrive * **Index blockchain data**: Update databases with the latest blockchain state * **Monitor network health**: Track block production rate and validator performance * **Implement event listeners**: Process events more efficiently by focusing on specific block ranges * **Build trading bots**: React to new blocks for time-sensitive DeFi operations * **Track finality**: Monitor block finalization in proof-of-stake networks * **Analyze gas trends**: Monitor gas usage patterns across blocks This WebSocket subscription method provides a more efficient alternative to polling, enabling real-time blockchain monitoring with lower latency and reduced bandwidth usage. # eth_subscribe ("syncing") | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-subscribe-syncing Subscribe to notifications about the node's synchronization status on Hyperliquid EVM. Receive updates when the node starts or stops syncing with the network. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_subscribe("syncing")` JSON-RPC method allows developers to subscribe to notifications about the synchronization status of a Hyperliquid EVM node. The subscription provides real-time updates when the node starts or stops syncing with the network, making it essential for monitoring node health and ensuring data consistency. ## Parameters 1. **subscription type** (string, required): Keyword identifying the type of event to subscribe to, `syncing` in this case ## Response The method returns a subscription ID that can be used to identify and manage the subscription. ### Response structure **Initial subscription response:** * `subscription` — the subscription ID **Sync status notification structure:** When the node is syncing: * `startingBlock` — the block number where sync started * `currentBlock` — the current block number being processed * `highestBlock` — the estimated highest block number to sync to When the node is not syncing: * Returns `false` ## Usage example ### Basic implementation > [!NOTE] > > Note that subscriptions require a WebSocket connection. Install WebSocket cat for testing: > > ```bash theme={"system"} > npm install -g wscat > ``` ```shell wscat theme={"system"} $ wscat -c wss://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["syncing"]} < {"jsonrpc":"2.0","id":1,"result":"0x1234567890abcdef"} # Sync status notifications will appear when sync state changes: # When node starts syncing: < {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1234567890abcdef","result":{"startingBlock":"0x0","currentBlock":"0x1234","highestBlock":"0x5678"}}} # When node is fully synced: < {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1234567890abcdef","result":false}} ``` ### JavaScript implementation ```javascript theme={"system"} const WebSocket = require('ws'); const CHAINSTACK_WSS_URL = 'wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm'; async function monitorSyncStatus() { const ws = new WebSocket(CHAINSTACK_WSS_URL); let syncStartTime = null; let lastProgress = 0; ws.on('open', () => { console.log('Connected to Hyperliquid EVM WebSocket'); // Subscribe to syncing status const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['syncing'] }; ws.send(JSON.stringify(request)); }); ws.on('message', (data) => { const response = JSON.parse(data); // Handle subscription confirmation if (response.id === 1) { console.log(`Subscribed to sync status with ID: ${response.result}`); } // Handle sync status notifications if (response.method === 'eth_subscription') { const syncStatus = response.params.result; if (syncStatus === false) { console.log('✅ Node is fully synchronized'); if (syncStartTime) { const duration = (Date.now() - syncStartTime) / 1000; console.log(`Sync completed in ${duration.toFixed(1)} seconds`); syncStartTime = null; } } else { if (!syncStartTime) { syncStartTime = Date.now(); console.log('⏳ Node synchronization started'); } const startBlock = parseInt(syncStatus.startingBlock, 16); const currentBlock = parseInt(syncStatus.currentBlock, 16); const highestBlock = parseInt(syncStatus.highestBlock, 16); // Calculate progress const totalBlocks = highestBlock - startBlock; const syncedBlocks = currentBlock - startBlock; const progress = (syncedBlocks / totalBlocks * 100).toFixed(2); // Calculate sync speed const blocksPerUpdate = currentBlock - lastProgress; lastProgress = currentBlock; console.log('📊 Sync Status:'); console.log(` Current Block: ${currentBlock.toLocaleString()}`); console.log(` Target Block: ${highestBlock.toLocaleString()}`); console.log(` Progress: ${progress}%`); console.log(` Blocks Behind: ${(highestBlock - currentBlock).toLocaleString()}`); if (blocksPerUpdate > 0) { console.log(` Sync Speed: ${blocksPerUpdate} blocks/update`); const remainingBlocks = highestBlock - currentBlock; const estimatedTime = remainingBlocks / blocksPerUpdate; console.log(` Estimated Time: ${estimatedTime.toFixed(0)} updates`); } } } }); ws.on('error', (error) => { console.error('WebSocket error:', error); }); ws.on('close', () => { console.log('WebSocket connection closed'); }); } // Start monitoring monitorSyncStatus(); ``` ### Python implementation with alerting ```python theme={"system"} import json import asyncio import websockets from datetime import datetime import time CHAINSTACK_WSS_URL = 'wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm' async def monitor_sync_with_alerts(): async with websockets.connect(CHAINSTACK_WSS_URL) as websocket: sync_start_time = None last_alert_time = 0 alert_threshold = 100 # Alert if behind by more than 100 blocks # Subscribe to syncing status subscribe_request = { "id": 1, "jsonrpc": "2.0", "method": "eth_subscribe", "params": ["syncing"] } await websocket.send(json.dumps(subscribe_request)) while True: response = json.loads(await websocket.recv()) # Handle subscription confirmation if response.get('id') == 1: print(f"✅ Subscribed with ID: {response['result']}") print("Monitoring sync status...") # Handle sync status notifications elif response.get('method') == 'eth_subscription': sync_status = response['params']['result'] current_time = datetime.now() if sync_status is False: print(f"\n[{current_time.strftime('%H:%M:%S')}] Node is fully synchronized ✅") if sync_start_time: duration = time.time() - sync_start_time print(f"Synchronization completed in {duration:.1f} seconds") sync_start_time = None else: if not sync_start_time: sync_start_time = time.time() print(f"\n[{current_time.strftime('%H:%M:%S')}] Synchronization started ⏳") current_block = int(sync_status['currentBlock'], 16) highest_block = int(sync_status['highestBlock'], 16) blocks_behind = highest_block - current_block print(f"\n[{current_time.strftime('%H:%M:%S')}] Sync Status:") print(f" Current: {current_block:,}") print(f" Target: {highest_block:,}") print(f" Behind: {blocks_behind:,} blocks") # Send alert if significantly behind if blocks_behind > alert_threshold: current_timestamp = time.time() if current_timestamp - last_alert_time > 60: # Alert at most once per minute print(f"⚠️ ALERT: Node is {blocks_behind:,} blocks behind!") last_alert_time = current_timestamp # Here you could send an email, webhook, etc. await send_alert(blocks_behind) async def send_alert(blocks_behind): """Placeholder for alert functionality""" # Implement your alerting mechanism here # e.g., send webhook, email, SMS, etc. print(f"📧 Alert sent: Node is {blocks_behind:,} blocks behind") # Run the monitoring asyncio.run(monitor_sync_with_alerts()) ``` ### Combined monitoring with health checks ```javascript theme={"system"} const WebSocket = require('ws'); class NodeHealthMonitor { constructor(wsUrl) { this.wsUrl = wsUrl; this.ws = null; this.subscriptions = new Map(); this.syncStatus = { isSyncing: false, blockssBehind: 0 }; this.lastBlockTime = Date.now(); this.healthCheckInterval = null; } async connect() { this.ws = new WebSocket(this.wsUrl); this.ws.on('open', () => { console.log('Connected to Hyperliquid EVM'); this.subscribeSyncing(); this.subscribeNewHeads(); this.startHealthCheck(); }); this.ws.on('message', (data) => { const response = JSON.parse(data); this.handleMessage(response); }); this.ws.on('error', (error) => { console.error('WebSocket error:', error); }); this.ws.on('close', () => { console.log('Connection closed, attempting reconnect...'); setTimeout(() => this.connect(), 5000); }); } subscribeSyncing() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['syncing'] }; this.ws.send(JSON.stringify(request)); } subscribeNewHeads() { const request = { id: 2, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'] }; this.ws.send(JSON.stringify(request)); } handleMessage(response) { // Store subscription IDs if (response.id) { if (response.id === 1) { this.subscriptions.set('syncing', response.result); } else if (response.id === 2) { this.subscriptions.set('newHeads', response.result); } return; } // Handle subscription updates if (response.method === 'eth_subscription') { const subscription = response.params.subscription; if (subscription === this.subscriptions.get('syncing')) { this.handleSyncUpdate(response.params.result); } else if (subscription === this.subscriptions.get('newHeads')) { this.handleNewBlock(response.params.result); } } } handleSyncUpdate(syncStatus) { if (syncStatus === false) { this.syncStatus.isSyncing = false; this.syncStatus.blocksBehind = 0; console.log('✅ Node synchronized'); } else { this.syncStatus.isSyncing = true; const current = parseInt(syncStatus.currentBlock, 16); const highest = parseInt(syncStatus.highestBlock, 16); this.syncStatus.blocksBehind = highest - current; console.log(`⏳ Syncing: ${this.syncStatus.blocksBehind} blocks behind`); } } handleNewBlock(block) { this.lastBlockTime = Date.now(); const blockNumber = parseInt(block.number, 16); console.log(`📦 New block: ${blockNumber}`); } startHealthCheck() { this.healthCheckInterval = setInterval(() => { const timeSinceLastBlock = (Date.now() - this.lastBlockTime) / 1000; if (timeSinceLastBlock > 30) { console.warn(`⚠️ No new blocks for ${timeSinceLastBlock.toFixed(0)} seconds`); } const health = { connected: this.ws.readyState === WebSocket.OPEN, syncing: this.syncStatus.isSyncing, blocksBehind: this.syncStatus.blocksBehind, lastBlockAge: timeSinceLastBlock }; console.log('Health Status:', health); }, 10000); // Check every 10 seconds } } // Start monitoring const monitor = new NodeHealthMonitor('wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm'); monitor.connect(); ``` ## Example request `eth_subscribe("syncing")` runs over a WebSocket connection. After subscribing, the node sends a notification with the current state and then another on every change: `false` when fully synced, or an object with `startingBlock`, `currentBlock`, and `highestBlock` while syncing. The Shell tab uses `wscat` for a quick check; the Python, JavaScript, and TypeScript tabs use the WebSocket transport of each library. ```shell wscat theme={"system"} $ wscat -c wss://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["syncing"]} < {"jsonrpc":"2.0","id":1,"result":"0x4f9f6872aa4c474b686cf23e352b725c"} # A notification is sent immediately with the current state, then on every change. # When the node is fully synced: < {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x4f9f6872aa4c474b686cf23e352b725c","result":false}} # When the node is syncing: < {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x4f9f6872aa4c474b686cf23e352b725c","result":{"startingBlock":"0x0","currentBlock":"0x1234","highestBlock":"0x5678"}}} ``` ```python web3.py theme={"system"} import asyncio from web3 import AsyncWeb3, WebSocketProvider async def main(): async with AsyncWeb3(WebSocketProvider("YOUR_CHAINSTACK_ENDPOINT")) as w3: subscription_id = await w3.eth.subscribe("syncing") print(f"Subscribed with ID: {subscription_id}") async for message in w3.socket.process_subscriptions(): # result is False when synced, or a progress object while syncing print(f"Sync status: {message['result']}") asyncio.run(main()) ``` ```javascript ethers.js theme={"system"} import { WebSocketProvider } from "ethers"; const provider = new WebSocketProvider("YOUR_CHAINSTACK_ENDPOINT"); // ethers has no high-level "syncing" event, so listen on the underlying // WebSocket and send the eth_subscribe request directly. provider.websocket.addEventListener("message", (event) => { const message = JSON.parse(event.data.toString()); if (message.method === "eth_subscription") { // result is false when synced, or a progress object while syncing console.log("Sync status:", message.params.result); } }); await provider.send("eth_subscribe", ["syncing"]); ``` ```typescript viem theme={"system"} import { createPublicClient, webSocket } from "viem"; const client = createPublicClient({ transport: webSocket("YOUR_CHAINSTACK_ENDPOINT"), }); // viem has no high-level syncing action, so use the transport's low-level subscribe. const { unsubscribe } = await client.transport.subscribe({ params: ["syncing"], onData: (data) => { // data.result is false when synced, or a progress object while syncing console.log("Sync status:", data.result); }, }); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_subscribe("syncing")` method is essential for applications that need to: * **Node health monitoring**: Track whether nodes are keeping up with the network * **Data consistency checks**: Ensure queries are made against fully synced nodes * **Load balancing**: Route requests away from syncing nodes * **Alerting systems**: Notify operators when nodes fall behind * **Deployment validation**: Verify new nodes are syncing correctly * **Performance monitoring**: Track sync speed and estimate completion times * **Failover management**: Automatically switch to backup nodes during sync issues * **Infrastructure scaling**: Identify when additional nodes are needed * **Maintenance planning**: Schedule updates when nodes are fully synced * **Service reliability**: Ensure high availability by monitoring sync status This WebSocket subscription method provides real-time synchronization monitoring, enabling proactive node management and ensuring reliable blockchain data access for applications. # eth_syncing | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-syncing openapi/hyperliquid_node_api/hyperevm/evm_eth_syncing.json post /evm Returns the synchronization status of the node. On Hyperliquid, this method always returns false as nodes are always considered synchronized. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the synchronization status of the node. On Hyperliquid, this method always returns `false` as nodes are always considered synchronized. ## Parameters This method takes no parameters. ## Returns Returns the synchronization status. On Hyperliquid, this is always `false`, indicating the node is fully synchronized. > [!NOTE] > > On Hyperliquid, this method always returns `false`. The network architecture ensures nodes are always synchronized, so there's no syncing state. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) syncing = w3.eth.syncing print(syncing) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const syncing = await provider.send("eth_syncing", []); console.log(syncing); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const syncing = await client.request({ method: "eth_syncing" }); console.log(syncing); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. Example response: ```json theme={"system"} {"jsonrpc":"2.0","id":1,"result":false} ``` ## Use cases * **EVM compatibility** — Maintain compatibility with standard Ethereum JSON-RPC interface * **Application initialization** — Check node readiness (always ready on Hyperliquid) * **Health monitoring** — Verify node status in monitoring systems * **Cross-chain compatibility** — Support applications that check sync status across different networks # eth_uninstallFilter | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-uninstall-filter openapi/hyperliquid_node_api/hyperevm/evm_eth_uninstall_filter.json post /evm The eth_uninstallFilter JSON-RPC method uninstalls a filter with the given ID. eth_uninstallFilter on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_uninstallFilter` JSON-RPC method uninstalls a filter with the given ID. This method should be called when polling is no longer needed to free up server resources and maintain optimal performance. Filters may also be automatically uninstalled if they are not polled for an extended period of time. ## Parameters 1. **filter\_id** (string) — The filter ID to uninstall (returned by `eth_newFilter` or `eth_newBlockFilter`) ## Response The method returns a boolean value indicating whether the filter was successfully uninstalled. ### Response structure **Return values:** * `true` — Filter was successfully uninstalled * `false` — Filter was not found (may have already expired or been uninstalled) ### Filter lifecycle **Automatic cleanup:** * Filters may be automatically uninstalled after a period of inactivity * The exact timeout varies by node implementation * Always explicitly uninstall filters when done to ensure cleanup **Resource management:** * Each filter consumes server resources for tracking state * Uninstalling filters frees these resources for other operations * Good practice to uninstall filters when no longer needed ## Usage example ### Basic implementation ```javascript theme={"system"} // Uninstall a filter const uninstallFilter = async (filterId) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_uninstallFilter', params: [filterId], id: 1 }) }); const data = await response.json(); return data.result; }; // Filter manager with automatic cleanup class FilterManager { constructor() { this.activeFilters = new Map(); this.cleanupTimeouts = new Map(); } async createLogFilter(filterOptions, timeoutMs = 300000) { // 5 minutes default const createResponse = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_newFilter', params: [filterOptions], id: 1 }) }); const createData = await createResponse.json(); const filterId = createData.result; // Track the filter this.activeFilters.set(filterId, { type: 'log', created: Date.now(), options: filterOptions }); // Set automatic cleanup if (timeoutMs > 0) { this.setCleanupTimeout(filterId, timeoutMs); } return filterId; } async createBlockFilter(timeoutMs = 300000) { // 5 minutes default const createResponse = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_newBlockFilter', params: [], id: 1 }) }); const createData = await createResponse.json(); const filterId = createData.result; // Track the filter this.activeFilters.set(filterId, { type: 'block', created: Date.now() }); // Set automatic cleanup if (timeoutMs > 0) { this.setCleanupTimeout(filterId, timeoutMs); } return filterId; } setCleanupTimeout(filterId, timeoutMs) { // Clear existing timeout if any if (this.cleanupTimeouts.has(filterId)) { clearTimeout(this.cleanupTimeouts.get(filterId)); } const timeoutId = setTimeout(() => { this.uninstallFilter(filterId); }, timeoutMs); this.cleanupTimeouts.set(filterId, timeoutId); } resetCleanupTimeout(filterId, timeoutMs = 300000) { if (this.activeFilters.has(filterId)) { this.setCleanupTimeout(filterId, timeoutMs); } } async uninstallFilter(filterId) { // Clear cleanup timeout if (this.cleanupTimeouts.has(filterId)) { clearTimeout(this.cleanupTimeouts.get(filterId)); this.cleanupTimeouts.delete(filterId); } // Uninstall the filter const success = await uninstallFilter(filterId); if (success) { this.activeFilters.delete(filterId); console.log(`Filter ${filterId} uninstalled successfully`); } else { console.log(`Filter ${filterId} was not found (may have already expired)`); // Still remove from our tracking this.activeFilters.delete(filterId); } return success; } async uninstallAllFilters() { const results = {}; const filterIds = Array.from(this.activeFilters.keys()); for (const filterId of filterIds) { results[filterId] = await this.uninstallFilter(filterId); } return results; } getActiveFilters() { return Array.from(this.activeFilters.entries()).map(([id, info]) => ({ filterId: id, ...info, age: Date.now() - info.created })); } // Keep a filter alive by resetting its timeout keepAlive(filterId, timeoutMs = 300000) { if (this.activeFilters.has(filterId)) { this.resetCleanupTimeout(filterId, timeoutMs); return true; } return false; } } // Usage with proper cleanup const useFilterWithCleanup = async () => { const manager = new FilterManager(); try { // Create filters const logFilterId = await manager.createLogFilter({ fromBlock: 'latest', toBlock: 'latest', address: '0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5' }, 60000); // 1 minute timeout const blockFilterId = await manager.createBlockFilter(60000); console.log('Created filters:', { logFilterId, blockFilterId }); // Simulate some work for (let i = 0; i < 5; i++) { await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds // Keep filters alive during work manager.keepAlive(logFilterId); manager.keepAlive(blockFilterId); console.log('Active filters:', manager.getActiveFilters().length); } } finally { // Clean up all filters await manager.uninstallAllFilters(); console.log('All filters cleaned up'); } }; // Batch filter operations const batchFilterOperations = async (filterIds) => { const results = { uninstalled: [], notFound: [], errors: [] }; for (const filterId of filterIds) { try { const success = await uninstallFilter(filterId); if (success) { results.uninstalled.push(filterId); } else { results.notFound.push(filterId); } } catch (error) { results.errors.push({ filterId, error: error.message }); } } return results; }; // Safe filter cleanup with retry const safeUninstallFilter = async (filterId, retries = 3) => { for (let attempt = 1; attempt <= retries; attempt++) { try { const success = await uninstallFilter(filterId); return { success, attempt }; } catch (error) { if (attempt === retries) { throw new Error(`Failed to uninstall filter ${filterId} after ${retries} attempts: ${error.message}`); } // Wait before retry await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); } } }; // Usage examples const filterId = '0x1'; // Simple uninstall uninstallFilter(filterId).then(success => { if (success) { console.log(`Filter ${filterId} uninstalled successfully`); } else { console.log(`Filter ${filterId} was not found`); } }); // Use filter manager const manager = new FilterManager(); manager.createLogFilter({ fromBlock: 'latest', toBlock: 'latest', address: '0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5' }).then(filterId => { console.log(`Created filter: ${filterId}`); // Uninstall after 30 seconds setTimeout(() => { manager.uninstallFilter(filterId); }, 30000); }); // Batch operations const filterIdsToCleanup = ['0x1', '0x2', '0x3']; batchFilterOperations(filterIdsToCleanup).then(results => { console.log('Batch cleanup results:', results); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_uninstallFilter", "params": [ "0x1" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) removed = w3.eth.uninstall_filter("0x1") print(removed) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const removed = await provider.send("eth_uninstallFilter", ["0x1"]); console.log(removed); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const removed = await client.request({ method: "eth_uninstallFilter", params: ["0x1"], }); console.log(removed); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_uninstallFilter` method is essential for applications that need to: * **Resource management**: Free up server resources when filters are no longer needed * **Memory cleanup**: Prevent memory leaks from accumulating unused filters * **Performance optimization**: Maintain optimal node performance by cleaning up filters * **Application lifecycle**: Properly clean up resources when applications shut down * **Session management**: Clean up filters when user sessions end * **Batch operations**: Clean up multiple filters efficiently * **Error handling**: Clean up filters when errors occur during processing * **Testing cleanup**: Clean up test filters in automated testing environments * **Load balancing**: Manage filter load across multiple connections * **Connection management**: Clean up filters when connections are lost * **Scheduled cleanup**: Implement scheduled cleanup of old filters * **Resource monitoring**: Monitor and manage filter resource usage * **Graceful shutdowns**: Ensure proper cleanup during application shutdowns * **Filter rotation**: Rotate filters to prevent resource exhaustion * **Development workflows**: Clean up filters during development and debugging This method provides essential resource management capabilities, ensuring efficient and clean operation of filter-based applications on the Hyperliquid EVM platform. # eth_unsubscribe | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-unsubscribe Cancel an active WebSocket subscription on Hyperliquid EVM. Stop receiving notifications for a previously established subscription. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `eth_unsubscribe` JSON-RPC method allows developers to cancel an active WebSocket subscription on the Hyperliquid EVM blockchain. This method is used to stop receiving notifications for subscriptions created with `eth_subscribe`, helping to manage resources and control data flow in applications. ## Parameters 1. **subscription ID** (string, required): The subscription identifier returned by `eth_subscribe` when the subscription was created ## Response The method returns a boolean value indicating whether the unsubscribe operation was successful. ### Response structure * `true` — the subscription was successfully cancelled * `false` — the subscription was not found or could not be cancelled ## Usage example ### Basic implementation > [!NOTE] > > Note that subscriptions require a WebSocket connection. Install WebSocket cat for testing: > > ```bash theme={"system"} > npm install -g wscat > ``` ```shell wscat theme={"system"} $ wscat -c wss://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm # Wait for the connection to be established Connected (press CTRL+C to quit) # First create a subscription > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} < {"jsonrpc":"2.0","id":1,"result":"0x1234567890abcdef"} # Now unsubscribe using the subscription ID > {"id":2,"jsonrpc":"2.0","method":"eth_unsubscribe","params":["0x1234567890abcdef"]} < {"jsonrpc":"2.0","id":2,"result":true} ``` ### JavaScript implementation with subscription management ```javascript theme={"system"} const WebSocket = require('ws'); const CHAINSTACK_WSS_URL = 'wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm'; class SubscriptionManager { constructor(wsUrl) { this.wsUrl = wsUrl; this.ws = null; this.subscriptions = new Map(); this.requestId = 0; } connect() { return new Promise((resolve, reject) => { this.ws = new WebSocket(this.wsUrl); this.ws.on('open', () => { console.log('Connected to Hyperliquid EVM WebSocket'); resolve(); }); this.ws.on('message', (data) => { this.handleMessage(JSON.parse(data)); }); this.ws.on('error', (error) => { console.error('WebSocket error:', error); reject(error); }); this.ws.on('close', () => { console.log('WebSocket connection closed'); this.subscriptions.clear(); }); }); } async subscribe(type, params = []) { const id = ++this.requestId; return new Promise((resolve) => { const request = { id, jsonrpc: '2.0', method: 'eth_subscribe', params: [type, ...params] }; // Store resolver to handle response this.subscriptions.set(`request_${id}`, { type: 'request', resolve, subscriptionType: type }); this.ws.send(JSON.stringify(request)); console.log(`Subscribing to ${type}...`); }); } async unsubscribe(subscriptionId) { const id = ++this.requestId; return new Promise((resolve) => { const request = { id, jsonrpc: '2.0', method: 'eth_unsubscribe', params: [subscriptionId] }; // Store resolver to handle response this.subscriptions.set(`request_${id}`, { type: 'unsubscribe', resolve, subscriptionId }); this.ws.send(JSON.stringify(request)); console.log(`Unsubscribing from ${subscriptionId}...`); }); } handleMessage(message) { // Handle subscription/unsubscription responses if (message.id) { const request = this.subscriptions.get(`request_${message.id}`); if (request) { if (request.type === 'request') { // Store the subscription ID this.subscriptions.set(message.result, { type: 'subscription', subscriptionType: request.subscriptionType, handler: null }); console.log(`Subscribed with ID: ${message.result}`); request.resolve(message.result); } else if (request.type === 'unsubscribe') { // Clean up subscription if (message.result === true) { this.subscriptions.delete(request.subscriptionId); console.log(`Successfully unsubscribed from ${request.subscriptionId}`); } else { console.log(`Failed to unsubscribe from ${request.subscriptionId}`); } request.resolve(message.result); } this.subscriptions.delete(`request_${message.id}`); } } // Handle subscription notifications if (message.method === 'eth_subscription') { const subscription = this.subscriptions.get(message.params.subscription); if (subscription && subscription.handler) { subscription.handler(message.params.result); } } } setHandler(subscriptionId, handler) { const subscription = this.subscriptions.get(subscriptionId); if (subscription) { subscription.handler = handler; } } async disconnect() { // Unsubscribe from all active subscriptions const activeSubscriptions = Array.from(this.subscriptions.entries()) .filter(([_, sub]) => sub.type === 'subscription') .map(([id, _]) => id); for (const subId of activeSubscriptions) { await this.unsubscribe(subId); } this.ws.close(); } } // Example usage async function main() { const manager = new SubscriptionManager(CHAINSTACK_WSS_URL); try { await manager.connect(); // Subscribe to new blocks const blockSubId = await manager.subscribe('newHeads'); manager.setHandler(blockSubId, (block) => { console.log(`New block: ${parseInt(block.number, 16)}`); }); // Subscribe to logs const logSubId = await manager.subscribe('logs', [{ address: '0x5555555555555555555555555555555555555555' }]); manager.setHandler(logSubId, (log) => { console.log(`New log from ${log.address}`); }); // Run for 30 seconds then clean up setTimeout(async () => { console.log('\nCleaning up subscriptions...'); // Unsubscribe from blocks const blockResult = await manager.unsubscribe(blockSubId); console.log(`Block unsubscribe result: ${blockResult}`); // Unsubscribe from logs const logResult = await manager.unsubscribe(logSubId); console.log(`Log unsubscribe result: ${logResult}`); // Disconnect await manager.disconnect(); process.exit(0); }, 30000); } catch (error) { console.error('Error:', error); } } main(); ``` ### Python implementation with context manager ```python theme={"system"} import json import asyncio import websockets from typing import Dict, Any, Optional from contextlib import asynccontextmanager class SubscriptionManager: def __init__(self, ws_url: str): self.ws_url = ws_url self.websocket = None self.subscriptions: Dict[str, Any] = {} self.request_id = 0 self.pending_requests = {} async def connect(self): self.websocket = await websockets.connect(self.ws_url) print("Connected to Hyperliquid EVM WebSocket") async def disconnect(self): if self.websocket: await self.websocket.close() print("Disconnected from WebSocket") async def subscribe(self, subscription_type: str, params: Optional[Dict] = None) -> str: self.request_id += 1 request = { "id": self.request_id, "jsonrpc": "2.0", "method": "eth_subscribe", "params": [subscription_type] + ([params] if params else []) } # Create a future to wait for response future = asyncio.Future() self.pending_requests[self.request_id] = future await self.websocket.send(json.dumps(request)) print(f"Subscribing to {subscription_type}...") # Wait for subscription ID subscription_id = await future self.subscriptions[subscription_id] = { "type": subscription_type, "params": params } print(f"Subscribed with ID: {subscription_id}") return subscription_id async def unsubscribe(self, subscription_id: str) -> bool: self.request_id += 1 request = { "id": self.request_id, "jsonrpc": "2.0", "method": "eth_unsubscribe", "params": [subscription_id] } # Create a future to wait for response future = asyncio.Future() self.pending_requests[self.request_id] = future await self.websocket.send(json.dumps(request)) print(f"Unsubscribing from {subscription_id}...") # Wait for unsubscribe result result = await future if result: del self.subscriptions[subscription_id] print(f"Successfully unsubscribed from {subscription_id}") else: print(f"Failed to unsubscribe from {subscription_id}") return result async def listen(self, handler): """Listen for messages and handle them""" async for message in self.websocket: data = json.loads(message) # Handle subscription/unsubscription responses if "id" in data: request_id = data["id"] if request_id in self.pending_requests: self.pending_requests[request_id].set_result(data.get("result")) del self.pending_requests[request_id] # Handle subscription notifications elif data.get("method") == "eth_subscription": subscription_id = data["params"]["subscription"] if subscription_id in self.subscriptions: await handler(subscription_id, data["params"]["result"]) async def cleanup(self): """Unsubscribe from all active subscriptions""" print("\nCleaning up subscriptions...") for sub_id in list(self.subscriptions.keys()): await self.unsubscribe(sub_id) @asynccontextmanager async def managed_subscription(ws_url: str): """Context manager for automatic subscription cleanup""" manager = SubscriptionManager(ws_url) try: await manager.connect() yield manager finally: await manager.cleanup() await manager.disconnect() # Example usage async def main(): ws_url = 'wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm' async with managed_subscription(ws_url) as manager: # Subscribe to different events block_sub = await manager.subscribe("newHeads") log_sub = await manager.subscribe("logs", { "address": "0x5555555555555555555555555555555555555555" }) sync_sub = await manager.subscribe("syncing") # Handler for notifications async def handle_notification(sub_id: str, result: Any): if sub_id == block_sub: print(f"New block: {int(result['number'], 16)}") elif sub_id == log_sub: print(f"New log from: {result['address']}") elif sub_id == sync_sub: if result is False: print("Node is synchronized") else: print(f"Syncing: current block {int(result['currentBlock'], 16)}") # Listen for 30 seconds listen_task = asyncio.create_task(manager.listen(handle_notification)) await asyncio.sleep(30) # Manual unsubscribe example (optional, cleanup will handle it anyway) await manager.unsubscribe(block_sub) listen_task.cancel() print("All subscriptions cleaned up automatically") # Run the example asyncio.run(main()) ``` ### Subscription lifecycle management ```javascript theme={"system"} // Complete subscription lifecycle with error handling class RobustSubscriptionManager { constructor(wsUrl, maxReconnectAttempts = 5) { this.wsUrl = wsUrl; this.maxReconnectAttempts = maxReconnectAttempts; this.reconnectAttempts = 0; this.subscriptions = new Map(); this.handlers = new Map(); } async manageSubscription(type, params, duration, handler) { console.log(`Starting ${type} subscription for ${duration}ms`); try { // Connect and subscribe await this.connect(); const subId = await this.subscribe(type, params); // Set up handler this.handlers.set(subId, handler); // Run for specified duration await new Promise(resolve => setTimeout(resolve, duration)); // Cleanup console.log(`Time's up, unsubscribing from ${type}...`); const success = await this.unsubscribe(subId); if (success) { console.log(`✅ Successfully completed ${type} subscription lifecycle`); } else { console.log(`⚠️ Failed to cleanly unsubscribe from ${type}`); } } catch (error) { console.error(`Error in subscription lifecycle: ${error.message}`); } finally { this.disconnect(); } } // ... rest of implementation similar to previous example } // Example: Monitor blocks for 1 minute const manager = new RobustSubscriptionManager(CHAINSTACK_WSS_URL); await manager.manageSubscription( 'newHeads', {}, 60000, // 1 minute (block) => console.log(`Block ${parseInt(block.number, 16)}`) ); ``` ## Example request `eth_unsubscribe` runs over a WebSocket connection — it cancels a subscription created by `eth_subscribe` and returns `true` on success. The Shell tab uses `wscat` to create a subscription and then cancel it; the Python, JavaScript, and TypeScript tabs use the WebSocket transport of each library and stop the subscription, which sends `eth_unsubscribe` for you. ```shell wscat theme={"system"} $ wscat -c wss://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm # Wait for the connection to be established Connected (press CTRL+C to quit) # First create a subscription > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} < {"jsonrpc":"2.0","id":1,"result":"0x1234567890abcdef"} # Now unsubscribe using the subscription ID > {"id":2,"jsonrpc":"2.0","method":"eth_unsubscribe","params":["0x1234567890abcdef"]} < {"jsonrpc":"2.0","id":2,"result":true} ``` ```python web3.py theme={"system"} import asyncio from web3 import AsyncWeb3, WebSocketProvider async def main(): async with AsyncWeb3(WebSocketProvider("YOUR_CHAINSTACK_ENDPOINT")) as w3: subscription_id = await w3.eth.subscribe("newHeads") print(f"Subscribed with ID: {subscription_id}") # Cancel the subscription — sends eth_unsubscribe and returns a boolean success = await w3.eth.unsubscribe(subscription_id) print(f"Unsubscribed: {success}") asyncio.run(main()) ``` ```javascript ethers.js theme={"system"} import { WebSocketProvider } from "ethers"; const provider = new WebSocketProvider("YOUR_CHAINSTACK_ENDPOINT"); const onBlock = (blockNumber) => console.log(`New block: ${blockNumber}`); provider.on("block", onBlock); // Removing the listener sends eth_unsubscribe for the underlying subscription provider.off("block", onBlock); provider.destroy(); ``` ```typescript viem theme={"system"} import { createPublicClient, webSocket } from "viem"; const client = createPublicClient({ transport: webSocket("YOUR_CHAINSTACK_ENDPOINT"), }); const unwatch = await client.watchBlocks({ onBlock: (block) => console.log(`New block: ${block.number}`), }); // Calling unwatch sends eth_unsubscribe for the underlying subscription unwatch(); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `eth_unsubscribe` method is essential for: * **Resource management**: Free up server and client resources by canceling unused subscriptions * **Dynamic subscription control**: Change subscription parameters by unsubscribing and resubscribing * **Application lifecycle management**: Clean up subscriptions when components unmount or disconnect * **Rate limiting compliance**: Manage the number of active subscriptions to stay within limits * **Cost optimization**: Reduce bandwidth and processing costs by removing unnecessary subscriptions * **Error recovery**: Cancel problematic subscriptions that are causing issues * **Testing and development**: Clean up test subscriptions during development * **User preference changes**: Update subscriptions when users change monitoring preferences * **Performance optimization**: Remove subscriptions that are no longer needed to improve performance * **Connection management**: Properly clean up before reconnecting or switching endpoints Proper use of `eth_unsubscribe` ensures efficient resource utilization and clean application shutdown, preventing memory leaks and unnecessary network traffic. # eth_usingBigBlocks | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-eth-using-big-blocks openapi/hyperliquid_node_api/hyperevm/evm_eth_using_big_blocks.json post /evm Returns whether a specific address is using big blocks on Hyperliquid EVM. Available on Hyperliquid EVM via Chainstack JSON-RPC nodes. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns whether a specific address is using big blocks on Hyperliquid EVM. This Hyperliquid-specific method helps determine if an address can utilize enhanced transaction processing capabilities. ## Example request `eth_usingBigBlocks` is a Hyperliquid-specific JSON-RPC method, so the EVM libraries call it through their generic raw-request escape hatch rather than a wrapped helper. ```shell cURL theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"eth_usingBigBlocks","params":["0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA"],"id":1}' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # eth_usingBigBlocks is Hyperliquid-specific, so use the raw provider request response = w3.provider.make_request("eth_usingBigBlocks", ["0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA"]) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // eth_usingBigBlocks is Hyperliquid-specific, so send a raw JSON-RPC request const result = await provider.send("eth_usingBigBlocks", [ "0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", ]); console.log(result); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // eth_usingBigBlocks is Hyperliquid-specific, so use the generic request method const result = await client.request({ method: "eth_usingBigBlocks" as any, params: ["0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA"] as any, }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Parameters * `address` (string, required) — The 20-byte address to check for big blocks configuration ## Returns Returns a boolean indicating whether the address is using big blocks. * `true` — Address is configured to use big blocks * `false` — Address is using standard block processing > [!NOTE] > > This is a Hyperliquid-specific method. Big blocks provide enhanced transaction processing capabilities for eligible addresses. ## Use cases * **Gas price optimization** — Choose appropriate gas pricing based on address capabilities * **Transaction processing** — Optimize transaction handling for big blocks users * **Feature detection** — Determine available features for specific addresses * **Wallet applications** — Provide enhanced options for big blocks users * **DeFi protocols** — Optimize protocol interactions based on user capabilities * **Analytics** — Track big blocks adoption and usage patterns # net_listening | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-net-listening openapi/hyperliquid_node_api/hyperevm/evm_net_listening.json post /evm The net_listening JSON-RPC method returns true if the client is actively listening for network connections. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `net_listening` JSON-RPC method returns true if the client is actively listening for network connections. This method is useful for checking the network connectivity status of the Hyperliquid EVM node and verifying that it can accept incoming peer connections. ## Parameters This method takes no parameters. The `params` field should be an empty array. ## Response The method returns a boolean value indicating whether the client is listening for network connections. ### Response structure **Network status:** * `result` — Boolean value indicating if the client is actively listening for network connections ### Listening status interpretation **True response:** * Node is actively listening for incoming network connections * Node is available for peer-to-peer communication * Network interface is properly configured and accessible **False response:** * Node is not accepting incoming connections * May indicate network configuration issues * Node might be in a restricted or maintenance mode ## Usage example ### Basic implementation ```javascript theme={"system"} // Check if node is listening for network connections const checkNodeListening = async () => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'net_listening', params: [], id: 1 }) }); const data = await response.json(); return data.result; }; // Monitor network connectivity status const monitorNetworkStatus = async () => { try { const isListening = await checkNodeListening(); if (isListening) { console.log('✅ Node is listening for network connections'); } else { console.log('❌ Node is not accepting network connections'); } return isListening; } catch (error) { console.error('Error checking network status:', error); return false; } }; // Health check implementation const performHealthCheck = async () => { const networkStatus = await monitorNetworkStatus(); return { network: { listening: networkStatus, status: networkStatus ? 'healthy' : 'degraded' }, timestamp: new Date().toISOString() }; }; // Usage performHealthCheck().then(health => { console.log('Node health check:', health); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"net_listening","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # net_listening is exposed as a property on the net module is_listening = w3.net.listening print(is_listening) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // ethers has no high-level wrapper, so send the raw JSON-RPC method const isListening = await provider.send("net_listening", []); console.log(isListening); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // viem has no high-level wrapper, so send the raw JSON-RPC method const isListening = await client.request({ method: "net_listening" }); console.log(isListening); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `net_listening` method is essential for applications that need to: * **Network monitoring**: Monitor node connectivity and network interface status * **Health checks**: Implement comprehensive node health monitoring systems * **Load balancing**: Make routing decisions based on node availability * **Troubleshooting**: Diagnose network connectivity issues and configuration problems * **Infrastructure management**: Automate node deployment and configuration validation * **Service discovery**: Verify node readiness before adding to service pools * **Monitoring dashboards**: Display real-time network status in operational dashboards * **Alerting systems**: Create alerts for network connectivity issues * **Failover logic**: Implement automatic failover based on network status * **Development tools**: Build development tools with network status awareness * **Testing frameworks**: Verify network configuration in testing environments * **Performance monitoring**: Track network availability as part of performance metrics * **Security monitoring**: Monitor for unexpected network state changes * **Deployment validation**: Verify successful node deployment and configuration * **Maintenance coordination**: Coordinate maintenance activities based on network status This method provides essential network connectivity information for maintaining robust and reliable blockchain infrastructure on the Hyperliquid EVM platform. # net_peerCount | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-net-peer-count openapi/hyperliquid_node_api/hyperevm/evm_net_peer_count.json post /evm The net_peerCount JSON-RPC method returns the number of peers currently connected to the client. Chainstack Hyperliquid EVM reference. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `net_peerCount` JSON-RPC method returns the number of peers currently connected to the client. This method provides information about the node's network connectivity and can be used to monitor network health, connectivity status, and the decentralization level of the blockchain network. ## Parameters This method takes no parameters. The `params` field should be an empty array. ## Response The method returns the number of connected peers as a hexadecimal string. ### Response structure **Peer count:** * Returns the number of connected peers as a hexadecimal string * Convert from hex to decimal for human-readable count * Example: "0x2a" equals 42 peers in decimal ### Peer connectivity interpretation **Network health indicators:** * Higher peer count generally indicates better network connectivity * Zero peers may indicate network connectivity issues * Peer count fluctuates based on network conditions and node behavior **Decentralization metrics:** * Peer count reflects the distributed nature of the network * More peers typically mean better data availability and redundancy * Useful for assessing network resilience and decentralization ## Usage example ### Basic implementation ```javascript theme={"system"} // Get peer count const getPeerCount = async () => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'net_peerCount', params: [], id: 1 }) }); const data = await response.json(); const peerCount = parseInt(data.result, 16); return { hex: data.result, decimal: peerCount }; }; // Network health monitor const monitorNetworkHealth = async () => { const healthStatus = { peerCount: 0, status: 'unknown', timestamp: new Date(), history: [] }; try { const { decimal: peerCount } = await getPeerCount(); healthStatus.peerCount = peerCount; // Determine network status based on peer count if (peerCount === 0) { healthStatus.status = 'disconnected'; } else if (peerCount < 5) { healthStatus.status = 'poor'; } else if (peerCount < 20) { healthStatus.status = 'fair'; } else if (peerCount < 50) { healthStatus.status = 'good'; } else { healthStatus.status = 'excellent'; } } catch (error) { healthStatus.status = 'error'; healthStatus.error = error.message; } return healthStatus; }; // Continuous network monitoring const startNetworkMonitoring = (callback, intervalMs = 30000) => { const monitor = { history: [], isRunning: false, intervalId: null }; const checkNetwork = async () => { const health = await monitorNetworkHealth(); monitor.history.push(health); // Keep only last 100 readings if (monitor.history.length > 100) { monitor.history = monitor.history.slice(-100); } callback(health, monitor.history); }; monitor.start = () => { if (!monitor.isRunning) { monitor.isRunning = true; checkNetwork(); // Initial check monitor.intervalId = setInterval(checkNetwork, intervalMs); } }; monitor.stop = () => { if (monitor.isRunning) { monitor.isRunning = false; clearInterval(monitor.intervalId); } }; monitor.getStats = () => { if (monitor.history.length === 0) return null; const peerCounts = monitor.history.map(h => h.peerCount); return { current: peerCounts[peerCounts.length - 1], min: Math.min(...peerCounts), max: Math.max(...peerCounts), avg: Math.round(peerCounts.reduce((a, b) => a + b, 0) / peerCounts.length), samples: monitor.history.length }; }; return monitor; }; // Network connectivity dashboard class NetworkDashboard { constructor() { this.data = { current: null, history: [], alerts: [] }; } async updateStatus() { try { const health = await monitorNetworkHealth(); this.data.current = health; this.data.history.push(health); // Keep history manageable if (this.data.history.length > 1000) { this.data.history = this.data.history.slice(-1000); } // Check for alerts this.checkAlerts(health); } catch (error) { console.error('Failed to update network status:', error); } } checkAlerts(health) { const now = Date.now(); // Low peer count alert if (health.peerCount < 5 && health.status !== 'error') { this.data.alerts.push({ type: 'low_peers', message: `Low peer count detected: ${health.peerCount}`, timestamp: now, severity: 'warning' }); } // Disconnection alert if (health.peerCount === 0) { this.data.alerts.push({ type: 'disconnected', message: 'Node appears to be disconnected (0 peers)', timestamp: now, severity: 'critical' }); } // Keep only recent alerts (last hour) this.data.alerts = this.data.alerts.filter( alert => now - alert.timestamp < 3600000 ); } getStatus() { return this.data.current; } getHistory(minutes = 60) { const cutoff = Date.now() - (minutes * 60 * 1000); return this.data.history.filter(h => h.timestamp.getTime() > cutoff); } getAlerts(minutes = 60) { const cutoff = Date.now() - (minutes * 60 * 1000); return this.data.alerts.filter(a => a.timestamp > cutoff); } generateReport() { const recent = this.getHistory(60); if (recent.length === 0) return null; const peerCounts = recent.map(h => h.peerCount); const statusCounts = recent.reduce((acc, h) => { acc[h.status] = (acc[h.status] || 0) + 1; return acc; }, {}); return { timeRange: '60 minutes', samples: recent.length, peerStats: { current: this.data.current?.peerCount || 0, min: Math.min(...peerCounts), max: Math.max(...peerCounts), avg: Math.round(peerCounts.reduce((a, b) => a + b, 0) / peerCounts.length) }, statusDistribution: statusCounts, recentAlerts: this.getAlerts(60).length }; } } // Usage examples getPeerCount().then(result => { console.log(`Connected peers: ${result.decimal} (${result.hex})`); }); // Start network monitoring const monitor = startNetworkMonitoring((health, history) => { console.log(`Network Status: ${health.status}, Peers: ${health.peerCount}`); if (health.status === 'poor' || health.status === 'disconnected') { console.warn('Network connectivity issues detected!'); } }, 10000); // Check every 10 seconds monitor.start(); // Stop monitoring after 5 minutes setTimeout(() => { monitor.stop(); console.log('Final stats:', monitor.getStats()); }, 5 * 60 * 1000); // Use network dashboard const dashboard = new NetworkDashboard(); // Update dashboard every 30 seconds setInterval(() => { dashboard.updateStatus().then(() => { const report = dashboard.generateReport(); if (report) { console.log('Network Report:', report); } }); }, 30000); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "net_peerCount", "params": [], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) peer_count = w3.net.peer_count print(peer_count) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const peerCount = await provider.send("net_peerCount", []); console.log(peerCount); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const peerCount = await client.request({ method: "net_peerCount" }); console.log(peerCount); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `net_peerCount` method is useful for applications that need to: * **Network monitoring**: Monitor blockchain network health and connectivity * **Node diagnostics**: Diagnose node connectivity and network issues * **Performance optimization**: Optimize application behavior based on network conditions * **Health checks**: Implement health checks for blockchain applications * **Alert systems**: Create alerts for network connectivity issues * **Load balancing**: Make load balancing decisions based on network health * **Monitoring dashboards**: Build network monitoring and status dashboards * **DevOps tools**: Monitor blockchain infrastructure health * **Research analytics**: Analyze network decentralization and topology * **Automatic failover**: Implement failover logic based on connectivity * **Network statistics**: Generate network connectivity statistics and reports * **Quality assurance**: Test network connectivity in different environments * **Troubleshooting**: Diagnose network-related issues and connectivity problems * **Infrastructure monitoring**: Monitor blockchain node infrastructure health * **SLA monitoring**: Monitor service level agreements for network connectivity This method provides essential network connectivity information, enabling robust network monitoring and health assessment on the Hyperliquid EVM platform. # net_version | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-net-version openapi/hyperliquid_node_api/hyperevm/evm_net_version.json post /evm Returns the current network ID for the Hyperliquid EVM. Use this to identify which network the client is connected to. This method takes no parameters. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the current network ID for the Hyperliquid EVM. Use this to identify which network the client is connected to. ## Parameters This method takes no parameters. ## Returns Returns the network ID as a string. For Hyperliquid mainnet, this is `"999"`. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"net_version","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) network_id = w3.net.version print(network_id) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const networkId = await provider.send("net_version", []); console.log(networkId); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const networkId = await client.request({ method: "net_version" }); console.log(networkId); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. Example response: ```json theme={"system"} {"jsonrpc":"2.0","id":1,"result":"999"} ``` ## Use cases * **Network verification** — Confirm connection to Hyperliquid before transactions * **Wallet configuration** — Configure wallets to recognize Hyperliquid network * **dApp development** — Build network-aware decentralized applications * **Multi-network support** — Support multiple EVM networks with proper identification # ots_getApiLevel | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-get-api-level openapi/hyperliquid_node_api/hyperevm/evm_ots_get_api_level.json post /evm The ots_getApiLevel JSON-RPC method returns the Otterscan API version level supported by the Hyperliquid EVM node. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_getApiLevel` JSON-RPC method returns the Otterscan API version level supported by the Hyperliquid EVM node. This method allows clients, particularly the Otterscan block explorer, to determine which API features are available on the connected node. ## Parameters This method accepts no parameters. ## Response The method returns an integer representing the API level version. ### Response structure * `result` — the API level number (e.g., 8 for the current version) ## Usage example ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_getApiLevel", "params": [], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # ots_getApiLevel is an Otterscan-namespace method, so call it as a raw JSON-RPC request. response = w3.provider.make_request("ots_getApiLevel", []) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // ots_getApiLevel is an Otterscan-namespace method, so use the generic send call. const apiLevel = await provider.send("ots_getApiLevel", []); console.log(apiLevel); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // ots_getApiLevel is an Otterscan-namespace method, so issue it as a custom request. const apiLevel = await client.request({ method: "ots_getApiLevel" as any, params: [], }); console.log(apiLevel); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": 8 } ``` ## API level features Different API levels indicate support for various Otterscan features: ### Level 8 (Current) * Full Otterscan API support including: * Block and transaction tracing * Internal operations tracking * Contract creator identification * Transaction error retrieval * Paginated queries * Address transaction history ### Version compatibility * Otterscan uses this method to: * Verify node compatibility * Enable/disable features based on API level * Provide appropriate fallbacks for missing features ## Use cases The `ots_getApiLevel` method is essential for: * **Compatibility checking**: Verify if a node supports required Otterscan features * **Feature detection**: Enable or disable UI features based on available APIs * **Version negotiation**: Ensure client-server compatibility * **Graceful degradation**: Provide fallback functionality for older API versions * **Development testing**: Verify API implementation completeness * **Node validation**: Confirm nodes are running with Otterscan support * **Explorer configuration**: Auto-configure block explorer features * **API documentation**: Determine which methods are available * **Integration testing**: Validate node setup for Otterscan compatibility * **Monitoring tools**: Check API availability across multiple nodes This method is typically the first call made by Otterscan to establish what features can be used with the connected node. # ots_getBlockDetails | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-get-block-details openapi/hyperliquid_node_api/hyperevm/evm_ots_get_block_details.json post /evm The ots_getBlockDetails JSON-RPC method retrieves expanded block details by block number on the Hyperliquid EVM blockchain. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_getBlockDetails` JSON-RPC method retrieves expanded block details by block number on the Hyperliquid EVM blockchain. This Otterscan-specific method provides a tailored response for block detail pages in block explorers, including calculated total fees and other aggregated information. ## Parameters 1. **block number** (integer, required): The block number to retrieve details for ## Response The method returns an enhanced block object with additional calculated fields. ### Response structure * `block` — standard block object with all transactions * `number` — the block number * `hash` — the block hash * `parentHash` — the parent block hash * `timestamp` — Unix timestamp of the block * `gasLimit` — maximum gas allowed in the block * `gasUsed` — total gas used by all transactions * `miner` — address that mined/validated the block * `baseFeePerGas` — base fee per gas (post-EIP-1559) * `transactions` — array of transaction objects * `transactionCount` — number of transactions in the block * `issuance` — block reward issuance information * `blockReward` — reward for mining the block * `uncleReward` — rewards for uncle blocks * `issuance` — total new ETH issued * `totalFees` — total transaction fees collected in the block (in wei) ## Usage example `ots_getBlockDetails` is an Otterscan-namespace method, so the mainstream EVM libraries have no typed helper for it — call it through each client's raw JSON-RPC request method, as shown below. ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_getBlockDetails", "params": [1000], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) response = w3.provider.make_request("ots_getBlockDetails", [1000]) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const result = await provider.send("ots_getBlockDetails", [1000]); console.log(result); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const result = await client.request({ method: "ots_getBlockDetails", params: [1000], }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": { "block": { "number": "0x3e8", "hash": "0xe8399bf41516f5ca663c79bc30db02b4989385c2f3a96f42958dea5753cbf4a9", "parentHash": "0x7c8f6a0e4d3b2a1c9e5f8b7d4a6c3e9f2b8d5a7c1e4f9b3a6d8c2e5f7a9b4c6d", "timestamp": "0x6734e8d0", "gasLimit": "0x1c9c380", "gasUsed": "0x5208", "miner": "0x0000000000000000000000000000000000000000", "baseFeePerGas": "0x7", "transactions": [ { "hash": "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381", "from": "0x1234567890abcdef1234567890abcdef12345678", "to": "0x5555555555555555555555555555555555555555", "value": "0xde0b6b3a7640000", "gas": "0x5208", "gasPrice": "0x3b9aca00" } ], "transactionCount": 1 }, "issuance": { "blockReward": "0x0", "uncleReward": "0x0", "issuance": "0x0" }, "totalFees": "0x1236efcbcbb340" } } ``` ## Use cases The `ots_getBlockDetails` method is essential for: * **Block explorers**: Display comprehensive block information pages * **Fee analysis**: Calculate and display total fees collected in blocks * **Mining analytics**: Track block rewards and issuance * **Network monitoring**: Monitor block production and transaction throughput * **Historical analysis**: Study block characteristics over time * **Gas price tracking**: Analyze gas usage and pricing trends * **MEV analysis**: Calculate MEV extraction by comparing fees * **Validator performance**: Track validator rewards and efficiency * **Chain analytics**: Generate statistics about block utilization * **Research tools**: Analyze blockchain economics and fee markets This method provides all the information needed to create detailed block explorer pages, eliminating the need for multiple API calls to gather block data. # ots_getBlockDetailsByHash | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-get-block-details-by-hash openapi/hyperliquid_node_api/hyperevm/evm_ots_get_block_details_by_hash.json post /evm The ots_getBlockDetailsByHash JSON-RPC method retrieves expanded block details by block hash on the Hyperliquid EVM blockchain. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_getBlockDetailsByHash` JSON-RPC method retrieves expanded block details by block hash on the Hyperliquid EVM blockchain. This Otterscan-specific method provides a tailored response for block detail pages in block explorers, including calculated total fees and other aggregated information. ## Parameters 1. **block hash** (string, required): The hash of the block to retrieve details for ## Response The method returns an enhanced block object with additional calculated fields. ### Response structure * `block` — standard block object with all transactions * `number` — the block number * `hash` — the block hash * `parentHash` — the parent block hash * `timestamp` — Unix timestamp of the block * `gasLimit` — maximum gas allowed in the block * `gasUsed` — total gas used by all transactions * `miner` — address that mined/validated the block * `baseFeePerGas` — base fee per gas (post-EIP-1559) * `transactions` — array of transaction objects * `transactionCount` — number of transactions in the block * `issuance` — block reward issuance information * `blockReward` — reward for mining the block * `uncleReward` — rewards for uncle blocks * `issuance` — total new ETH issued * `totalFees` — total transaction fees collected in the block (in wei) ## Usage example ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_getBlockDetailsByHash", "params": ["0xe8399bf41516f5ca663c79bc30db02b4989385c2f3a96f42958dea5753cbf4a9"], "id": 1 }' ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) block_hash = "0xe8399bf41516f5ca663c79bc30db02b4989385c2f3a96f42958dea5753cbf4a9" # ots_getBlockDetailsByHash is an Otterscan method, so call it via the raw provider response = w3.provider.make_request("ots_getBlockDetailsByHash", [block_hash]) print(response["result"]) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const blockHash = "0xe8399bf41516f5ca663c79bc30db02b4989385c2f3a96f42958dea5753cbf4a9"; // ots_getBlockDetailsByHash is an Otterscan method, so use the generic send() const details = await provider.send("ots_getBlockDetailsByHash", [blockHash]); console.log(details); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const blockHash = "0xe8399bf41516f5ca663c79bc30db02b4989385c2f3a96f42958dea5753cbf4a9"; // ots_getBlockDetailsByHash is an Otterscan method, so call it via request() const details = await client.request({ method: "ots_getBlockDetailsByHash", params: [blockHash], } as any); console.log(details); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": { "block": { "number": "0x3e8", "hash": "0xe8399bf41516f5ca663c79bc30db02b4989385c2f3a96f42958dea5753cbf4a9", "parentHash": "0x7c8f6a0e4d3b2a1c9e5f8b7d4a6c3e9f2b8d5a7c1e4f9b3a6d8c2e5f7a9b4c6d", "timestamp": "0x6734e8d0", "gasLimit": "0x1c9c380", "gasUsed": "0x5208", "miner": "0x0000000000000000000000000000000000000000", "baseFeePerGas": "0x7", "transactions": [ { "hash": "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381", "from": "0x1234567890abcdef1234567890abcdef12345678", "to": "0x5555555555555555555555555555555555555555", "value": "0xde0b6b3a7640000", "gas": "0x5208", "gasPrice": "0x3b9aca00" } ], "transactionCount": 1 }, "issuance": { "blockReward": "0x0", "uncleReward": "0x0", "issuance": "0x0" }, "totalFees": "0x1236efcbcbb340" } } ``` ## Use cases The `ots_getBlockDetailsByHash` method is essential for: * **Block explorers**: Display comprehensive block information when accessed by hash * **Block verification**: Retrieve detailed information for specific known blocks * **Cross-reference validation**: Verify block data using hash identifiers * **Fee reconciliation**: Calculate total fees for specific blocks * **Fork detection**: Analyze blocks on different chain branches * **Archive queries**: Access historical block data by hash * **Integration testing**: Verify specific blocks in test scenarios * **Audit trails**: Reference specific blocks in compliance reports * **Performance benchmarking**: Analyze specific blocks for metrics * **Research tools**: Study particular blocks of interest This method is particularly useful when you have a block hash from an event log or transaction receipt and need to retrieve comprehensive block information. # ots_getBlockTransactions | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-get-block-transactions openapi/hyperliquid_node_api/hyperevm/evm_ots_get_block_transactions.json post /evm The ots_getBlockTransactions JSON-RPC method retrieves paginated transactions for a specific block on the Hyperliquid EVM blockchain. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_getBlockTransactions` JSON-RPC method retrieves paginated transactions for a specific block on the Hyperliquid EVM blockchain. This Otterscan-specific method optimizes data transfer by removing verbose fields like logs and providing pagination support for blocks with many transactions. ## Parameters 1. **block number** (integer, required): The block number to retrieve transactions from 2. **page number** (integer, required): The page number to retrieve (0-indexed) 3. **page size** (integer, required): The number of transactions per page ## Response The method returns a paginated response with transaction data and receipts. ### Response structure * `fullblock` — the complete block information * `number` — block number * `hash` — block hash * `parentHash` — parent block hash * `timestamp` — block timestamp * `gasLimit` — block gas limit * `gasUsed` — total gas used * `baseFeePerGas` — base fee per gas * `receipts` — array of transaction receipts (simplified, without logs) * `transactionHash` — hash of the transaction * `status` — success status (0x1 for success, 0x0 for failure) * `cumulativeGasUsed` — cumulative gas used up to this transaction * `gasUsed` — gas used by this transaction * `effectiveGasPrice` — actual gas price paid * `contractAddress` — contract address created (if applicable) * `txs` — array of transactions for the requested page * Standard transaction fields (hash, from, to, value, input, etc.) ## Usage example ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_getBlockTransactions", "params": [1000, 0, 10], "id": 1 }' ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # ots_getBlockTransactions is an Otterscan method, so call it through the raw provider. response = w3.provider.make_request("ots_getBlockTransactions", [1000, 0, 10]) print(response["result"]) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // ots_getBlockTransactions is an Otterscan method, so send it as a raw request. const result = await provider.send("ots_getBlockTransactions", [1000, 0, 10]); console.log(result); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // ots_getBlockTransactions is an Otterscan method, so issue it as a raw request. const result = await client.request({ method: "ots_getBlockTransactions", params: [1000, 0, 10], }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": { "fullblock": { "number": "0x3e8", "hash": "0xe8399bf41516f5ca663c79bc30db02b4989385c2f3a96f42958dea5753cbf4a9", "parentHash": "0x7c8f6a0e4d3b2a1c9e5f8b7d4a6c3e9f2b8d5a7c1e4f9b3a6d8c2e5f7a9b4c6d", "timestamp": "0x6734e8d0", "gasLimit": "0x1c9c380", "gasUsed": "0xa410", "baseFeePerGas": "0x7" }, "receipts": [ { "transactionHash": "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381", "status": "0x1", "cumulativeGasUsed": "0x5208", "gasUsed": "0x5208", "effectiveGasPrice": "0x3b9aca00", "contractAddress": null } ], "txs": [ { "hash": "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381", "from": "0x1234567890abcdef1234567890abcdef12345678", "to": "0x5555555555555555555555555555555555555555", "value": "0xde0b6b3a7640000", "gas": "0x5208", "gasPrice": "0x3b9aca00", "input": "0x", "nonce": "0x0", "transactionIndex": "0x0" } ] } } ``` ### Pagination example To retrieve all transactions from a large block: ```javascript theme={"system"} // Page 0: transactions 0-9 params: [1000, 0, 10] // Page 1: transactions 10-19 params: [1000, 1, 10] // Page 2: transactions 20-29 params: [1000, 2, 10] ``` ## Use cases The `ots_getBlockTransactions` method is essential for: * **Block explorers**: Display transaction lists with pagination controls * **Large block handling**: Efficiently process blocks with many transactions * **Performance optimization**: Reduce bandwidth by excluding verbose log data * **Transaction indexing**: Build transaction databases with pagination * **API rate limiting**: Control data flow with page-based retrieval * **Mobile applications**: Optimize data usage with smaller response sizes * **Real-time monitoring**: Process new blocks transaction by transaction * **Data analysis**: Systematically analyze all transactions in blocks * **Archive services**: Export block transaction data in manageable chunks * **User interfaces**: Implement infinite scroll or pagination in dApps This method is particularly valuable for handling blocks with hundreds or thousands of transactions, where retrieving all data at once would be inefficient. # ots_getContractCreator | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-get-contract-creator openapi/hyperliquid_node_api/hyperevm/evm_ots_get_contract_creator.json post /evm The ots_getContractCreator JSON-RPC method retrieves the transaction hash and creator address for a deployed contract on the Hyperliquid EVM blockchain. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_getContractCreator` JSON-RPC method retrieves the transaction hash and creator address for a deployed contract on the Hyperliquid EVM blockchain. This Otterscan-specific method helps identify who deployed a smart contract and in which transaction. ## Parameters 1. **contract address** (string, required): The address of the deployed contract ## Response The method returns an object containing the creator address and deployment transaction hash, or null if the address is not a contract or information is unavailable. ### Response structure * `hash` — the transaction hash where the contract was created * `creator` — the address that deployed the contract ## Usage example ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_getContractCreator", "params": ["0x5555555555555555555555555555555555555555"], "id": 1 }' ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # ots_getContractCreator is an Otterscan-namespace method, so call it as a raw JSON-RPC request response = w3.provider.make_request( "ots_getContractCreator", ["0x5555555555555555555555555555555555555555"], ) print(response["result"]) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // ots_getContractCreator is an Otterscan-namespace method, so send it as a raw JSON-RPC request const result = await provider.send("ots_getContractCreator", [ "0x5555555555555555555555555555555555555555", ]); console.log(result); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // ots_getContractCreator is an Otterscan-namespace method, so call it as a raw JSON-RPC request const result = await client.request({ method: "ots_getContractCreator", params: ["0x5555555555555555555555555555555555555555"], }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response (contract found) ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": { "hash": "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381", "creator": "0x1234567890abcdef1234567890abcdef12345678" } } ``` ### Example response (not a contract) ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": null } ``` ## Contract creation methods This method detects contracts created through: * Direct deployment transactions * CREATE opcode from other contracts * CREATE2 opcode for deterministic addresses ## Use cases The `ots_getContractCreator` method is essential for: * **Contract verification**: Verify the authenticity of contract deployments * **Security auditing**: Identify who deployed suspicious contracts * **Attribution tracking**: Link contracts to their deployers * **Factory pattern analysis**: Track contracts created by factory contracts * **Deployment history**: Research when and by whom contracts were deployed * **Trust verification**: Verify contracts were deployed by expected addresses * **Forensic investigation**: Trace the origin of malicious contracts * **Documentation**: Record deployment information for contract registries * **Multi-sig verification**: Confirm contracts deployed by authorized accounts * **Development debugging**: Track contract deployments during testing This method is particularly valuable for security analysis and verifying the provenance of smart contracts in DeFi protocols. # ots_getHeaderByNumber | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-get-header-by-number openapi/hyperliquid_node_api/hyperevm/evm_ots_get_header_by_number.json post /evm The ots_getHeaderByNumber JSON-RPC method retrieves block header information by block number on the Hyperliquid EVM blockchain. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_getHeaderByNumber` JSON-RPC method retrieves block header information by block number on the Hyperliquid EVM blockchain. This Otterscan-specific method provides a lightweight way to fetch block headers without retrieving full block data, making it efficient for block explorer operations. ## Parameters 1. **block number** (integer, required): The block number to retrieve the header for ## Response The method returns a block header object with the following fields: ### Response structure * `number` — the block number * `hash` — the block hash * `parentHash` — the parent block hash * `nonce` — proof-of-work nonce (always 0x0000000000000000 for PoS) * `sha3Uncles` — SHA3 hash of the uncles data * `logsBloom` — bloom filter for the logs * `transactionsRoot` — root of the transaction trie * `stateRoot` — root of the final state trie * `receiptsRoot` — root of the receipts trie * `miner` — address of the beneficiary * `difficulty` — difficulty for this block * `totalDifficulty` — total difficulty of the chain until this block * `extraData` — extra data field of this block * `size` — size of this block in bytes * `gasLimit` — maximum gas allowed in this block * `gasUsed` — total gas used by all transactions * `timestamp` — Unix timestamp of the block * `mixHash` — mix hash for the block * `baseFeePerGas` — base fee per gas (post-EIP-1559) ## Usage example `ots_getHeaderByNumber` is an Otterscan-namespace method, so the EVM libraries below call it through their raw JSON-RPC request interface rather than a named helper. ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_getHeaderByNumber", "params": [1000], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # ots_getHeaderByNumber has no named web3.py helper, so call it via the raw provider request. response = w3.provider.make_request("ots_getHeaderByNumber", [1000]) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // ots_getHeaderByNumber has no named ethers.js helper, so use the generic send method. const header = await provider.send("ots_getHeaderByNumber", [1000]); console.log(header); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // ots_getHeaderByNumber is not a built-in viem action, so call it through client.request. const header = await client.request({ method: "ots_getHeaderByNumber", params: [1000], } as any); console.log(header); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": { "number": "0x3e8", "hash": "0xe8399bf41516f5ca663c79bc30db02b4989385c2f3a96f42958dea5753cbf4a9", "parentHash": "0x7c8f6a0e4d3b2a1c9e5f8b7d4a6c3e9f2b8d5a7c1e4f9b3a6d8c2e5f7a9b4c6d", "nonce": "0x0000000000000000", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "stateRoot": "0x9f3c6b0d8e5a2d1f7c9e4b3a8d6c5e2f9a7b4c8d1e6f3a5b7d9c2e4f6a8b3c5d", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "miner": "0x0000000000000000000000000000000000000000", "difficulty": "0x0", "totalDifficulty": "0x0", "extraData": "0x", "size": "0x220", "gasLimit": "0x1c9c380", "gasUsed": "0x0", "timestamp": "0x6734e8d0", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "baseFeePerGas": "0x7" } } ``` ## Use cases The `ots_getHeaderByNumber` method is essential for: * **Block explorers**: Efficiently fetch block header information without full block data * **Light clients**: Verify blockchain state with minimal data requirements * **Node detection**: Identify if a node supports Otterscan APIs * **Block monitoring**: Track new blocks with reduced bandwidth usage * **Chain analysis**: Analyze block production patterns and timing * **Header verification**: Validate block headers for security checks * **Timestamp tracking**: Monitor block timestamps for time-based operations * **Gas analysis**: Track gas usage and limits across blocks * **Fork detection**: Monitor chain reorganizations via parent hash tracking * **Performance optimization**: Reduce data transfer when only header data is needed This method is part of the Otterscan API suite, providing optimized access patterns for blockchain exploration tools. # ots_getInternalOperations | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-get-internal-operations openapi/hyperliquid_node_api/hyperevm/evm_ots_get_internal_operations.json post /evm Reference docs for the ots_getInternalOperations JSON-RPC method on the Hyperliquid EVM blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_getInternalOperations` JSON-RPC method retrieves all internal ETH transfers and operations that occurred within a transaction on the Hyperliquid EVM blockchain. This Otterscan-specific method reveals value transfers that happen inside smart contract execution, which are not visible in standard transaction receipts. ## Parameters 1. **transaction hash** (string, required): The hash of the transaction to analyze ## Response The method returns an array of internal operation objects representing all value transfers within the transaction. ### Response structure Each internal operation contains: * `type` — the operation type (0: transfer, 1: self-destruct, 2: create, 3: create2) * `from` — the address sending the value * `to` — the address receiving the value * `value` — the amount of ETH transferred in wei (hex) ## Usage example ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_getInternalOperations", "params": ["0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381"], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) tx_hash = "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381" # ots_getInternalOperations is an Otterscan method, so call it via the raw JSON-RPC provider response = w3.provider.make_request("ots_getInternalOperations", [tx_hash]) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const txHash = "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381"; // ots_getInternalOperations is an Otterscan method, so send a raw JSON-RPC request const operations = await provider.send("ots_getInternalOperations", [txHash]); console.log(operations); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const txHash = "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381"; // ots_getInternalOperations is an Otterscan method, so use the raw request method const operations = await client.request({ method: "ots_getInternalOperations" as any, params: [txHash] as any, }); console.log(operations); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": [ { "type": 0, "from": "0x5555555555555555555555555555555555555555", "to": "0x6666666666666666666666666666666666666666", "value": "0xde0b6b3a7640000" }, { "type": 0, "from": "0x6666666666666666666666666666666666666666", "to": "0x7777777777777777777777777777777777777777", "value": "0x6f05b59d3b20000" } ] } ``` ### Operation types The `type` field indicates the operation: * `0` — Regular ETH transfer * `1` — Self-destruct (contract deletion with balance transfer) * `2` — Contract creation via CREATE opcode * `3` — Contract creation via CREATE2 opcode ## Use cases The `ots_getInternalOperations` method is essential for: * **Value flow analysis**: Track how ETH moves through complex contract interactions * **DeFi debugging**: Understand token swaps and liquidity operations * **Security auditing**: Detect unexpected value transfers in contracts * **Payment splitting**: Analyze how contracts distribute payments * **Fee tracking**: Monitor protocol fees and revenue sharing * **MEV analysis**: Study arbitrage and sandwich attack patterns * **Wallet tracking**: Follow funds through mixer and privacy protocols * **Tax reporting**: Identify all value transfers for accounting * **Contract verification**: Ensure contracts handle ETH as expected * **Forensic analysis**: Investigate exploits and fund movements This method is particularly valuable for understanding complex DeFi transactions where multiple contracts interact and transfer value in ways not visible through standard transaction data. # ots_getTransactionBySenderAndNonce | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-get-transaction-by-sender-and-nonce openapi/hyperliquid_node_api/hyperevm/evm_ots_get_transaction_by_sender_and_nonce.json post /evm Reference docs for the ots_getTransactionBySenderAndNonce JSON-RPC method on the Hyperliquid EVM blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_getTransactionBySenderAndNonce` JSON-RPC method retrieves a transaction hash by specifying the sender address and nonce on the Hyperliquid EVM blockchain. This Otterscan-specific method helps locate transactions when you know the sender and nonce but not the transaction hash. ## Parameters 1. **sender address** (string, required): The address that sent the transaction 2. **nonce** (integer, required): The nonce value used for the transaction ## Response The method returns the transaction hash if found, or null if no transaction exists with the specified sender and nonce. ### Response structure * `result` — the transaction hash (32-byte hex string), or `null` if not found ## Usage example ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_getTransactionBySenderAndNonce", "params": ["0x5555555555555555555555555555555555555555", 0], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 # ots_getTransactionBySenderAndNonce is an Otterscan namespace method, so # call it through the provider directly instead of a typed web3.py binding. w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) response = w3.provider.make_request( "ots_getTransactionBySenderAndNonce", ["0x5555555555555555555555555555555555555555", 0], ) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // ots_getTransactionBySenderAndNonce is an Otterscan namespace method, so // send the raw JSON-RPC request through the provider. const txHash = await provider.send("ots_getTransactionBySenderAndNonce", [ "0x5555555555555555555555555555555555555555", 0, ]); console.log(txHash); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // ots_getTransactionBySenderAndNonce is an Otterscan namespace method, so // issue the raw JSON-RPC request through the client. const txHash = await client.request({ method: "ots_getTransactionBySenderAndNonce", params: ["0x5555555555555555555555555555555555555555", 0], }); console.log(txHash); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response (transaction found) ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381" } ``` ### Example response (not found) ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": null } ``` ### Finding multiple transactions To find a sequence of transactions from an address: ```javascript theme={"system"} // Find transactions with nonces 0 through 5 for (let nonce = 0; nonce <= 5; nonce++) { const params = ["0x5555555555555555555555555555555555555555", nonce]; // Make RPC call with params } ``` ## Use cases The `ots_getTransactionBySenderAndNonce` method is essential for: * **Transaction recovery**: Find lost transaction hashes using sender and nonce * **Wallet debugging**: Track down specific transactions from an account * **Nonce management**: Verify which nonces have been used by an address * **Transaction replacement**: Find original transactions before replacement * **Account auditing**: Systematically review all transactions from an address * **Support tools**: Help users find their transactions without the hash * **Forensic analysis**: Track transaction sequences from specific accounts * **Testing verification**: Confirm test transactions were submitted * **MEV analysis**: Study transaction ordering and nonce patterns * **Integration recovery**: Recover from lost transaction tracking in dApps This method is particularly useful when debugging wallet issues or when transaction hashes weren't properly stored by an application. # ots_getTransactionError | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-get-transaction-error openapi/hyperliquid_node_api/hyperevm/evm_ots_get_transaction_error.json post /evm The ots_getTransactionError JSON-RPC method retrieves the raw revert reason for failed transactions on the Hyperliquid EVM blockchain. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_getTransactionError` JSON-RPC method retrieves the raw revert reason for failed transactions on the Hyperliquid EVM blockchain. This Otterscan-specific method provides detailed error messages from smart contract failures, helping developers debug transaction issues. ## Parameters 1. **transaction hash** (string, required): The hash of the failed transaction to analyze ## Response The method returns the raw error data from the failed transaction, or null if the transaction succeeded or error data is unavailable. ### Response structure * `result` — hex-encoded error message from the revert reason, or `null` if not available ## Usage example `ots_getTransactionError` is an Otterscan-specific method, so the EVM libraries below call it through their generic JSON-RPC request interfaces rather than a dedicated helper. ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_getTransactionError", "params": ["0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381"], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) tx_hash = "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381" response = w3.provider.make_request("ots_getTransactionError", [tx_hash]) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const txHash = "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381"; const result = await provider.send("ots_getTransactionError", [txHash]); console.log(result); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const txHash = "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381"; const result = await client.request({ method: "ots_getTransactionError", params: [txHash], }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response (with error) ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001e496e73756666696369656e742062616c616e636520666f72207472616e736665720000000000000000000000000000000000000000000000000000000000" } ``` ### Example response (no error) ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": null } ``` ### Decoding error messages The returned hex data typically contains an ABI-encoded error message. Common patterns: * `0x08c379a0...` — Standard revert with string message * Custom error selectors — Contract-specific error codes To decode the message, remove the function selector (first 4 bytes) and ABI-decode the remaining data as a string. ## Use cases The `ots_getTransactionError` method is essential for: * **Smart contract debugging**: Understand why transactions failed * **User experience**: Display meaningful error messages to users * **Testing automation**: Validate expected failure conditions * **Error monitoring**: Track and categorize contract failures * **Support tools**: Help users understand transaction failures * **Development workflow**: Debug contract interactions during development * **Audit trails**: Document failure reasons for compliance * **Gas optimization**: Identify operations that consistently fail * **Integration testing**: Verify error handling in dApps * **Customer support**: Quickly diagnose user transaction issues This method is particularly valuable during smart contract development and when providing support for dApp users experiencing transaction failures. # ots_hasCode | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-has-code openapi/hyperliquid_node_api/hyperevm/evm_ots_has_code.json post /evm The ots_hasCode JSON-RPC method checks whether a specified address contains deployed contract code on the Hyperliquid EVM blockchain. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_hasCode` JSON-RPC method checks whether a specified address contains deployed contract code on the Hyperliquid EVM blockchain. This Otterscan-specific method provides a quick way to determine if an address is a smart contract or an externally owned account (EOA). ## Parameters 1. **address** (string, required): The address to check for deployed code 2. **block identifier** (string, optional): The block at which to check the code * Can be a block number, block hash, or one of: `"earliest"`, `"latest"`, `"pending"` * Defaults to `"latest"` ## Response The method returns a boolean value indicating whether the address contains code. ### Response structure * `true` — the address contains deployed contract code * `false` — the address is an EOA or does not exist ## Usage example ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_hasCode", "params": ["0x5555555555555555555555555555555555555555", "latest"], "id": 1 }' ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # ots_hasCode is an Otterscan namespace method, so call it as a raw JSON-RPC request. response = w3.provider.make_request( "ots_hasCode", ["0x5555555555555555555555555555555555555555", "latest"], ) print(response["result"]) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // ots_hasCode is an Otterscan namespace method, so send it as a raw JSON-RPC request. const hasCode = await provider.send("ots_hasCode", [ "0x5555555555555555555555555555555555555555", "latest", ]); console.log(hasCode); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // ots_hasCode is an Otterscan namespace method, so issue it through the low-level request method. const hasCode = await client.request({ method: "ots_hasCode" as any, params: ["0x5555555555555555555555555555555555555555", "latest"] as any, }); console.log(hasCode); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": true } ``` ### Check at specific block ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_hasCode", "params": ["0x5555555555555555555555555555555555555555", "0x1000"], "id": 1 }' ``` ## Use cases The `ots_hasCode` method is essential for: * **Address classification**: Quickly determine if an address is a contract or EOA * **Transaction validation**: Check if transaction targets are contracts before sending * **Security checks**: Verify addresses before interacting with them * **UI differentiation**: Display different interfaces for contracts vs EOAs * **Gas estimation**: Optimize gas estimates based on target address type * **Contract verification**: Confirm successful contract deployment * **Historical analysis**: Check when contracts were deployed by testing different blocks * **Wallet integration**: Warn users when sending to contracts vs regular addresses * **Smart contract interaction**: Pre-validate addresses before calling contract methods * **Block explorer optimization**: Efficiently categorize addresses without fetching full code This method provides a lightweight alternative to `eth_getCode` when you only need to know if code exists, not the actual bytecode itself. # ots_traceTransaction | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-ots-trace-transaction openapi/hyperliquid_node_api/hyperevm/evm_ots_trace_transaction.json post /evm The ots_traceTransaction JSON-RPC method returns the complete execution trace of a transaction on the Hyperliquid EVM blockchain. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `ots_traceTransaction` JSON-RPC method returns the complete execution trace of a transaction on the Hyperliquid EVM blockchain. This Otterscan-specific method extracts all variations of calls, contract creations, and self-destructs, presenting them as a detailed call tree that reveals the full execution flow. ## Parameters 1. **transaction hash** (string, required): The hash of the transaction to trace ## Response The method returns an array of trace entries representing the complete execution tree, or null if the transaction doesn't exist. ### Response structure Each trace entry contains: * `type` — the type of operation (`CALL`, `CREATE`, `CREATE2`, `SELFDESTRUCT`) * `depth` — the depth in the call stack * `from` — the address initiating the operation * `to` — the target address (for calls) or created contract address * `value` — the amount of ETH transferred (in wei, hex) * `input` — the input data for the operation * `output` — the output data from the operation (optional) ## Usage example ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "ots_traceTransaction", "params": ["0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381"], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) tx_hash = "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381" # ots_traceTransaction has no typed helper in web3.py, so call it directly. trace = w3.manager.request_blocking("ots_traceTransaction", [tx_hash]) print(trace) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const txHash = "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381"; // ots_traceTransaction has no typed helper in ethers, so use the generic send. const trace = await provider.send("ots_traceTransaction", [txHash]); console.log(trace); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const txHash = "0xf94f3d2ed5b59aefb6a0e566af8e86552014d84f6ed2f38a1366dedffe723381"; // ots_traceTransaction has no typed action in viem, so use the EIP-1193 request. const trace = await client.request({ method: "ots_traceTransaction" as any, params: [txHash] as any, }); console.log(trace); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ### Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": [ { "type": "CALL", "depth": 0, "from": "0x1234567890abcdef1234567890abcdef12345678", "to": "0x5555555555555555555555555555555555555555", "value": "0x0", "input": "0xa9059cbb0000000000000000000000006666666666666666666666666666666666666666000000000000000000000000000000000000000000000000000000000000000a" }, { "type": "CALL", "depth": 1, "from": "0x5555555555555555555555555555555555555555", "to": "0x7777777777777777777777777777777777777777", "value": "0x0", "input": "0x70a08231000000000000000000000000000000005555555555555555555555555555555555", "output": "0x0000000000000000000000000000000000000000000000000000000000000064" } ] } ``` ### Trace types * `CALL` — Regular function call to a contract * `DELEGATECALL` — Delegate call preserving msg.sender * `STATICCALL` — Read-only call that cannot modify state * `CREATE` — Contract deployment using CREATE opcode * `CREATE2` — Contract deployment using CREATE2 opcode * `SELFDESTRUCT` — Contract self-destruction ## Use cases The `ots_traceTransaction` method is essential for: * **Transaction debugging**: Understand the complete execution flow of complex transactions * **DeFi analysis**: Trace token swaps through multiple DEX routers * **Security auditing**: Identify unexpected contract interactions * **Gas optimization**: Find expensive operations in the call tree * **MEV analysis**: Study arbitrage bot strategies and execution paths * **Contract testing**: Verify expected call patterns in integration tests * **Forensic investigation**: Analyze exploit transactions step by step * **Performance profiling**: Identify bottlenecks in contract interactions * **Educational tools**: Visualize how smart contracts interact * **Block explorers**: Display detailed transaction execution trees This method provides the most comprehensive view of transaction execution, making it invaluable for debugging complex smart contract interactions and understanding DeFi protocol operations. # trace_block | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-trace-block openapi/hyperliquid_node_api/hyperevm/evm_trace_block.json post /evm The trace_block JSON-RPC method returns trace information for all transactions in a specific block. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `trace_block` JSON-RPC method returns trace information for all transactions in a specific block. This method provides execution traces for all transactions within a block using OpenEthereum-style tracing, making it essential for comprehensive block analysis and monitoring. ## Parameters 1. **Block identifier** (string, required): Block number, hash, or "latest"/"earliest"/"pending" ## Response The method returns an array of trace objects for all transactions in the specified block. ### Response structure **Block traces:** * Array of trace objects, one for each transaction in the block * Each trace contains execution details, gas usage, and call hierarchy ## Usage example ### Basic implementation ```javascript theme={"system"} // Get traces for all transactions in a block const traceBlock = async (blockIdentifier) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'trace_block', params: [blockIdentifier], id: 1 }) }); const data = await response.json(); return data.result; }; // Analyze block execution const analyzeBlockExecution = async (blockIdentifier = 'latest') => { const traces = await traceBlock(blockIdentifier); console.log(`Block analysis for ${blockIdentifier}:`); console.log(`Total transactions: ${traces.length}`); let totalGasUsed = 0; let successfulTxs = 0; traces.forEach((trace, index) => { const gasUsed = parseInt(trace.result.gasUsed, 16); totalGasUsed += gasUsed; if (trace.result.output && trace.result.output !== '0x') { successfulTxs++; } console.log(` TX ${index + 1}: ${gasUsed} gas, ${trace.action.from} -> ${trace.action.to}`); }); console.log(`Successful transactions: ${successfulTxs}/${traces.length}`); console.log(`Total gas used: ${totalGasUsed.toLocaleString()}`); console.log(`Average gas per transaction: ${Math.round(totalGasUsed / traces.length).toLocaleString()}`); return traces; }; // Usage analyzeBlockExecution('latest').then(traces => { console.log('Block analysis completed'); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_block", "params": [ "latest" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # trace_block is an OpenEthereum-style method, so call it through the raw provider response = w3.provider.make_request("trace_block", ["latest"]) traces = response["result"] print(f"Traces in block: {len(traces)}") ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // send() reaches the OpenEthereum-style trace_block method directly const traces = await provider.send("trace_block", ["latest"]); console.log(`Traces in block: ${traces.length}`); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // trace_block is outside viem's typed schema, so use the request escape hatch const traces = await (client.request as (args: { method: "trace_block"; params: [string]; }) => Promise)({ method: "trace_block", params: ["latest"], }); console.log(`Traces in block: ${traces.length}`); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `trace_block` method is essential for applications that need to: * **Block monitoring**: Monitor block execution and transaction patterns * **Performance analysis**: Analyze block performance and gas efficiency * **Analytics platforms**: Build comprehensive blockchain analytics tools * **Forensic investigation**: Investigate suspicious blocks and activities * **Compliance monitoring**: Monitor regulatory compliance across blocks * **Development tools**: Build block-level debugging and analysis tools * **Research platforms**: Support blockchain research and analysis * **MEV analysis**: Analyze Maximum Extractable Value opportunities * **Security monitoring**: Monitor for suspicious patterns in block execution * **Network health**: Monitor network health and transaction success rates * **Gas analysis**: Analyze gas usage patterns across blocks * **Protocol monitoring**: Monitor protocol behavior and adoption * **Trading analysis**: Analyze trading patterns and market activity * **DeFi monitoring**: Monitor DeFi protocol activity and usage * **Educational tools**: Create educational content about block execution This method provides comprehensive block-level transaction analysis capabilities on the Hyperliquid EVM platform. # trace_call | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-trace-call openapi/hyperliquid_node_api/hyperevm/evm_trace_call.json post /evm The trace_call JSON-RPC method executes a call and returns trace information using OpenEthereum-style tracing. On Hyperliquid EVM. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `trace_call` JSON-RPC method executes a call and returns trace information using OpenEthereum-style tracing. This method simulates a transaction and provides detailed execution traces without committing changes to the blockchain, making it ideal for transaction analysis, debugging, and testing before actual execution. ## Parameters 1. **Call object** (object, required): Transaction call details 2. **Trace types** (array, required): Array of trace types to include 3. **Block parameter** (string, required): Block number, hash, or "latest"/"earliest"/"pending" ### Call object structure * `from` (string, optional): Address the transaction is sent from * `to` (string, required): Address the transaction is directed to * `gas` (string, optional): Gas provided for transaction execution * `gasPrice` (string, optional): Gas price for the transaction * `value` (string, optional): Value sent with the transaction * `data` (string, optional): Hash of the method signature and encoded parameters ### Trace types * `"trace"`: Basic execution trace information * `"vmTrace"`: Virtual machine execution trace * `"stateDiff"`: State differences caused by the transaction ## Response The method returns an array of trace objects containing detailed execution information. ### Response structure **Trace objects:** * `action` — Details about the call action (from, to, value, gas, input, callType) * `result` — Execution result (gasUsed, output) * `traceAddress` — Address path within the call hierarchy * `type` — Type of trace (call, create, suicide, etc.) ## Usage example ### Basic implementation ```javascript theme={"system"} // Execute a traced call const traceCall = async (callObject, traceTypes = ['trace'], blockParam = 'latest') => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'trace_call', params: [callObject, traceTypes, blockParam], id: 1 }) }); const data = await response.json(); return data.result; }; // Analyze contract interaction const analyzeContractCall = async () => { const callObject = { from: '0x69835D480110e4919B7899f465aAB101e21c8A87', to: '0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5', gas: '0x76c0', gasPrice: '0x9184e72a000', value: '0xde0b6b3a7640000', data: '0x' }; const traces = await traceCall(callObject); console.log('Execution Trace Analysis:'); traces.forEach((trace, index) => { console.log(`Trace ${index + 1}:`); console.log(` Type: ${trace.type}`); console.log(` From: ${trace.action.from}`); console.log(` To: ${trace.action.to}`); console.log(` Gas Used: ${parseInt(trace.result.gasUsed, 16)}`); console.log(` Call Type: ${trace.action.callType}`); }); return traces; }; // Usage analyzeContractCall().then(traces => { console.log('Analysis completed'); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_call", "params": [ { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", "gas": "0x76c0", "gasPrice": "0x9184e72a000", "value": "0xde0b6b3a7640000", "data": "0x" }, ["trace"], "latest" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) call_object = { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", "gas": "0x76c0", "gasPrice": "0x9184e72a000", "value": "0xde0b6b3a7640000", "data": "0x", } # trace_call is OpenEthereum-style tracing, not a core eth_* method, # so call it through the provider's raw JSON-RPC interface. response = w3.provider.make_request( "trace_call", [call_object, ["trace"], "latest"] ) print(response["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const callObject = { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", gas: "0x76c0", gasPrice: "0x9184e72a000", value: "0xde0b6b3a7640000", data: "0x", }; // trace_call is OpenEthereum-style tracing, not a core eth_* method, // so send it as a raw JSON-RPC request. const trace = await provider.send("trace_call", [callObject, ["trace"], "latest"]); console.log(trace); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const callObject = { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", gas: "0x76c0", gasPrice: "0x9184e72a000", value: "0xde0b6b3a7640000", data: "0x", } as const; // trace_call is OpenEthereum-style tracing, not a core eth_* method, // so reach it through the client's low-level request method. const trace = await client.request({ method: "trace_call" as any, params: [callObject, ["trace"], "latest"] as any, }); console.log(trace); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `trace_call` method is essential for applications that need to: * **Transaction simulation**: Simulate transactions before execution to predict outcomes * **Gas estimation**: Get accurate gas estimates for complex contract interactions * **Debugging tools**: Debug smart contract execution and identify issues * **Security analysis**: Analyze potential security vulnerabilities in contract calls * **DeFi testing**: Test complex DeFi operations before execution * **MEV analysis**: Analyze Maximum Extractable Value opportunities * **Development workflows**: Integrate simulation into development pipelines * **User interfaces**: Provide transaction previews in user applications * **Risk assessment**: Assess risks before executing high-value transactions * **Compliance checking**: Verify regulatory compliance before transaction execution * **Educational tools**: Create educational content about transaction execution * **Research platforms**: Support blockchain research and analysis * **Performance optimization**: Optimize contract interactions for better efficiency * **Integration testing**: Test smart contract integrations thoroughly * **Protocol development**: Develop and test new blockchain protocols This method provides comprehensive transaction simulation capabilities with detailed execution traces, enabling thorough analysis and testing on the Hyperliquid EVM platform. # trace_callMany | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-trace-call-many openapi/hyperliquid_node_api/hyperevm/evm_trace_call_many.json post /evm The trace_callMany JSON-RPC method executes multiple calls and returns trace information for each call. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `trace_callMany` JSON-RPC method executes multiple calls and returns trace information for each call. This method allows batch simulation of multiple transactions with detailed execution traces, making it ideal for complex scenario testing, batch analysis, and multi-step transaction planning. ## Parameters 1. **Call/trace pairs array** (array, required): Array of \[call\_object, trace\_types] pairs 2. **Block parameter** (string, required): Block number, hash, or "latest"/"earliest"/"pending" ### Call/trace pair structure Each element in the array contains: * Call object with transaction details (from, to, gas, gasPrice, value, data) * Array of trace types (e.g., \["trace"]) ## Response The method returns an array of trace results, one for each call in the input array. ### Response structure **Trace results array:** * Array of trace result arrays, one per input call * Each trace result contains execution details, gas usage, and call hierarchy ## Usage example ### Basic implementation ```javascript theme={"system"} // Execute multiple traced calls const traceCallMany = async (callTracePairs, blockParam = 'latest') => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'trace_callMany', params: [callTracePairs, blockParam], id: 1 }) }); const data = await response.json(); return data.result; }; // Test multiple transaction scenarios const testMultipleScenarios = async () => { const scenarios = [ [ { from: '0x69835D480110e4919B7899f465aAB101e21c8A87', to: '0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5', gas: '0x76c0', gasPrice: '0x9184e72a000', value: '0xde0b6b3a7640000', data: '0x' }, ['trace'] ], [ { from: '0x69835D480110e4919B7899f465aAB101e21c8A87', to: '0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c', gas: '0x5208', gasPrice: '0x9184e72a000', value: '0x1bc16d674ec80000', data: '0x' }, ['trace'] ] ]; const results = await traceCallMany(scenarios); console.log('Multi-call simulation results:'); results.forEach((result, index) => { console.log(`Call ${index + 1}:`); result.forEach(trace => { console.log(` Gas used: ${parseInt(trace.result.gasUsed, 16)}`); console.log(` Success: ${trace.result.output !== '0x'}`); }); }); return results; }; // Usage testMultipleScenarios().then(results => { console.log('Batch simulation completed'); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_callMany", "params": [ [ [ { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", "gas": "0x76c0", "gasPrice": "0x9184e72a000", "value": "0xde0b6b3a7640000", "data": "0x" }, ["trace"] ], [ { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", "gas": "0x5208", "gasPrice": "0x9184e72a000", "value": "0x1bc16d674ec80000", "data": "0x" }, ["trace"] ] ], "latest" ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # trace_callMany is a Parity/Erigon-style trace method, so call it via the raw RPC interface call_trace_pairs = [ [ { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", "gas": "0x76c0", "gasPrice": "0x9184e72a000", "value": "0xde0b6b3a7640000", "data": "0x", }, ["trace"], ], [ { "from": "0x69835D480110e4919B7899f465aAB101e21c8A87", "to": "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", "gas": "0x5208", "gasPrice": "0x9184e72a000", "value": "0x1bc16d674ec80000", "data": "0x", }, ["trace"], ], ] result = w3.manager.request_blocking("trace_callMany", [call_trace_pairs, "latest"]) print(result) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // trace_callMany is a Parity/Erigon-style trace method, so call it via the generic send const callTracePairs = [ [ { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", gas: "0x76c0", gasPrice: "0x9184e72a000", value: "0xde0b6b3a7640000", data: "0x", }, ["trace"], ], [ { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", gas: "0x5208", gasPrice: "0x9184e72a000", value: "0x1bc16d674ec80000", data: "0x", }, ["trace"], ], ]; const result = await provider.send("trace_callMany", [callTracePairs, "latest"]); console.log(result); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // trace_callMany is a Parity/Erigon-style trace method, so call it via the generic request const callTracePairs = [ [ { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xB7C609cFfa0e47DB2467ea03fF3e598bf59361A5", gas: "0x76c0", gasPrice: "0x9184e72a000", value: "0xde0b6b3a7640000", data: "0x", }, ["trace"], ], [ { from: "0x69835D480110e4919B7899f465aAB101e21c8A87", to: "0xb4DcFE4590adBd275aC3Ef1A3dd10e819d11648c", gas: "0x5208", gasPrice: "0x9184e72a000", value: "0x1bc16d674ec80000", data: "0x", }, ["trace"], ], ]; const result = await client.request({ method: "trace_callMany", params: [callTracePairs, "latest"], }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `trace_callMany` method is essential for applications that need to: * **Batch simulation**: Simulate multiple transactions in a single request * **Multi-step transaction planning**: Plan complex multi-transaction operations * **Performance comparison**: Compare gas usage across different transaction approaches * **Scenario testing**: Test multiple scenarios efficiently in batch * **DeFi strategy testing**: Test complex DeFi strategies with multiple steps * **Arbitrage analysis**: Analyze multi-step arbitrage opportunities * **MEV research**: Research Maximum Extractable Value across multiple transactions * **Protocol testing**: Test protocol interactions across multiple calls * **Integration testing**: Test smart contract integrations comprehensively * **Risk assessment**: Assess risks across multiple related transactions * **Development workflows**: Integrate batch simulation into development pipelines * **Educational tools**: Create educational content about complex transaction flows * **Research platforms**: Support blockchain research requiring multiple simulations * **Trading strategy testing**: Test complex trading strategies before execution * **Compliance verification**: Verify regulatory compliance across transaction batches This method provides efficient batch transaction simulation capabilities, enabling comprehensive multi-call analysis on the Hyperliquid EVM platform. # trace_filter | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-trace-filter openapi/hyperliquid_node_api/hyperevm/evm_trace_filter.json post /evm The trace_filter JSON-RPC method returns traces matching specified filter criteria. trace_filter on Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `trace_filter` JSON-RPC method returns traces matching specified filter criteria. This method allows filtering traces by block range, addresses, and other criteria to find specific transaction patterns, making it essential for blockchain analysis, forensic investigations, and monitoring specific address activities. ## Parameters 1. **Filter object** (object, required): Criteria for filtering traces ### Filter object structure * `fromBlock` (string, optional): Starting block for the search (default: "earliest") * `toBlock` (string, optional): Ending block for the search (default: "latest") * `fromAddress` (array, optional): Array of addresses to filter by sender * `toAddress` (array, optional): Array of addresses to filter by recipient * `count` (integer, optional): Maximum number of traces to return ## Response The method returns an array of trace objects matching the specified filter criteria. ### Response structure **Trace objects:** * `action` — Details about the call action (from, to, value, gas, input, callType) * `result` — Execution result (gasUsed, output) * `traceAddress` — Address path within the call hierarchy * `type` — Type of trace (call, create, suicide, etc.) * `blockNumber` — Block number containing the transaction * `transactionHash` — Hash of the transaction ## Usage example ### Basic implementation ```javascript theme={"system"} // Filter traces by criteria const filterTraces = async (filterObject) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'trace_filter', params: [filterObject], id: 1 }) }); const data = await response.json(); return data.result; }; // Monitor specific address activity const monitorAddressActivity = async (address, blockRange = 1000) => { const currentBlock = await getCurrentBlockNumber(); // Assume this function exists const filter = { fromBlock: `0x${(currentBlock - blockRange).toString(16)}`, toBlock: 'latest', fromAddress: [address], toAddress: [address], count: 100 }; const traces = await filterTraces(filter); console.log(`Activity for ${address}:`); console.log(`Found ${traces.length} traces in last ${blockRange} blocks`); // Analyze activity patterns const activity = { outgoing: traces.filter(t => t.action.from.toLowerCase() === address.toLowerCase()), incoming: traces.filter(t => t.action.to.toLowerCase() === address.toLowerCase()), totalGasUsed: traces.reduce((sum, t) => sum + parseInt(t.result.gasUsed, 16), 0), uniqueInteractions: new Set() }; traces.forEach(trace => { if (trace.action.from.toLowerCase() === address.toLowerCase()) { activity.uniqueInteractions.add(trace.action.to); } else { activity.uniqueInteractions.add(trace.action.from); } }); console.log(` Outgoing transactions: ${activity.outgoing.length}`); console.log(` Incoming transactions: ${activity.incoming.length}`); console.log(` Total gas used: ${activity.totalGasUsed.toLocaleString()}`); console.log(` Unique interactions: ${activity.uniqueInteractions.size}`); return { traces, activity }; }; // Find high-value transactions const findHighValueTransactions = async (minValue, maxResults = 50) => { const filter = { fromBlock: 'latest', toBlock: 'latest', count: maxResults }; const traces = await filterTraces(filter); const highValueTraces = traces .filter(trace => { const value = parseInt(trace.action.value, 16); return value >= minValue; }) .sort((a, b) => parseInt(b.action.value, 16) - parseInt(a.action.value, 16)); console.log(`High-value transactions (>= ${minValue} wei):`); highValueTraces.forEach((trace, index) => { const value = parseInt(trace.action.value, 16); console.log(` ${index + 1}. ${value.toLocaleString()} wei: ${trace.action.from} -> ${trace.action.to}`); console.log(` TX: ${trace.transactionHash}`); }); return highValueTraces; }; // Analyze contract deployment patterns const analyzeContractDeployments = async (blockRange = 1000) => { const currentBlock = await getCurrentBlockNumber(); const filter = { fromBlock: `0x${(currentBlock - blockRange).toString(16)}`, toBlock: 'latest', count: 200 }; const traces = await filterTraces(filter); const deployments = traces.filter(trace => trace.type === 'create' || trace.type === 'create2' ); console.log(`Contract Deployments in last ${blockRange} blocks:`); console.log(`Total deployments: ${deployments.length}`); const deploymentAnalysis = { byDeployer: new Map(), totalGasUsed: 0, averageGasUsed: 0 }; deployments.forEach(deployment => { const deployer = deployment.action.from; const gasUsed = parseInt(deployment.result.gasUsed, 16); deploymentAnalysis.byDeployer.set( deployer, (deploymentAnalysis.byDeployer.get(deployer) || 0) + 1 ); deploymentAnalysis.totalGasUsed += gasUsed; }); deploymentAnalysis.averageGasUsed = deploymentAnalysis.totalGasUsed / deployments.length; console.log(` Total gas used: ${deploymentAnalysis.totalGasUsed.toLocaleString()}`); console.log(` Average gas per deployment: ${Math.round(deploymentAnalysis.averageGasUsed).toLocaleString()}`); return { deployments, analysis: deploymentAnalysis }; }; // Usage examples const address = '0x69835D480110e4919B7899f465aAB101e21c8A87'; monitorAddressActivity(address, 1000); const minValue = 1e18; // 1 ETH in wei findHighValueTransactions(minValue, 20); analyzeContractDeployments(500); ``` ## Example request ```shell Shell theme={"system"} curl -X POST https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_filter", "params": [ { "fromBlock": "0xb92f20", "toBlock": "0xb92f6d", "toAddress": ["0x5555555555555555555555555555555555555555"], "count": 5 } ], "id": 1 }' ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) trace_filter = { "fromBlock": "0xb92f20", "toBlock": "0xb92f6d", "toAddress": ["0x5555555555555555555555555555555555555555"], "count": 5, } traces = w3.tracing.trace_filter(trace_filter) print(traces) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const filter = { fromBlock: "0xb92f20", toBlock: "0xb92f6d", toAddress: ["0x5555555555555555555555555555555555555555"], count: 5, }; const traces = await provider.send("trace_filter", [filter]); console.log(traces); ``` ```typescript viem theme={"system"} import { createPublicClient, http, rpcSchema, type Address, type Hex } from "viem"; type TraceFilterSchema = [ { Method: "trace_filter"; Parameters: [ { fromBlock?: Hex; toBlock?: Hex; fromAddress?: Address[]; toAddress?: Address[]; count?: number; }, ]; ReturnType: unknown[]; }, ]; const client = createPublicClient({ rpcSchema: rpcSchema(), transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const traces = await client.request({ method: "trace_filter", params: [ { fromBlock: "0xb92f20", toBlock: "0xb92f6d", toAddress: ["0x5555555555555555555555555555555555555555"], count: 5, }, ], }); console.log(traces); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `trace_filter` method is essential for applications that need to: * **Address monitoring**: Monitor specific addresses for activity and transactions * **Forensic analysis**: Investigate suspicious transactions and trace fund flows * **Compliance tracking**: Track regulatory compliance and generate audit reports * **Analytics platforms**: Build comprehensive blockchain analytics and reporting tools * **Security monitoring**: Monitor for suspicious patterns and potential attacks * **DeFi analysis**: Analyze DeFi protocol interactions and user behaviors * **MEV detection**: Detect Maximum Extractable Value extraction patterns * **Arbitrage monitoring**: Monitor arbitrage opportunities and execution patterns * **Protocol research**: Research blockchain protocol usage and adoption patterns * **Risk assessment**: Assess risks associated with specific addresses or contracts * **Regulatory reporting**: Generate regulatory compliance reports and audits * **Trading analysis**: Analyze trading patterns and market manipulation * **Wallet tracking**: Track wallet activities across multiple transactions * **Contract usage analysis**: Analyze how smart contracts are being used * **Network health monitoring**: Monitor overall network health and activity patterns * **Academic research**: Support academic blockchain research and analysis This method provides powerful filtering capabilities for blockchain trace data, enabling detailed analysis and monitoring of specific transaction patterns on the Hyperliquid EVM platform. # trace_rawTransaction | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-trace-raw-transaction openapi/hyperliquid_node_api/hyperevm/evm_trace_raw_transaction.json post /evm The trace_rawTransaction JSON-RPC method executes a raw transaction and returns trace information. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `trace_rawTransaction` JSON-RPC method executes a raw transaction and returns trace information. This method simulates the execution of a raw transaction and provides detailed execution traces, making it useful for analyzing pre-signed transactions and testing transaction execution without broadcasting. ## Parameters 1. **Raw transaction data** (string, required): Raw transaction in hexadecimal format 2. **Trace types** (array, required): Array of trace types to include in response ### Trace types * `"trace"`: Basic execution trace information * `"vmTrace"`: Virtual machine execution trace * `"stateDiff"`: State differences caused by the transaction ## Response The method returns an array of trace objects containing detailed execution information. ### Response structure **Trace objects:** * `action` — Details about the call action * `result` — Execution result * `traceAddress` — Address path within call hierarchy * `type` — Type of trace (call, create, suicide, etc.) ## Usage example ### Basic implementation ```javascript theme={"system"} // Execute a raw transaction with tracing const traceRawTransaction = async (rawTx, traceTypes = ['trace']) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'trace_rawTransaction', params: [rawTx, traceTypes], id: 1 }) }); const data = await response.json(); return data.result; }; // Example usage const rawTx = '0xf86d80843b9aca0082520894b7c609cffa0e47db2467ea03ff3e598bf59361a5880de0b6b3a7640000808207f2a02d013b9980176ca8674f77797ac04e928b8de3be92dd452501ec26acbb2b1abca054041f43109eeadbb4d6b712a20dc71e5dbe1225e549644be3de803b55041a89'; traceRawTransaction(rawTx).then(traces => { console.log('Raw transaction trace:'); traces.forEach(trace => { console.log(`Type: ${trace.type}`); console.log(`Gas used: ${parseInt(trace.result.gasUsed, 16)}`); }); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_rawTransaction", "params": [ "0xf86d80843b9aca0082520894b7c609cffa0e47db2467ea03ff3e598bf59361a5880de0b6b3a7640000808207f2a02d013b9980176ca8674f77797ac04e928b8de3be92dd452501ec26acbb2b1abca054041f43109eeadbb4d6b712a20dc71e5dbe1225e549644be3de803b55041a89", ["trace"] ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) raw_tx = "0xf86d80843b9aca0082520894b7c609cffa0e47db2467ea03ff3e598bf59361a5880de0b6b3a7640000808207f2a02d013b9980176ca8674f77797ac04e928b8de3be92dd452501ec26acbb2b1abca054041f43109eeadbb4d6b712a20dc71e5dbe1225e549644be3de803b55041a89" # trace_rawTransaction is a non-standard JSON-RPC method, so call it directly. response = w3.provider.make_request("trace_rawTransaction", [raw_tx, ["trace"]]) print(response["result"]) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const rawTx = "0xf86d80843b9aca0082520894b7c609cffa0e47db2467ea03ff3e598bf59361a5880de0b6b3a7640000808207f2a02d013b9980176ca8674f77797ac04e928b8de3be92dd452501ec26acbb2b1abca054041f43109eeadbb4d6b712a20dc71e5dbe1225e549644be3de803b55041a89"; // trace_rawTransaction is a non-standard JSON-RPC method, so send it directly. const traces = await provider.send("trace_rawTransaction", [rawTx, ["trace"]]); console.log(traces); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const rawTx = "0xf86d80843b9aca0082520894b7c609cffa0e47db2467ea03ff3e598bf59361a5880de0b6b3a7640000808207f2a02d013b9980176ca8674f77797ac04e928b8de3be92dd452501ec26acbb2b1abca054041f43109eeadbb4d6b712a20dc71e5dbe1225e549644be3de803b55041a89"; // trace_rawTransaction is a non-standard JSON-RPC method, so request it directly. const traces = await client.request({ method: "trace_rawTransaction", params: [rawTx, ["trace"]], }); console.log(traces); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `trace_rawTransaction` method is essential for applications that need to: * **Pre-signed transaction analysis**: Analyze transactions before broadcasting * **Offline transaction testing**: Test transaction execution offline * **Transaction debugging**: Debug transaction execution issues * **Security analysis**: Analyze transaction security and potential vulnerabilities * **Gas estimation**: Get accurate gas estimates for raw transactions * **Development tools**: Build transaction analysis and debugging tools * **Wallet applications**: Provide transaction previews in wallet interfaces * **Compliance checking**: Verify regulatory compliance before broadcasting * **Educational tools**: Create educational content about transaction execution * **Research platforms**: Support academic and commercial blockchain research This method provides comprehensive analysis of raw transactions before execution on the Hyperliquid EVM platform. # trace_replayBlockTransactions | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-trace-replay-block-transactions openapi/hyperliquid_node_api/hyperevm/evm_trace_replay_block_transactions.json post /evm The trace_replayBlockTransactions JSON-RPC method replays all transactions in a block and returns trace information for each transaction. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `trace_replayBlockTransactions` JSON-RPC method replays all transactions in a block and returns trace information for each transaction. This method provides detailed execution traces for all transactions in a specific block by replaying their execution, making it essential for comprehensive block analysis. ## Parameters 1. **Block identifier** (string, required): Block number, hash, or "latest"/"earliest"/"pending" 2. **Trace types** (array, required): Array of trace types to include in response ### Trace types * `"trace"`: Basic execution trace information * `"vmTrace"`: Virtual machine execution trace * `"stateDiff"`: State differences caused by transactions ## Response The method returns an array of trace results for each transaction in the block. ### Response structure **Block trace results:** * Array of trace results, one per transaction in the block * Each result contains detailed execution traces for the transaction ## Usage example ### Basic implementation ```javascript theme={"system"} // Replay all transactions in a block with tracing const replayBlockTransactions = async (blockIdentifier, traceTypes = ['trace']) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'trace_replayBlockTransactions', params: [blockIdentifier, traceTypes], id: 1 }) }); const data = await response.json(); return data.result; }; // Example usage replayBlockTransactions('latest').then(results => { console.log(`Replayed ${results.length} transactions`); results.forEach((result, index) => { console.log(`Transaction ${index + 1}:`); if (result.trace) { result.trace.forEach(trace => { console.log(` Gas used: ${parseInt(trace.result.gasUsed, 16)}`); }); } }); }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_replayBlockTransactions", "params": [ "latest", ["trace"] ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python (web3.py) theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) # trace_replayBlockTransactions has no typed wrapper in web3.py — use the generic JSON-RPC call response = w3.provider.make_request( "trace_replayBlockTransactions", ["latest", ["trace"]], ) print(response["result"]) ``` ```javascript JavaScript (ethers.js) theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); // trace_replayBlockTransactions has no typed wrapper in ethers.js — use the generic send method const result = await provider.send("trace_replayBlockTransactions", [ "latest", ["trace"], ]); console.log(result); ``` ```typescript TypeScript (viem) theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); // trace_replayBlockTransactions has no typed action in viem — use the generic request method const result = await client.request({ method: "trace_replayBlockTransactions", params: ["latest", ["trace"]], }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `trace_replayBlockTransactions` method is essential for applications that need to: * **Block analysis**: Analyze complete blocks with detailed execution traces * **Performance monitoring**: Monitor transaction execution performance across blocks * **Analytics platforms**: Build comprehensive blockchain analytics tools * **Forensic investigation**: Investigate suspicious blocks with complete trace data * **Compliance monitoring**: Monitor regulatory compliance across entire blocks * **Development tools**: Build block-level debugging and analysis tools * **Research platforms**: Support detailed blockchain research and analysis * **Security auditing**: Audit block execution for security issues * **MEV analysis**: Analyze Maximum Extractable Value opportunities across blocks * **Protocol monitoring**: Monitor protocol behavior across multiple transactions This method provides comprehensive block-level transaction replay capabilities with detailed traces on the Hyperliquid EVM platform. # trace_replayTransaction | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-trace-replay-transaction openapi/hyperliquid_node_api/hyperevm/evm_trace_replay_transaction.json post /evm The trace_replayTransaction JSON-RPC method replays a specific transaction and returns trace information. Hyperliquid EVM via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `trace_replayTransaction` JSON-RPC method replays a specific transaction and returns trace information. This method provides detailed execution traces for a single transaction by replaying its execution, making it essential for transaction debugging, analysis, and forensic investigation. ## Parameters 1. **Transaction hash** (string, required): Hash of the transaction to replay 2. **Trace types** (array, required): Array of trace types to include in response ### Trace types * `"trace"`: Basic execution trace information * `"vmTrace"`: Virtual machine execution trace * `"stateDiff"`: State differences caused by the transaction ## Response The method returns detailed trace information for the replayed transaction. ### Response structure **Transaction trace:** * `trace` — Array of trace objects containing execution details * Each trace contains action details, results, and call hierarchy ## Usage example ### Basic implementation ```javascript theme={"system"} // Replay a specific transaction with tracing const replayTransaction = async (txHash, traceTypes = ['trace']) => { const response = await fetch('https://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', method: 'trace_replayTransaction', params: [txHash, traceTypes], id: 1 }) }); const data = await response.json(); return data.result; }; // Example usage const txHash = '0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31'; replayTransaction(txHash).then(result => { console.log('Transaction replay result:'); if (result.trace) { result.trace.forEach((trace, index) => { console.log(`Trace ${index + 1}:`); console.log(` Type: ${trace.type}`); console.log(` Gas used: ${parseInt(trace.result.gasUsed, 16)}`); }); } }); ``` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_replayTransaction", "params": [ "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31", ["trace"] ], "id": 1 }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) tx_hash = "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31" # trace_replayTransaction is not a typed web3.py method, so call it via the raw provider. result = w3.provider.make_request("trace_replayTransaction", [tx_hash, ["trace"]]) print(result["result"]) ``` ```javascript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const txHash = "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31"; // trace_replayTransaction is not a typed ethers method, so send the raw JSON-RPC request. const result = await provider.send("trace_replayTransaction", [txHash, ["trace"]]); console.log(result); ``` ```typescript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const txHash = "0x07712544ce8f50091c6c3b227921f763b342bf9465a22f0226d651a3246adb31"; // trace_replayTransaction is not a typed viem action, so use the raw request method. const result = await client.request({ method: "trace_replayTransaction", params: [txHash, ["trace"]], }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases The `trace_replayTransaction` method is essential for applications that need to: * **Transaction debugging**: Debug specific transaction execution issues * **Forensic analysis**: Investigate suspicious or failed transactions * **Performance analysis**: Analyze transaction performance and gas usage * **Development tools**: Build transaction debugging and analysis tools * **Security auditing**: Audit specific transactions for security issues * **Educational tools**: Create educational content about transaction execution * **Research platforms**: Support detailed transaction research and analysis * **Compliance investigation**: Investigate transactions for regulatory compliance * **MEV analysis**: Analyze specific transactions for MEV extraction * **Protocol analysis**: Analyze how specific transactions interact with protocols This method provides detailed transaction replay capabilities for comprehensive analysis on the Hyperliquid EVM platform. # web3_clientVersion | Hyperliquid EVM Source: https://docs.chainstack.com/reference/hyperliquid-evm-web3-client-version openapi/hyperliquid_node_api/hyperevm/evm_web3_client_version.json post /evm Returns the current client version for the Hyperliquid EVM. Use this to identify the client implementation and version. This method takes no parameters. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Returns the current client version for the Hyperliquid EVM. Use this to identify the client implementation and version. ## Parameters This method takes no parameters. ## Returns Returns the client version as a string (e.g., "HyperEVM/v1.0.0"). ## cURL example ```bash Shell curl theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm ``` ```python Python web3.py theme={"system"} from web3 import Web3 w3 = Web3(Web3.HTTPProvider("YOUR_CHAINSTACK_ENDPOINT")) client_version = w3.client_version print(client_version) ``` ```javascript JavaScript ethers.js theme={"system"} import { JsonRpcProvider } from "ethers"; const provider = new JsonRpcProvider("YOUR_CHAINSTACK_ENDPOINT"); const clientVersion = await provider.send("web3_clientVersion", []); console.log(clientVersion); ``` ```typescript TypeScript viem theme={"system"} import { createPublicClient, http } from "viem"; const client = createPublicClient({ transport: http("YOUR_CHAINSTACK_ENDPOINT"), }); const clientVersion = await client.request({ method: "web3_clientVersion" }); console.log(clientVersion); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. Example response: ```json theme={"system"} {"jsonrpc":"2.0","id":1,"result":"hyperliquid evm Mainnet"} ``` ## Use cases * **Client identification** — Identify the specific client implementation and version * **Compatibility checking** — Verify compatibility with specific client features * **Debug information** — Gather client information for troubleshooting * **Development tools** — Build tools that adapt to different client implementations # agentEnableDexAbstraction | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-agent-enable-dex-abstraction openapi/hyperliquid_node_api/hypercore_exchange/exchange_agent_enable_dex_abstraction.json post /exchange Enables DEX abstraction on behalf of the user via an approved agent. This is being deprecated in favor of the unified account system. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Enables DEX abstraction on behalf of the user via an approved agent. This is being deprecated in favor of the unified account system. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"agentEnableDexAbstraction"` * `dexAbstraction` (object) — DEX abstraction configuration * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "agentEnableDexAbstraction"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from eth_account import Account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # The wallet must be the approved agent wallet for the user. wallet = Account.from_key("YOUR_AGENT_PRIVATE_KEY") exchange = Exchange(wallet, constants.MAINNET_API_URL) # Signs the action (EIP-712) and posts it. Deprecated in favor of agent_set_abstraction. result = exchange.agent_enable_dex_abstraction() print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // The wallet must be the approved agent wallet for the user. const wallet = privateKeyToAccount("YOUR_AGENT_PRIVATE_KEY"); // viem or ethers const transport = new hl.HttpTransport(); // public mainnet API const client = new hl.ExchangeClient({ transport, wallet }); // Signs the action (EIP-712) and posts it. Deprecated in favor of agentSetAbstraction. const result = await client.agentEnableDexAbstraction(); console.log(result); ``` ## Use case * **Agent enable DEX abstraction** — Enables DEX abstraction on behalf of the user via an approved agent. This is being deprecated in favor of the unified account system. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # agentSetAbstraction | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-agent-set-abstraction openapi/hyperliquid_node_api/hypercore_exchange/exchange_agent_set_abstraction.json post /exchange Sets the account abstraction configuration on behalf of the user via an approved agent. Chainstack Hyperliquid exchange reference. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Sets the account abstraction configuration on behalf of the user via an approved agent. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"agentSetAbstraction"` * `abstraction` (object) — Abstraction configuration * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "agentSetAbstraction"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from eth_account import Account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Agent wallet approved to act on behalf of the user agent = Account.from_key("AGENT_PRIVATE_KEY") exchange = Exchange(agent, constants.MAINNET_API_URL) # abstraction is one of "u" (unifiedAccount), "p" (portfolioMargin), "i" (disabled) result = exchange.agent_set_abstraction("u") print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Agent wallet approved to act on behalf of the user const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); // default public Hyperliquid API const exchange = new ExchangeClient({ transport, wallet }); // abstraction is one of "u" (unifiedAccount), "p" (portfolioMargin), "i" (disabled) const result = await exchange.agentSetAbstraction({ abstraction: "u" }); console.log(result); ``` ## Use case * **Agent set abstraction** — Sets the account abstraction configuration on behalf of the user via an approved agent. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # approveAgent | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-approve-agent openapi/hyperliquid_node_api/hypercore_exchange/exchange_approve_agent.json post /exchange Approves an agent address to trade on behalf of the user. Agents can place and cancel orders but cannot withdraw funds. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Approves an agent address to trade on behalf of the user. Agents can place and cancel orders but cannot withdraw funds. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"approveAgent"` * `hyperliquidChain` (string) — Chain identifier * `agentAddress` (string) — Agent wallet address to approve * `agentName` (string) — Human-readable agent name (optional) * `nonce` (integer) — Unique nonce for the approval * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "approveAgent"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from eth_account.signers.local import LocalAccount from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Your master account wallet (this signs the approval). wallet: LocalAccount = eth_account.Account.from_key("0x_your_private_key") # Public Hyperliquid mainnet API. exchange = Exchange(wallet, constants.MAINNET_API_URL) # approve_agent generates a fresh agent key internally and returns the API # response together with the agent's private key. Pass an optional name to # create a named (persistent) agent. approve_result, agent_key = exchange.approve_agent("myAgent") print(approve_result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Your master account wallet (this signs the approval). const wallet = privateKeyToAccount("0x_your_private_key"); // Public Hyperliquid mainnet API. const transport = new HttpTransport(); const client = new ExchangeClient({ transport, wallet }); const result = await client.approveAgent({ agentAddress: "0x_agent_address", agentName: "myAgent", }); console.log(result); ``` ## Use case * **Approve agent** — Approves an agent address to trade on behalf of the user. Agents can place and cancel orders but cannot withdraw funds. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # approveBuilderFee | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-approve-builder-fee openapi/hyperliquid_node_api/hypercore_exchange/exchange_approve_builder_fee.json post /exchange Approves a builder fee for a specific builder address. This allows the builder to charge fees on orders placed through their interface. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Approves a builder fee for a specific builder address. This allows the builder to charge fees on orders placed through their interface. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"approveBuilderFee"` * `hyperliquidChain` (string) — Chain identifier * `maxFeeRate` (string) — Maximum fee rate the builder can charge * `builder` (string) — Builder address to approve * `nonce` (integer) — Unique nonce * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "approveBuilderFee"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Wallet that signs the EIP-712 action wallet = eth_account.Account.from_key("YOUR_PRIVATE_KEY") # Public Hyperliquid API (this method is not available through Chainstack) exchange = Exchange(wallet, constants.MAINNET_API_URL) result = exchange.approve_builder_fee( builder="0x0000000000000000000000000000000000000000", max_fee_rate="0.01%", ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Wallet that signs the EIP-712 action const wallet = privateKeyToAccount("0x..."); // viem or ethers // Public Hyperliquid API (this method is not available through Chainstack) const transport = new hl.HttpTransport(); const client = new hl.ExchangeClient({ transport, wallet }); const result = await client.approveBuilderFee({ maxFeeRate: "0.01%", builder: "0x0000000000000000000000000000000000000000", }); console.log(result); ``` ## Use case * **Approve builder fee** — Approves a builder fee for a specific builder address. This allows the builder to charge fees on orders placed through their interface. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Batch modify orders | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-batch-modify openapi/hyperliquid_node_api/hypercore_exchange/exchange_batch_modify.json post /exchange Modifies multiple existing orders in a single atomic request on the Hyperliquid exchange. Chainstack Hyperliquid exchange reference. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Modifies multiple existing orders in a single atomic request on the Hyperliquid exchange. This is more efficient than sending individual modify requests for each order. ## Parameters ### Required parameters * `action` (object, required) — The batch modify action object containing: * `type` (string) — Must be `"batchModify"` * `modifies` (array) — Array of modification objects, each containing: * `oid` (number or string) — Order ID to modify, or cloid if using client order ID * `order` (object) — New order parameters: * `a` (number) — Asset index * `b` (boolean) — Is buy order (true for buy/long, false for sell/short) * `p` (string) — New limit price * `s` (string) — New size in units of the base asset * `r` (boolean) — Reduce only order * `t` (object) — Order type specification: * For limit orders: `{"limit": {"tif": "Alo" | "Ioc" | "Gtc"}}` * For trigger orders: `{"trigger": {"isMarket": boolean, "triggerPx": string, "tpsl": "tp" | "sl"}}` * `c` (string, optional) — New client order ID (128-bit hex string) * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected ## Returns Returns an object with batch modification status: * `status` — `"ok"` if request processed * `response` — Contains modification details: * `type` — `"batchModify"` * `data.statuses` — Array of modification results for each order ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "batchModify", "modifies": [ { "oid": 77738308, "order": { "a": 0, "b": true, "p": "51000", "s": "0.02", "r": false, "t": {"limit": {"tif": "Gtc"}} } }, { "oid": 77738309, "order": { "a": 1, "b": false, "p": "3200", "s": "0.5", "r": false, "t": {"limit": {"tif": "Gtc"}} } } ] }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Initialize with your private key (public Hyperliquid API) account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Modify multiple orders at once modifies = [ { "oid": 77738308, "order": { "coin": "BTC", "is_buy": True, "sz": 0.02, "limit_px": 51000, "order_type": {"limit": {"tif": "Gtc"}}, "reduce_only": False, }, }, { "oid": 77738309, "order": { "coin": "ETH", "is_buy": False, "sz": 0.5, "limit_px": 3200, "order_type": {"limit": {"tif": "Gtc"}}, "reduce_only": False, }, }, ] batch_result = exchange.bulk_modify_orders_new(modifies) print(batch_result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Initialize with your private key (public Hyperliquid API) const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Modify multiple orders at once const result = await exchange.batchModify({ modifies: [ { oid: 77738308, order: { a: 0, b: true, p: "51000", s: "0.02", r: false, t: { limit: { tif: "Gtc" } }, }, }, { oid: 77738309, order: { a: 1, b: false, p: "3200", s: "0.5", r: false, t: { limit: { tif: "Gtc" } }, }, }, ], }); console.log(result); ``` ## Batch processing * **Atomic execution** — All modifications are processed together * **Independent validation** — Each modification is validated independently * **Partial success** — Some orders may succeed while others fail * **Efficiency** — Single signature and request for multiple modifications ## Use cases * **Market making** — Update multiple orders across different price levels * **Portfolio management** — Adjust multiple positions simultaneously * **Grid trading** — Modify entire grids of orders efficiently * **Strategy updates** — Bulk update orders when strategy parameters change > [!NOTE] > > Batch modifications are processed sequentially in the order provided. If one modification fails, it doesn't affect the others in the batch. > [!WARNING] > > Each modification in the batch counts toward rate limits. Ensure you don't exceed rate limits when modifying many orders at once. # Borrow lend | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-borrow-lend openapi/hyperliquid_node_api/hypercore_exchange/exchange_borrow_lend.json post /exchange Supply, withdraw, repay, or borrow a token in Hyperliquid's borrow/lend markets. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `borrowLend` action supplies, withdraws, repays, or borrows a token in Hyperliquid's borrow/lend markets. Choose the operation with the `operation` field. ## Parameters ### Required parameters * `action` (object, required) — The borrow/lend action: * `type` (string) — Must be `"borrowLend"`. * `operation` (string) — One of `"supply"`, `"withdraw"`, `"repay"`, or `"borrow"`. * `token` (number) — The token ID. * `amount` (string or null) — The amount to act on; `null` means the full amount. * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ### Optional parameters * `vaultAddress` (string, optional) — Address when acting on behalf of a vault. * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "borrowLend", "operation": "supply", "token": 0, "amount": "1"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a borrowLend helper # yet, so build and post the signed action directly. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = {"type": "borrowLend", "operation": "supply", "token": 0, "amount": "1"} signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.borrowLend({ operation: "supply", // "supply" | "withdraw" | "repay" | "borrow" token: 0, amount: "1", }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Earn yield** — Supply idle tokens to the borrow/lend markets * **Access leverage** — Borrow against supplied collateral * **Manage positions** — Repay borrows or withdraw supplied balances # cDeposit | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-c-deposit openapi/hyperliquid_node_api/hypercore_exchange/exchange_c_deposit.json post /exchange Deposits tokens into the consensus layer for staking or validator operations. Available on Hyperliquid exchange via Chainstack JSON-RPC nodes. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Deposits tokens into the consensus layer for staking or validator operations. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"cDeposit"` * `amount` (string) — Amount to deposit * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "cDeposit"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # The hyperliquid-python-sdk has no cDeposit convenience method, # so build and sign the user-signed action with the SDK primitives. import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils.constants import MAINNET_API_URL from hyperliquid.utils.signing import get_timestamp_ms, sign_user_signed_action # EIP-712 payload for the cDeposit action. C_DEPOSIT_SIGN_TYPES = [ {"name": "hyperliquidChain", "type": "string"}, {"name": "wei", "type": "uint64"}, {"name": "nonce", "type": "uint64"}, ] wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, MAINNET_API_URL) # public mainnet endpoint nonce = get_timestamp_ms() action = { "type": "cDeposit", "wei": 1 * 10**8, # amount of wei to deposit into staking (float \* 1e8) "nonce": nonce, } signature = sign_user_signed_action( wallet, action, C_DEPOSIT_SIGN_TYPES, "HyperliquidTransaction:CDeposit", True, # is_mainnet ) result = exchange._post_action(action, signature, nonce) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const wallet = privateKeyToAccount("0x..."); // your private key const transport = new HttpTransport(); // public mainnet endpoint const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.cDeposit({ wei: 1 * 1e8, // amount of wei to deposit into staking (float \* 1e8) }); console.log(result); ``` ## Use case * **C deposit** — Deposits tokens into the consensus layer for staking or validator operations. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # CSignerAction | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-c-signer-action openapi/hyperliquid_node_api/hypercore_exchange/exchange_c_signer_action.json post /exchange Performs a consensus signer action for validator operations on the Hyperliquid network. Chainstack Hyperliquid exchange reference. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Performs a consensus signer action for validator operations on the Hyperliquid network. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"CSignerAction"` * `payload` (object) — Signer action payload * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "CSignerAction"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from eth_account import Account # Validator signer wallet — replace with your own key handling wallet = Account.from_key("0x...") # Public-only method: use the Hyperliquid mainnet API exchange = Exchange(wallet, constants.MAINNET_API_URL) # Jail self as a validator signer result = exchange.c_signer_jail_self() # Unjail self as a validator signer # result = exchange.c_signer_unjail_self() print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Validator signer wallet — replace with your own key handling const wallet = privateKeyToAccount("0x..."); // Public-only method: default transport targets the Hyperliquid mainnet API const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Jail self as a validator signer const result = await exchange.cSignerAction({ jailSelf: null }); // Unjail self as a validator signer // const result = await exchange.cSignerAction({ unjailSelf: null }); console.log(result); ``` ## Use case * **C signer action** — Performs a consensus signer action for validator operations on the Hyperliquid network. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # CValidatorAction | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-c-validator-action openapi/hyperliquid_node_api/hypercore_exchange/exchange_c_validator_action.json post /exchange Performs a validator-specific action on the Hyperliquid consensus layer. Available on Hyperliquid exchange via Chainstack JSON-RPC nodes. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Performs a validator-specific action on the Hyperliquid consensus layer. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"CValidatorAction"` * `payload` (object) — Validator action payload * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "CValidatorAction"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Load the validator signer wallet (keep the key out of source control). wallet = eth_account.Account.from_key("0x_your_private_key") # CValidatorAction is public-only — use the public Hyperliquid mainnet API. exchange = Exchange(wallet, constants.MAINNET_API_URL) # Register a validator. result = exchange.c_validator_register( node_ip="1.2.3.4", name="my-validator", description="My Hyperliquid validator", delegations_disabled=False, commission_bps=100, signer="0x_validator_signer_address", unjailed=True, initial_wei=10000000000, ) print(result) # Update the validator profile. exchange.c_validator_change_profile( node_ip="1.2.3.4", name="my-validator", description="Updated description", unjailed=True, disable_delegations=False, commission_bps=100, signer=None, ) # Unregister the validator. exchange.c_validator_unregister() ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Load the validator signer wallet (keep the key out of source control). const wallet = privateKeyToAccount("0x_your_private_key"); // CValidatorAction is public-only — HttpTransport defaults to the public Hyperliquid API. const transport = new hl.HttpTransport(); const client = new hl.ExchangeClient({ transport, wallet }); await client.cValidatorAction({ changeProfile: { node_ip: { Ip: "1.2.3.4" }, name: "my-validator", description: "Updated description", unjailed: true, disable_delegations: false, commission_bps: 100, signer: null, }, }); ``` ## Use case * **C validator action** — Performs a validator-specific action on the Hyperliquid consensus layer. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # cWithdraw | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-c-withdraw openapi/hyperliquid_node_api/hypercore_exchange/exchange_c_withdraw.json post /exchange Withdraws tokens from the consensus layer. cWithdraw JSON-RPC method available on the Hyperliquid exchange blockchain via Chainstack. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Withdraws tokens from the consensus layer. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"cWithdraw"` * `amount` (string) — Amount to withdraw * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "cWithdraw"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import get_timestamp_ms, sign_user_signed_action # EIP-712 types for the cWithdraw action (withdraw from staking). CWITHDRAW_TYPES = [ {"name": "hyperliquidChain", "type": "string"}, {"name": "wei", "type": "uint64"}, {"name": "nonce", "type": "uint64"}, ] wallet = eth_account.Account.from_key("0x...") exchange = Exchange(wallet, constants.MAINNET_API_URL) # Amount of wei to withdraw from the staking balance (float * 1e8). nonce = get_timestamp_ms() action = {"type": "cWithdraw", "wei": int(1 * 1e8), "nonce": nonce} # The Python SDK exposes no dedicated cWithdraw helper, so sign the # user-signed action directly with the generic signer. signature = sign_user_signed_action( wallet, action, CWITHDRAW_TYPES, "HyperliquidTransaction:CWithdraw", is_mainnet=True, ) result = exchange._post_action(action, signature, nonce) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const wallet = privateKeyToAccount("0x..."); // viem or ethers const transport = new HttpTransport(); // public Hyperliquid API const client = new ExchangeClient({ transport, wallet }); // Amount of wei to withdraw from staking into the spot account (float * 1e8). const result = await client.cWithdraw({ wei: 1 * 1e8 }); console.log(result); ``` ## Use case * **C withdraw** — Withdraws tokens from the consensus layer. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Cancel order | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-cancel-order openapi/hyperliquid_node_api/hypercore_exchange/exchange_cancel_order.json post /exchange Cancels one or multiple open orders by order ID (oid) on the Hyperliquid exchange. Available on Hyperliquid exchange via Chainstack. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Cancels one or multiple open orders by order ID (oid) on the Hyperliquid exchange. ## Parameters ### Required parameters * `action` (object, required) — The cancel action object containing: * `type` (string) — Must be `"cancel"` * `cancels` (array) — Array of cancel objects with: * `a` (number) — Asset index * `o` (number) — Order ID to cancel * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected ## Returns Returns an object with cancellation status: * `status` — `"ok"` if request processed * `response` — Contains cancellation details: * `type` — `"cancel"` * `data.statuses` — Array of status results: * `"success"` — Order successfully canceled * `error` — Object with error message if cancellation failed ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "cancel", "cancels": [{ "a": 0, "o": 77738308 }] }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Initialize with your private key account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Cancel a single order by ID cancel_result = exchange.cancel(name="BTC", oid=77738308) print(cancel_result) # Cancel multiple orders in one request cancel_results = exchange.bulk_cancel([ {"coin": "BTC", "oid": 77738308}, {"coin": "ETH", "oid": 77738309}, ]) print(cancel_results) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Initialize with your private key const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Cancel one or multiple orders by asset index (a) and order ID (o) const cancelResult = await exchange.cancel({ cancels: [{ a: 0, o: 77738308 }], }); console.log(cancelResult); ``` ## Response examples ### Successful cancellation ```json theme={"system"} { "status": "ok", "response": { "type": "cancel", "data": { "statuses": [ "success" ] } } } ``` ### Failed cancellation ```json theme={"system"} { "status": "ok", "response": { "type": "cancel", "data": { "statuses": [{ "error": "Order was never placed, already canceled, or filled." }] } } } ``` ### Multiple orders mixed results ```json theme={"system"} { "status": "ok", "response": { "type": "cancel", "data": { "statuses": [ "success", { "error": "Order was never placed, already canceled, or filled." } ] } } } ``` ## Use cases * **Risk management** — Quickly cancel orders when market conditions change * **Order management** — Clean up open orders that are no longer needed * **Algorithmic trading** — Programmatically manage order lifecycle * **Portfolio rebalancing** — Cancel orders before placing new ones > [!NOTE] > > You can cancel multiple orders in a single request by including multiple objects in the `cancels` array. Each cancellation is processed independently. > [!WARNING] > > Attempting to cancel an order that doesn't exist, was already canceled, or was filled will return an error but won't affect other cancellations in the same request. # Cancel order by cloid | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-cancel-order-by-cloid openapi/hyperliquid_node_api/hypercore_exchange/exchange_cancel_by_cloid.json post /exchange Cancels one or multiple open orders by client order ID (cloid) on the Hyperliquid exchange. Chainstack Hyperliquid exchange reference. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Cancels one or multiple open orders by client order ID (cloid) on the Hyperliquid exchange. This is useful when you want to cancel orders using your own tracking IDs rather than the exchange-generated order IDs. ## Parameters ### Required parameters * `action` (object, required) — The cancel action object containing: * `type` (string) — Must be `"cancelByCloid"` * `cancels` (array) — Array of cancel objects with: * `asset` (number) — Asset index * `cloid` (string) — Client order ID (128-bit hex string, e.g., `0x1234567890abcdef1234567890abcdef`) * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected ## Returns Returns an object with cancellation status: * `status` — `"ok"` if request processed * `response` — Contains cancellation details: * `type` — `"cancelByCloid"` * `data.statuses` — Array of status results for each cancellation attempt ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "cancelByCloid", "cancels": [{ "asset": 0, "cloid": "0x1234567890abcdef1234567890abcdef" }] }, "nonce": 1234567890123, "signature": {...} }' ``` ```python hyperliquid-python-sdk theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.types import Cloid import eth_account # Initialize with your private key account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Cancel an order by client order ID cancel_result = exchange.cancel_by_cloid( name="BTC", cloid=Cloid.from_str("0x1234567890abcdef1234567890abcdef"), ) print(cancel_result) ``` ```typescript @nktkas/hyperliquid theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Set up the client with a wallet and the public transport const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Cancel an order by client order ID const result = await exchange.cancelByCloid({ cancels: [ { asset: 0, cloid: "0x1234567890abcdef1234567890abcdef" }, ], }); console.log(result); ``` ## Client Order ID (cloid) The client order ID is a 128-bit hexadecimal string that you provide when placing an order. It allows you to: * Track orders using your own identification system * Cancel orders without needing to store exchange-generated order IDs * Implement idempotent order placement strategies Example cloid format: `0x1234567890abcdef1234567890abcdef` ## Use cases * **Custom order tracking** — Cancel orders using your internal order management system IDs * **Failover systems** — Cancel orders without needing to query order IDs first * **Multi-system integration** — Use consistent order IDs across different systems * **Idempotent operations** — Safely retry cancellations using deterministic IDs > [!NOTE] > > Client order IDs must be unique per user. Attempting to place an order with a duplicate cloid will be rejected. > [!WARNING] > > Make sure to store the cloid when placing orders if you plan to use this cancellation method. The cloid must match exactly what was provided during order placement. # convertToMultiSigUser | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-convert-to-multi-sig-user openapi/hyperliquid_node_api/hypercore_exchange/exchange_convert_to_multi_sig_user.json post /exchange Converts the user's account to a multi-signature account requiring multiple signers for authorization. Hyperliquid exchange via Chainstack. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Converts the user's account to a multi-signature account requiring multiple signers for authorization. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"convertToMultiSigUser"` * `signers` (array) — Array of signer addresses * `threshold` (integer) — Number of required signatures * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "convertToMultiSigUser"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Load the wallet that authorizes the conversion wallet = eth_account.Account.from_key("YOUR_PRIVATE_KEY") # Public Hyperliquid mainnet endpoint exchange = Exchange(wallet, constants.MAINNET_API_URL) # Convert the account to multi-sig with a list of authorized signers and a threshold result = exchange.convert_to_multi_sig_user( authorized_users=[ "0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000002", "0x0000000000000000000000000000000000000003", ], threshold=2, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Load the wallet that authorizes the conversion const wallet = privateKeyToAccount("0x..."); // Public Hyperliquid mainnet endpoint const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Convert the account to multi-sig with a list of authorized signers and a threshold const result = await exchange.convertToMultiSigUser({ signers: { authorizedUsers: [ "0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000002", "0x0000000000000000000000000000000000000003", ], threshold: 2, }, }); console.log(result); ``` ## Use case * **Convert to multi-sig** — Converts the user's account to a multi-signature account requiring multiple signers for authorization. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # createSubAccount | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-create-sub-account openapi/hyperliquid_node_api/hypercore_exchange/exchange_create_sub_account.json post /exchange Creates a new sub-account under the user's main account. Sub-accounts have separate positions and margins but share the parent's authentication. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Creates a new sub-account under the user's main account. Sub-accounts have separate positions and margins but share the parent's authentication. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"createSubAccount"` * `name` (string) — Sub-account name * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "createSubAccount"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Load your wallet from a private key (use a secure source in production) wallet = eth_account.Account.from_key("0x...") # Public Hyperliquid mainnet API — this method is not available through Chainstack exchange = Exchange(wallet, constants.MAINNET_API_URL) # Create a sub-account under your main account result = exchange.create_sub_account(name="my-sub-account") print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Load your wallet from a private key (use a secure source in production) const wallet = privateKeyToAccount("0x..."); // Default transport targets the public Hyperliquid mainnet API const transport = new hl.HttpTransport(); const client = new hl.ExchangeClient({ transport, wallet }); // Create a sub-account under your main account const result = await client.createSubAccount({ name: "my-sub-account" }); console.log(result); ``` ## Use case * **Create sub-account** — Creates a new sub-account under the user's main account. Sub-accounts have separate positions and margins but share the parent's authentication. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Create vault | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-create-vault openapi/hyperliquid_node_api/hypercore_exchange/exchange_create_vault.json post /exchange Create a new vault on Hyperliquid. Returns the new vault address. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `createVault` action creates a new vault. On success the response includes the address of the newly created vault. ## Parameters ### Required parameters * `action` (object, required) — The create-vault action: * `type` (string) — Must be `"createVault"`. * `name` (string) — Vault name (3-50 characters). * `description` (string) — Vault description (10-250 characters). * `initialUsd` (number) — Initial balance as a USD float multiplied by 1e6 (minimum 100 USD, i.e. `100000000`). * `nonce` (number) — Timestamp in milliseconds, equal to the envelope nonce. * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ### Optional parameters * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains `type` `"createVault"` and `data`, the address of the new vault. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "createVault", "name": "My vault", "description": "A vault for copy trading", "initialUsd": 100000000, "nonce": 1234567890123 }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a createVault helper # yet, so build and post the signed action directly. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = { "type": "createVault", "name": "My vault", "description": "A vault for copy trading", "initialUsd": 100000000, # 100 USD * 1e6 "nonce": timestamp, } signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.createVault({ name: "My vault", description: "A vault for copy trading", initialUsd: 100000000, // 100 USD * 1e6 }); console.log(result); // response.data is the new vault address ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "createVault", "data": "0x1719884eb866cb12b2287399b15f7db5e7d775ea" } } ``` ## Use cases * **Launch a vault** — Open a vault others can deposit into and follow * **Copy trading** — Let depositors mirror your strategy * **Capital pooling** — Aggregate capital under a single managed account # evmUserModify | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-evm-user-modify openapi/hyperliquid_node_api/hypercore_exchange/exchange_evm_user_modify.json post /exchange Modifies user settings from the HyperEVM side. Allows EVM-based accounts to update their Hyperliquid L1 configuration. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Modifies user settings from the HyperEVM side. Allows EVM-based accounts to update their Hyperliquid L1 configuration. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"evmUserModify"` * `modifications` (array) — Array of modification objects * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "evmUserModify"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Load the wallet that signs the action. wallet = eth_account.Account.from_key("YOUR_PRIVATE_KEY") # evmUserModify is public-only, so use the Hyperliquid mainnet API. exchange = Exchange(wallet, constants.MAINNET_API_URL) # Configure the block type for HyperEVM transactions. result = exchange.use_big_blocks(True) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Load the wallet that signs the action. const wallet = privateKeyToAccount("0x..."); // evmUserModify is public-only, so use the default Hyperliquid transport. const transport = new HttpTransport(); const client = new ExchangeClient({ transport, wallet }); // Configure the block type for HyperEVM transactions. const result = await client.evmUserModify({ usingBigBlocks: true }); console.log(result); ``` ## Use case * **EVM user modify** — Modifies user settings from the HyperEVM side. Allows EVM-based accounts to update their Hyperliquid L1 configuration. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Finalize EVM contract | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-finalize-evm-contract openapi/hyperliquid_node_api/hypercore_exchange/exchange_finalize_evm_contract.json post /exchange Finalize the link between a HyperCore token and its HyperEVM contract. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `finalizeEvmContract` action finalizes the link between a HyperCore token and its HyperEVM contract, using a verification method that matches how the contract was deployed. ## Parameters ### Required parameters * `action` (object, required) — The finalize-EVM-contract action: * `type` (string) — Must be `"finalizeEvmContract"`. * `token` (number) — The token identifier to link. * `input` — The verification method, one of: * `{ "create": { "nonce": } }` — for a contract deployed from an EOA; uses the EVM deployment nonce. * `"firstStorageSlot"` — the finalizer address is stored at the contract's first storage slot. * `"customStorageSlot"` — the finalizer address is stored at slot `keccak256("HyperCore deployer")`. * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ### Optional parameters * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "finalizeEvmContract", "token": 0, "input": "firstStorageSlot"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a finalizeEvmContract # helper yet, so build and post the signed action directly. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = { "type": "finalizeEvmContract", "token": 0, "input": "firstStorageSlot", # or "customStorageSlot", or {"create": {"nonce": 0}} } signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.finalizeEvmContract({ token: 0, input: "firstStorageSlot", // or "customStorageSlot", or { create: { nonce: 0 } } }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Link a token to its EVM contract** — Complete HyperCore/HyperEVM token linkage * **Token deployment tooling** — Finalize contracts as part of a deploy pipeline * **Verify deployment ownership** — Prove control via deploy nonce or storage slot # Gossip priority bid | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-gossip-priority-bid openapi/hyperliquid_node_api/hypercore_exchange/exchange_gossip_priority_bid.json post /exchange Bid in the gossip priority auction to prioritize an IP address for gossip on Hyperliquid. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `gossipPriorityBid` action places a bid in the gossip priority auction to prioritize an IP address. Lower-indexed slots are strictly prioritized over higher ones. Use [gossipPriorityAuctionStatus](/reference/hyperliquid-info-gossip-priority-auction-status) to read the current auction state. ## Parameters ### Required parameters * `action` (object, required) — The gossip-priority-bid action: * `type` (string) — Must be `"gossipPriorityBid"`. * `slotId` (number) — Auction slot identifier (`0` or `1`). Lower-indexed slots are strictly prioritized. * `ip` (string) — The IP address (IPv4 or IPv6) to prioritize. Any address may bid on behalf of any IP. * `maxGas` (number) — Max gas in wei (1 HYPE = 10^8 wei) charged from the spot balance. The minimum auction price is 0.1 HYPE. * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ### Optional parameters * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "gossipPriorityBid", "slotId": 0, "ip": "1.2.3.4", "maxGas": 10000000}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) # slot_id is 0 or 1; max_gas is in wei (1 HYPE = 1e8 wei) result = exchange.gossip_priority_bid(slot_id=0, ip="1.2.3.4", max_gas=10000000) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.gossipPriorityBid({ slotId: 0, ip: "1.2.3.4", maxGas: 10000000, // wei; 1 HYPE = 1e8 wei }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Prioritize a node's gossip** — Win a priority slot for a given IP * **Node operations** — Improve propagation for latency-sensitive infrastructure * **Auction tooling** — Automate bidding against the current auction status # Link staking user | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-link-staking-user openapi/hyperliquid_node_api/hypercore_exchange/exchange_link_staking_user.json post /exchange Link a staking account and a trading account for fee-discount attribution on Hyperliquid. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `linkStakingUser` action links a staking account and a trading account so that staking is attributed for fee discounts. The link is a two-step flow: the trading user initiates the request (`isFinalize: false`), then the staking user finalizes the permanent link (`isFinalize: true`). This is a user-signed EIP-712 action. ## Parameters ### Required parameters * `action` (object, required) — The link-staking-user action: * `type` (string) — Must be `"linkStakingUser"`. * `signatureChainId` (string) — Chain ID in hex used for EIP-712 signing. * `hyperliquidChain` (string) — `"Mainnet"` or `"Testnet"`. * `user` (string) — Target account address. When the trading user initiates, this is the staking account address; when the staking user finalizes, this is the trading account address. * `isFinalize` (boolean) — `false` to initiate (trading user), `true` to finalize the permanent link (staking user). * `nonce` (number) — Timestamp in milliseconds, equal to the envelope nonce. * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "linkStakingUser", "signatureChainId": "0x66eee", "hyperliquidChain": "Mainnet", "user": "0x0000000000000000000000000000000000000000", "isFinalize": false, "nonce": 1234567890123 }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a linkStakingUser # helper yet. This is a user-signed (EIP-712) action, so supply its typed-data # fields and primary type when signing. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_user_signed_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = { "type": "linkStakingUser", "signatureChainId": "0x66eee", "hyperliquidChain": "Mainnet", "user": "0x0000000000000000000000000000000000000000", "isFinalize": False, "nonce": timestamp, } payload_types = [ {"name": "hyperliquidChain", "type": "string"}, {"name": "user", "type": "address"}, {"name": "isFinalize", "type": "bool"}, {"name": "nonce", "type": "uint64"}, ] signature = sign_user_signed_action( wallet, action, payload_types, "HyperliquidTransaction:LinkStakingUser", exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Trading user initiates the link request const result = await exchange.linkStakingUser({ user: "0x0000000000000000000000000000000000000000", isFinalize: false, }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Attribute staking for fee discounts** — Link a staking account to a trading account * **Separate custody and trading** — Keep staked funds and trading funds in different accounts * **Two-step linking** — Initiate from the trading user, finalize from the staking user # Modify order | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-modify-order openapi/hyperliquid_node_api/hypercore_exchange/exchange_modify_order.json post /exchange Modifies an existing order on the Hyperliquid exchange. You can change the price, size, and other parameters without canceling and replacing the order. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Modifies an existing order on the Hyperliquid exchange. You can change the price, size, and other parameters without canceling and replacing the order. ## Parameters ### Required parameters * `action` (object, required) — The modify action object containing: * `type` (string) — Must be `"modify"` * `oid` (number or string) — Order ID to modify, or cloid if using client order ID * `order` (object) — New order parameters: * `a` (number) — Asset index * `b` (boolean) — Is buy order (true for buy/long, false for sell/short) * `p` (string) — New limit price * `s` (string) — New size in units of the base asset * `r` (boolean) — Reduce only order * `t` (object) — Order type specification: * For limit orders: `{"limit": {"tif": "Alo" | "Ioc" | "Gtc"}}` * For trigger orders: `{"trigger": {"isMarket": boolean, "triggerPx": string, "tpsl": "tp" | "sl"}}` * `c` (string, optional) — New client order ID (128-bit hex string) * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected ## Returns Returns an object with modification status: * `status` — `"ok"` if request processed * `response` — Contains modification details: * `type` — `"modify"` * `data` — Modification result information ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "modify", "oid": 77738308, "order": { "a": 0, "b": true, "p": "51000", "s": "0.02", "r": false, "t": {"limit": {"tif": "Gtc"}} } }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Initialize with your private key account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Modify an existing order modify_result = exchange.modify_order( oid=77738308, name="BTC", is_buy=True, sz=0.02, limit_px=51000, order_type={"limit": {"tif": "Gtc"}}, reduce_only=False, ) print(modify_result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Initialize with your private key const wallet = privateKeyToAccount("0x..."); // viem or ethers const transport = new HttpTransport(); // public Hyperliquid API const exchange = new ExchangeClient({ transport, wallet }); // Modify an existing order const modifyResult = await exchange.modify({ oid: 77738308, order: { a: 0, b: true, p: "51000", s: "0.02", r: false, t: { limit: { tif: "Gtc" } }, }, }); console.log(modifyResult); ``` ## Modification behavior * **Priority preservation** — Modifying only the size preserves queue priority * **Price changes** — Changing the price moves the order to the back of the queue * **Partial fills** — Can modify partially filled orders (remaining size only) * **Order types** — Can change between different order types ## Use cases * **Price adjustment** — Update limit price based on market movements * **Size scaling** — Increase or decrease order size * **Strategy updates** — Adjust orders without losing position in queue * **Risk management** — Convert orders to reduce-only > [!NOTE] > > Modifying an order is more efficient than cancel-and-replace, especially when only changing the size, as it can preserve your position in the order queue. > [!WARNING] > > Some modifications may cause loss of queue priority. Price changes always move the order to the back of the queue at the new price level. # multiSig | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-multi-sig openapi/hyperliquid_node_api/hypercore_exchange/exchange_multi_sig.json post /exchange Submits or signs a multi-signature action for a multi-sig account. Used to propose or approve actions that require multiple signatures. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Submits or signs a multi-signature action for a multi-sig account. Used to propose or approve actions that require multiple signatures. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"multiSig"` * `signatureChainId` (string) — Chain ID for signature * `payload` (object) — The action payload to sign * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "multiSig"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # pip install hyperliquid-python-sdk eth-account import time import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # The leader (outer signer) collects the inner signatures and submits the action. leader = eth_account.Account.from_key("0xLEADER_PRIVATE_KEY") exchange = Exchange(leader, constants.MAINNET_API_URL) multi_sig_user = "0xMULTI_SIG_ACCOUNT_ADDRESS" nonce = int(time.time() * 1000) # inner_action is the action the multi-sig account performs (for example, an order). inner_action = {"type": "scheduleCancel", "time": None} # signatures collected from each authorized signer of the multi-sig account. signatures = [ # sign_multi_sig_inner_action(...) output per signer ] result = exchange.multi_sig( multi_sig_user=multi_sig_user, inner_action=inner_action, signatures=signatures, nonce=nonce, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} // npm install @nktkas/hyperliquid viem import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const multiSigUser = "0xMULTI_SIG_ACCOUNT_ADDRESS"; // the multi-sig account address const signers = [ privateKeyToAccount("0xLEADER_PRIVATE_KEY"), // leader — signs the wrapper privateKeyToAccount("0xOTHER_SIGNER_PRIVATE_KEY"), ] as const; const transport = new HttpTransport(); // public mainnet endpoint const client = new ExchangeClient({ transport, signers, multiSigUser }); // The client wraps each action in a multiSig action automatically. const result = await client.order({ orders: [/* ... */], grouping: "na" }); console.log(result); ``` ## Use case * **Multi-sig action** — Submits or signs a multi-signature action for a multi-sig account. Used to propose or approve actions that require multiple signatures. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # noop | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-noop openapi/hyperliquid_node_api/hypercore_exchange/exchange_noop.json post /exchange Performs a no-operation action. Useful for testing signature authentication or consuming rate limit weight without side effects. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Performs a no-operation action. Useful for testing signature authentication or consuming rate limit weight without side effects. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"noop"` * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "noop"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from eth_account.signers.local import LocalAccount from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import get_timestamp_ms # The noop endpoint is public-only, so use the Hyperliquid mainnet URL. wallet: LocalAccount = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) # noop takes the nonce (current timestamp in milliseconds); the SDK signs the action for you. result = exchange.noop(get_timestamp_ms()) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const wallet = privateKeyToAccount("0x..."); // your private key (viem or ethers) // The noop endpoint is public-only, so use the default public transport. const transport = new hl.HttpTransport(); const client = new hl.ExchangeClient({ transport, wallet }); // noop takes the nonce (current timestamp in milliseconds); the SDK signs the action for you. const result = await client.noop({ nonce: Date.now() }); console.log(result); ``` ## Use case * **No-op** — Performs a no-operation action. Useful for testing signature authentication or consuming rate limit weight without side effects. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # perpDeploy | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-perp-deploy openapi/hyperliquid_node_api/hypercore_exchange/exchange_perp_deploy.json post /exchange Deploys a new perpetual contract on a Hyperliquid perp DEX. Chainstack supports perpDeploy on Hyperliquid exchange JSON-RPC nodes. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Deploys a new perpetual contract on a Hyperliquid perp DEX. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"perpDeploy"` * `config` (object) — Perpetual deployment configuration * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "perpDeploy"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from eth_account import Account # Signing is required: load the deployer wallet from its private key. wallet = Account.from_key("0x...") exchange = Exchange(wallet, constants.MAINNET_API_URL) # perpDeploy / registerAsset: register a new perp asset on a builder-deployed perp DEX. result = exchange.perp_deploy_register_asset( dex="test", max_gas=1000000, coin="USDC", sz_decimals=8, oracle_px="1", margin_table_id=1, only_isolated=False, schema=None, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Signing is required: load the deployer wallet from its private key. const wallet = privateKeyToAccount("0x..."); // viem or ethers const transport = new hl.HttpTransport(); // public mainnet API const client = new hl.ExchangeClient({ transport, wallet }); // perpDeploy / registerAsset: register a new perp asset on a builder-deployed perp DEX. const result = await client.perpDeploy({ registerAsset: { maxGas: 1000000, assetRequest: { coin: "USDC", szDecimals: 8, oraclePx: "1", marginTableId: 1, onlyIsolated: false, }, dex: "test", schema: null, }, }); console.log(result); ``` ## Use case * **Perp deploy** — Deploys a new perpetual contract on a Hyperliquid perp DEX. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Place order | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-place-order openapi/hyperliquid_node_api/hypercore_exchange/exchange_place_order.json post /exchange Places one or multiple orders on the Hyperliquid exchange. Supports limit orders, market orders, and trigger orders (stop-loss/take-profit). > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Places one or multiple orders on the Hyperliquid exchange. Supports limit orders, market orders, and trigger orders (stop-loss/take-profit). ## Parameters ### Required parameters * `action` (object, required) — The order action object containing: * `type` (string) — Must be `"order"` * `orders` (array) — Array of order objects with: * `a` (number) — Asset index (see asset notation below) * `b` (boolean) — Is buy order (true for buy/long, false for sell/short) * `p` (string) — Limit price (use "0" for market orders) * `s` (string) — Size in units of the base asset * `r` (boolean) — Reduce only order * `t` (object) — Order type specification: * For limit orders: `{"limit": {"tif": "Alo" | "Ioc" | "Gtc"}}` * For trigger orders: `{"trigger": {"isMarket": boolean, "triggerPx": string, "tpsl": "tp" | "sl"}}` * `c` (string, optional) — Client order ID (128-bit hex string) * `grouping` (string) — Order grouping: `"na"` | `"normalTpsl"` | `"positionTpsl"` * `builder` (object, optional) — Builder fee configuration: * `b` (string) — Builder address to receive fees * `f` (number) — Fee in tenths of a basis point * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the order is rejected ## Asset notation For perpetuals, use the index from the `universe` field in the meta response. For spot assets, use `10000 + index` where index is from `spotMeta.universe`. Example: `PURR/USDC` spot has index 0 in spot metadata, so use asset `10000`. ## Order types ### Time in Force (TIF) for limit orders * `Alo` — Add liquidity only (post-only), canceled if would immediately match * `Ioc` — Immediate or cancel, unfilled portion canceled * `Gtc` — Good til canceled, rests on book until filled or canceled ### Trigger orders * `tp` — Take profit order * `sl` — Stop loss order * `isMarket` — Whether to place market order when triggered * `triggerPx` — Price at which to trigger the order ## Returns Returns an object with order placement status: * `status` — `"ok"` if successful * `response` — Contains order details: * `type` — `"order"` * `data.statuses` — Array of status objects for each order: * `resting` — Order placed on book with `oid` (order ID) * `filled` — Order immediately filled with `totalSz`, `avgPx`, and `oid` * `error` — Error message if order failed ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "order", "orders": [{ "a": 0, "b": true, "p": "50000", "s": "0.01", "r": false, "t": {"limit": {"tif": "Gtc"}} }], "grouping": "na" }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Initialize with your private key. The public mainnet API is used # because this signing endpoint is not served through Chainstack. account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Place a limit buy order for BTC. order_result = exchange.order( name="BTC", is_buy=True, sz=0.01, limit_px=50000, order_type={"limit": {"tif": "Gtc"}}, reduce_only=False, ) print(order_result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // The default HttpTransport targets the public mainnet API because // this signing endpoint is not served through Chainstack. const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Place a limit buy order for asset index 0 (BTC perpetual). const result = await exchange.order({ orders: [{ a: 0, b: true, p: "50000", s: "0.01", r: false, t: { limit: { tif: "Gtc" } }, }], grouping: "na", }); console.log(result); ``` ## Response examples ### Successful resting order ```json theme={"system"} { "status": "ok", "response": { "type": "order", "data": { "statuses": [{ "resting": { "oid": 77738308 } }] } } } ``` ### Filled order ```json theme={"system"} { "status": "ok", "response": { "type": "order", "data": { "statuses": [{ "filled": { "totalSz": "0.02", "avgPx": "1891.4", "oid": 77747314 } }] } } } ``` ### Error response ```json theme={"system"} { "status": "ok", "response": { "type": "order", "data": { "statuses": [{ "error": "Order must have minimum value of $10." }] } } } ``` ## Use cases * **Spot and perpetual trading** — Execute trades on Hyperliquid's order books * **Algorithmic trading** — Place orders programmatically with custom logic * **Risk management** — Set stop-loss and take-profit orders * **Market making** — Place limit orders to provide liquidity > [!NOTE] > > Orders consume address-based rate limits. The `expiresAfter` field consumes 5x rate limit when orders expire due to staleness. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Register referrer | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-register-referrer openapi/hyperliquid_node_api/hypercore_exchange/exchange_register_referrer.json post /exchange Register your own referral code on Hyperliquid so others can sign up under it. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `registerReferrer` action registers your own referral code so other users can sign up under it. ## Parameters ### Required parameters * `action` (object, required) — The register-referrer action: * `type` (string) — Must be `"registerReferrer"`. * `code` (string) — The referral code to create (1-20 characters). * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ### Optional parameters * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "registerReferrer", "code": "CHAINSTACK"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a registerReferrer # helper yet, so build and post the signed action directly. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = {"type": "registerReferrer", "code": "CHAINSTACK"} signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.registerReferrer({ code: "CHAINSTACK" }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Run a referral program** — Create a code to share with new users * **Track attribution** — Attribute sign-ups to your code * **Grow your network** — Earn referral rewards from referred activity # reserveRequestWeight | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-reserve-request-weight openapi/hyperliquid_node_api/hypercore_exchange/exchange_reserve_request_weight.json post /exchange Reserves rate limit weight for upcoming requests. Useful for high-frequency trading applications that need guaranteed request capacity. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Reserves rate limit weight for upcoming requests. Useful for high-frequency trading applications that need guaranteed request capacity. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"reserveRequestWeight"` * `weight` (integer) — Amount of rate limit weight to reserve * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "reserveRequestWeight"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # The hyperliquid-python-sdk does not expose a dedicated reserveRequestWeight # method, so build and sign the L1 action with the SDK signing primitives. from eth_account import Account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms wallet = Account.from_key("0x...") # your API wallet private key exchange = Exchange(wallet, constants.MAINNET_API_URL) action = {"type": "reserveRequestWeight", "weight": 10} nonce = get_timestamp_ms() signature = sign_l1_action( wallet, action, exchange.vault_address, nonce, exchange.expires_after, exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, nonce) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const wallet = privateKeyToAccount("0x..."); // your API wallet private key const transport = new HttpTransport(); // default public Hyperliquid API const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.reserveRequestWeight({ weight: 10 }); console.log(result); ``` ## Use case * **Reserve request weight** — Reserves rate limit weight for upcoming requests. Useful for high-frequency trading applications that need guaranteed request capacity. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Schedule cancel | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-schedule-cancel openapi/hyperliquid_node_api/hypercore_exchange/exchange_schedule_cancel.json post /exchange Schedules an automatic cancel-all operation at a future time, acting as a dead man's switch for risk management. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Schedules an automatic cancel-all operation at a future time, acting as a dead man's switch for risk management. This safety feature ensures all open orders are canceled if your trading system becomes unresponsive. ## Parameters ### Required parameters * `action` (object, required) — The schedule cancel action object containing: * `type` (string) — Must be `"scheduleCancel"` * `time` (number, optional) — Unix timestamp in milliseconds when to cancel all orders. Omit to remove existing scheduled cancel * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected ## Returns Returns an object with scheduling status: * `status` — `"ok"` if request processed * `response` — Contains operation details: * `type` — `"scheduleCancel"` ## Scheduling rules * **Minimum delay** — Scheduled time must be at least 5 seconds in the future * **Daily limit** — Maximum 10 triggers per day (resets at 00:00 UTC) * **Cancel operation** — Omit the `time` parameter to remove an existing scheduled cancel * **Auto-execution** — When triggered, cancels all open orders for the user ## Example request ```shell cURL theme={"system"} # Schedule cancel in 60 seconds curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "scheduleCancel", "time": 1234567950123 }, "nonce": 1234567890123, "signature": {...} }' # Remove scheduled cancel curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "scheduleCancel" }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account import time # Initialize with your private key account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Schedule cancel in 60 seconds future_time = int((time.time() + 60) * 1000) schedule_result = exchange.schedule_cancel(time=future_time) # Remove scheduled cancel remove_result = exchange.schedule_cancel(time=None) print(schedule_result) ``` ```typescript TypeScript theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Initialize with your private key const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Schedule cancel in 60 seconds const scheduleResult = await exchange.scheduleCancel({ time: Date.now() + 60_000 }); // Remove scheduled cancel const removeResult = await exchange.scheduleCancel(); console.log(scheduleResult); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "scheduleCancel" } } ``` ## Use cases * **System failure protection** — Automatically cancel orders if your trading system crashes * **Connection loss safety** — Protect against network disconnections * **Daily trading limits** — Schedule end-of-day order cleanup * **Risk management** — Implement automatic position closure during maintenance ## Best practices 1. **Regular heartbeats** — Continuously push the scheduled cancel forward while your system is healthy 2. **Buffer time** — Set cancellation time with enough buffer (e.g., 30-60 seconds) 3. **Monitor triggers** — Track your daily trigger count to avoid hitting the limit 4. **Graceful shutdown** — Remove scheduled cancels when intentionally stopping your system > [!NOTE] > > The dead man's switch is a critical safety feature. Always implement it in production trading systems to prevent orders from remaining open during system failures. > [!WARNING] > > Remember the 10 triggers per day limit. If you hit this limit, you won't be able to use the scheduled cancel feature until 00:00 UTC. Plan your heartbeat intervals accordingly. # sendAsset | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-send-asset openapi/hyperliquid_node_api/hypercore_exchange/exchange_send_asset.json post /exchange Sends a spot asset to another address on Hyperliquid L1. sendAsset JSON-RPC method available on the Hyperliquid exchange blockchain via Chainstack. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Sends a spot asset to another address on Hyperliquid L1. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"sendAsset"` * `hyperliquidChain` (string) — Chain identifier * `destination` (string) — Recipient address * `token` (string) — Token to send * `amount` (string) — Amount to send * `time` (integer) — Current timestamp in milliseconds * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "sendAsset"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Load your wallet from a private key (keep it secret — never hardcode in production) wallet = eth_account.Account.from_key("0x...") # Public Hyperliquid API (this method is available on the official API only) exchange = Exchange(wallet, constants.MAINNET_API_URL) # Send a spot asset to another address. # sourceDex/destinationDex: "" for the default perp dex, "spot" for spot. result = exchange.send_asset( destination="0x0000000000000000000000000000000000000001", source_dex="spot", destination_dex="spot", token="USDC:0xeb62eee3685fc4c43992febcd9e75443", amount=1, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Load your wallet from a private key (keep it secret — never hardcode in production) const wallet = privateKeyToAccount("0x..."); // Public Hyperliquid API (this method is available on the official API only) const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Send a spot asset to another address. // sourceDex/destinationDex: "" for the default perp dex, "spot" for spot. const result = await exchange.sendAsset({ destination: "0x0000000000000000000000000000000000000001", sourceDex: "spot", destinationDex: "spot", token: "USDC:0xeb62eee3685fc4c43992febcd9e75443", amount: "1", }); console.log(result); ``` ## Use case * **Send asset** — Sends a spot asset to another address on Hyperliquid L1. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # sendToEvmWithData | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-send-to-evm-with-data openapi/hyperliquid_node_api/hypercore_exchange/exchange_send_to_evm_with_data.json post /exchange Sends assets from Hyperliquid L1 to a HyperEVM address with optional calldata. Enables programmatic interactions between L1 and HyperEVM. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Sends assets from Hyperliquid L1 to a HyperEVM address with optional calldata. Enables programmatic interactions between L1 and HyperEVM. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"sendToEvmWithData"` * `destination` (string) — EVM recipient address * `token` (string) — Token to send * `amount` (string) — Amount to send * `data` (string) — Optional calldata for the EVM transaction * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "sendToEvmWithData"}, "nonce": 1234567890123, "signature": {...} }' ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet API (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // SDK signs the EIP-712 action and posts it to /exchange const result = await exchange.sendToEvmWithData({ token: "USDC", amount: "1", sourceDex: "spot", destinationRecipient: "0x...", addressEncoding: "hex", destinationChainId: 42161, gasLimit: 200000, data: "0x", }); console.log(result); ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: hyperliquid-python-sdk does not expose a sendToEvmWithData helper. # Build, sign, and post the action manually against the public mainnet API. import time import requests from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_user_signed_action # Your wallet (e.g. from eth_account.Account) wallet = ... # LocalAccount action = { "type": "sendToEvmWithData", "signatureChainId": "0xa4b1", "hyperliquidChain": "Mainnet", "token": "USDC", "amount": "1", "sourceDex": "spot", "destinationRecipient": "0x...", "addressEncoding": "hex", "destinationChainId": 42161, "gasLimit": 200000, "data": "0x", "nonce": int(time.time() * 1000), } # EIP-712 sign (payload_types must match the action fields) signature = sign_user_signed_action( wallet, action, payload_types=[ {"name": "hyperliquidChain", "type": "string"}, {"name": "token", "type": "string"}, {"name": "amount", "type": "string"}, {"name": "sourceDex", "type": "string"}, {"name": "destinationRecipient", "type": "string"}, {"name": "addressEncoding", "type": "string"}, {"name": "destinationChainId", "type": "uint64"}, {"name": "gasLimit", "type": "uint64"}, {"name": "data", "type": "bytes"}, {"name": "nonce", "type": "uint64"}, ], primary_type="HyperliquidTransaction:SendToEvmWithData", is_mainnet=True, ) resp = requests.post( f"{constants.MAINNET_API_URL}/exchange", json={"action": action, "nonce": action["nonce"], "signature": signature}, ) print(resp.json()) ``` ## Use case * **Send to EVM with data** — Sends assets from Hyperliquid L1 to a HyperEVM address with optional calldata. Enables programmatic interactions between L1 and HyperEVM. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Set display name | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-set-display-name openapi/hyperliquid_node_api/hypercore_exchange/exchange_set_display_name.json post /exchange Set or clear your account display name on Hyperliquid (for leaderboards and social features). On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `setDisplayName` action sets your account's display name, used in leaderboards and social features. Set an empty string to remove the display name. ## Parameters ### Required parameters * `action` (object, required) — The set-display-name action: * `type` (string) — Must be `"setDisplayName"`. * `displayName` (string) — The display name (max 20 characters). An empty string removes it. * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ### Optional parameters * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "setDisplayName", "displayName": "satoshi"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a setDisplayName # helper yet, so build and post the signed action directly. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = {"type": "setDisplayName", "displayName": "satoshi"} signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.setDisplayName({ displayName: "satoshi" }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Personalize your account** — Show a name on leaderboards * **Social features** — Make your account recognizable * **Clear your name** — Send an empty string to remove the display name # setReferrer | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-set-referrer openapi/hyperliquid_node_api/hypercore_exchange/exchange_set_referrer.json post /exchange Sets a referral code for the user's account. Once set, the referrer earns a share of the user's trading fees. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Sets a referral code for the user's account. Once set, the referrer earns a share of the user's trading fees. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"setReferrer"` * `code` (string) — Referral code * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "setReferrer"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from eth_account import Account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Load the wallet that sets the referral code (replace with your own private key) wallet = Account.from_key("0x...") # Public Hyperliquid mainnet endpoint (this method is public-only) exchange = Exchange(wallet, constants.MAINNET_API_URL) # Set the referral code for the wallet's account result = exchange.set_referrer("YOUR_CODE") print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Load the wallet that sets the referral code (replace with your own private key) const wallet = privateKeyToAccount("0x..."); // Public Hyperliquid mainnet endpoint (this method is public-only) const transport = new HttpTransport(); const exchange = new ExchangeClient({ wallet, transport }); // Set the referral code for the wallet's account const result = await exchange.setReferrer({ code: "YOUR_CODE" }); console.log(result); ``` ## Use case * **Set referrer** — Sets a referral code for the user's account. Once set, the referrer earns a share of the user's trading fees. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # spotDeploy | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-spot-deploy openapi/hyperliquid_node_api/hypercore_exchange/exchange_spot_deploy.json post /exchange Deploys a new spot token on Hyperliquid. Requires winning the spot pair deployment auction. Chainstack Hyperliquid exchange reference. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Deploys a new spot token on Hyperliquid. Requires winning the spot pair deployment auction. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"spotDeploy"` * `config` (object) — Spot token deployment configuration * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "spotDeploy"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Public Hyperliquid mainnet — this method is not served by Chainstack wallet = eth_account.Account.from_key("0xYOUR_PRIVATE_KEY") exchange = Exchange(wallet, constants.MAINNET_API_URL) # Step 1 of the spot deploy flow: register the token (registerToken2) result = exchange.spot_deploy_register_token( token_name="USDC", sz_decimals=8, wei_decimals=8, max_gas=1000000, full_name="USD Coin", ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const wallet = privateKeyToAccount("0xYOUR_PRIVATE_KEY"); // viem or ethers // Public Hyperliquid mainnet — this method is not served by Chainstack const transport = new hl.HttpTransport(); const client = new hl.ExchangeClient({ transport, wallet }); // Step 1 of the spot deploy flow: register the token (registerToken2) const result = await client.spotDeploy({ registerToken2: { spec: { name: "USDC", szDecimals: 8, weiDecimals: 8, }, maxGas: 1000000, fullName: "USD Coin", }, }); console.log(result); ``` ## Use case * **Spot deploy** — Deploys a new spot token on Hyperliquid. Requires winning the spot pair deployment auction. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Spot ↔ Perp transfer | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-spot-perp-transfer openapi/hyperliquid_node_api/hypercore_exchange/exchange_spot_perp_transfer.json post /exchange Transfers USDC between your spot wallet and perpetual wallet on Hyperliquid. Spot ↔ Perp transfer on Hyperliquid exchange via Chainstack. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Transfers USDC between your spot wallet and perpetual wallet on Hyperliquid. This allows you to move collateral between different trading modes without withdrawing from the platform. ## Parameters ### Required parameters * `action` (object, required) — The transfer action object containing: * `type` (string) — Must be `"spotUser"` * `classTransfer` (object) — Transfer details: * `usdc` (number) — Amount to transfer in USDC with 6 decimals (e.g., 100000000 = 100 USDC) * `toPerp` (boolean) — Direction of transfer: * `true` — Transfer from spot to perp * `false` — Transfer from perp to spot * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected ## Transfer direction * **Spot → Perp** (`toPerp: true`) * Moves USDC from spot wallet to perpetual trading account * Required for perpetual trading and margin * **Perp → Spot** (`toPerp: false`) * Moves USDC from perpetual account to spot wallet * Required for spot trading or token purchases ## Returns Returns an object with transfer status: * `status` — `"ok"` if successful * `response` — Contains transfer details: * `type` — `"default"` ## Example request ```shell cURL theme={"system"} # Transfer 100 USDC from spot to perp curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "spotUser", "classTransfer": { "usdc": 100000000, "toPerp": true } }, "nonce": 1234567890123, "signature": {...} }' # Transfer 50 USDC from perp to spot curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "spotUser", "classTransfer": { "usdc": 50000000, "toPerp": false } }, "nonce": 1234567890123, "signature": {...} }' ``` ```python hyperliquid-python-sdk theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Initialize with your private key account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Transfer 100 USDC from spot to perp to_perp = exchange.usd_class_transfer( amount=100.0, to_perp=True, ) # Transfer 50 USDC from perp to spot to_spot = exchange.usd_class_transfer( amount=50.0, to_perp=False, ) print(to_perp) ``` ```typescript @nktkas/hyperliquid theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Initialize with your private key const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Transfer 100 USDC from spot to perp const toPerp = await exchange.usdClassTransfer({ amount: "100", toPerp: true, }); // Transfer 50 USDC from perp to spot const toSpot = await exchange.usdClassTransfer({ amount: "50", toPerp: false, }); console.log(toPerp); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Important considerations * **Balance requirements** — Must have sufficient balance in the source wallet * **Instant transfer** — Transfers are immediate within Hyperliquid * **No fees** — Internal transfers between spot and perp are free * **Position checks** — Cannot transfer from perp if it would cause liquidation ## Use cases * **Trading mode switch** — Move funds between spot and perpetual trading * **Capital allocation** — Optimize fund distribution across trading strategies * **Risk management** — Move profits from perp to spot for safer storage * **Liquidity management** — Quickly reallocate capital based on opportunities > [!NOTE] > > USDC in the spot wallet can be used for spot trading and token purchases. USDC in the perp wallet is used as collateral for perpetual positions. > [!WARNING] > > When transferring from perp to spot, ensure you maintain sufficient margin for any open perpetual positions to avoid liquidation. # Spot token transfer | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-spot-send openapi/hyperliquid_node_api/hypercore_exchange/exchange_spot_send.json post /exchange Transfers spot tokens directly to another address on the Hyperliquid chain. Spot token transfer on Hyperliquid exchange via Chainstack. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Transfers spot tokens directly to another address on the Hyperliquid chain. This is an internal transfer that doesn't interact with the EVM bridge and uses a human-readable signature format for wallet interfaces. ## Parameters ### Required parameters * `action` (object, required) — The spot send action object containing: * `type` (string) — Must be `"spotSend"` * `hyperliquidChain` (string) — `"Mainnet"` for mainnet, `"Testnet"` for testnet * `signatureChainId` (string) — Chain ID used for signing in hex format (e.g., `"0xa4b1"` for Arbitrum) * `destination` (string) — Recipient address in 42-character hexadecimal format * `token` (string) — Token identifier in format `"TOKEN_NAME:TOKEN_ID"` (e.g., `"PURR:0xc1fb593aeffbeb02f85e0308e9956a90"`) * `amount` (string) — Amount of tokens to send (e.g., `"100.5"`) * `time` (number) — Current timestamp in milliseconds (must match nonce) * `nonce` (number, required) — Current timestamp in milliseconds (must match action.time) * `signature` (object, required) — EIP-712 signature of the action ## Token format The token parameter must include both the token name and its ID: * Format: `"TOKEN_NAME:TOKEN_ID"` * Example: `"PURR:0xc1fb593aeffbeb02f85e0308e9956a90"` * The token ID can be found in the spot metadata response ## Signature format This endpoint uses EIP-712 typed data signing with a human-readable format: ```json theme={"system"} { "types": { "HyperliquidTransaction:SpotSend": [ {"name": "hyperliquidChain", "type": "string"}, {"name": "destination", "type": "string"}, {"name": "token", "type": "string"}, {"name": "amount", "type": "string"}, {"name": "time", "type": "uint64"} ] }, "primaryType": "HyperliquidTransaction:SpotSend", "domain": { "name": "HyperliquidSignTransaction", "version": "1", "chainId": 42161, "verifyingContract": "0x0000000000000000000000000000000000000000" }, "message": { "hyperliquidChain": "Mainnet", "destination": "0x...", "token": "PURR:0xc1fb593aeffbeb02f85e0308e9956a90", "amount": "100.0", "time": 1234567890123 } } ``` ## Returns Returns an object with transfer status: * `status` — `"ok"` if successful * `response` — Contains transfer details: * `type` — `"default"` ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "spotSend", "hyperliquidChain": "Mainnet", "signatureChainId": "0xa4b1", "destination": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "token": "PURR:0xc1fb593aeffbeb02f85e0308e9956a90", "amount": "100.0", "time": 1234567890123 }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Initialize with your private key account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Send 100 PURR tokens to another address (signs and posts a spotSend action) transfer_result = exchange.spot_transfer( amount=100.0, destination="0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", token="PURR:0xc1fb593aeffbeb02f85e0308e9956a90", ) print(transfer_result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Initialize with your private key const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); // default public Hyperliquid API const exchange = new ExchangeClient({ transport, wallet }); // Send 100 PURR tokens to another address (signs and posts a spotSend action) const transferResult = await exchange.spotSend({ destination: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", token: "PURR:0xc1fb593aeffbeb02f85e0308e9956a90", amount: "100.0", }); console.log(transferResult); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Important notes * **Spot tokens only** — This endpoint is for spot tokens, not perpetual positions * **Token balance required** — Must have sufficient token balance * **Internal transfers only** — Doesn't interact with external bridges * **Instant settlement** — Transfers are immediate within Hyperliquid ## Use cases * **Token distribution** — Send tokens to multiple recipients * **P2P transfers** — Direct token transfers between users * **Vault operations** — Move tokens to/from vault addresses * **Airdrops** — Programmatic token distribution > [!NOTE] > > You need to know the token ID for the transfer. This can be obtained from the spot metadata endpoint or by checking existing token balances. > [!WARNING] > > Always verify both the destination address and token ID. Transfers are irreversible once confirmed on the Hyperliquid chain. # Staking link disable trading user | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-staking-link-disable-trading-user openapi/hyperliquid_node_api/hypercore_exchange/exchange_staking_link_disable_trading_user.json post /exchange Permanently disable a linked trading user from the staking side on Hyperliquid. This action is irreversible. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `stakingLinkDisableTradingUser` action is sent by the staking user to permanently disable a linked trading user, locking its funds. After one year of locking, the trading user's funds are automatically transferred to the staking user. This is a user-signed EIP-712 action. > [!WARNING] > > This action is irreversible. It permanently disables the linked trading user and locks its funds. ## Parameters ### Required parameters * `action` (object, required) — The disable-trading-user action: * `type` (string) — Must be `"stakingLinkDisableTradingUser"`. * `signatureChainId` (string) — Chain ID in hex used for EIP-712 signing. * `hyperliquidChain` (string) — `"Mainnet"` or `"Testnet"`. * `tradingUser` (string) — The trading user address to disable. * `nonce` (number) — Timestamp in milliseconds, equal to the envelope nonce. * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "stakingLinkDisableTradingUser", "signatureChainId": "0x66eee", "hyperliquidChain": "Mainnet", "tradingUser": "0x0000000000000000000000000000000000000000", "nonce": 1234567890123 }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a # stakingLinkDisableTradingUser helper yet. This is a user-signed (EIP-712) # action, so supply its typed-data fields and primary type when signing. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_user_signed_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = { "type": "stakingLinkDisableTradingUser", "signatureChainId": "0x66eee", "hyperliquidChain": "Mainnet", "tradingUser": "0x0000000000000000000000000000000000000000", "nonce": timestamp, } payload_types = [ {"name": "hyperliquidChain", "type": "string"}, {"name": "tradingUser", "type": "address"}, {"name": "nonce", "type": "uint64"}, ] signature = sign_user_signed_action( wallet, action, payload_types, "HyperliquidTransaction:StakingLinkDisableTradingUser", exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.stakingLinkDisableTradingUser({ tradingUser: "0x0000000000000000000000000000000000000000", }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Revoke a trading link** — Permanently disable a linked trading user * **Secure staked funds** — Lock and eventually reclaim a trading user's funds * **Account lifecycle** — Wind down a delegated trading relationship # Sub-account modify | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-sub-account-modify openapi/hyperliquid_node_api/hypercore_exchange/exchange_sub_account_modify.json post /exchange Rename a sub-account under your master account on Hyperliquid. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `subAccountModify` action renames a sub-account under your master account. ## Parameters ### Required parameters * `action` (object, required) — The sub-account-modify action: * `type` (string) — Must be `"subAccountModify"`. * `subAccountUser` (string) — The sub-account address to modify. * `name` (string) — The new sub-account name (1-16 characters). * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ### Optional parameters * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "subAccountModify", "subAccountUser": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", "name": "Strategy A" }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a subAccountModify # helper yet, so build and post the signed action directly. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = { "type": "subAccountModify", "subAccountUser": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", "name": "Strategy A", } signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.subAccountModify({ subAccountUser: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", name: "Strategy A", }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Organize sub-accounts** — Give sub-accounts meaningful names * **Strategy labeling** — Name sub-accounts by strategy or desk * **Account management** — Maintain a clear sub-account structure # subAccountSpotTransfer | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-sub-account-spot-transfer openapi/hyperliquid_node_api/hypercore_exchange/exchange_sub_account_spot_transfer.json post /exchange Transfers spot tokens between the main account and a sub-account. Available on Hyperliquid exchange via Chainstack JSON-RPC nodes. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Transfers spot tokens between the main account and a sub-account. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"subAccountSpotTransfer"` * `subAccountUser` (string) — Sub-account address * `isDeposit` (boolean) — True to deposit into sub-account, false to withdraw * `token` (string) — Token to transfer * `amount` (string) — Amount to transfer * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "subAccountSpotTransfer"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from eth_account import Account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Wallet that signs the action wallet = Account.from_key("YOUR_PRIVATE_KEY") # Public Hyperliquid mainnet API (this method is not served by Chainstack) exchange = Exchange(wallet, constants.MAINNET_API_URL) # Deposit spot tokens from the main account into the sub-account result = exchange.sub_account_spot_transfer( sub_account_user="0xSUB_ACCOUNT_ADDRESS", is_deposit=True, token="USDC:0xeb62eee3685fc4c43992febcd9e75443", amount=1, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Wallet that signs the action const wallet = privateKeyToAccount("YOUR_PRIVATE_KEY"); // Public Hyperliquid mainnet API (this method is not served by Chainstack) const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Deposit spot tokens from the main account into the sub-account const result = await exchange.subAccountSpotTransfer({ subAccountUser: "0xSUB_ACCOUNT_ADDRESS", isDeposit: true, token: "USDC:0xeb62eee3685fc4c43992febcd9e75443", amount: "1", }); console.log(result); ``` ## Use case * **Sub-account spot transfer** — Transfers spot tokens between the main account and a sub-account. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # subAccountTransfer | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-sub-account-transfer openapi/hyperliquid_node_api/hypercore_exchange/exchange_sub_account_transfer.json post /exchange Transfers USDC between the main account and a sub-account, or between sub-accounts. Transfers USDC between the main account and a sub-account. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Transfers USDC between the main account and a sub-account, or between sub-accounts. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"subAccountTransfer"` * `subAccountUser` (string) — Sub-account address * `isDeposit` (boolean) — True to deposit into sub-account, false to withdraw * `usd` (integer) — Amount in raw USD units (multiply dollars by 1e6) * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "subAccountTransfer"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from eth_account import Account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Load the main account wallet that owns the sub-account wallet = Account.from_key("YOUR_PRIVATE_KEY") exchange = Exchange(wallet, constants.MAINNET_API_URL) # Deposit \$1 USDC into the sub-account (usd is in raw units: dollars * 1e6) result = exchange.sub_account_transfer( sub_account_user="0x0000000000000000000000000000000000000000", is_deposit=True, usd=1 * 1_000_000, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Load the main account wallet that owns the sub-account const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Deposit \$1 USDC into the sub-account (usd is in raw units: dollars * 1e6) const result = await exchange.subAccountTransfer({ subAccountUser: "0x0000000000000000000000000000000000000000", isDeposit: true, usd: 1 * 1e6, }); console.log(result); ``` ## Use case * **Sub-account transfer** — Transfers USDC between the main account and a sub-account, or between sub-accounts. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # tokenDelegate | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-token-delegate openapi/hyperliquid_node_api/hypercore_exchange/exchange_token_delegate.json post /exchange Delegates tokens to a validator for staking on Hyperliquid. Hyperliquid exchange developers can call tokenDelegate via Chainstack. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Delegates tokens to a validator for staking on Hyperliquid. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"tokenDelegate"` * `validator` (string) — Validator address to delegate to * `amount` (string) — Amount to delegate * `isUndelegate` (boolean) — True to undelegate, false to delegate * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "tokenDelegate"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python hyperliquid-python-sdk theme={"system"} from eth_account import Account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Wallet that signs the EIP-712 action. wallet = Account.from_key("0x...") # Public Hyperliquid mainnet endpoint (this method is public-only). exchange = Exchange(wallet, constants.MAINNET_API_URL) # wei is the amount in token units * 1e8; is_undelegate=False delegates. result = exchange.token_delegate( validator="0x...", wei=1 * 10**8, is_undelegate=False, ) print(result) ``` ```typescript @nktkas/hyperliquid theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Wallet that signs the EIP-712 action. const wallet = privateKeyToAccount("0x..."); // Public Hyperliquid mainnet endpoint (this method is public-only). const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // wei is the amount in token units * 1e8; isUndelegate: false delegates. const result = await exchange.tokenDelegate({ validator: "0x...", wei: 1 * 1e8, isUndelegate: false, }); console.log(result); ``` ## Use case * **Token delegate** — Delegates tokens to a validator for staking on Hyperliquid. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # twapCancel | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-twap-cancel openapi/hyperliquid_node_api/hypercore_exchange/exchange_twap_cancel.json post /exchange Cancels an active TWAP order by its ID. twapCancel JSON-RPC method available on the Hyperliquid exchange blockchain via Chainstack. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Cancels an active TWAP order by its ID. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"twapCancel"` * `a` (integer) — Asset index * `t` (integer) — TWAP order ID to cancel * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "twapCancel"}, "nonce": 1234567890123, "signature": {...} }' ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new hl.HttpTransport(); const client = new hl.ExchangeClient({ transport, wallet }); // Cancel an active TWAP order by asset ID (a) and TWAP order ID (t) const result = await client.twapCancel({ a: 0, // asset index t: 1, // TWAP order ID to cancel }); console.log(result); ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a twapCancel # helper yet, so build and post the signed action directly. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = { "type": "twapCancel", "a": 0, # asset index "t": 1, # TWAP order ID to cancel } signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ## Use case * **TWAP cancel** — Cancels an active TWAP order by its ID. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # twapOrder | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-twap-order openapi/hyperliquid_node_api/hypercore_exchange/exchange_twap_order.json post /exchange Places a TWAP (Time-Weighted Average Price) order that splits a large order into smaller slices executed over a specified duration. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Places a TWAP (Time-Weighted Average Price) order that splits a large order into smaller slices executed over a specified duration. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"twapOrder"` * `a` (integer) — Asset index * `b` (boolean) — Is buy order * `s` (string) — Total size * `r` (boolean) — Reduce only * `m` (integer) — Duration in minutes * `t` (boolean) — Randomize slice timing * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "twapOrder"}, "nonce": 1234567890123, "signature": {...} }' ``` ```typescript @nktkas/hyperliquid theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const wallet = privateKeyToAccount("0x..."); // viem or ethers const transport = new hl.HttpTransport(); // public Hyperliquid mainnet const client = new hl.ExchangeClient({ transport, wallet }); // Place a TWAP order (twapOrder) const data = await client.twapOrder({ twap: { a: 0, // asset index b: true, // is buy s: "1", // total size r: false, // reduce only m: 10, // duration in minutes (5–1440) t: true, // randomize slice timing }, }); console.log(data); ``` ```python hyperliquid-python-sdk theme={"system"} # The official hyperliquid-python-sdk does not expose a twapOrder exchange # action (no Exchange.twap_order method as of this SDK version). Use the curl # request above, or the @nktkas/hyperliquid TypeScript SDK, to place a TWAP order. # The Python SDK can read TWAP slice fills via Info.user_twap_slice_fills(...). ``` ## Use case * **TWAP order** — Places a TWAP (Time-Weighted Average Price) order that splits a large order into smaller slices executed over a specified duration. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Update isolated margin | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-update-isolated-margin openapi/hyperliquid_node_api/hypercore_exchange/exchange_update_isolated_margin.json post /exchange Adds or removes margin from an isolated perpetual position on the Hyperliquid exchange. Chainstack Hyperliquid exchange reference. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Adds or removes margin from an isolated perpetual position on the Hyperliquid exchange. This allows you to adjust the margin allocated to a specific position without closing it. ## Parameters ### Required parameters * `action` (object, required) — The update isolated margin action object containing: * `type` (string) — Must be `"updateIsolatedMargin"` or `"topUpIsolatedOnlyMargin"` * For `"updateIsolatedMargin"`: * `asset` (number) — Asset index of the coin * `isBuy` (boolean) — `true` (reserved for future hedge mode support) * `ntli` (number) — Amount to add (positive) or remove (negative) in USDC with 6 decimals (e.g., 1000000 = 1 USDC) * For `"topUpIsolatedOnlyMargin"`: * `asset` (number) — Asset index of the coin * `leverage` (string) — Target leverage as a float string (e.g., "5.0") * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected ## Margin adjustment types ### Direct USDC adjustment Use `"updateIsolatedMargin"` to add or remove a specific USDC amount: * Positive `ntli` — Adds margin to the position * Negative `ntli` — Removes margin from the position ### Target leverage adjustment Use `"topUpIsolatedOnlyMargin"` to set a target leverage: * Calculates required margin to achieve the specified leverage * Only allows adding margin (top-up), not removal ## Returns Returns an object with update status: * `status` — `"ok"` if successful * `response` — Contains update details: * `type` — `"default"` ## Example request ```shell cURL theme={"system"} # Add 100 USDC to isolated position curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "updateIsolatedMargin", "asset": 0, "isBuy": true, "ntli": 100000000 }, "nonce": 1234567890123, "signature": {...} }' # Set position to 5x leverage curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "topUpIsolatedOnlyMargin", "asset": 0, "leverage": "5.0" }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Initialize with your private key account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Add 100 USDC to the isolated BTC position add_result = exchange.update_isolated_margin(amount=100.0, name="BTC") # Remove 50 USDC from the isolated BTC position (negative for removal) remove_result = exchange.update_isolated_margin(amount=-50.0, name="BTC") print(add_result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Add 100 USDC to the isolated position (ntli is the float amount * 1e6) const addResult = await exchange.updateIsolatedMargin({ asset: 0, isBuy: true, ntli: 100 * 1e6, }); // Remove 50 USDC from the isolated position (negative for removal) const removeResult = await exchange.updateIsolatedMargin({ asset: 0, isBuy: true, ntli: -50 * 1e6, }); // Set the position to 5x leverage by topping up isolated margin const leverageResult = await exchange.topUpIsolatedOnlyMargin({ asset: 0, leverage: "5.0", }); console.log(addResult); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Important considerations * **Isolated only** — This only works for isolated margin positions, not cross margin * **Position required** — Must have an existing isolated position * **Removal limits** — Cannot remove margin below minimum requirements * **Liquidation risk** — Removing margin increases liquidation risk ## Use cases * **Risk management** — Add margin to reduce liquidation risk during volatility * **Capital optimization** — Remove excess margin for use elsewhere * **Dynamic adjustment** — Adjust position margin based on market conditions * **Leverage targeting** — Set specific leverage levels for risk management > [!NOTE] > > The `isBuy` parameter is currently always `true` but is included for future hedge mode support where long and short positions can be held simultaneously. > [!WARNING] > > Removing margin from a position increases its effective leverage and liquidation risk. Always ensure remaining margin meets minimum requirements. # Update leverage | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-update-leverage openapi/hyperliquid_node_api/hypercore_exchange/exchange_update_leverage.json post /exchange Updates the leverage setting for a specific coin on the Hyperliquid exchange. You can adjust leverage for both cross and isolated margin modes. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Updates the leverage setting for a specific coin on the Hyperliquid exchange. You can adjust leverage for both cross and isolated margin modes. ## Parameters ### Required parameters * `action` (object, required) — The update leverage action object containing: * `type` (string) — Must be `"updateLeverage"` * `asset` (number) — Asset index of the coin * `isCross` (boolean) — `true` for cross leverage, `false` for isolated leverage * `leverage` (number) — New leverage value (must be within allowed range for the asset) * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected ## Leverage constraints Each asset has specific leverage constraints: * **Minimum leverage** — Usually 1x * **Maximum leverage** — Varies by asset (3x to 40x depending on liquidity and volatility) * **Cross margin** — Shares margin across all positions * **Isolated margin** — Margin isolated to specific position ## Returns Returns an object with update status: * `status` — `"ok"` if successful * `response` — Contains update details: * `type` — `"default"` ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "updateLeverage", "asset": 0, "isCross": true, "leverage": 10 }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Initialize with your private key account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Update cross leverage for BTC to 10x update_result = exchange.update_leverage( leverage=10, name="BTC", is_cross=True, ) # Update isolated leverage for ETH to 5x update_isolated = exchange.update_leverage( leverage=5, name="ETH", is_cross=False, ) print(update_result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Initialize with your private key const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Update cross leverage for asset 0 (BTC) to 10x const updateResult = await exchange.updateLeverage({ asset: 0, isCross: true, leverage: 10, }); // Update isolated leverage for asset 1 (ETH) to 5x const updateIsolated = await exchange.updateLeverage({ asset: 1, isCross: false, leverage: 5, }); console.log(updateResult); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Important considerations * **Existing positions** — Leverage changes affect margin requirements for existing positions * **Liquidation risk** — Increasing leverage increases liquidation risk * **Available margin** — Ensure sufficient margin before increasing leverage * **Asset limits** — Each asset has different maximum leverage limits ## Use cases * **Risk adjustment** — Dynamically adjust risk based on market conditions * **Capital efficiency** — Optimize margin usage across positions * **Strategy implementation** — Set appropriate leverage for different trading strategies * **Position management** — Adjust leverage before opening new positions > [!NOTE] > > Leverage is only checked when opening a position. After opening, monitor your positions to avoid liquidation as leverage effectively changes with unrealized PnL. > [!WARNING] > > Increasing leverage on existing positions increases liquidation risk. Always ensure you have sufficient margin to support the new leverage level. # USDC transfer | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-usd-send openapi/hyperliquid_node_api/hypercore_exchange/exchange_usd_send.json post /exchange Transfers USDC directly to another address on the Hyperliquid chain. Available on Hyperliquid exchange via Chainstack JSON-RPC nodes. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Transfers USDC directly to another address on the Hyperliquid chain. This is an internal transfer that doesn't interact with the EVM bridge and uses a human-readable signature format for wallet interfaces. ## Parameters ### Required parameters * `action` (object, required) — The USD send action object containing: * `type` (string) — Must be `"usdSend"` * `hyperliquidChain` (string) — `"Mainnet"` for mainnet, `"Testnet"` for testnet * `signatureChainId` (string) — Chain ID used for signing in hex format (e.g., `"0xa4b1"` for Arbitrum) * `destination` (string) — Recipient address in 42-character hexadecimal format * `amount` (string) — Amount of USDC to send (e.g., `"100.5"` for 100.5 USDC) * `time` (number) — Current timestamp in milliseconds (must match nonce) * `nonce` (number, required) — Current timestamp in milliseconds (must match action.time) * `signature` (object, required) — EIP-712 signature of the action ## Signature format This endpoint uses EIP-712 typed data signing with a human-readable format: ```json theme={"system"} { "types": { "HyperliquidTransaction:UsdSend": [ {"name": "hyperliquidChain", "type": "string"}, {"name": "destination", "type": "string"}, {"name": "amount", "type": "string"}, {"name": "time", "type": "uint64"} ] }, "primaryType": "HyperliquidTransaction:UsdSend", "domain": { "name": "HyperliquidSignTransaction", "version": "1", "chainId": 42161, "verifyingContract": "0x0000000000000000000000000000000000000000" }, "message": { "hyperliquidChain": "Mainnet", "destination": "0x...", "amount": "100.5", "time": 1234567890123 } } ``` ## Returns Returns an object with transfer status: * `status` — `"ok"` if successful * `response` — Contains transfer details: * `type` — `"default"` ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "usdSend", "hyperliquidChain": "Mainnet", "signatureChainId": "0xa4b1", "destination": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "amount": "100.5", "time": 1234567890123 }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Initialize with your private key account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Send 100.5 USDC to another address transfer_result = exchange.usd_transfer( amount=100.5, destination="0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", ) print(transfer_result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Initialize with your private key const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Send 100.5 USDC to another address const transferResult = await exchange.usdSend({ destination: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", amount: "100.5", }); console.log(transferResult); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Important notes * **Internal transfers only** — This doesn't interact with external bridges * **Instant settlement** — Transfers are immediate within Hyperliquid * **No gas fees** — Internal transfers don't require gas * **Decimal precision** — Amount is a string to preserve decimal precision ## Use cases * **P2P transfers** — Send USDC directly to other users * **Account funding** — Transfer between your own accounts * **Payment processing** — Programmatic USDC payments * **Vault operations** — Transfer funds to/from vault addresses > [!NOTE] > > The `time` parameter in the action must exactly match the `nonce` parameter. This ensures signature uniqueness and prevents replay attacks. > [!WARNING] > > Always verify the destination address. Transfers are irreversible once confirmed on the Hyperliquid chain. # userDexAbstraction | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-user-dex-abstraction openapi/hyperliquid_node_api/hypercore_exchange/exchange_user_dex_abstraction.json post /exchange Sets the user's DEX abstraction configuration for perp DEX routing. This is being deprecated in favor of the unified account system. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Sets the user's DEX abstraction configuration for perp DEX routing. This is being deprecated in favor of the unified account system. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"userDexAbstraction"` * `dexAbstraction` (object) — DEX abstraction configuration * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "userDexAbstraction"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Load the wallet that signs the action. wallet = eth_account.Account.from_key("YOUR_PRIVATE_KEY") # Public Hyperliquid API (this method is public-only). exchange = Exchange(wallet, constants.MAINNET_API_URL) result = exchange.user_dex_abstraction( user=wallet.address, enabled=True, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Wallet that signs the action (viem or ethers). const wallet = privateKeyToAccount("0xYOUR_PRIVATE_KEY"); // Public Hyperliquid API (this method is public-only). const transport = new hl.HttpTransport(); const client = new hl.ExchangeClient({ transport, wallet }); const result = await client.userDexAbstraction({ user: wallet.address, enabled: true, }); console.log(result); ``` ## Use case * **Set user DEX abstraction** — Sets the user's DEX abstraction configuration for perp DEX routing. This is being deprecated in favor of the unified account system. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # User outcome | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-user-outcome openapi/hyperliquid_node_api/hypercore_exchange/exchange_user_outcome.json post /exchange Split or merge outcome-market shares to convert between quote tokens and Yes/No shares on Hyperliquid. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `userOutcome` action manually splits or merges outcome-market shares to convert between quote tokens and Yes/No shares. Outcome markets are binary (Yes/No) prediction markets on Hyperliquid. A single `userOutcome` action carries exactly one of four operations: `splitOutcome`, `mergeOutcome`, `mergeQuestion`, or `negateOutcome`. ## Parameters ### Required parameters * `action` (object, required) — The user outcome action. Set `type` to `"userOutcome"` and include exactly one of the following operations: * `splitOutcome` — Split `X` quote tokens into `X` Yes and `X` No shares of an outcome. Fields: `outcome` (number), `amount` (string, e.g. `"123.0"`). * `mergeOutcome` — Merge `X` Yes and `X` No shares of an outcome into `X` quote tokens. Fields: `outcome` (number), `amount` (string or `null`, where `null` means the maximum available). * `mergeQuestion` — Merge `X` Yes shares from each outcome associated to the same question into `X` quote tokens. Fields: `question` (number), `amount` (string or `null`, where `null` means the maximum available). * `negateOutcome` — Convert `X` No shares from an outcome associated with a question into `X` Yes shares of every other outcome associated with the question. Fields: `question` (number), `outcome` (number), `amount` (string). * `nonce` (number, required) — Current timestamp in milliseconds (must be recent). * `signature` (object, required) — EIP-712 signature of the action. ### Optional parameters * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details: * `type` — `"default"`. ## Example request ```shell cURL theme={"system"} # Split outcome: split 1 quote token into 1 Yes and 1 No share of outcome 0 curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "userOutcome", "splitOutcome": {"outcome": 0, "amount": "1"} }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a userOutcome # helper yet, so build and post the signed action directly. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = { "type": "userOutcome", "splitOutcome": {"outcome": 0, "amount": "1"}, } signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Split 1 quote token into 1 Yes and 1 No share of outcome 0 const result = await exchange.userOutcome({ splitOutcome: { outcome: 0, amount: "1" }, }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Provide outcome liquidity** — Split quote tokens into Yes and No shares to make markets on both sides * **Redeem shares** — Merge matched Yes and No shares back into quote tokens * **Manage positions across a question** — Merge or negate shares across the outcomes of a single question # User portfolio margin | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-user-portfolio-margin openapi/hyperliquid_node_api/hypercore_exchange/exchange_user_portfolio_margin.json post /exchange Enable or disable portfolio margin for a Hyperliquid account. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `userPortfolioMargin` action enables or disables portfolio margin for an account. This is a user-signed EIP-712 action. ## Parameters ### Required parameters * `action` (object, required) — The portfolio-margin action: * `type` (string) — Must be `"userPortfolioMargin"`. * `signatureChainId` (string) — Chain ID in hex used for EIP-712 signing. * `hyperliquidChain` (string) — `"Mainnet"` or `"Testnet"`. * `user` (string) — The user address. * `enabled` (boolean) — `true` to enable portfolio margin, `false` to disable it. * `nonce` (number) — Timestamp in milliseconds, equal to the envelope nonce. * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "userPortfolioMargin", "signatureChainId": "0x66eee", "hyperliquidChain": "Mainnet", "user": "0x0000000000000000000000000000000000000000", "enabled": true, "nonce": 1234567890123 }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a userPortfolioMargin # helper yet. This is a user-signed (EIP-712) action, so supply its typed-data # fields and primary type when signing. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_user_signed_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = { "type": "userPortfolioMargin", "signatureChainId": "0x66eee", "hyperliquidChain": "Mainnet", "user": "0x0000000000000000000000000000000000000000", "enabled": True, "nonce": timestamp, } payload_types = [ {"name": "hyperliquidChain", "type": "string"}, {"name": "user", "type": "address"}, {"name": "enabled", "type": "bool"}, {"name": "nonce", "type": "uint64"}, ] signature = sign_user_signed_action( wallet, action, payload_types, "HyperliquidTransaction:UserPortfolioMargin", exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.userPortfolioMargin({ user: "0x0000000000000000000000000000000000000000", enabled: true, }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Enable portfolio margin** — Margin across the whole portfolio rather than per-position * **Toggle margin mode** — Switch portfolio margin on or off * **Capital efficiency** — Optimize margin usage for diversified portfolios # userSetAbstraction | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-user-set-abstraction openapi/hyperliquid_node_api/hypercore_exchange/exchange_user_set_abstraction.json post /exchange Sets the user's account abstraction configuration. This controls how the user's account interacts with the unified account system. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Sets the user's account abstraction configuration. This controls how the user's account interacts with the unified account system. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"userSetAbstraction"` * `abstraction` (object) — Abstraction configuration * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "userSetAbstraction"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from eth_account import Account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # The wallet that signs the EIP-712 action wallet = Account.from_key("0x...") # your private key # Public Hyperliquid mainnet API (userSetAbstraction is public-only) exchange = Exchange(wallet, constants.MAINNET_API_URL) # Set the account abstraction mode for the user result = exchange.user_set_abstraction( user=wallet.address, abstraction="unifiedAccount", # "unifiedAccount" | "portfolioMargin" | "disabled" ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // The wallet that signs the EIP-712 action const wallet = privateKeyToAccount("0x..."); // your private key // Public Hyperliquid mainnet API (userSetAbstraction is public-only) const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Set the account abstraction mode for the user const result = await exchange.userSetAbstraction({ user: wallet.address, abstraction: "unifiedAccount", // "dexAbstraction" | "unifiedAccount" | "portfolioMargin" | "disabled" }); console.log(result); ``` ## Use case * **Set user abstraction** — Sets the user's account abstraction configuration. This controls how the user's account interacts with the unified account system. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # validatorL1Stream | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-validator-l1-stream openapi/hyperliquid_node_api/hypercore_exchange/exchange_validator_l1_stream.json post /exchange Submits a validator L1 stream action for processing consensus-layer events. validatorL1Stream on Hyperliquid exchange via Chainstack. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Submits a validator L1 stream action for processing consensus-layer events. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"validatorL1Stream"` * `payload` (object) — L1 stream payload * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "validatorL1Stream"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # hyperliquid-python-sdk has no typed validatorL1Stream wrapper, so build and # sign the L1 action with the same primitives its typed methods use internally. import eth_account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms wallet = eth_account.Account.from_key("0x...") # your validator key exchange = Exchange(wallet, constants.MAINNET_API_URL) action = {"type": "validatorL1Stream", "riskFreeRate": "0.05"} nonce = get_timestamp_ms() signature = sign_l1_action( wallet, action, None, # vault_address nonce, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, nonce) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; const wallet = privateKeyToAccount("0x..."); // your validator key const transport = new HttpTransport(); // public mainnet const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.validatorL1Stream({ riskFreeRate: "0.05", }); console.log(result); ``` ## Use case * **Validator L1 stream** — Submits a validator L1 stream action for processing consensus-layer events. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Vault distribute | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-vault-distribute openapi/hyperliquid_node_api/hypercore_exchange/exchange_vault_distribute.json post /exchange Distribute funds from a vault to its depositors, or close the vault by distributing zero. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `vaultDistribute` action distributes funds from a vault to its depositors. Setting `usd` to `0` closes the vault. ## Parameters ### Required parameters * `action` (object, required) — The vault-distribute action: * `type` (string) — Must be `"vaultDistribute"`. * `vaultAddress` (string) — The vault address. * `usd` (number) — Amount to distribute as a USD float multiplied by 1e6. Set to `0` to close the vault. * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ### Optional parameters * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "vaultDistribute", "vaultAddress": "0x1719884eb866cb12b2287399b15f7db5e7d775ea", "usd": 1000000 }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a vaultDistribute # helper yet, so build and post the signed action directly. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = { "type": "vaultDistribute", "vaultAddress": "0x1719884eb866cb12b2287399b15f7db5e7d775ea", "usd": 1000000, # 1 USD * 1e6; use 0 to close the vault } signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.vaultDistribute({ vaultAddress: "0x1719884eb866cb12b2287399b15f7db5e7d775ea", usd: 1000000, // 1 USD * 1e6; use 0 to close the vault }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Return profits** — Distribute gains to vault depositors * **Wind down** — Close a vault by distributing zero * **Vault administration** — Manage payouts as a vault leader # Vault modify | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-vault-modify openapi/hyperliquid_node_api/hypercore_exchange/exchange_vault_modify.json post /exchange Modify a vault's settings — whether it accepts deposits and whether it always closes positions on withdrawal. On Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. The `vaultModify` action updates a vault's settings — whether it accepts deposits from followers and whether it always closes positions on withdrawal. ## Parameters ### Required parameters * `action` (object, required) — The vault-modify action: * `type` (string) — Must be `"vaultModify"`. * `vaultAddress` (string) — The vault address to modify. * `allowDeposits` (boolean or null) — Allow deposits from followers; `null` leaves it unchanged. * `alwaysCloseOnWithdraw` (boolean or null) — Always close positions on withdrawal; `null` leaves it unchanged. * `nonce` (number, required) — Current timestamp in milliseconds. * `signature` (object, required) — EIP-712 signature of the action. ### Optional parameters * `expiresAfter` (number, optional) — Timestamp in milliseconds after which the request is rejected. ## Returns Returns an object with the action status: * `status` — `"ok"` if the request was processed. * `response` — Contains operation details, with `type` `"default"`. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "vaultModify", "vaultAddress": "0x1719884eb866cb12b2287399b15f7db5e7d775ea", "allowDeposits": true, "alwaysCloseOnWithdraw": false }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} # Note: the official hyperliquid-python-sdk does not expose a vaultModify helper # yet, so build and post the signed action directly. from hyperliquid.exchange import Exchange from hyperliquid.utils import constants from hyperliquid.utils.signing import sign_l1_action, get_timestamp_ms import eth_account # Public Hyperliquid mainnet endpoint wallet = eth_account.Account.from_key("0x...") # your private key exchange = Exchange(wallet, constants.MAINNET_API_URL) timestamp = get_timestamp_ms() action = { "type": "vaultModify", "vaultAddress": "0x1719884eb866cb12b2287399b15f7db5e7d775ea", "allowDeposits": True, "alwaysCloseOnWithdraw": False, } signature = sign_l1_action( wallet, action, None, # vault_address timestamp, None, # expires_after exchange.base_url == constants.MAINNET_API_URL, ) result = exchange._post_action(action, signature, timestamp) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Public Hyperliquid mainnet endpoint (default transport) const wallet = privateKeyToAccount("0x..."); // viem or ethers account const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); const result = await exchange.vaultModify({ vaultAddress: "0x1719884eb866cb12b2287399b15f7db5e7d775ea", allowDeposits: true, alwaysCloseOnWithdraw: false, }); console.log(result); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Use cases * **Open or close to deposits** — Control whether followers can deposit * **Set withdrawal behavior** — Toggle always-close-on-withdraw * **Vault administration** — Adjust vault policy as a vault leader # vaultTransfer | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-vault-transfer openapi/hyperliquid_node_api/hypercore_exchange/exchange_vault_transfer.json post /exchange Deposits into or withdraws from a Hyperliquid vault. vaultTransfer JSON-RPC method available on the Hyperliquid exchange blockchain via Chainstack. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Deposits into or withdraws from a Hyperliquid vault. ## Parameters ### Required parameters * `action` (object, required) — The action object containing: * `type` (string) — Must be `"vaultTransfer"` * `vaultAddress` (string) — Vault address * `isDeposit` (boolean) — True to deposit, false to withdraw * `usd` (integer) — Amount in raw USD units * `nonce` (number, required) — Current timestamp in milliseconds (must be recent) * `signature` (object, required) — EIP-712 signature of the action ### Optional parameters * `vaultAddress` (string, optional) — Address when trading on behalf of a vault or subaccount ## Returns Returns an object with the action status: * `status` — `"ok"` if request processed * `response` — Contains action result data ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": {"type": "vaultTransfer"}, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from eth_account import Account from hyperliquid.exchange import Exchange from hyperliquid.utils import constants # Load the wallet that signs the action wallet = Account.from_key("0xYOUR_PRIVATE_KEY") # Public Hyperliquid mainnet API exchange = Exchange(wallet, constants.MAINNET_API_URL) # Deposit into the vault. usd is the amount in raw USD units (float * 1e6), # so 10 USDC is 10 * 1_000_000. Set is_deposit=False to withdraw. result = exchange.vault_usd_transfer( vault_address="0x1719884eb866cb12b2287399b15f7db5e7d775ea", is_deposit=True, usd=10 * 1_000_000, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Wallet that signs the action const wallet = privateKeyToAccount("0xYOUR_PRIVATE_KEY"); // Public Hyperliquid mainnet API (default transport) const transport = new hl.HttpTransport(); const client = new hl.ExchangeClient({ transport, wallet }); // Deposit into the vault. usd is the amount in raw USD units (float * 1e6), // so 10 USDC is 10 * 1e6. Set isDeposit: false to withdraw. const result = await client.vaultTransfer({ vaultAddress: "0x1719884eb866cb12b2287399b15f7db5e7d775ea", isDeposit: true, usd: 10 * 1e6, }); console.log(result); ``` ## Use case * **Vault transfer** — Deposits into or withdraws from a Hyperliquid vault. > [!WARNING] > > Always ensure your system clock is synchronized. Nonce must be within a reasonable time window of the current server time or the request will be rejected. # Withdraw USDC | Hyperliquid exchange Source: https://docs.chainstack.com/reference/hyperliquid-exchange-withdraw openapi/hyperliquid_node_api/hypercore_exchange/exchange_withdraw.json post /exchange Initiates a USDC withdrawal from Hyperliquid to Arbitrum. After making this request, L1 validators sign and send the withdrawal to the bridge contract. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > This endpoint requires signature authentication. See our comprehensive [Authentication via Signatures guide](/docs/hyperliquid-authentication-guide) for implementation details. Initiates a USDC withdrawal from Hyperliquid to Arbitrum. After making this request, L1 validators sign and send the withdrawal to the bridge contract. Withdrawals typically complete in approximately 5 minutes. ## Parameters ### Required parameters * `action` (object, required) — The withdrawal action object containing: * `type` (string) — Must be `"withdraw3"` * `hyperliquidChain` (string) — `"Mainnet"` for mainnet, `"Testnet"` for testnet * `signatureChainId` (string) — Chain ID used for signing in hex format (e.g., `"0xa4b1"` for Arbitrum) * `destination` (string) — Arbitrum destination address in 42-character hexadecimal format * `amount` (string) — Amount of USDC to withdraw (e.g., `"100.5"` for 100.5 USDC) * `time` (number) — Current timestamp in milliseconds (must match nonce) * `nonce` (number, required) — Current timestamp in milliseconds (must match action.time) * `signature` (object, required) — EIP-712 signature of the action ## Withdrawal details * **Fee** — \$1 USDC withdrawal fee * **Processing time** — Approximately 5 minutes * **Destination** — Withdrawals go to Arbitrum network * **Minimum amount** — Must be greater than the withdrawal fee ## Signature format This endpoint uses EIP-712 typed data signing: ```json theme={"system"} { "types": { "HyperliquidTransaction:Withdraw": [ {"name": "hyperliquidChain", "type": "string"}, {"name": "destination", "type": "string"}, {"name": "amount", "type": "string"}, {"name": "time", "type": "uint64"} ] }, "primaryType": "HyperliquidTransaction:Withdraw", "domain": { "name": "HyperliquidSignTransaction", "version": "1", "chainId": 42161, "verifyingContract": "0x0000000000000000000000000000000000000000" }, "message": { "hyperliquidChain": "Mainnet", "destination": "0x...", "amount": "100.5", "time": 1234567890123 } } ``` ## Returns Returns an object with withdrawal initiation status: * `status` — `"ok"` if withdrawal initiated * `response` — Contains withdrawal details: * `type` — `"default"` ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/exchange \ -H "Content-Type: application/json" \ -d '{ "action": { "type": "withdraw3", "hyperliquidChain": "Mainnet", "signatureChainId": "0xa4b1", "destination": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "amount": "100.5", "time": 1234567890123 }, "nonce": 1234567890123, "signature": {...} }' ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.exchange import Exchange from hyperliquid.utils import constants import eth_account # Initialize with your private key account = eth_account.Account.from_key("0x...") exchange = Exchange(account, constants.MAINNET_API_URL) # Withdraw 100.5 USDC to Arbitrum withdrawal_result = exchange.withdraw_from_bridge( amount=100.5, destination="0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", ) print(withdrawal_result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { ExchangeClient, HttpTransport } from "@nktkas/hyperliquid"; import { privateKeyToAccount } from "viem/accounts"; // Initialize with your private key const wallet = privateKeyToAccount("0x..."); const transport = new HttpTransport(); const exchange = new ExchangeClient({ transport, wallet }); // Withdraw 100.5 USDC to Arbitrum const withdrawalResult = await exchange.withdraw3({ destination: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", amount: "100.5", }); console.log(withdrawalResult); ``` ## Response example ```json theme={"system"} { "status": "ok", "response": { "type": "default" } } ``` ## Withdrawal process 1. **Initiation** — Request sent to Hyperliquid 2. **Validation** — System validates balance and parameters 3. **Signing** — L1 validators sign the withdrawal 4. **Bridge interaction** — Withdrawal sent to Arbitrum bridge contract 5. **Completion** — USDC arrives in Arbitrum wallet (\~5 minutes) ## Use cases * **Profit taking** — Withdraw trading profits to Arbitrum * **Bridge to other chains** — Move funds to Arbitrum for further bridging * **Risk management** — Reduce exposure by moving funds off-platform * **Treasury management** — Regular withdrawal schedules > [!NOTE] > > Ensure you have enough USDC to cover both the withdrawal amount and the \$1 fee. The fee is deducted from your Hyperliquid balance, not from the withdrawal amount. > [!WARNING] > > Withdrawals are irreversible once initiated. Always verify the destination address on Arbitrum. The address must be a valid Arbitrum address that can receive USDC. # Hyperliquid API reference: HyperCore and HyperEVM RPC Source: https://docs.chainstack.com/reference/hyperliquid-getting-started Use the Hyperliquid API on Chainstack to access HyperCore order books and HyperEVM JSON-RPC for perpetuals, spot trading, and smart contracts. Chain ID 999, 129 methods served, archive and debug & trace on mainnet and testnet. The Hyperliquid API gives developers programmatic access to two execution layers on one chain: HyperCore, the on-chain order book for perpetuals and spot trading, and HyperEVM, the Ethereum-compatible smart contract layer. Chainstack serves both through a single Hyperliquid node endpoint. ## Hyperliquid API at a glance | Property | Value | | - | - | | Chain | Hyperliquid — Layer 1, HyperBFT consensus (HotStuff lineage) | | Execution layers | HyperCore (Rust order book) and HyperEVM (EVM smart contracts) | | HyperEVM chain ID | `999` (mainnet), `998` (testnet) | | HyperCore throughput | 200,000 orders per second, one-block finality | | API endpoints | `/info` and `/exchange` (HyperCore), `/evm` and `/nanoreth` (HyperEVM) | | Methods served by Chainstack | 129 — all 83 HyperEVM JSON-RPC methods + 46 HyperCore `/info` queries | | Methods via public Hyperliquid API | 83 — 30 HyperCore `/info` + 53 HyperCore `/exchange` | | Real-time data | WebSocket `eth_subscribe` (newHeads, logs, syncing) on HyperEVM | | Debug & trace | `debug_*`, `trace_*`, `erigon_*`, and Otterscan `ots_*` namespaces | | Networks | Mainnet and testnet, full and archive nodes | | Request units | 1 RU per full request, 2 RUs per archive request | ## What is the Hyperliquid protocol Hyperliquid is a high-performance Layer 1 blockchain that focuses on providing a deep liquidity DEX for spot trading and for perpetual trading. The Hyperliquid founder calls it [the AWS of liquidity](https://mobile.x.com/chameleon_jeff/status/1841392573199585291). Hyperliquid uses a custom consensus algorithm called HyperBFT, which is inspired by HotStuff and its successors. State execution operates through two components that share consensus and produce interleaved blocks: * HyperCore — provides fully on-chain perpetual futures and spot order books with one-block finality. HyperCore supports 200,000 orders per second, with throughput continuously improving through node software optimization. Order matching, margin, and position management run natively in Rust with zero gas fees. * HyperEVM — brings Ethereum-compatible smart contract functionality to Hyperliquid. It runs on chain ID `999` (mainnet) and `998` (testnet), and makes HyperCore's liquidity and financial primitives available as permissionless building blocks for developers. The two layers are tightly integrated. HyperEVM contracts read HyperCore order book prices, positions, and balances directly through precompiles — no external oracles — and write actions back to HyperCore through the CoreWriter system contract. See the [CoreWriter guide](/docs/hyperliquid-corewriter) and the [infrastructure FAQ](/docs/hyperliquid-infrastructure-faq) for the full architecture. ## What is the Hyperliquid API The Hyperliquid API lets developers build trading applications, analytics tools, and portfolio management systems. To read market data and execute trades, an application connects to a Hyperliquid node endpoint, then calls the layer it needs: * HyperCore exposes `/info` for queries (market data, account state) and `/exchange` for trading actions (orders, cancels, transfers). * HyperEVM exposes a standard JSON-RPC interface, plus WebSocket subscriptions for real-time data streaming. ## HyperCore and HyperEVM API surface The platform exposes distinct surfaces for each layer. The table below shows what runs where, and which surfaces Chainstack serves directly versus which route through the official Hyperliquid public API. | API surface | Endpoint path | Engine | Mainnet | Testnet | Served by | | - | - | - | - | - | - | | HyperEVM JSON-RPC | `/evm`, `/nanoreth` | HyperEVM | Yes | Yes | Chainstack | | HyperEVM WebSocket | `/evm` (WSS) | HyperEVM | Yes | Yes | Chainstack | | HyperEVM debug & trace | `/evm` | HyperEVM | Yes | Yes | Chainstack | | HyperCore info — queries | `/info` | HyperCore | Yes | Yes | Chainstack (46 methods) + public API (30) | | HyperCore exchange — trading | `/exchange` | HyperCore | Yes | Yes | Public Hyperliquid API only | > [!NOTE] > > Chainstack serves HyperEVM on two paths that exist by default on every node: `/evm` (hl-node-compliant — system transactions stripped from block-shaped responses) and `/nanoreth` (system transactions included). Append the path you need to your endpoint URL. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration). ### What you can build With a Chainstack Hyperliquid endpoint you can: * Query HyperEVM chain state — balances, blocks, logs, receipts, and gas — through the standard Ethereum JSON-RPC interface, compatible with web3.js, ethers, viem, and web3.py. * Deploy and call Solidity contracts on HyperEVM with Hardhat, Foundry, and Remix, and read HyperCore state from those contracts through precompiles. * Debug and trace transactions with `debug_traceTransaction`, `trace_block`, and the Otterscan `ots_*` namespace on archive nodes. * Stream real-time data over WebSocket with `eth_subscribe` for `newHeads`, `logs`, and `syncing`. * Read HyperCore market and account state — `meta`, `clearinghouseState`, `openOrders`, `l2Book`, `candleSnapshot`, `fundingHistory`, and more — through `/info`. * Place, cancel, and modify orders and run transfers, staking, and vault actions through `/exchange` on the public Hyperliquid API. ## Methods Chainstack documents 212 Hyperliquid methods across the two layers: | Engine | Endpoint | Methods | Availability | | - | - | - | - | | HyperEVM | `/evm` | 83 | All served by Chainstack | | HyperCore | `/info` | 76 | 46 served by Chainstack, 30 on the public Hyperliquid API | | HyperCore | `/exchange` | 53 | Public Hyperliquid API (routed to validators) | For the complete, per-method availability matrix, see [Hyperliquid methods](/docs/hyperliquid-methods). > [!NOTE] > > Methods marked "Hyperliquid public RPC" are proprietary to the Hyperliquid DEX and have not been open sourced, so they are only available on the public Hyperliquid infrastructure. If you receive `Failed to deserialize the JSON body into the target type`, switch that call to the public Hyperliquid RPC. ## How to start using the Hyperliquid API To use the Hyperliquid API, you need access to a Hyperliquid node endpoint. Follow these steps to sign up, deploy a node, and find your credentials: 1. [Sign up with Chainstack](https://console.chainstack.com/user/account/create). 2. [Deploy a node](/docs/manage-your-networks). 3. [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the Hyperliquid blockchain and build trading applications. ## SDKs and tooling You can call the API directly over HTTP and WebSocket, or use a maintained SDK: * Python — the official [hyperliquid-python-sdk](https://github.com/hyperliquid-dex/hyperliquid-python-sdk) (`pip install hyperliquid-python-sdk`). * TypeScript — the community [@nktkas/hyperliquid](https://github.com/nktkas/hyperliquid) SDK with full type safety (`npm install @nktkas/hyperliquid`). * Rust — the official [hyperliquid-rust-sdk](https://github.com/hyperliquid-dex/hyperliquid-rust-sdk) (`cargo add hyperliquid_rust_sdk`). Point any SDK at your Chainstack endpoint. See [Hyperliquid tooling](/docs/hyperliquid-tooling) for setup and the [nktkas TypeScript SDK guide](/docs/hyperliquid-typescript-sdk-nktkas) for end-to-end examples. ## Rate limits The limits below apply to the public Hyperliquid infrastructure. A Chainstack private node removes the HyperEVM request ceiling and gives you dedicated bandwidth and geographic routing. | Surface | Public infrastructure limit | | - | - | | HyperEVM (`/evm`) | 100 requests/min | | REST API (`/info`, `/exchange`) | 1,200 weight/min | | WebSocket connections | 100 simultaneous | | WebSocket subscriptions | 1,000 across all connections | | WebSocket messages | 2,000 messages/min | > [!TIP] > > Private RPC becomes necessary for applications above 100 HyperEVM requests per minute, or that need SLA guarantees, event indexing, or high-frequency trading. For the full weight table and address-based action limits, see the [infrastructure FAQ](/docs/hyperliquid-infrastructure-faq#public-infrastructure-rate-limiting). ## Networks Chainstack supports both Hyperliquid mainnet and testnet, with full and archive node modes, WebSocket connections, and debug & trace APIs. | Feature | Mainnet | Testnet | | - | - | - | | HyperEVM chain ID | `999` | `998` | | Full nodes | Yes | Yes | | Archive nodes | Yes | Yes | | WebSocket | Yes | Yes | | Debug & trace APIs | Yes | Yes | > [!NOTE] > > On testnet, archive data is available starting from block `34112653`. Queries for blocks before this height will not return historical state. ## Frequently asked questions ### What is the difference between HyperCore and HyperEVM? HyperCore is Hyperliquid's native, Rust-based trading layer — it runs the perpetual and spot order books on-chain with one-block finality and zero gas fees. HyperEVM is the Ethereum-compatible smart contract layer (chain ID `999`) where you deploy Solidity contracts. Both share HyperBFT consensus, and HyperEVM contracts can read HyperCore state through precompiles. ### What is the HyperEVM chain ID? `999` on mainnet and `998` on testnet. Use the standard JSON-RPC interface at the `/evm` path of your Chainstack endpoint. ### Can I place trades through Chainstack? Trading actions on the HyperCore `/exchange` endpoint are available only on validator nodes, so they route through the official Hyperliquid API at `api.hyperliquid.xyz`. Chainstack serves all 83 HyperEVM JSON-RPC methods and 46 HyperCore `/info` query methods directly, which covers market data, account state, smart contract calls, and HyperEVM transaction submission. ### Which Hyperliquid methods are available on Chainstack? 129 of 212 documented methods run directly on Chainstack nodes: all 83 HyperEVM JSON-RPC methods and 46 HyperCore `/info` queries. The remaining 83 (30 proprietary `/info` methods and 53 `/exchange` actions) are only available on the public Hyperliquid infrastructure. See the [methods reference](/docs/hyperliquid-methods) for the per-method breakdown. ### Does Hyperliquid support archive data and tracing? Yes. Chainstack offers full and archive Hyperliquid nodes on both mainnet and testnet, with `debug_*`, `trace_*`, `erigon_*`, and Otterscan `ots_*` methods for transaction tracing and smart contract debugging. On testnet, archive history starts at block `34112653`. ### What are the Hyperliquid API rate limits? The public Hyperliquid infrastructure caps HyperEVM at 100 requests/min and REST (`/info`, `/exchange`) at 1,200 weight/min. A Chainstack private node removes the 100 requests/min HyperEVM ceiling. See the [infrastructure FAQ](/docs/hyperliquid-infrastructure-faq#public-infrastructure-rate-limiting) for the full weight and address-based limits. ### What is the difference between the `/evm` and `/nanoreth` paths? Both serve HyperEVM JSON-RPC and exist by default on every node. `/evm` is hl-node-compliant and strips HyperCore ↔ HyperEVM system transactions from block-shaped responses; `/nanoreth` includes them. See [Hyperliquid node configuration](/docs/hyperliquid-node-configuration). ## Related Hyperliquid resources * [Hyperliquid methods](/docs/hyperliquid-methods) — complete per-method availability reference * [Hyperliquid development](/docs/hyperliquid-development) — build overview and quickstart * [Hyperliquid tooling](/docs/hyperliquid-tooling) — SDKs and development setup * [nktkas TypeScript SDK](/docs/hyperliquid-typescript-sdk-nktkas) — end-to-end TypeScript examples * [Hyperliquid node configuration](/docs/hyperliquid-node-configuration) — `/evm` vs `/nanoreth`, system transactions * [Infrastructure FAQ](/docs/hyperliquid-infrastructure-faq) — architecture, node types, and rate limits * [CoreWriter guide](/docs/hyperliquid-corewriter) — write to HyperCore from HyperEVM contracts * [Bridging USDC](/docs/hyperliquid-bridging-usdc) — move USDC between HyperCore and HyperEVM * [Forking HyperEVM with Foundry](/docs/hyperliquid-forking-evm-foundry) — local mainnet fork testing * [Signing overview](/docs/hyperliquid-signing-overview) — authentication and action signing * [L1 action signing](/docs/hyperliquid-l1-action-signing) — sign HyperCore exchange actions * [User-signed actions](/docs/hyperliquid-user-signed-actions) — transfers and account actions * [Authentication guide](/docs/hyperliquid-authentication-guide) — authenticate with the exchange API * [Debugging signature errors](/docs/hyperliquid-debugging-signature-errors) — fix common signing issues * [Order precision](/docs/hyperliquid-order-precision) — tick and lot sizing rules * [TWAP orders](/docs/hyperliquid-twap-orders) — time-weighted average price execution * [Funding rate arbitrage](/docs/hyperliquid-funding-rate-arbitrage) — funding strategy walkthrough * [Copy trading over WebSocket](/docs/hyperliquid-copy-trading-websocket) — real-time fill streaming * [HIP-4 outcome markets](/docs/hyperliquid-hip4-outcome-markets-trading) — trade prediction markets # activeAssetData | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-activeassetdata openapi/hyperliquid_node_api/hypercore_info/info_activeassetdata.json post /4f8d8f4040bdacd1577bff8058438274/info Retrieves active asset data for a specific user and coin, including leverage settings, trading limits, and current market price. On Hyperliquid info. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Retrieves active asset data for a specific user and coin, including leverage settings, trading limits, and current market price. > [!NOTE] > > Perpetuals only. The `activeAssetData` type only supports perp markets. ## Parameters * `type` (string, required) — Must be `"activeAssetData"` * `user` (string, required) — Address in 42-character hexadecimal format * `coin` (string, required) — Asset symbol (e.g., "BTC", "ETH", "SOL") ## Returns * `user` (string) — User address * `coin` (string) — Asset symbol * `leverage` (object) — Leverage configuration: * `type` (string) — Leverage type ("cross" or "isolated") * `value` (number) — Current leverage value * `maxTradeSzs` (array) — Maximum trade sizes \[long, short] * `availableToTrade` (array) — Available amounts to trade \[long, short] * `markPx` (string) — Current mark price ## Example request ```bash Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "type": "activeAssetData", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b", "coin": "BTC" }' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python hyperliquid-python-sdk theme={"system"} from hyperliquid.info import Info # The hyperliquid-python-sdk exposes activeAssetData as a websocket # subscription, so keep the websocket manager running (do not pass skip_ws=True). info = Info("YOUR_CHAINSTACK_ENDPOINT") def on_active_asset_data(message): print(message) info.subscribe( { "type": "activeAssetData", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b", "coin": "BTC", }, on_active_asset_data, ) ``` ```typescript @nktkas/hyperliquid theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const data = await info.activeAssetData({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", coin: "BTC", }); console.log(data); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use cases * **Trading interface** — Display current leverage and trading limits to users * **Risk management** — Monitor available trading capacity and leverage settings * **Position sizing** — Calculate optimal position sizes based on available limits * **Market analysis** — Track mark prices and leverage utilization across assets # alignedQuoteTokenInfo | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-aligned-quote-token-info openapi/hyperliquid_node_api/hypercore_info/info_aligned_quote_token_info.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "alignedQuoteTokenInfo" retrieves supply, rate, and pending payment information for an aligned quote token. > [!WARNING] > > **Removed by Hyperliquid (\~June 2026).** The Hyperliquid API no longer recognizes the `alignedQuoteTokenInfo` request type — requests fail to deserialize — and the method has been dropped from the official SDKs. There is no direct replacement. For the aligned quote asset mechanism, see [Aligned quote assets](https://hyperliquid.gitbook.io/hyperliquid-docs/hypercore/aligned-quote-assets) in the Hyperliquid docs; for spot token metadata, see [tokenDetails](/reference/hyperliquid-info-token-details). This page is kept for reference. The `info` endpoint with `type: "alignedQuoteTokenInfo"` returned supply, rate, and pending payment information for an aligned quote token. Aligned quote tokens are HIP-1 spot tokens that deployers had registered as collateral for HIP-3 perpetual DEXes through the CoreWriter contract on HyperEVM. It returned `null` if the token had not been registered as an aligned quote asset. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"alignedQuoteTokenInfo"`. * `token` (integer, required) — Spot token index from the `spotMeta` universe (e.g., `0` for USDC, `1` for PURR). Only tokens registered as aligned quote assets return data. ## Response Returns an aligned quote token info object, or `null` if the token is not registered as an aligned quote asset. * `isAligned` (boolean) — Whether the token is currently aligned. * `firstAlignedTime` (integer) — Unix timestamp in milliseconds when the token was first aligned. * `evmMintedSupply` (string) — Total EVM minted supply of the token as reported by CoreWriter. * `dailyAmountOwed` (array) — Array of `[date, amount]` tuples showing daily owed amounts, where `date` is a `YYYY-MM-DD` string and `amount` is a decimal string. * `predictedRate` (string) — Predicted alignment rate as a decimal string. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "alignedQuoteTokenInfo", "token": 1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ## Example response When the token is an aligned quote asset: ```json theme={"system"} { "isAligned": true, "firstAlignedTime": 1759914226913, "evmMintedSupply": "0.0", "dailyAmountOwed": [ ["2025-10-08", "0.0"], ["2025-10-09", "0.0"] ], "predictedRate": "0.03154807" } ``` When the token is not an aligned quote asset: ```json theme={"system"} null ``` ## Use case The `info` endpoint with `type: "alignedQuoteTokenInfo"` is useful for: * Checking whether a HIP-1 token is registered as an aligned quote asset before deploying a HIP-3 perp DEX with it as collateral * Monitoring the EVM minted supply and daily owed amounts for aligned tokens * Building dashboards that track alignment status and rates for custom quote tokens # allBorrowLendReserveStates | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-all-borrow-lend-reserve-states openapi/hyperliquid_node_api/hypercore_info/info_all_borrow_lend_reserve_states.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "allBorrowLendReserveStates" retrieves reserve states for all tokens in the Hyperliquid borrow/lend market. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "allBorrowLendReserveStates"` retrieves reserve states for all tokens in the Hyperliquid borrow/lend market. Returns an array of token index and reserve state pairs. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"allBorrowLendReserveStates"`. ## Response The response is an array of tuples, where each tuple contains: 1. Token index (integer) — The token identifier. 2. Reserve state (object) — Contains `borrowYearlyRate`, `supplyYearlyRate`, `balance`, `utilization`, `oraclePx`, `ltv`, `totalSupplied`, and `totalBorrowed`. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "allBorrowLendReserveStates"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # hyperliquid-python-sdk has no dedicated wrapper for this type yet, # so post the request directly through the generic Info.post method. reserve_states = info.post("/info", {"type": "allBorrowLendReserveStates"}) print(reserve_states) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const client = new InfoClient({ transport }); const reserveStates = await client.allBorrowLendReserveStates(); console.log(reserveStates); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ [ 0, { "borrowYearlyRate": "0.05", "supplyYearlyRate": "0.001", "balance": "3464255.81", "utilization": "0.024", "oraclePx": "1.0", "ltv": "0.0", "totalSupplied": "3548088.69", "totalBorrowed": "83832.95" } ], [ 150, { "borrowYearlyRate": "0.05", "supplyYearlyRate": "0.0", "balance": "14552.99", "utilization": "0.0", "oraclePx": "30.24", "ltv": "0.5", "totalSupplied": "14552.99", "totalBorrowed": "0.0" } ] ] ``` ## Use case The `info` endpoint with `type: "allBorrowLendReserveStates"` is useful for: * Building a complete borrow/lend market overview dashboard * Comparing rates across all available tokens * Finding the best supply yields or lowest borrow rates * Monitoring overall lending market utilization and liquidity # allPerpMetas | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-all-perp-metas openapi/hyperliquid_node_api/hypercore_info/info_all_perp_metas.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "allPerpMetas" retrieves metadata for all perpetual DEXes on Hyperliquid. Hyperliquid info via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "allPerpMetas"` retrieves metadata for all perpetual DEXes on Hyperliquid. Returns an array of universe and margin table configurations for each perp DEX. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"allPerpMetas"`. ## Response The response is an array of objects, one per perp DEX, each containing: * `universe` (array) — Array of perpetual assets with `name`, `szDecimals`, `maxLeverage`, `marginTableId`, and optional `onlyIsolated` and `isDelisted` flags. * `marginTables` (array) — Array of margin table tuples with table ID and margin tier configurations. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "allPerpMetas"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # skip_ws=True avoids opening a WebSocket connection for this REST call info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The SDK has no dedicated allPerpMetas helper, so post the request directly. all_perp_metas = info.post("/info", {"type": "allPerpMetas"}) print(all_perp_metas) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // apiUrl points the transport at a custom server (your Chainstack endpoint) const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const allPerpMetas = await info.allPerpMetas(); console.log(allPerpMetas); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ { "universe": [ { "szDecimals": 5, "name": "BTC", "maxLeverage": 40, "marginTableId": 56 }, { "szDecimals": 4, "name": "ETH", "maxLeverage": 25, "marginTableId": 55 } ], "marginTables": [] } ] ``` ## Use case The `info` endpoint with `type: "allPerpMetas"` is useful for: * Retrieving perpetual metadata across all DEXes in a single call * Building multi-DEX trading interfaces * Comparing available assets and leverage limits across different perp DEXes * Caching exchange-wide perpetual configuration data # allMids | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-allmids openapi/hyperliquid_node_api/hypercore_info/info_allmids.json post /info Retrieves mid prices for all available coins on the Hyperliquid exchange. If the order book is empty, the last trade price is used as a fallback. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Retrieves mid prices for all available coins on the Hyperliquid exchange. If the order book is empty, the last trade price is used as a fallback. ## Parameters * `type` (string, required) — Must be `"allMids"` * `dex` (string, optional) — Perp dex name. Defaults to empty string (first perp dex). Spot mids are only included with the first perp dex ## Returns Returns an object with coin symbols as keys and mid prices as string values. > [!NOTE] > > Mid price = (best bid + best ask) ÷ 2. When the order book is empty, the last trade price is used as fallback. > [!NOTE] > > The response may also include internal index keys like `"@142"` alongside human‑readable symbols (for example, `"BTC"`, `"ETH"`). Use symbol keys for application logic and ignore `"@..."` keys if not needed. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "type": "allMids", "dex": "" }' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants # allMids is a public-only method — use the official Hyperliquid API URL. info = Info(constants.MAINNET_API_URL, skip_ws=True) # Posts {"type": "allMids", "dex": ""} to /info mids = info.all_mids() print(mids) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // allMids is a public-only method — the default transport targets the official Hyperliquid API. const transport = new HttpTransport(); const info = new InfoClient({ transport }); // Posts {"type": "allMids"} to /info const mids = await info.allMids(); console.log(mids); ``` ## Use cases * **Price discovery** — Get current market prices for all available assets * **Portfolio valuation** — Calculate total portfolio value using current prices * **Trading interfaces** — Display real-time prices in dashboards * **Market monitoring** — Track price movements across all markets # approvedBuilders | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-approved-builders openapi/hyperliquid_node_api/hypercore_info/info_approved_builders.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "approvedBuilders" lists the builder addresses a user has approved to attach builder fees to their orders. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "approvedBuilders"` returns the list of builder addresses a user has approved. When a user approves a builder, that builder can attach a builder fee to the user's orders. Use this method to check which builders a given address has authorized. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"approvedBuilders"`. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. ## Response Returns an array of builder addresses that the user has approved. The array is empty if the user has not approved any builders. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "approvedBuilders", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # skip_ws=True avoids opening a WebSocket connection for this REST call info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The SDK has no dedicated approvedBuilders helper, so post the request directly. approved_builders = info.post("/info", { "type": "approvedBuilders", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }) print(approved_builders) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // apiUrl points the transport at a custom server (your Chainstack endpoint) const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const approvedBuilders = await info.approvedBuilders({ user: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }); console.log(approvedBuilders); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} ["0x1cc34f6af34653c515b47a83e1de70ba9b0cda1f"] ``` ## Use cases The `info` endpoint with `type: "approvedBuilders"` is useful for: * Checking which builders a user has authorized before submitting orders with builder fees * Building wallet and frontend integrations that surface a user's approved builders * Auditing builder-fee relationships for an address # batchClearinghouseStates | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-batch-clearinghouse-states openapi/hyperliquid_node_api/hypercore_info/info_batch_clearinghouse_states.json post /info The info endpoint with type: "batchClearinghouseStates" retrieves perpetuals account summaries for multiple users in one call. On Hyperliquid info. > [!WARNING] > > **Not available.** This batch method is not served by the public Hyperliquid API — requests return `null` for every address — and it is not available through Chainstack. To fetch perpetuals account state for multiple users, call [clearinghouseState](/reference/hyperliquid-info-clearinghousestate) once per address. Some third-party providers offer their own batch aggregators built on per-user calls. This page is kept for reference. The `info` endpoint with `type: "batchClearinghouseStates"` was intended to retrieve perpetuals account summaries for multiple users in one call — positions, margin usage, and account values across many addresses. ## Parameters ### Request body * `type` (string, required) — Must be `"batchClearinghouseStates"`. * `users` (array of strings, required) — One or more onchain addresses in 42‑character hexadecimal format. * `dex` (string, optional) — Perp dex name. Defaults to the empty string which represents the first perp dex. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "type": "batchClearinghouseStates", "users": [ "0x31ca8395cf837de08b24da3f660e77761dfb974b", "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036" ] }' \ https://api.hyperliquid.xyz/info ``` ## Response Returns an array. Each item matches the `clearinghouseState` schema with fields like `assetPositions`, `marginSummary`, `crossMarginSummary`, `time`, and `withdrawable`. ### Example response ```json theme={"system"} [ { "assetPositions": [ { "position": { "coin": "ETH", "cumFunding": { "allTime": "514.085417", "sinceChange": "0.0", "sinceOpen": "0.0" }, "entryPx": "2986.3", "leverage": { "rawUsd": "-95.059824", "type": "isolated", "value": 20 }, "liquidationPx": "2866.26936529", "marginUsed": "4.967826", "maxLeverage": 50, "positionValue": "100.02765", "returnOnEquity": "-0.0026789", "szi": "0.0335", "unrealizedPnl": "-0.0134" }, "type": "oneWay" } ], "crossMaintenanceMarginUsed": "0.0", "crossMarginSummary": { "accountValue": "13104.514502", "totalMarginUsed": "0.0", "totalNtlPos": "0.0", "totalRawUsd": "13104.514502" }, "marginSummary": { "accountValue": "13109.482328", "totalMarginUsed": "4.967826", "totalNtlPos": "100.02765", "totalRawUsd": "13009.454678" }, "time": 1708622398623, "withdrawable": "13104.514502" }, { "assetPositions": [], "crossMaintenanceMarginUsed": "0.0", "crossMarginSummary": { "accountValue": "0.0", "totalMarginUsed": "0.0", "totalNtlPos": "0.0", "totalRawUsd": "0.0" }, "marginSummary": { "accountValue": "0.0", "totalMarginUsed": "0.0", "totalNtlPos": "0.0", "totalRawUsd": "0.0" }, "time": 1708622398623, "withdrawable": "0.0" } ] ``` ## Use cases * Fetch multiple users' perpetuals states for portfolio screens or multi‑account dashboards. * Power backtesting or analytics jobs that require snapshots across many addresses. * Reduce API round trips by batching reads. # borrowLendReserveState | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-borrow-lend-reserve-state openapi/hyperliquid_node_api/hypercore_info/info_borrow_lend_reserve_state.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "borrowLendReserveState" retrieves the reserve state for a specific token in the Hyperliquid borrow/lend market. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "borrowLendReserveState"` retrieves the reserve state for a specific token in the Hyperliquid borrow/lend market. Returns current rates, utilization, and supply/borrow totals. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"borrowLendReserveState"`. * `token` (integer, required) — Token index (e.g., `0` for USDC). ## Response The response is an object containing: * `borrowYearlyRate` (string) — Annual borrow interest rate. * `supplyYearlyRate` (string) — Annual supply interest rate. * `balance` (string) — Available balance in the reserve. * `utilization` (string) — Current utilization ratio of the reserve. * `oraclePx` (string) — Oracle price of the token. * `ltv` (string) — Loan-to-value ratio for the token as collateral. * `totalSupplied` (string) — Total amount supplied to the reserve. * `totalBorrowed` (string) — Total amount borrowed from the reserve. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "borrowLendReserveState", "token": 0}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # The Python SDK has no dedicated borrowLendReserveState method, # so post the request type directly through the shared post() helper. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) reserve_state = info.post("/info", {"type": "borrowLendReserveState", "token": 0}) print(reserve_state) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const reserveState = await info.borrowLendReserveState({ token: 0 }); console.log(reserveState); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "borrowYearlyRate": "0.05", "supplyYearlyRate": "0.0010632437", "balance": "3464255.80696283", "utilization": "0.0236276367", "oraclePx": "1.0", "ltv": "0.0", "totalSupplied": "3548088.6923081698", "totalBorrowed": "83832.95076168" } ``` ## Use case The `info` endpoint with `type: "borrowLendReserveState"` is useful for: * Displaying current borrow and supply rates for a specific token * Monitoring reserve utilization to anticipate rate changes * Building lending market dashboards with real-time reserve data * Calculating potential yields before supplying assets # borrowLendUserState | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-borrow-lend-user-state openapi/hyperliquid_node_api/hypercore_info/info_borrow_lend_user_state.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "borrowLendUserState" retrieves a user's borrow/lend positions and health status on Hyperliquid. On Hyperliquid info. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "borrowLendUserState"` retrieves a user's borrow/lend positions and health status on Hyperliquid. Returns the user's active token positions, overall health status, and health factor. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"borrowLendUserState"`. * `user` (string, required) — Onchain address in 42-character hexadecimal format. ## Response The response is an object containing: * `tokenToState` (array) — Array of the user's borrow/lend positions per token. * `health` (string) — Overall health status of the user's borrow/lend positions (e.g., `"healthy"`). * `healthFactor` (string or null) — Numeric health factor, or `null` if the user has no active borrow/lend positions. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "borrowLendUserState", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # The Python SDK has no dedicated borrowLendUserState wrapper, so post the # request type directly through the Info client. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) response = info.post( "/info", { "type": "borrowLendUserState", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b", }, ) print(response) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const response = await info.borrowLendUserState({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(response); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "tokenToState": [], "health": "healthy", "healthFactor": null } ``` ## Use case The `info` endpoint with `type: "borrowLendUserState"` is useful for: * Displaying a user's borrow/lend positions and balances * Monitoring health factor to assess liquidation risk * Building lending dashboards that show active supply and borrow positions * Implementing risk alerts when health factor drops below a threshold # candleSnapshot | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-candle-snapshot openapi/hyperliquid_node_api/hypercore_info/info_candle_snapshot.json post /info Retrieves historical candlestick (OHLCV) data for a specific asset within a time range. Only the most recent 5000 candles are available. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. Retrieves historical candlestick (OHLCV) data for a specific asset within a time range. Only the most recent 5000 candles are available. ## Parameters * `type` (string, required) — Must be `"candleSnapshot"` * `req` (object, required) — Request parameters: * `coin` (string, required) — Asset identifier ("BTC", "ETH" for perpetuals; "@107" for spot) * `interval` (string, required) — Candle interval (see supported intervals below) * `startTime` (integer, required) — Start time in milliseconds (epoch timestamp) * `endTime` (integer, required) — End time in milliseconds (epoch timestamp) ## Returns Returns an array of candlestick objects with OHLCV data: * `t` — Open time timestamp (milliseconds) * `T` — Close time timestamp (milliseconds) * `o` — Open price (string) * `h` — High price (string) * `l` — Low price (string) * `c` — Close price (string) * `v` — Volume traded (string) * `n` — Number of trades (integer) * `i` — Interval (string) * `s` — Symbol (string) > [!WARNING] > > Only the most recent 5000 candles are available. Older data beyond this limit is not accessible. ## Supported intervals `"1m"`, `"3m"`, `"5m"`, `"15m"`, `"30m"`, `"1h"`, `"2h"`, `"4h"`, `"8h"`, `"12h"`, `"1d"`, `"3d"`, `"1w"`, `"1M"` ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "type": "candleSnapshot", "req": { "coin": "BTC", "interval": "1h", "startTime": 1754300000000, "endTime": 1754400000000 } }' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) candles = info.candles_snapshot( name="BTC", interval="1h", startTime=1754300000000, endTime=1754400000000, ) print(candles) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; const transport = new hl.HttpTransport(); const client = new hl.InfoClient({ transport }); const candles = await client.candleSnapshot({ coin: "BTC", interval: "1h", startTime: 1754300000000, endTime: 1754400000000, }); console.log(candles); ``` ## Use cases * **Technical analysis** — Analyze price patterns and calculate technical indicators * **Charting applications** — Display candlestick charts with OHLCV data * **Trading algorithms** — Backtest strategies using historical price data * **Market research** — Study price movements and volatility patterns # clearinghouseState | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-clearinghousestate openapi/hyperliquid_node_api/hypercore_info/info_clearinghousestate.json post /4f8d8f4040bdacd1577bff8058438274/info Reference docs for the clearinghouseState JSON-RPC method on the Hyperliquid info blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "clearinghouseState"` retrieves a user's perpetuals account summary including open positions and margin information for the Hyperliquid exchange. This endpoint provides comprehensive data about a user's trading positions, leverage, margin usage, and account value. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"clearinghouseState"` to retrieve user's account summary. * `user` (string, required) — Onchain address in 42-character hexadecimal format (e.g., `0x31ca8395cf837de08b24da3f660e77761dfb974b`). * `dex` (string, optional) — Perp dex name. Defaults to the empty string which represents the first perp dex. ## Response The response is an object containing comprehensive account information: ### Top-level fields * `assetPositions` (array) — Array of the user's current asset positions. * `crossMaintenanceMarginUsed` (string) — Cross maintenance margin used. * `crossMarginSummary` (object) — Cross margin account summary. * `marginSummary` (object) — Overall margin summary. * `time` (number) — Timestamp of the data. * `withdrawable` (string) — Amount available for withdrawal. ### Asset positions structure Each item in the `assetPositions` array contains: * `position` (object) — Position details: * `coin` (string) — Asset symbol (e.g., "ETH", "BTC"). * `cumFunding` (object) — Cumulative funding information: * `allTime` (string) — All-time cumulative funding. * `sinceChange` (string) — Funding since last position change. * `sinceOpen` (string) — Funding since position opened. * `entryPx` (string) — Entry price of the position. * `leverage` (object) — Leverage information: * `rawUsd` (string) — Raw USD value. * `type` (string) — Leverage type ("isolated" or "cross"). * `value` (number) — Leverage multiplier. * `liquidationPx` (string) — Price at which position would be liquidated. * `marginUsed` (string) — Margin used for this position. * `maxLeverage` (number) — Maximum leverage allowed for this asset. * `positionValue` (string) — Current value of the position. * `returnOnEquity` (string) — Return on equity percentage. * `szi` (string) — Position size. * `unrealizedPnl` (string) — Unrealized profit and loss. * `type` (string) — Position type (e.g., "oneWay"). ### Margin summary objects Both `crossMarginSummary` and `marginSummary` contain: * `accountValue` (string) — Total account value. * `totalMarginUsed` (string) — Total margin used. * `totalNtlPos` (string) — Total notional position. * `totalRawUsd` (string) — Total raw USD. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "clearinghouseState", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python hyperliquid-python-sdk theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # user_state posts {"type": "clearinghouseState", "user": address}. state = info.user_state("0x31ca8395cf837de08b24da3f660e77761dfb974b") print(state) ``` ```typescript @nktkas/hyperliquid theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT", }); const info = new InfoClient({ transport }); // clearinghouseState posts {"type": "clearinghouseState", "user": address}. const state = await info.clearinghouseState({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(state); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "assetPositions": [ { "position": { "coin": "ETH", "cumFunding": { "allTime": "514.085417", "sinceChange": "0.0", "sinceOpen": "0.0" }, "entryPx": "2986.3", "leverage": { "rawUsd": "-95.059824", "type": "isolated", "value": 20 }, "liquidationPx": "2866.26936529", "marginUsed": "4.967826", "maxLeverage": 50, "positionValue": "100.02765", "returnOnEquity": "-0.0026789", "szi": "0.0335", "unrealizedPnl": "-0.0134" }, "type": "oneWay" } ], "crossMaintenanceMarginUsed": "0.0", "crossMarginSummary": { "accountValue": "13104.514502", "totalMarginUsed": "0.0", "totalNtlPos": "0.0", "totalRawUsd": "13104.514502" }, "marginSummary": { "accountValue": "13109.482328", "totalMarginUsed": "4.967826", "totalNtlPos": "100.02765", "totalRawUsd": "13009.454678" }, "time": 1708622398623, "withdrawable": "13104.514502" } ``` ## Use case The `info` endpoint with `type: "clearinghouseState"` is essential for perpetuals trading applications that need to: * Display user's current positions and their performance * Show margin usage and available leverage * Calculate liquidation risks and price levels * Display account value and withdrawable amounts * Monitor position-level funding payments * Implement risk management features * Build trading dashboards with comprehensive position data This endpoint is typically called frequently to keep trading interfaces updated with real-time position and account information. # delegations | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-delegations openapi/hyperliquid_node_api/hypercore_info/info_delegations.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "delegations" retrieves a user's active delegations on the Hyperliquid network. Hyperliquid info via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "delegations"` retrieves a user's active delegations on the Hyperliquid network. This endpoint provides a simple list of current delegations showing the validator address, delegated amount, and lock expiration timestamp for each delegation. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"delegations"` to retrieve user's delegation information. * `user` (string, required) — Address in 42-character hexadecimal format (e.g., `0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036`). ## Response The response is an array of delegation objects, each representing an active delegation to a validator: ### Delegation object structure Each delegation object in the array contains: * `validator` (string) — The validator's address in 42-character hexadecimal format. * `amount` (string) — The amount of tokens delegated to this validator. * `lockedUntilTimestamp` (number) — Timestamp (in milliseconds) until which the delegation is locked. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "delegations", "user": "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) delegations = info.user_staking_delegations("0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036") print(delegations) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const delegations = await info.delegations({ user: "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", }); console.log(delegations); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ { "validator": "0x5ac99df645f3414876c816caa18b2d234024b487", "amount": "12060.16529862", "lockedUntilTimestamp": 1735466781353 }, { "validator": "0x1234567890abcdef1234567890abcdef12345678", "amount": "5000.0", "lockedUntilTimestamp": 1735556781353 } ] ``` ## Use case The `info` endpoint with `type: "delegations"` is essential for applications that need to: * **Display active delegations**: Show users their current validator delegations and amounts * **Track delegation amounts**: Monitor how much is delegated to each validator * **Check lock periods**: Display when delegations will become available for undelegation * **Portfolio overview**: Provide a simple view of delegation distribution across validators * **Validator management**: List which validators a user has delegated to * **Delegation planning**: Help users understand their current delegation commitments * **Lock time monitoring**: Track when delegations expire and become withdrawable This endpoint provides basic delegation information and is typically used alongside other staking endpoints like `delegatorSummary` for comprehensive staking portfolio management on the Hyperliquid network. # delegatorHistory | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-delegator-history openapi/hyperliquid_node_api/hypercore_info/info_delegator_history.json post /info The info endpoint with type: "delegatorHistory" retrieves comprehensive staking history for a specific user on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "delegatorHistory"` retrieves comprehensive staking history for a specific user on the Hyperliquid exchange. This endpoint provides detailed historical data about delegation and undelegation events, including timestamps, transaction hashes, and complete event details, enabling thorough analysis of staking behavior and validator relationships. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"delegatorHistory"` to retrieve staking history. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. ## Response The response is an array of staking history events, ordered chronologically. Each event represents a delegation or undelegation action performed by the user. ### Response structure Each staking event contains: **Event metadata:** * `time` — Timestamp of the staking event in milliseconds since Unix epoch * `hash` — Transaction hash of the staking event for blockchain verification **Event details:** * `delta` — Object containing the details of the staking change * `delegate` — Specific delegation event information ### Delegation event structure The `delegate` object within `delta` contains: **Validator information:** * `validator` — Address of the validator being delegated to or undelegated from * Identifies the specific validator node in the network **Staking amount:** * `amount` — Amount being delegated or undelegated as a string for precision * Represents the quantity of tokens involved in the staking action **Event type:** * `isUndelegate` — Boolean indicating the type of staking event * `false` indicates a delegation event (staking tokens) * `true` indicates an undelegation event (unstaking tokens) ### Data interpretation **Delegation events (`isUndelegate: false`):** * User is staking tokens with a validator * Increases the user's staked position with that validator * Contributes to the validator's total delegated stake * Begins earning staking rewards **Undelegation events (`isUndelegate: true`):** * User is unstaking tokens from a validator * Decreases the user's staked position with that validator * May be subject to unbonding periods * Stops earning rewards on the undelegated amount **Historical tracking:** * Events are ordered chronologically * Transaction hashes provide blockchain verification * Complete audit trail of all staking activities ## Staking analysis insights ### Staking behavior patterns **Delegation strategies:** * Analyze which validators the user prefers to delegate to * Track delegation amounts and timing patterns * Identify diversification strategies across multiple validators **Staking duration analysis:** * Calculate time between delegation and undelegation events * Analyze staking commitment periods * Identify patterns in staking behavior over time **Amount analysis:** * Track the size of delegation and undelegation events * Analyze whether user prefers large or small staking amounts * Monitor changes in staking strategy over time ### Validator relationships **Validator preferences:** * Identify which validators the user delegates to most frequently * Analyze loyalty to specific validators * Track changes in validator preferences over time **Risk distribution:** * Assess how the user distributes stake across validators * Analyze concentration risk in validator selection * Monitor diversification strategies ### Staking timeline analysis **Activity frequency:** * Analyze how often the user performs staking operations * Identify periods of high or low staking activity * Correlate staking activity with market conditions **Timing patterns:** * Identify patterns in delegation and undelegation timing * Analyze whether user times staking actions strategically * Monitor response to network events or validator changes ### Portfolio management **Staking allocation:** * Track total staked amounts over time * Analyze allocation changes between validators * Monitor overall staking strategy evolution **Liquidity management:** * Analyze patterns of delegation vs undelegation * Track how user manages staked vs liquid token balance * Identify liquidity needs and staking optimization ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "delegatorHistory", "user": "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) history = info.delegator_history("0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036") print(history) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; const transport = new hl.HttpTransport(); const client = new hl.InfoClient({ transport }); const history = await client.delegatorHistory({ user: "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", }); console.log(history); ``` ## Use cases The `info` endpoint with `type: "delegatorHistory"` is essential for applications that need to: * **Track staking history**: Monitor all delegation and undelegation events over time * **Analyze staking patterns**: Understand user behavior and validator preferences * **Calculate rewards**: Determine staking rewards based on historical delegation timing * **Audit staking activity**: Provide complete audit trails with transaction hashes * **Portfolio analytics**: Analyze staking strategy and performance over time * **Validator research**: Study relationships between delegators and validators * **Tax reporting**: Generate historical staking data for tax calculation purposes * **Risk management**: Assess staking concentration and diversification patterns * **Liquidity planning**: Plan future delegations based on historical patterns * **Compliance tracking**: Maintain detailed records of staking activities This endpoint provides comprehensive staking history, enabling detailed analysis and management of delegation activities on the Hyperliquid network. # delegatorRewards | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-delegator-rewards openapi/hyperliquid_node_api/hypercore_info/info_delegator_rewards.json post /info The info endpoint with type: "delegatorRewards" retrieves comprehensive staking rewards history for a specific user on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "delegatorRewards"` retrieves comprehensive staking rewards history for a specific user on the Hyperliquid exchange. This endpoint provides detailed historical data about rewards earned from delegation and validator commission, including timestamps and reward amounts, enabling thorough analysis of staking reward performance and validator earnings. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"delegatorRewards"` to retrieve staking rewards. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. ## Response The response is an array of staking reward events, ordered chronologically. Each event represents rewards earned from either delegation or validator commission activities. ### Response structure Each reward event contains: **Event timing:** * `time` — Timestamp of the reward event in milliseconds since Unix epoch * Indicates when the rewards were distributed or earned **Reward source:** * `source` — Source of the reward, indicating the type of staking activity * `"delegation"` — Rewards earned from delegating tokens to validators * `"commission"` — Commission earned from operating as a validator **Reward amount:** * `totalAmount` — Total amount of rewards received as a string for precision * Represents the quantity of tokens earned from the staking activity ### Reward source types **Delegation rewards (`source: "delegation"`):** * Rewards earned by delegating tokens to validators * Proportional to the amount staked and validator performance * Regular rewards distributed based on network parameters * Represents passive staking income **Commission rewards (`source: "commission"`):** * Commission earned from operating as a validator * Percentage of rewards from users who delegate to this validator * Only applicable if the user operates a validator node * Represents active validator income ### Data interpretation **Reward frequency:** * Events show when rewards are distributed * Frequency depends on network reward distribution schedule * Regular patterns indicate consistent staking participation **Reward amounts:** * Amounts vary based on staked quantity and network conditions * Delegation rewards scale with delegation amount * Commission rewards scale with total delegated stake to validator **Performance tracking:** * Historical rewards enable performance analysis * Compare rewards across different time periods * Assess the effectiveness of staking strategies ## Staking rewards analysis ### Reward performance metrics **Total rewards calculation:** * Sum all reward amounts to calculate total earnings * Separate delegation rewards from commission rewards * Track reward accumulation over time **Reward rate analysis:** * Calculate average rewards per time period * Analyze reward rate trends and variations * Compare performance across different validators **Yield calculation:** * Calculate annualized percentage yield (APY) * Compare staking yields to other investment options * Assess risk-adjusted returns from staking ### Validator performance assessment **Delegation reward consistency:** * Analyze consistency of delegation rewards over time * Identify periods of high or low reward performance * Correlate rewards with validator uptime and performance **Commission earnings (for validators):** * Track commission earnings from delegators * Analyze growth in commission over time * Assess validator business performance ### Reward optimization insights **Staking strategy evaluation:** * Compare rewards from different validators * Identify optimal delegation strategies * Analyze the impact of delegation timing on rewards **Compound growth analysis:** * Track how rewards contribute to overall staking position * Analyze the effect of reward compounding * Optimize reward reinvestment strategies ### Risk and return analysis **Reward volatility:** * Analyze variability in reward amounts over time * Assess reward stability across different market conditions * Identify factors affecting reward consistency **Diversification benefits:** * Compare rewards from multiple validators * Analyze risk reduction through validator diversification * Optimize validator selection for risk-adjusted returns ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "delegatorRewards", "user": "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants # delegatorRewards is public-only, so use the Hyperliquid mainnet API URL info = Info(constants.MAINNET_API_URL, skip_ws=True) rewards = info.user_staking_rewards("0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036") print(rewards) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; // delegatorRewards is public-only, so use the default public transport const transport = new hl.HttpTransport(); const client = new hl.InfoClient({ transport }); const rewards = await client.delegatorRewards({ user: "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", }); console.log(rewards); ``` ## Example response ```json theme={"system"} [ { "time": 1736726400073, "source": "delegation", "totalAmount": "0.73117184" }, { "time": 1736726400073, "source": "commission", "totalAmount": "130.76445876" }, { "time": 1736640000073, "source": "delegation", "totalAmount": "0.69823456" } ] ``` ## Use cases The `info` endpoint with `type: "delegatorRewards"` is essential for applications that need to: * **Track staking rewards**: Monitor all delegation and commission rewards over time * **Calculate total earnings**: Sum up rewards to determine total staking income * **Analyze reward patterns**: Study reward frequency and amounts across time periods * **Tax reporting**: Generate historical reward data for tax calculation purposes * **Performance analysis**: Analyze staking yield and validator performance * **Validator comparison**: Compare rewards earned from different validators * **Portfolio management**: Manage staking portfolios and reward reinvestment strategies * **Yield optimization**: Optimize staking strategies based on historical reward data * **Commission tracking**: Monitor validator commission earnings (for validator operators) * **Audit trails**: Provide complete records of all staking reward activities This endpoint provides comprehensive staking reward history, enabling detailed analysis and optimization of delegation strategies on the Hyperliquid network. # delegatorSummary | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-delegator-summary openapi/hyperliquid_node_api/hypercore_info/info_delegator_summary.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "delegatorSummary" provides a concise overview of a user's delegation status on the Hyperliquid network. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "delegatorSummary"` provides a concise overview of a user's delegation status on the Hyperliquid network. This endpoint returns key metrics about the user's staking position, including total delegated amounts, undelegated amounts, and pending withdrawal information. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"delegatorSummary"` to retrieve user's delegation summary. * `user` (string, required) — Address in 42-character hexadecimal format (e.g., `0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036`). ## Response The response is an object containing a summary of the user's delegation status: ### Delegation metrics * `delegated` (string) — Total amount currently delegated by the user across all validators * `undelegated` (string) — Total amount that has been undelegated but may still be in unbonding period * `totalPendingWithdrawal` (string) — Total amount pending withdrawal (currently in unbonding period) * `nPendingWithdrawals` (integer) — Number of pending withdrawal transactions ### Understanding the metrics **Delegated amount:** * Represents the total tokens actively staked with validators * These tokens are earning rewards but are locked for network security * Can be undelegated at any time, subject to unbonding period **Undelegated amount:** * Shows tokens that have been removed from active delegation * May include tokens that have completed the unbonding process * Represents tokens no longer earning staking rewards **Pending withdrawals:** * Tokens currently in the unbonding period after undelegation * Cannot be transferred or re-delegated until unbonding completes * The number of pending withdrawals indicates separate undelegation transactions **Use cases for summary data:** * Quick portfolio overview for staking dashboards * Balance checks before making new delegations * Monitoring unbonding progress * Calculating available vs locked token amounts * Portfolio allocation analysis ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "delegatorSummary", "user": "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) summary = info.user_staking_summary("0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036") print(summary) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const summary = await info.delegatorSummary({ user: "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", }); console.log(summary); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "delegated": "100233.20588006", "undelegated": "0.0", "totalPendingWithdrawal": "0.0", "nPendingWithdrawals": 0 } ``` ## Use case The `info` endpoint with `type: "delegatorSummary"` is essential for applications that need to: * **Portfolio dashboards**: Display high-level staking metrics and account status * **Balance verification**: Check available delegation capacity before new stakes * **Withdrawal tracking**: Monitor pending undelegations and unbonding progress * **Quick status checks**: Provide instant overview of user's staking position * **Mobile applications**: Show essential staking data with minimal API calls * **Notification systems**: Alert users about completed unbonding periods * **Risk assessment**: Evaluate liquidity constraints from locked tokens * **Automated rebalancing**: Determine available tokens for delegation adjustments * **Tax reporting**: Calculate total staked amounts for compliance purposes * **User onboarding**: Show new users their current staking status * **Performance monitoring**: Track changes in delegation amounts over time * **Liquidity planning**: Understand token availability for trading or transfers This endpoint is particularly valuable for applications requiring efficient data retrieval with minimal overhead, mobile apps with bandwidth constraints, and systems that need quick validation of user staking status before executing delegation-related operations on the Hyperliquid network. # exchangeStatus | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-exchangestatus openapi/hyperliquid_node_api/hypercore_info/info_exchangestatus.json post /4f8d8f4040bdacd1577bff8058438274/info Reference docs for the exchangeStatus JSON-RPC method on the Hyperliquid info blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "exchangeStatus"` retrieves the current status of the Hyperliquid exchange, including information about all available trading assets and their configurations. This endpoint provides essential metadata about the exchange's operational status and available markets. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"exchangeStatus"` to retrieve exchange status information. ## Response The response is an object containing exchange status information: ### Exchange status fields * `universe` (array) — List of all available assets on the exchange, each containing: * `name` (string) — Asset name or symbol (e.g., "BTC", "ETH", "SOL") * `szDecimals` (integer) — Number of decimal places used for size precision in trading ### Asset information The `universe` array provides comprehensive information about all tradeable assets: * **Asset names**: Standard cryptocurrency symbols like "BTC", "ETH", "SOL", etc. * **Size decimals**: Precision configuration for order sizes and position calculations * **Market availability**: All assets listed are available for trading on the exchange ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "exchangeStatus"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # The hyperliquid-python-sdk has no dedicated exchangeStatus wrapper, # so post the request type directly through the generic Info.post method. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) status = info.post("/info", {"type": "exchangeStatus"}) print(status) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const status = await info.exchangeStatus(); console.log(status); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use case The `info` endpoint with `type: "exchangeStatus"` is essential for trading applications that need to: * Discover all available trading pairs and assets on the exchange * Validate asset symbols before placing orders * Configure proper decimal precision for order sizes * Build dynamic trading interfaces that adapt to available markets * Implement asset selection dropdowns or search functionality * Check exchange operational status and market availability * Initialize trading bots with current market configuration * Verify asset support before implementing trading strategies * Build market scanners and portfolio management tools * Ensure compatibility with exchange-specific asset naming conventions This endpoint is typically called during application initialization or periodically to stay updated with any changes to available markets and asset configurations on the Hyperliquid exchange. # extraAgents | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-extraagents openapi/hyperliquid_node_api/hypercore_info/info_extraagents.json post /4f8d8f4040bdacd1577bff8058438274/info Reference for the extraAgents JSON-RPC method on the Hyperliquid info blockchain via Chainstack nodes. Parameters, response, and examples included. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "extraAgents"` retrieves information about additional agents or authorized accounts associated with a user's wallet on the Hyperliquid exchange. This endpoint provides details about delegated permissions, agent activities, and access control configurations for advanced account management. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"extraAgents"` to retrieve extra agents associated with a user. * `user` (string, required) — Address in 42-character hexadecimal format (e.g., `0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036`). ## Response The response is an array of agent objects: ### Agent identification * `name` (string) — Human-readable label for the agent * `address` (string) — Agent address in 42-character hexadecimal format * `validUntil` (integer) — Timestamp in milliseconds until which the agent is valid ### Understanding agent management **Agent types:** * **Trading bots**: Automated trading systems with trade permissions * **Management tools**: Applications with specific operational permissions * **Backup accounts**: Secondary wallets with limited access rights * **Service integrations**: Third-party services with delegated permissions **Permission levels:** * **Read-only**: Access to account information without modification rights * **Trade-only**: Ability to execute trades but not withdraw funds * **Full access**: Complete account management capabilities * **Custom permissions**: Specific combinations of authorized actions **Security considerations:** * Agents provide controlled access without sharing private keys * Permissions can be revoked or modified as needed * Activity tracking enables audit trails for agent actions ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "extraAgents", "user": "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) agents = info.extra_agents(user="0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036") print(agents) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const client = new InfoClient({ transport }); const agents = await client.extraAgents({ user: "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", }); console.log(agents); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ { "name": "Mobile QR", "address": "0x1715462edd45a87eea74e402428392ffc744eb20", "validUntil": 1756349176478 }, { "name": "p1", "address": "0xd56a73c11336d712403d346c7502f5af29027cda", "validUntil": 1766846919956 } ] ``` ## Use case The `info` endpoint with `type: "extraAgents"` is essential for applications that need to: * **Security auditing**: Review all authorized agents and their permissions * **Access management**: Monitor and control delegated account access * **Account administration**: Manage multiple agents for different purposes * **Permission tracking**: Understand what actions each agent can perform * **Activity monitoring**: Track agent usage and last activity timestamps * **Security dashboards**: Display comprehensive account access information * **Compliance monitoring**: Ensure appropriate access controls are in place * **Agent lifecycle management**: Track creation and activity of authorized agents * **Risk management**: Assess potential security exposure from delegated permissions * **Multi-account coordination**: Manage complex trading setups with multiple agents * **Automated trading oversight**: Monitor trading bot permissions and activity * **Third-party integrations**: Track service permissions and access levels * **Account recovery planning**: Understand backup access mechanisms * **Institutional management**: Handle enterprise-level account delegation This endpoint is particularly valuable for users who employ sophisticated trading setups with multiple automated systems, institutional users with complex permission structures, or anyone who needs comprehensive visibility into account access controls and delegated permissions on the Hyperliquid exchange. # frontendOpenOrders | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-frontendopenorders openapi/hyperliquid_node_api/hypercore_info/info_frontendopenorders.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "frontendOpenOrders" retrieves a user's open orders with additional frontend-specific formatting and metadata. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "frontendOpenOrders"` retrieves a user's open orders with additional frontend-specific formatting and metadata. This endpoint provides enhanced order information specifically designed for display in trading interfaces, including order types, trigger conditions, and other UI-relevant data. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"frontendOpenOrders"` to retrieve user's open orders with frontend formatting. * `user` (string, required) — Address in 42-character hexadecimal format (e.g., `0x31ca8395cf837de08b24da3f660e77761dfb974b`). * `dex` (string, optional) — Perp dex name. Defaults to the empty string which represents the first perp dex. Spot open orders are only included with the first perp dex. ## Response The response is an array of open order objects with enhanced frontend-specific information: ### Core order fields * `coin` (string) — Trading pair or asset symbol (e.g., "BTC", "ETH") * `limitPx` (string) — Limit price of the order in string format for precision * `oid` (integer) — Order ID, a unique identifier for the order * `side` (string) — Order side: * `"A"` — Ask (sell order) * `"B"` — Bid (buy order) * `sz` (string) — Current order size in string format for precision * `timestamp` (integer) — Order creation timestamp in milliseconds ### Frontend-enhanced fields * `origSz` (string) — Original order size when the order was created * `orderType` (string) — Human-readable order type (e.g., "Limit", "Market", "Stop", "Stop Limit") * `triggerCondition` (string) — Trigger condition for conditional orders * `triggerPx` (string) — Trigger price for conditional orders * `reduceOnly` (boolean) — Whether this is a reduce-only order that can only decrease position size * `isPositionTpsl` (boolean) — Whether this is a position take-profit/stop-loss order * `isTrigger` (boolean) — Whether this is a trigger order ### Order side interpretation * **"A" (Ask)**: Sell order - user wants to sell the asset at the specified price * **"B" (Bid)**: Buy order - user wants to buy the asset at the specified price ### Order types * **Limit**: Standard limit order at a specific price * **Market**: Immediate execution at current market price * **Stop**: Stop-loss order triggered when price reaches trigger level * **Stop Limit**: Stop order that becomes a limit order when triggered ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "frontendOpenOrders", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) orders = info.frontend_open_orders("0x31ca8395cf837de08b24da3f660e77761dfb974b") print(orders) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const orders = await info.frontendOpenOrders({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(orders); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ { "coin": "BTC", "isPositionTpsl": false, "isTrigger": false, "limitPx": "29792.0", "oid": 91490942, "orderType": "Limit", "origSz": "5.0", "reduceOnly": false, "side": "A", "sz": "5.0", "timestamp": 1681247412573, "triggerCondition": "N/A", "triggerPx": "0.0" } ] ``` ## Use case The `info` endpoint with `type: "frontendOpenOrders"` is specifically designed for trading applications that need to: * Display comprehensive order information in trading interfaces * Show order types and trigger conditions in user-friendly formats * Implement advanced order management features with conditional logic * Build order books and trading dashboards with enhanced metadata * Provide detailed order tooltips and information panels * Support complex order types like stop-loss and take-profit orders * Display reduce-only orders with appropriate visual indicators * Create order history views with complete order context * Implement order modification workflows with type-specific handling * Build mobile trading apps with condensed but complete order data * Support advanced trading strategies requiring order type awareness * Provide audit trails and order analytics with detailed classifications This endpoint is ideal for production trading interfaces that require rich order metadata beyond basic order information, enabling sophisticated user experiences and advanced trading functionality. # fundingHistory | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-funding-history openapi/hyperliquid_node_api/hypercore_info/info_funding_history.json post /info The info endpoint with type: "fundingHistory" retrieves historical funding rates and premiums for a specific perpetual contract on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "fundingHistory"` retrieves historical funding rates and premiums for a specific perpetual contract on the Hyperliquid exchange. This endpoint provides comprehensive data about funding costs over time, enabling analysis of market sentiment, carry trade opportunities, and long-term trading strategy optimization. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"fundingHistory"` to retrieve funding rate history. * `coin` (string, required) — Asset symbol to retrieve funding history for (e.g., "BTC", "ETH", "SOL"). * `startTime` (integer, required) — Start timestamp in milliseconds, inclusive. Filters funding history from this time forward. * `endTime` (integer, optional) — End timestamp in milliseconds, inclusive. Defaults to current time if not provided. ## Response The response is an array of funding rate records, chronologically ordered from oldest to newest. Each record represents a funding rate snapshot taken at regular intervals (typically every 8 hours for most exchanges). ### Response structure Each funding rate record contains: **Asset identification:** * `coin` — Asset symbol (e.g., "BTC", "ETH", "SOL") **Funding metrics:** * `fundingRate` — The funding rate applied to positions (decimal format) * `premium` — Market premium component used in funding calculation * `time` — Unix timestamp in milliseconds when the rate was applied ### Data interpretation **Funding rate analysis:** * Positive rates indicate longs pay shorts (bullish market bias) * Negative rates indicate shorts pay longs (bearish market bias) * Rate magnitude shows intensity of directional demand **Premium components:** * Premium represents the difference between perpetual and spot prices * Combined with interest rate component to determine final funding rate * Higher premiums suggest stronger directional pressure **Time series patterns:** * Funding rates typically reset every 8 hours * Persistent positive funding indicates sustained bullish sentiment * Rate volatility reflects changing market conditions ## Market analysis insights ### Sentiment indicators **Directional bias measurement:** * Consistently positive funding rates suggest bullish market structure * Negative funding periods indicate bearish sentiment dominance * Rate reversals often coincide with market trend changes **Market efficiency:** * Extreme funding rates indicate pricing inefficiencies * Rate mean reversion suggests arbitrage opportunities * Funding rate divergence across assets reveals relative strength ### Trading strategy applications **Carry trade opportunities:** * Identify assets with consistently favorable funding rates * Calculate cumulative funding costs for position holding periods * Optimize position timing around funding rate cycles **Position management:** * Avoid high-cost funding periods for direction-neutral strategies * Time entries and exits around funding rate extremes * Use funding rate trends for position sizing decisions ### Risk management **Cost analysis:** * Calculate total funding costs as percentage of trading profits * Monitor funding rate volatility for strategy stability * Assess impact of funding on long-term position holding **Market timing:** * Use funding rate extremes as contrarian indicators * Identify potential trend reversal points * Combine with other metrics for comprehensive market analysis ## Historical patterns ### Cycle analysis **Funding rate cycles:** * Identify recurring patterns in funding rate behavior * Analyze seasonal effects and market cycle correlations * Track funding rate recovery periods after extreme events **Cross-asset comparison:** * Compare funding rates across different perpetual contracts * Identify relative value opportunities between assets * Analyze correlation patterns during market stress ### Market structure insights **Liquidity conditions:** * High funding rate volatility often indicates lower liquidity * Stable funding rates suggest efficient arbitrage mechanisms * Rate spikes may indicate temporary liquidity shortages **Institutional vs retail participation:** * Funding rate patterns can reveal trader composition changes * Professional arbitrageurs tend to stabilize funding rates * Retail-driven markets often show more volatile funding patterns ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "type": "fundingHistory", "coin": "BTC", "startTime": 1681923833000 }' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # Retrieve funding history for BTC from a given start time. funding = info.funding_history(name="BTC", startTime=1681923833000) print(funding) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; const transport = new hl.HttpTransport(); const client = new hl.InfoClient({ transport }); // Retrieve funding history for BTC from a given start time. const funding = await client.fundingHistory({ coin: "BTC", startTime: 1681923833000, }); console.log(funding); ``` ## Use cases The `info` endpoint with `type: "fundingHistory"` is essential for applications that need to: * **Analyze funding trends**: Study historical funding rates to understand market sentiment patterns * **Calculate funding costs**: Determine total funding expenses for long-term position holding * **Strategy optimization**: Build funding-aware trading strategies and optimize position timing * **Market analysis**: Analyze funding rate patterns for market sentiment and trend analysis * **Risk management**: Assess funding cost impact on strategy performance and profitability * **Arbitrage opportunities**: Identify periods with favorable funding rates for carry trades * **Backtesting systems**: Include realistic funding costs in strategy simulation and testing * **Performance attribution**: Separate trading gains from funding costs for accurate analysis * **Research and analytics**: Study perpetual futures funding mechanisms and market dynamics * **Automated alerts**: Set up notifications for funding rate threshold breaches or patterns This endpoint provides comprehensive funding rate history, enabling sophisticated analysis of perpetual futures markets and the development of funding-aware trading strategies on the Hyperliquid platform. # gossipPriorityAuctionStatus | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-gossip-priority-auction-status openapi/hyperliquid_node_api/hypercore_info/info_gossip_priority_auction_status.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "gossipPriorityAuctionStatus" returns the current gossip priority auction status — prioritized IPs and the active per-slot auctions. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "gossipPriorityAuctionStatus"` returns the current status of the gossip priority auction — the IP addresses currently prioritized for gossip, and the active per-slot auctions with their gas pricing. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"gossipPriorityAuctionStatus"`. ## Response Returns a two-element array: the list of currently prioritized IP addresses, followed by the list of active per-slot auctions. Each auction includes `startTimeSeconds`, `durationSeconds`, `startGas`, `currentGas`, and `endGas`. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "gossipPriorityAuctionStatus"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # skip_ws=True avoids opening a WebSocket connection for this REST call info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The SDK has no dedicated gossipPriorityAuctionStatus helper, so post the request directly. status = info.post("/info", {"type": "gossipPriorityAuctionStatus"}) print(status) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // apiUrl points the transport at a custom server (your Chainstack endpoint) const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const status = await info.gossipPriorityAuctionStatus(); console.log(status); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ ["54.64.2.87", "18.180.228.50"], [ { "startTimeSeconds": 1782287280, "durationSeconds": 180, "startGas": "11.9842756", "currentGas": "9.30001388", "endGas": null } ] ] ``` ## Use cases The `info` endpoint with `type: "gossipPriorityAuctionStatus"` is useful for: * Monitoring the live gossip priority auction and its gas pricing * Tooling for node operators bidding for gossip priority * Observing which IP addresses are currently prioritized # gossipRootIps | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-gossip-root-ips openapi/hyperliquid_node_api/hypercore_info/info_gossip_root_ips.json post /info Reference for the gossipRootIps JSON-RPC method on the Hyperliquid info blockchain via Chainstack nodes. Parameters, response, and examples included. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "gossipRootIps"` retrieves a list of recently available seed peer IP addresses for non-validators looking to connect to the Hyperliquid network. This endpoint provides reliable peer addresses that can be used as seed peers for node synchronization and network connectivity. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"gossipRootIps"` to retrieve the list of seed peer IP addresses. ## Response The response is an array of IP addresses (strings) representing recently available seed peers that can be used for network connectivity. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "gossipRootIps"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # The Python SDK has no dedicated gossipRootIps helper, so post the request directly. root_ips = info.post("/info", {"type": "gossipRootIps"}) print(root_ips) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; const transport = new hl.HttpTransport(); const client = new hl.InfoClient({ transport }); const rootIps = await client.gossipRootIps(); console.log(rootIps); ``` ## Example response ```json theme={"system"} [ "54.199.122.133", "116.199.229.233", "54.168.150.28", "35.190.230.32", "13.230.48.38", "218.33.8.227", "35.72.88.240", "45.76.206.35", "54.65.106.241", "18.180.228.50", "35.79.116.97", "13.158.102.147", "52.198.152.3", "43.207.77.116", "57.181.193.239", "52.193.108.65", "54.64.2.87" ] ``` ## Use case The `info` endpoint with `type: "gossipRootIps"` is particularly useful for: * Non-validators setting up new nodes who need reliable seed peers for initial synchronization * Node operators looking to expand their peer connectivity with known good peers * Applications that need to dynamically discover active network peers * Network monitoring tools that track peer availability and distribution * Backup peer discovery when primary seed nodes are unavailable This endpoint ensures that new nodes joining the network can quickly find active peers without relying on hardcoded IP addresses that may become stale over time. # historicalOrders | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-historical-orders openapi/hyperliquid_node_api/hypercore_info/info_historical_orders.json post /info The info endpoint with type: "historicalOrders" retrieves a user's historical orders on the Hyperliquid exchange. On Hyperliquid info. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "historicalOrders"` retrieves a user's historical orders on the Hyperliquid exchange. This endpoint provides comprehensive order history including detailed order information and final status for each order, making it essential for trade analysis, reporting, and order management. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"historicalOrders"` to retrieve historical orders. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. ## Response The response is an array of historical order objects, each containing detailed order information and status: ### Response structure Each historical order object contains: * `order` — Detailed order information object * `status` — Final order status * `statusTimestamp` — Timestamp when the status was last updated ### Order details structure The `order` object contains comprehensive order information: **Basic order data:** * `coin` — Asset identifier (simple names like "BTC", "ETH" for perpetuals; spot format like "@107" for spot trades) * `side` — Order side: "A" for Ask/Sell, "B" for Bid/Buy * `limitPx` — Limit price as a string for precision * `sz` — Current order size (remaining quantity) * `origSz` — Original order size when placed * `oid` — Order ID (unique identifier) * `timestamp` — Order creation timestamp in milliseconds **Order type and execution:** * `orderType` — Order type (e.g., "Market", "Limit", "Stop", "StopLimit") * `tif` — Time in force (e.g., "FrontendMarket", "Gtc", "Ioc", "Fok") * `reduceOnly` — Boolean indicating if this is a reduce-only order * `cloid` — Client order ID if provided (null if not set) **Conditional order data:** * `isTrigger` — Boolean indicating if this is a trigger order * `triggerCondition` — Trigger condition for conditional orders * `triggerPx` — Trigger price for conditional orders * `isPositionTpsl` — Boolean indicating if this is a position-level TP/SL order * `children` — Array of child orders (for TP/SL orders) ### Data limits and ordering **Response limits:** * Maximum 2000 most recent historical orders * Orders are returned in reverse chronological order (most recent first) * Older orders beyond the 2000 limit are not accessible **Historical coverage:** * Includes orders from all states: filled, canceled, rejected, etc. * Provides complete order lifecycle information * Covers both active and completed orders ### Order status values The `status` field indicates the final state of each order: **Completed states:** * `filled` — Order completely executed * `triggered` — Trigger order activated and converted **Canceled states:** * `canceled` — Order canceled by user * `marginCanceled` — Canceled due to insufficient margin * `vaultWithdrawalCanceled` — Canceled due to vault withdrawal * `openInterestCapCanceled` — Canceled due to open interest cap * `selfTradeCanceled` — Canceled due to self-trade prevention * `reduceOnlyCanceled` — Canceled reduce-only order that wouldn't reduce position * `siblingFilledCanceled` — TP/SL canceled due to sibling order execution * `delistedCanceled` — Canceled due to asset delisting * `liquidatedCanceled` — Canceled due to liquidation * `scheduledCancel` — Canceled due to scheduled cancel (dead man's switch) **Rejected states:** * `rejected` — Order rejected at placement * `tickRejected` — Rejected due to invalid tick price * `minTradeNtlRejected` — Rejected due to minimum notional requirement * `perpMarginRejected` — Rejected due to insufficient margin * `reduceOnlyRejected` — Rejected due to reduce-only constraints * `badAloPxRejected` — Rejected due to post-only immediate match * `iocCancelRejected` — IOC order rejected due to no immediate match * `badTriggerPxRejected` — Rejected due to invalid trigger price * `marketOrderNoLiquidityRejected` — Market order rejected due to insufficient liquidity * `positionIncreaseAtOpenInterestCapRejected` — Rejected due to open interest cap * `positionFlipAtOpenInterestCapRejected` — Rejected due to position flip at OI cap * `tooAggressiveAtOpenInterestCapRejected` — Rejected due to aggressive pricing at OI cap * `openInterestIncreaseRejected` — Rejected due to open interest increase limits * `insufficientSpotBalanceRejected` — Rejected due to insufficient spot balance * `oracleRejected` — Rejected due to price deviation from oracle * `perpMaxPositionRejected` — Rejected due to position size limits **Active states:** * `open` — Order still active (rare in historical data) ### Asset identification **Perpetual contracts:** * Use simple asset names: "BTC", "ETH", "AVAX", "SOL" * Represent standard perpetual futures contracts **Spot markets:** * Use indexed format: "@107", "@1", etc. * Index corresponds to the spot pair position in the universe * Some assets may have remapped names in user interfaces ## Time in force (TIF) values **Common TIF values:** * `Gtc` — Good Till Canceled (remains active until filled or canceled) * `Ioc` — Immediate Or Cancel (execute immediately or cancel) * `Fok` — Fill Or Kill (execute completely or cancel) * `FrontendMarket` — Market order placed through frontend interface ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "historicalOrders", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://api.hyperliquid.xyz/info ``` ```python Python theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) orders = info.historical_orders("0x31ca8395cf837de08b24da3f660e77761dfb974b") print(orders) ``` ```typescript TypeScript theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const orders = await info.historicalOrders({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(orders); ``` ## Example response ```json theme={"system"} [ { "order": { "coin": "ETH", "side": "A", "limitPx": "2412.7", "sz": "0.0", "oid": 1, "timestamp": 1724361546645, "triggerCondition": "N/A", "isTrigger": false, "triggerPx": "0.0", "children": [], "isPositionTpsl": false, "reduceOnly": true, "orderType": "Market", "origSz": "0.0076", "tif": "FrontendMarket", "cloid": null }, "status": "filled", "statusTimestamp": 1724361546645 } ] ``` ## Use cases The `info` endpoint with `type: "historicalOrders"` is essential for applications that need to: * **Order history tracking**: Display comprehensive order history in trading applications * **Trade analysis**: Analyze historical trading patterns, order placement strategies, and execution quality * **Performance reporting**: Generate trading reports and performance analytics * **Order debugging**: Investigate rejected, canceled, or failed orders to improve strategies * **Compliance reporting**: Maintain detailed audit trails of all trading activity * **Strategy backtesting**: Use historical order data to validate and improve trading strategies * **Risk management**: Review order behavior patterns and identify risk issues * **Fee analysis**: Calculate historical trading costs and analyze execution patterns * **Portfolio reconciliation**: Match orders against expected executions and identify discrepancies * **Automated trading systems**: Review algorithmic trading performance and optimize strategies This endpoint provides comprehensive historical order data with detailed status information, enabling thorough analysis and reporting capabilities for trading activities on the Hyperliquid platform. # isVip | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-is-vip openapi/hyperliquid_node_api/hypercore_info/info_is_vip.json post /info The info endpoint with type: "isVip" returns whether a user currently has VIP status on Hyperliquid. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "isVip"` returns whether a user currently has VIP status on Hyperliquid. VIP status affects fee tiers and access to certain features. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"isVip"`. * `user` (string, required) — Address in 42-character hexadecimal format. ## Response Returns a boolean indicating whether the user currently has VIP status (`true`), or `false` otherwise. May be `null` if the status is unknown. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "isVip", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no dedicated isVip helper, so post the request directly. is_vip = info.post("/info", { "type": "isVip", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }) print(is_vip) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const isVip = await info.isVip({ user: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }); console.log(isVip); ``` ## Example response ```json theme={"system"} false ``` ## Use cases The `info` endpoint with `type: "isVip"` is useful for: * Displaying a user's fee tier or VIP badge * Gating VIP-only features in a frontend * Analytics on VIP participation # l2Book | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-l2-book openapi/hyperliquid_node_api/hypercore_info/info_l2_book.json post /info The info endpoint with type: "l2Book" retrieves a Level 2 order book snapshot for a specific asset on the Hyperliquid exchange. On Hyperliquid info. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "l2Book"` retrieves a Level 2 order book snapshot for a specific asset on the Hyperliquid exchange. This endpoint provides real-time market depth information showing the best bid and ask prices with their corresponding quantities and order counts. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"l2Book"` to retrieve order book snapshot. * `coin` (string, required) — Asset identifier (simple names like "BTC", "ETH" for perpetuals; spot format like "@107" for spot trades). * `nSigFigs` (integer, optional) — Optional field to aggregate levels to `nSigFigs` significant figures. Valid values are 2, 3, 4, 5, and `null` (full precision). * `mantissa` (integer, optional) — Optional field to aggregate levels. This field is only allowed if nSigFigs is 5. Accepts values of 1, 2 or 5. ## Response The response is an array containing exactly two arrays representing the order book sides: ### Response structure **Array format:** `[bids, asks]` * **First array (index 0):** Bid levels (buy orders) sorted by price descending * **Second array (index 1):** Ask levels (sell orders) sorted by price ascending ### Level data structure Each price level contains the following fields: **Core level data:** * `px` — Price level as a string for precision * `sz` — Total size (quantity) at this price level as a string * `n` — The number of different orders that comprise this level ### Data limits and ordering **Response limits:** * Maximum 20 levels per side (bids and asks) * Bids are sorted by price in descending order (highest to lowest) * Asks are sorted by price in ascending order (lowest to highest) **Price precision:** * All prices and sizes are returned as strings to maintain precision * Actual precision depends on the asset's tick size ### Price aggregation options **Significant figures (`nSigFigs`):** * `2` — Aggregate to 2 significant figures (e.g., 12000, 1200) * `3` — Aggregate to 3 significant figures (e.g., 12300, 1230) * `4` — Aggregate to 4 significant figures (e.g., 12340, 1234) * `5` — Aggregate to 5 significant figures (e.g., 12345, 1234.5) * `null` — Full precision (no aggregation) **Mantissa (only when `nSigFigs` is 5):** * `1` — Round to nearest 1 (e.g., 12345) * `2` — Round to nearest 2 (e.g., 12344, 12346) * `5` — Round to nearest 5 (e.g., 12345, 12350) ### Asset identification **Perpetual contracts:** * Use simple asset names: "BTC", "ETH", "AVAX", "SOL" * Represent standard perpetual futures contracts **Spot markets:** * Use indexed format: "@107", "@1", etc. * Index corresponds to the spot pair position in the universe * Some assets may have remapped names in user interfaces ## Order book interpretation ### Understanding the data **Bid side (buyers):** * Shows prices where buyers are willing to purchase * Higher prices indicate stronger buying interest * Sorted from highest price to lowest price **Ask side (sellers):** * Shows prices where sellers are willing to sell * Lower prices indicate more competitive selling * Sorted from lowest price to highest price **Spread calculation:** * Spread = Lowest ask price - Highest bid price * Tighter spreads indicate more liquid markets ### Order count significance **Order count (`n` field):** * Indicates market depth and liquidity at each level * Higher counts suggest more distributed interest * Single large orders vs. many small orders provide different market signals ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "l2Book", "coin": "ETH"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # l2_snapshot posts {"type": "l2Book", "coin": "ETH"} book = info.l2_snapshot("ETH") print(book) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); // l2Book posts {"type": "l2Book", "coin": "ETH"} const book = await info.l2Book({ coin: "ETH" }); console.log(book); ``` ## Example response ```json theme={"system"} [ [ { "px": "19900", "sz": "1", "n": 1 }, { "px": "19800", "sz": "2", "n": 2 }, { "px": "19700", "sz": "3", "n": 3 } ], [ { "px": "20100", "sz": "1", "n": 1 }, { "px": "20200", "sz": "2", "n": 2 }, { "px": "20300", "sz": "3", "n": 3 } ] ] ``` ## Use cases The `info` endpoint with `type: "l2Book"` is essential for applications that need to: * **Market making**: Analyze current market depth to place competitive bid and ask orders * **Trading algorithms**: Make informed decisions based on order book liquidity and price levels * **Price discovery**: Understand current market sentiment and bid-ask spread * **Liquidity analysis**: Assess market depth and potential price impact of large orders * **Trading interfaces**: Display real-time order book data in trading applications * **Execution optimization**: Choose optimal order placement strategies based on current book state * **Risk management**: Evaluate market conditions before executing large trades * **Arbitrage detection**: Identify price discrepancies and trading opportunities * **Market surveillance**: Monitor for unusual order book patterns or liquidity changes * **Quantitative analysis**: Study market microstructure and order flow patterns This endpoint provides essential Level 2 market data, enabling sophisticated trading strategies and real-time market analysis on the Hyperliquid platform. # leadingVaults | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-leadingvaults openapi/hyperliquid_node_api/hypercore_info/info_leadingvaults.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "leadingVaults" retrieves information about all vaults managed by a specific vault leader on the Hyperliquid exchange. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "leadingVaults"` retrieves information about all vaults managed by a specific vault leader on the Hyperliquid exchange. This endpoint provides comprehensive data about a vault leader's managed funds, including performance metrics, vault configurations, and management details. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"leadingVaults"` to retrieve vaults led by a specific user. * `user` (string, required) — Address in 42-character hexadecimal format of the vault leader (e.g., `0x31ca8395cf837de08b24da3f660e77761dfb974b`). ## Response The response is an array of vault objects managed by the specified vault leader: ### Vault identification * `vaultAddress` (string) — Contract address of the vault (42-character hexadecimal) * `name` (string) — Human-readable name of the vault * `leader` (string) — Address of the vault leader (matches the requested user) ### Vault metrics * `totalShares` (string) — Total number of shares issued by the vault * `tvl` (string) — Total value locked in the vault expressed in USD * `sharePrice` (string) — Current price per share reflecting cumulative performance ### Performance metrics * `pnl1D` (string) — 1-day profit and loss percentage * `pnl7D` (string) — 7-day profit and loss percentage * `pnl30D` (string) — 30-day profit and loss percentage * `pnlAllTime` (string) — All-time profit and loss percentage since vault inception * `maxDD` (string) — Maximum drawdown percentage (worst peak-to-trough decline) ### Vault configuration * `isOpen` (boolean) — Whether the vault is currently accepting new deposits * `minDeposit` (string) — Minimum deposit amount required to invest in the vault * `createdTime` (integer) — Vault creation timestamp in milliseconds * `leaderCommission` (string) — Commission percentage taken by the vault leader ### Understanding vault leadership **Vault leader responsibilities:** * **Trading decisions**: Making all buy/sell decisions for the vault * **Risk management**: Managing leverage and position sizing * **Strategy execution**: Implementing trading strategies * **Performance accountability**: Responsible for vault returns **Commission structure:** * Leaders typically earn a percentage of profits as commission * Commission rates vary by vault and leader agreement * Incentivizes leaders to maximize vault performance **Vault lifecycle:** * **Creation**: Leaders can create new vaults with specific parameters * **Management**: Ongoing trading and strategy execution * **Performance tracking**: Historical record of all trading decisions ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "leadingVaults", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # The hyperliquid-python-sdk Info class has no dedicated leadingVaults # helper, so use its generic post method, which targets the same /info # endpoint and {"type": "leadingVaults"} request body as the curl above. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) leading_vaults = info.post( "/info", { "type": "leadingVaults", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b", }, ) print(leading_vaults) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const leadingVaults = await info.leadingVaults({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(leadingVaults); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use case The `info` endpoint with `type: "leadingVaults"` is essential for applications that need to: * **Vault leader analysis**: Evaluate the track record and performance of specific vault leaders * **Due diligence**: Research vault leaders before making investment decisions * **Performance tracking**: Monitor the performance of vaults managed by specific leaders * **Leader dashboards**: Build management interfaces for vault leaders to track their funds * **Portfolio analysis**: Analyze the investment style and strategy of vault leaders * **Leader comparison**: Compare performance across different vault leaders * **Risk assessment**: Evaluate the risk profile of vaults managed by specific leaders * **Investment research**: Identify top-performing vault leaders for potential investments * **Vault management tools**: Build tools for vault leaders to manage their funds * **Performance analytics**: Analyze leader performance across different market conditions * **Leader reputation systems**: Build reputation scoring based on vault performance * **Investment platforms**: Display leader-specific vault information to investors * **Commission analysis**: Understand the fee structure of different vault leaders * **Strategy research**: Study successful trading strategies employed by top leaders This endpoint is particularly valuable for both investors who want to research vault leaders before investing and for vault leaders themselves who need to monitor and manage their vault portfolio. It provides comprehensive visibility into a leader's vault management track record and current performance. # legalCheck | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-legal-check openapi/hyperliquid_node_api/hypercore_info/info_legal_check.json post /info The info endpoint with type: "legalCheck" returns a user's legal/compliance status — accepted terms, trading permission, and restrictions. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "legalCheck"` returns a user's legal and compliance status — whether they have accepted the terms, whether they are allowed to trade, and any restrictions that apply. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"legalCheck"`. * `user` (string, required) — Address in 42-character hexadecimal format. ## Response Returns a legal-status object: * `acceptedTerms` (boolean) — Whether the user has accepted the terms. * `userAllowed` (boolean) — Whether the user is allowed to trade. * `restrictions` (string) — A restriction code applied to the user, if any. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "legalCheck", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no dedicated legalCheck helper, so post the request directly. legal = info.post("/info", { "type": "legalCheck", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }) print(legal) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const legal = await info.legalCheck({ user: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }); console.log(legal); ``` ## Example response ```json theme={"system"} { "acceptedTerms": false, "userAllowed": true, "restrictions": "o" } ``` ## Use cases The `info` endpoint with `type: "legalCheck"` is useful for: * Compliance gating before allowing trading actions * Surfacing onboarding steps (for example, accepting terms) * Displaying any restrictions that apply to a user # liquidatable | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-liquidatable openapi/hyperliquid_node_api/hypercore_info/info_liquidatable.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "liquidatable" checks whether a user's positions are currently at risk of liquidation on the Hyperliquid exchange. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "liquidatable"` checks whether a user's positions are currently at risk of liquidation on the Hyperliquid exchange. This endpoint provides critical risk management information including liquidation status, leverage ratios, margin usage, and account health metrics. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"liquidatable"` to check user's liquidation status. * `user` (string, required) — Address in 42-character hexadecimal format (e.g., `0x31ca8395cf837de08b24da3f660e77761dfb974b`). ## Response The response is an object containing liquidation status and risk metrics: ### Liquidation status fields * `liquidatable` (boolean) — Whether the user's positions are currently liquidatable: * `true` — Positions are at risk and may be liquidated * `false` — Positions are healthy and not at liquidation risk ### Risk metrics (optional fields) * `leverage` (string) — Current effective leverage ratio of the user's positions * `marginUsed` (string) — Amount of margin currently used for open positions * `marginRemaining` (string) — Amount of available margin before liquidation threshold * `accountValue` (string) — Total account value including unrealized profit and loss * `totalRawUsd` (string) — Total raw USD value of the account before adjustments * `totalNtlPos` (string) — Total notional position value across all assets ### Understanding liquidation risk **Liquidation occurs when:** * Account leverage exceeds maximum allowed ratios * Margin requirements are not met due to adverse price movements * Account value falls below maintenance margin requirements **Risk levels:** * **Safe**: `liquidatable: false` with sufficient margin remaining * **At Risk**: `liquidatable: true` indicating immediate liquidation danger * **Critical**: High leverage with minimal margin remaining ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "liquidatable", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # The Python SDK has no dedicated liquidatable() helper, so post the # request directly. Info.post() is the same primitive every typed # method wraps. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) result = info.post( "/info", {"type": "liquidatable", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const result = await info.liquidatable(); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use case The `info` endpoint with `type: "liquidatable"` is essential for trading applications that need to: * **Risk monitoring**: Continuously monitor account health and liquidation risk * **Alert systems**: Trigger warnings when positions approach liquidation thresholds * **Position management**: Make informed decisions about position sizing and leverage * **Automated trading**: Implement risk management rules in trading bots * **Portfolio dashboards**: Display real-time risk metrics and account health * **Margin management**: Calculate available margin for new positions * **Liquidation protection**: Automatically reduce positions when risk is high * **Risk analytics**: Track leverage ratios and margin utilization over time * **Trading interfaces**: Show visual indicators for account risk levels * **Compliance monitoring**: Ensure positions stay within risk parameters * **Emergency protocols**: Trigger emergency position closures when necessary * **Performance analysis**: Understand the relationship between leverage and returns This endpoint is critical for any serious trading application, as it provides the real-time risk assessment needed to prevent liquidations and manage portfolio risk effectively on the Hyperliquid exchange. # marginTable | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-margin-table openapi/hyperliquid_node_api/hypercore_info/info_margin_table.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "marginTable" retrieves a specific margin table by ID. marginTable on Hyperliquid info via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "marginTable"` retrieves a specific margin table by ID. Returns the margin tier configuration including description and leverage tiers. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"marginTable"`. * `id` (integer, required) — Margin table ID. ## Response The response is an object containing: * `description` (string) — Description of the margin table. * `marginTiers` (array) — Array of margin tier objects with: * `lowerBound` (string) — Lower bound of the tier in USD. * `maxLeverage` (integer) — Maximum leverage allowed for this tier. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "marginTable", "id": 1}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # The hyperliquid-python-sdk has no named method for the marginTable type, # so post the request body directly through the shared Info.post primitive. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) margin_table = info.post("/info", {"type": "marginTable", "id": 1}) print(margin_table) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const marginTable = await info.marginTable({ id: 1 }); console.log(marginTable); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "description": "", "marginTiers": [ { "lowerBound": "0.0", "maxLeverage": 1 } ] } ``` ## Use case The `info` endpoint with `type: "marginTable"` is useful for: * Looking up specific margin tier configurations by ID * Displaying leverage limits for different position size tiers * Building risk management tools that account for tiered leverage * Validating leverage settings against margin table constraints # maxBuilderFee | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-max-builder-fee openapi/hyperliquid_node_api/hypercore_info/info_max_builder_fee.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "maxBuilderFee" retrieves the maximum builder fee that a user has approved for a specific builder on the Hyperliquid network. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "maxBuilderFee"` retrieves the maximum builder fee that a user has approved for a specific builder on the Hyperliquid network. This endpoint is essential for understanding fee arrangements between users and builders in the MEV (Maximum Extractable Value) ecosystem. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"maxBuilderFee"` to retrieve the maximum builder fee. * `user` (string, required) — User address in 42-character hexadecimal format (e.g., `0x47fc45cebfc47cef07a09a98405b6ebaef00ef75`). * `builder` (string, required) — Builder address in 42-character hexadecimal format (e.g., `0x1922810825c90f4270048b96da7b1803cd8609ef`). ## Response The response is a single integer representing the maximum builder fee: * **Fee value** (integer) — Maximum fee approved in tenths of a basis point * Example: `1` means 0.001% (1/10 of a basis point) * Example: `10` means 0.01% (1 basis point) * Example: `100` means 0.1% (10 basis points) ### Understanding builder fees **Builder fee structure:** * Fees are expressed in tenths of a basis point for precise control * One basis point = 0.01%, so one tenth of a basis point = 0.001% * This granular fee structure allows for competitive builder markets **Fee approval process:** * Users pre-approve maximum fees for specific builders * Actual fees charged may be less than the approved maximum * Approvals can be updated or revoked as needed * Zero values indicate no fee approval for that builder ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "maxBuilderFee", "user": "0x47fc45cebfc47cef07a09a98405b6ebaef00ef75", "builder": "0x1922810825c90f4270048b96da7b1803cd8609ef"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # The hyperliquid-python-sdk has no dedicated maxBuilderFee helper, # so call the info endpoint directly with the generic post method. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) response = info.post( "/info", { "type": "maxBuilderFee", "user": "0x47fc45cebfc47cef07a09a98405b6ebaef00ef75", "builder": "0x1922810825c90f4270048b96da7b1803cd8609ef", }, ) print(response) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const maxBuilderFee = await info.maxBuilderFee({ user: "0x47fc45cebfc47cef07a09a98405b6ebaef00ef75", builder: "0x1922810825c90f4270048b96da7b1803cd8609ef", }); console.log(maxBuilderFee); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} 1 ``` This response indicates that the user has approved a maximum builder fee of 1 tenth of a basis point (0.001%) for the specified builder. ## Use case The `info` endpoint with `type: "maxBuilderFee"` is essential for applications that need to: * **Trading interfaces**: Display current builder fee approvals before order submission * **Fee management**: Allow users to review and adjust their builder fee settings * **Builder selection**: Help users choose builders based on approved fee levels * **Cost analysis**: Calculate potential trading costs including builder fees * **Compliance monitoring**: Track fee arrangements for regulatory reporting * **MEV optimization**: Optimize builder relationships for better execution * **Risk management**: Monitor fee exposure across different builders * **Automated trading**: Verify fee approvals before executing algorithmic strategies * **Portfolio analytics**: Include builder fees in total cost calculations * **Builder comparison**: Compare fee structures across different builders * **Execution quality**: Correlate fees with execution performance metrics * **Fee auditing**: Track historical fee approvals and changes * **Smart routing**: Route orders based on fee-adjusted execution quality * **User education**: Show users how builder fees impact their trading costs This endpoint is particularly valuable for sophisticated traders who actively manage their builder relationships, institutional users with complex execution requirements, and applications that provide comprehensive trading cost analysis and optimization on the Hyperliquid network. # maxMarketOrderNtls | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-maxmarketorderntls openapi/hyperliquid_node_api/hypercore_info/info_maxmarketorderntls.json post /4f8d8f4040bdacd1577bff8058438274/info Reference docs for the maxMarketOrderNtls JSON-RPC method on the Hyperliquid info blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "maxMarketOrderNtls"` retrieves the maximum notional values and sizes allowed for market orders across all available assets on the Hyperliquid exchange. This endpoint provides essential trading limits that help prevent excessive market impact and ensure orderly market operations. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"maxMarketOrderNtls"` to retrieve maximum market order limits. ## Response The response is an array of objects containing market order limits for each asset: ### Market order limit fields * `coin` (string) — Asset symbol (e.g., "BTC", "ETH", "SOL", "AVAX") * `maxNtl` (string) — Maximum notional value allowed for market orders in USD * `maxSz` (string, optional) — Maximum size allowed for market orders in asset units ### Understanding market order limits **Notional value limits:** * Expressed in USD regardless of the asset being traded * Prevents single orders from causing excessive market impact * Varies by asset based on liquidity and volatility characteristics **Size limits:** * Expressed in native asset units (e.g., BTC, ETH) * May not be present for all assets * Provides alternative sizing constraint alongside notional limits **Risk management:** * Limits protect both traders and the exchange from extreme market movements * Larger, more liquid assets typically have higher limits * Limits may be adjusted dynamically based on market conditions ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "maxMarketOrderNtls"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The SDK has no dedicated helper for this type, so post it directly. result = info.post("/info", {"type": "maxMarketOrderNtls"}) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const result = await info.maxMarketOrderNtls(); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use case The `info` endpoint with `type: "maxMarketOrderNtls"` is essential for trading applications that need to: * **Order validation**: Ensure market orders comply with exchange limits before submission * **Risk management**: Implement pre-trade risk checks for large orders * **Trading interface design**: Display maximum order sizes in trading forms * **Order splitting**: Break large orders into smaller chunks that respect limits * **Liquidity analysis**: Understand market depth constraints for different assets * **Automated trading**: Configure trading bots with appropriate size limits * **Portfolio management**: Plan large position entries within exchange constraints * **Market making**: Design strategies that respect maximum order sizes * **Institutional trading**: Implement compliance checks for large order flows * **Order routing**: Optimize order execution strategies based on size limits * **Risk monitoring**: Track order sizes relative to exchange maximums * **Trading education**: Inform users about platform trading limits * **API integration**: Validate order parameters before sending to exchange * **Performance optimization**: Avoid rejected orders due to size violations This endpoint is particularly important for institutional traders, algorithmic trading systems, and any application that handles large order volumes, as it provides the current exchange limits needed to ensure successful order execution and compliance with platform rules. # meta | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-meta openapi/hyperliquid_node_api/hypercore_info/info_meta.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "meta" retrieves perpetuals metadata including universe and margin tables for the Hyperliquid exchange. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "meta"` retrieves perpetuals metadata including universe and margin tables for the Hyperliquid exchange. This endpoint provides essential information about available trading pairs, their decimal precision, maximum leverage, and margin tier configurations. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"meta"` to retrieve perpetuals metadata. * `dex` (string, optional) — Perp dex name. Defaults to the empty string which represents the first perp dex. ## Response The response contains two main sections: ### Universe An array of perpetual assets with their trading parameters: * `name` (string) — The asset name (e.g., "BTC", "ETH"). * `szDecimals` (integer) — Number of decimal places for size precision. * `maxLeverage` (integer) — Maximum leverage allowed for this asset. * `onlyIsolated` (boolean, optional) — Whether the asset can only be traded in isolated margin mode. * `isDelisted` (boolean, optional) — Whether the asset is delisted and no longer available for trading. ### Margin tables An array of margin tier configurations, where each entry is a tuple containing: 1. **Margin table ID** (integer) — Unique identifier for the margin table. 2. **Margin table object** containing: * `description` (string) — Description of the margin table. * `marginTiers` (array) — Array of margin tiers with: * `lowerBound` (string) — Lower bound of the tier in USD. * `maxLeverage` (integer) — Maximum leverage allowed for this tier. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "meta"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # Point the SDK at your Chainstack endpoint; skip_ws avoids opening a websocket. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # Posts {"type": "meta"} to /info. meta = info.meta() print(meta) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // apiUrl points the transport at your Chainstack endpoint. const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); // Posts {"type": "meta"} to /info. const meta = await info.meta(); console.log(meta); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "universe": [ { "name": "BTC", "szDecimals": 5, "maxLeverage": 50 }, { "name": "ETH", "szDecimals": 4, "maxLeverage": 50 }, { "name": "HPOS", "szDecimals": 0, "maxLeverage": 3, "onlyIsolated": true }, { "name": "LOOM", "szDecimals": 1, "maxLeverage": 3, "onlyIsolated": true, "isDelisted": true } ], "marginTables": [ [ 50, { "description": "", "marginTiers": [ { "lowerBound": "0.0", "maxLeverage": 50 } ] } ], [ 51, { "description": "tiered 10x", "marginTiers": [ { "lowerBound": "0.0", "maxLeverage": 10 }, { "lowerBound": "3000000.0", "maxLeverage": 5 } ] } ] ] } ``` ## Use case The `info` endpoint with `type: "meta"` is essential for trading applications that need to: * Display available trading pairs and their specifications * Validate trade sizes according to decimal precision requirements * Implement dynamic leverage limits based on position size * Determine which assets require isolated margin mode * Filter out delisted assets from trading interfaces This metadata is typically cached and refreshed periodically to ensure trading applications have up-to-date information about exchange parameters. # metaAndAssetCtxs | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-meta-and-asset-ctxs openapi/hyperliquid_node_api/hypercore_info/info_meta_and_asset_ctxs.json post /info The info endpoint with type: "metaAndAssetCtxs" retrieves comprehensive asset contexts for perpetuals trading on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "metaAndAssetCtxs"` retrieves comprehensive asset contexts for perpetuals trading on the Hyperliquid exchange. This endpoint provides both universe metadata defining available assets and real-time market data including mark prices, funding rates, open interest, volume metrics, and price impact information for all perpetual contracts. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"metaAndAssetCtxs"` to retrieve asset contexts. ## Response The response is an array containing two elements: universe metadata and asset contexts. This structure provides both static asset definitions and dynamic market data for all perpetual contracts. ### Response structure The response contains exactly two elements: 1. **Universe metadata** — Static asset definitions and trading parameters 2. **Asset contexts** — Real-time market data for each asset ### Universe metadata structure The first element contains the `universe` array with asset definitions: **Asset identification:** * `name` — Asset symbol (e.g., "BTC", "ETH", "AVAX") * Corresponds to the perpetual contract identifier **Trading parameters:** * `szDecimals` — Number of decimal places for size precision * `maxLeverage` — Maximum leverage allowed for this asset * `onlyIsolated` — Boolean indicating if asset requires isolated margin mode **Precision and constraints:** * Size decimals determine minimum order size increments * Max leverage sets risk management limits * Isolated margin requirement affects trading strategies ### Asset contexts structure The second element is an array of market data objects, one for each asset in the universe: **Price information:** * `markPx` — Current mark price used for margin calculations * `midPx` — Mid price between best bid and ask * `oraclePx` — Oracle price from external price feeds * `prevDayPx` — Price from 24 hours ago for daily change calculation **Market depth and impact:** * `impactPxs` — Array of two impact prices \[buy impact, sell impact] * Shows expected price impact for market orders * Useful for assessing market liquidity and slippage **Funding and premium:** * `funding` — Current funding rate for the perpetual contract * `premium` — Premium of mark price over oracle price * Indicates market sentiment and arbitrage opportunities **Volume and interest:** * `dayNtlVlm` — Daily notional volume traded * `openInterest` — Total open interest across all positions * Measures market activity and participation ### Data interpretation **Price relationships:** * Mark price vs oracle price indicates market premium/discount * Mid price vs mark price shows funding impact * Impact prices indicate market depth and liquidity **Market health indicators:** * High volume with stable prices indicates healthy liquidity * Large premium suggests strong directional bias * High open interest indicates active participation **Trading insights:** * Funding rates affect carry costs for positions * Impact prices help estimate execution costs * Volume patterns indicate market activity cycles ## Market analysis insights ### Liquidity assessment **Market depth analysis:** * Impact prices reveal available liquidity at different levels * Tight spreads between impact prices indicate deep markets * Wide spreads suggest lower liquidity or higher volatility **Volume analysis:** * Daily notional volume shows market activity levels * Compare volume across assets to identify active markets * Volume trends indicate changing market interest ### Funding rate analysis **Carry cost calculation:** * Funding rates determine the cost of holding positions * Positive funding means longs pay shorts * Negative funding means shorts pay longs **Market sentiment:** * Persistent positive funding indicates bullish sentiment * Persistent negative funding indicates bearish sentiment * Funding rate changes signal sentiment shifts ### Price discovery insights **Oracle vs mark price:** * Premium indicates market's view vs external prices * Persistent premiums suggest strong directional bias * Premium convergence indicates arbitrage efficiency **Price impact assessment:** * Impact prices help estimate execution costs * Compare impact across assets for relative liquidity * Use for optimal order sizing and execution strategies ### Risk management applications **Leverage constraints:** * Max leverage varies by asset based on volatility and liquidity * Isolated margin requirements for certain assets * Use constraints for position sizing and risk management **Volatility indicators:** * Price changes from previous day show daily volatility * Premium volatility indicates market uncertainty * Impact price spreads reflect intraday volatility ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "metaAndAssetCtxs"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # Posts {"type": "metaAndAssetCtxs"} to /info meta_and_asset_ctxs = info.meta_and_asset_ctxs() print(meta_and_asset_ctxs) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); // Posts {"type": "metaAndAssetCtxs"} to /info const metaAndAssetCtxs = await info.metaAndAssetCtxs(); console.log(metaAndAssetCtxs); ``` ## Example response ```json theme={"system"} [ { "universe": [ { "name": "BTC", "szDecimals": 5, "maxLeverage": 50 }, { "name": "ETH", "szDecimals": 4, "maxLeverage": 50 }, { "name": "HPOS", "szDecimals": 0, "maxLeverage": 3, "onlyIsolated": true } ] }, [ { "dayNtlVlm": "1169046.29406", "funding": "0.0000125", "impactPxs": [ "14.3047", "14.3444" ], "markPx": "14.3161", "midPx": "14.314", "openInterest": "688.11", "oraclePx": "14.32", "premium": "0.00031774", "prevDayPx": "15.322" }, { "dayNtlVlm": "1426126.295175", "funding": "0.0000125", "impactPxs": [ "6.0386", "6.0562" ], "markPx": "6.0436", "midPx": "6.0431", "openInterest": "1882.55", "oraclePx": "6.0457", "premium": "0.00028119", "prevDayPx": "6.3611" } ] ] ``` ## Use cases The `info` endpoint with `type: "metaAndAssetCtxs"` is essential for applications that need to: * **Trading platform development**: Build trading interfaces with real-time market data * **Market making**: Develop market making strategies using price and liquidity data * **Risk management**: Implement risk controls based on leverage limits and market conditions * **Portfolio management**: Manage multi-asset portfolios with comprehensive market context * **Price discovery**: Analyze price relationships and arbitrage opportunities * **Liquidity analysis**: Assess market liquidity and execution costs across assets * **Funding rate strategies**: Develop strategies based on funding rate patterns * **Market surveillance**: Monitor market conditions and detect anomalies * **Academic research**: Study perpetual futures markets and price discovery mechanisms * **Algorithmic trading**: Build automated trading systems with market context * **Order management**: Optimize order execution based on impact prices and liquidity * **Market data feeds**: Provide real-time market data to trading applications * **Analytics platforms**: Build comprehensive market analysis and reporting tools * **Educational tools**: Create educational content about perpetual futures markets * **Compliance monitoring**: Monitor market conditions for regulatory compliance * **Arbitrage detection**: Identify arbitrage opportunities between mark and oracle prices * **Volatility analysis**: Analyze market volatility patterns across different assets * **Liquidity provision**: Optimize liquidity provision strategies based on market depth * **Cross-asset analysis**: Compare market conditions across different perpetual contracts * **Market timing**: Time market entries and exits based on funding and premium data * **Position sizing**: Optimize position sizes based on leverage limits and market impact * **Execution algorithms**: Develop sophisticated execution algorithms using market context * **Market microstructure research**: Study the microstructure of perpetual futures markets * **Trading strategy backtesting**: Use historical contexts for strategy development and testing * **Client advisory**: Provide market insights and trading recommendations to clients This endpoint provides comprehensive market intelligence, enabling sophisticated trading, risk management, and analysis applications for perpetual futures markets on the Hyperliquid platform. # openOrders | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-openorders openapi/hyperliquid_node_api/hypercore_info/info_openorders.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "openOrders" retrieves a user's open orders for both perpetuals and spot trading on the Hyperliquid exchange. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "openOrders"` retrieves a user's open orders for both perpetuals and spot trading on the Hyperliquid exchange. This endpoint provides essential information about all pending orders that have not yet been filled or canceled. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"openOrders"` to retrieve user's open orders. * `user` (string, required) — Address in 42-character hexadecimal format (e.g., `0x31ca8395cf837de08b24da3f660e77761dfb974b`). * `dex` (string, optional) — Perp dex name. Defaults to the empty string which represents the first perp dex. Spot open orders are only included with the first perp dex. ## Response The response is an array of open order objects, each containing: ### Order fields * `coin` (string) — Trading pair or asset symbol: * For perpetuals: Asset name (e.g., "BTC", "ETH") * For spot: Token symbol or index format (e.g., "PURR/USDC", "@107") * `limitPx` (string) — Limit price of the order in string format for precision * `oid` (integer) — Order ID, a unique identifier for the order * `side` (string) — Order side: * `"A"` — Ask (sell order) * `"B"` — Bid (buy order) * `sz` (string) — Remaining size of the order (unfilled amount) * `timestamp` (integer) — Order creation timestamp in milliseconds ### Order side interpretation * **"A" (Ask)**: Sell order - user wants to sell the asset at the specified price * **"B" (Bid)**: Buy order - user wants to buy the asset at the specified price ### Asset naming conventions * **Perpetuals**: Use simple asset names like "BTC", "ETH" * **Spot**: Use either pair format like "PURR/USDC" or index format like "@107" ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "openOrders", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) open_orders = info.open_orders("0x31ca8395cf837de08b24da3f660e77761dfb974b") print(open_orders) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const openOrders = await info.openOrders({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(openOrders); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ { "coin": "BTC", "limitPx": "29792.0", "oid": 91490942, "side": "A", "sz": "0.0", "timestamp": 1681247412573 } ] ``` ## Use case The `info` endpoint with `type: "openOrders"` is essential for trading applications that need to: * Display user's current open orders in a trading interface * Calculate total order exposure and risk management * Implement order management features (cancel, modify) * Show pending orders across both perpetuals and spot markets * Track order status and remaining unfilled quantities * Build comprehensive trading dashboards * Monitor liquidity provision through open limit orders * Implement automated trading strategies that need order awareness This endpoint is typically called frequently to keep trading interfaces updated with the current state of all pending orders, enabling users to manage their trading positions effectively. # orderStatus | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-order-status openapi/hyperliquid_node_api/hypercore_info/info_order_status.json post /info The info endpoint with type: "orderStatus" retrieves the current status and details of a specific order on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "orderStatus"` retrieves the current status and details of a specific order on the Hyperliquid exchange. This endpoint accepts either an order ID (oid) or client order ID (cloid) to query order information, making it essential for order tracking and management. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"orderStatus"` to query order status. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. * `oid` (integer or string, required) — Either u64 representing the order id or 16-byte hex string representing the client order id. ## Response The response varies depending on whether the order is found or not: ### Successful order lookup When an order is found, the response contains detailed order information: **Response structure:** * `status` — Always "order" when order information is available * `order` — Object containing the order wrapper with order details and status **Order wrapper structure:** * `order` — Object containing the actual order details * `status` — Current order status (see status values below) * `statusTimestamp` — Timestamp when the status was last updated **Order details (within the nested `order` object):** * `coin` — Asset identifier (simple names like "BTC", "ETH" for perpetuals; spot format like "@107" for spot trades) * `side` — Order side: "A" for Ask/Sell, "B" for Bid/Buy * `limitPx` — Limit price as a string for precision * `sz` — Current order size (remaining quantity) * `oid` — Order ID * `timestamp` — Order creation timestamp in milliseconds * `triggerCondition` — Trigger condition for conditional orders * `isTrigger` — Boolean indicating if this is a trigger order * `triggerPx` — Trigger price for conditional orders * `children` — Array of child orders (for TP/SL orders) * `isPositionTpsl` — Boolean indicating if this is a position-level TP/SL order * `reduceOnly` — Boolean indicating if this is a reduce-only order * `orderType` — Order type (e.g., "Market", "Limit") * `origSz` — Original order size * `tif` — Time in force (e.g., "FrontendMarket", "Gtc", "Ioc") * `cloid` — Client order ID if provided (null if not set) ### Order not found When an order ID is not found: * `status` — "unknownOid" indicating the order was not found ## Order status values The `status` field in the order response can have the following values: ### Active states * `open` — Order placed successfully and waiting for execution * `triggered` — Trigger order has been triggered and converted to a regular order ### Completed states * `filled` — Order has been completely executed ### Canceled states * `canceled` — Order canceled by user * `marginCanceled` — Canceled due to insufficient margin to fill * `vaultWithdrawalCanceled` — Vaults only. Canceled due to user withdrawal from vault * `openInterestCapCanceled` — Canceled due to order being too aggressive when open interest was at cap * `selfTradeCanceled` — Canceled due to self-trade prevention * `reduceOnlyCanceled` — Canceled reduce-only order that does not reduce position * `siblingFilledCanceled` — TP/SL only. Canceled due to sibling order being filled * `delistedCanceled` — Canceled due to asset delisting * `liquidatedCanceled` — Canceled due to liquidation * `scheduledCancel` — API only. Canceled due to exceeding scheduled cancel deadline (dead man's switch) ### Rejected states * `rejected` — Order rejected at time of placement * `tickRejected` — Rejected due to invalid tick price * `minTradeNtlRejected` — Rejected due to order notional below minimum * `perpMarginRejected` — Rejected due to insufficient margin * `reduceOnlyRejected` — Rejected due to reduce only constraints * `badAloPxRejected` — Rejected due to post-only immediate match * `iocCancelRejected` — Rejected due to IOC not able to match * `badTriggerPxRejected` — Rejected due to invalid TP/SL price * `marketOrderNoLiquidityRejected` — Rejected due to lack of liquidity for market order * `positionIncreaseAtOpenInterestCapRejected` — Rejected due to open interest cap * `positionFlipAtOpenInterestCapRejected` — Rejected due to open interest cap * `tooAggressiveAtOpenInterestCapRejected` — Rejected due to price too aggressive at open interest cap * `openInterestIncreaseRejected` — Rejected due to open interest cap * `insufficientSpotBalanceRejected` — Rejected due to insufficient spot balance * `oracleRejected` — Rejected due to price too far from oracle * `perpMaxPositionRejected` — Rejected due to exceeding margin tier limit at current leverage ## Order identification ### Order ID (oid) * Numeric identifier assigned by the exchange * Used as a 64-bit unsigned integer * Automatically generated when orders are placed ### Client Order ID (cloid) * Optional custom identifier provided by the client * 16-byte hexadecimal string format * Allows clients to track orders using their own identifiers * Must be unique per user ## Asset identification **Perpetual contracts:** * Use simple asset names: "BTC", "ETH", "AVAX", "SOL" * Represent standard perpetual futures contracts **Spot markets:** * Use indexed format: "@107", "@1", etc. * Index corresponds to the spot pair position in the universe * Some assets may have remapped names in user interfaces ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "orderStatus", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b", "oid": 127244980388}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # Query order status by order id (oid) order_status = info.query_order_by_oid( "0x31ca8395cf837de08b24da3f660e77761dfb974b", 127244980388, ) print(order_status) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); // Query order status by order id (oid) const orderStatus = await info.orderStatus({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", oid: 127244980388, }); console.log(orderStatus); ``` ## Example response ### Order found ```json theme={"system"} { "status": "order", "order": { "order": { "coin": "ETH", "side": "A", "limitPx": "2412.7", "sz": "0.0", "oid": 1, "timestamp": 1724361546645, "triggerCondition": "N/A", "isTrigger": false, "triggerPx": "0.0", "children": [], "isPositionTpsl": false, "reduceOnly": true, "orderType": "Market", "origSz": "0.0076", "tif": "FrontendMarket", "cloid": null }, "status": "filled", "statusTimestamp": 1724361546645 } } ``` ### Order not found ```json theme={"system"} { "status": "unknownOid" } ``` ## Use cases The `info` endpoint with `type: "orderStatus"` is essential for applications that need to: * **Order tracking**: Monitor the current status and execution progress of placed orders * **Order management**: Verify order placement and track order lifecycle from creation to completion * **Risk management**: Check order status before making position adjustments or placing additional orders * **Reconciliation**: Match internal order records with exchange order status for accounting purposes * **Error handling**: Identify rejected orders and understand rejection reasons for debugging * **Automated trading**: Programmatically verify order execution in algorithmic trading systems * **Portfolio management**: Track order status as part of broader portfolio monitoring * **Compliance reporting**: Maintain audit trails of order status changes for regulatory requirements * **User interfaces**: Display real-time order status in trading applications and dashboards * **Notification systems**: Trigger alerts based on order status changes (fills, cancellations, rejections) * **Strategy validation**: Confirm order execution as part of trading strategy implementation * **Performance analysis**: Analyze order execution patterns and identify optimization opportunities * **Client order tracking**: Use client order IDs to track orders placed through custom systems * **Order debugging**: Investigate order issues by examining detailed order information and status history * **Integration testing**: Verify order placement and status updates during system integration This endpoint provides comprehensive order status information, enabling robust order management and tracking capabilities for trading applications on the Hyperliquid platform. # outcomeMeta | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-outcomemeta openapi/hyperliquid_node_api/hypercore_info/info_outcomemeta.json post /info The info endpoint with type: "outcomeMeta" retrieves the live HIP-4 outcome markets and any categorical questions on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "outcomeMeta"` retrieves the live HIP-4 outcome markets and any categorical questions on the Hyperliquid exchange. Outcome markets are fully collateralized binary contracts that settle within a fixed range — the primitive that powers prediction markets and bounded options-like instruments on Hyperliquid. ## Parameters * `type` (string, required) — Must be `"outcomeMeta"` ## Returns Returns an object with two arrays: ### Outcomes An array of live outcome markets. Each entry contains: * `outcome` (integer) — Outcome id. Used together with the side index to derive the asset encoding. * `name` (string) — Human-readable market name (for example, `"Recurring"` for the recurring BTC daily binary, or a custom name for one-off markets). * `description` (string) — For recurring binary markets, a pipe-separated `key:value` string with `class`, `underlying`, `expiry`, `targetPrice`, `period` (for example, `"class:priceBinary|underlying:BTC|expiry:20260504-0600|targetPrice:78213|period:1d"`). For other market types, free-form text. * `sideSpecs` (array) — Two-element array describing the YES (index 0) and NO (index 1) sides. Each element has a `name` field. ### Questions An array of categorical multi-outcome questions (testnet only at the time of writing — mainnet returns an empty array). Each entry contains: * `question` (integer) — Question id. * `name` (string) — Question text. * `description` (string) — Free-form description. * `fallbackOutcome` (integer) — Outcome id used if none of the named outcomes matches. * `namedOutcomes` (array of integers) — Outcome ids for the named legs. * `settledNamedOutcomes` (array of integers) — Outcome ids that have already settled. ## Asset encoding `outcomeMeta` does not return per-side coin strings or asset ids. Compute them client-side from `outcome` and `side` (`0` for YES, `1` for NO): ``` encoding = 10 * outcome + side trade coin = "#" # for /info l2Book and Exchange.order(...) balance coin = "+" # for spotClearinghouseState asset id = 100_000_000 + encoding ``` For the mainnet BTC daily YES (outcome `1`, side `0`), the encoding is `10`, the trade coin is `#10`, the balance coin is `+10`, and the asset id is `100_000_010`. See the full guide at [Trading HIP-4 outcome markets on Hyperliquid](/docs/hyperliquid-hip4-outcome-markets-trading) for the rest of the trading flow. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "outcomeMeta"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants # This endpoint is public-only, so use the default Hyperliquid mainnet API. info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no typed wrapper for "outcomeMeta", so post the request directly. # Every typed Info method is a thin wrapper over this same post(...) call. outcome_meta = info.post("/info", {"type": "outcomeMeta"}) print(outcome_meta["outcomes"]) print(outcome_meta["questions"]) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; // This endpoint is public-only, so use the default public HTTP transport. const transport = new hl.HttpTransport(); const client = new hl.InfoClient({ transport }); const data = await client.outcomeMeta(); console.log(data.outcomes); console.log(data.questions); ``` ## Use cases * **Market discovery** — enumerate every live outcome market and its side names before placing orders. * **Settlement parser** — read `description` to extract the `expiry` and `targetPrice` for binary price-based markets. * **Categorical markets** — list named legs of a `question` and their settled status (testnet). * **Inventory reconciliation** — track which `outcome` ids are still live so you can mark settled positions out of your book. # perpAnnotation | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-perp-annotation openapi/hyperliquid_node_api/hypercore_info/info_perp_annotation.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "perpAnnotation" retrieves the annotation metadata for a specific HIP-3 deployed perpetual asset. On Hyperliquid info. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "perpAnnotation"` retrieves the annotation metadata for a specific HIP-3 deployed perpetual asset. Returns the deployer-set category and description for the given coin, or `null` if no annotation has been set. Annotations are set by HIP-3 deployers using the `setPerpAnnotation` deployer action. Native Hyperliquid perps (BTC, ETH, etc.) are not HIP-3 deployed and will always return `null`. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"perpAnnotation"`. * `coin` (string, required) — HIP-3 asset symbol in `deployer:TICKER` format (e.g., `"xyz:GOLD"`, `"birb:PENGU"`). Native perps like `"BTC"` always return `null`. ## Response Returns an annotation object when the deployer has set metadata, or `null` if no annotation exists: * `category` (string) — Category label, at most 15 characters. The official Hyperliquid UI only displays categories from a predefined set (e.g., `"indices"`, `"commodities"`, `"ai"`, `"meme"`, `"defi"`). * `description` (string) — Description text, at most 400 characters. Deployers can use this as an onchain spec or link to external documentation. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "perpAnnotation", "coin": "xyz:GOLD"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # The hyperliquid-python-sdk Info class has no dedicated perpAnnotation # helper, so post the request type directly with the low-level post method. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) annotation = info.post("/info", {"type": "perpAnnotation", "coin": "xyz:GOLD"}) print(annotation) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const annotation = await info.perpAnnotation({ coin: "xyz:GOLD" }); console.log(annotation); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response When an annotation exists: ```json theme={"system"} { "category": "commodities", "description": "Gold spot price tracking contract" } ``` When no annotation has been set (or for native perps like BTC): ```json theme={"system"} null ``` ## Use case The `info` endpoint with `type: "perpAnnotation"` is useful for: * Displaying deployer-provided metadata about HIP-3 perpetual assets * Building informational pages for deployed perpetual contracts * Checking whether a perpetual has custom annotations before rendering UI * Filtering HIP-3 assets by their deployer-assigned categories # perpCategories | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-perp-categories openapi/hyperliquid_node_api/hypercore_info/info_perp_categories.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "perpCategories" retrieves all HIP-3 perpetual assets that have a category set via setPerpAnnotation. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "perpCategories"` retrieves all HIP-3 perpetual assets that have a category set via `setPerpAnnotation`. Returns an array of `[coin, category]` tuples, or an empty array if no deployers have set categories. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"perpCategories"`. ## Response An array of two-element arrays, where each entry contains: * Index 0 (string) — HIP-3 asset symbol in `deployer:TICKER` format. * Index 1 (string) — Category label assigned by the deployer. Returns an empty array if no deployers have set categories. Only HIP-3 deployed assets appear — native Hyperliquid perps (BTC, ETH, etc.) are never included. The official Hyperliquid UI only displays categories from a predefined set in lowercase with no special characters (e.g., `"indices"`, `"commodities"`, `"ai"`, `"meme"`, `"defi"`, `"gaming"`, `"layer1"`, `"layer2"`, `"stocks"`, `"forex"`). ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "perpCategories"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # The hyperliquid-python-sdk has no dedicated perpCategories method, # so post the request type directly with the generic post helper. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) categories = info.post("/info", {"type": "perpCategories"}) print(categories) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; const transport = new hl.HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const client = new hl.InfoClient({ transport }); const categories = await client.perpCategories(); console.log(categories); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response When categories exist: ```json theme={"system"} [ ["xyz:GOLD", "commodities"], ["xyz:SPX", "indices"], ["abc:AGENT", "ai"] ] ``` When no deployers have set categories: ```json theme={"system"} [] ``` ## Use case The `info` endpoint with `type: "perpCategories"` is useful for: * Organizing HIP-3 perpetual assets into categories for trading interfaces * Building category-based navigation and filtering for asset discovery * Displaying a categorized catalog of deployer-created perpetual contracts # perpConciseAnnotations | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-perp-concise-annotations openapi/hyperliquid_node_api/hypercore_info/info_perp_concise_annotations.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "perpConciseAnnotations" returns concise annotations (category, display name, keywords) for all perpetual assets. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "perpConciseAnnotations"` returns concise annotations for all perpetual assets. Each annotation classifies a perpetual into a category and can carry a frontend display name and search keywords. Use this method to categorize and label perpetual markets in dashboards and search interfaces. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"perpConciseAnnotations"`. ## Response Returns an array of `[coin, annotation]` tuples. Each annotation object contains: * `category` (string) — The classification category assigned to the perpetual. * `displayName` (string, optional) — A display name for frontends to use instead of the L1 name. * `keywords` (array of strings, optional) — Keywords used as hints to match against searches. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "perpConciseAnnotations"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # skip_ws=True avoids opening a WebSocket connection for this REST call info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The SDK has no dedicated perpConciseAnnotations helper, so post the request directly. perp_concise_annotations = info.post("/info", {"type": "perpConciseAnnotations"}) print(perp_concise_annotations) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // apiUrl points the transport at a custom server (your Chainstack endpoint) const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const perpConciseAnnotations = await info.perpConciseAnnotations(); console.log(perpConciseAnnotations); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ ["flx:BTC", { "category": "crypto" }], ["flx:COIN", { "category": "stocks" }], ["flx:GOLD", { "category": "commodities" }] ] ``` ## Use cases The `info` endpoint with `type: "perpConciseAnnotations"` is useful for: * Grouping perpetual markets by category in dashboards and market screens * Showing frontend-friendly display names instead of raw L1 names * Powering search and filtering with per-asset keywords # perpDexLimits | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-perp-dex-limits openapi/hyperliquid_node_api/hypercore_info/info_perp_dex_limits.json post /4f8d8f4040bdacd1577bff8058438274/info Reference for the perpDexLimits JSON-RPC method on the Hyperliquid info blockchain via Chainstack nodes. Parameters, response, and examples included. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "perpDexLimits"` retrieves the market limits for a builder-deployed perpetual DEX on Hyperliquid — the total open interest cap, the per-perpetual open interest size cap, the maximum transfer notional, and the per-asset open interest caps. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"perpDexLimits"`. * `dex` (string, required) — The name of the builder-deployed perp DEX, for example `"xyz"`. The empty string is not allowed. Use [perpDexs](/reference/hyperliquid-info-perpdexs) to list deployed perp DEX names. ## Response Returns a perp DEX limits object, or `null` if the DEX has no configured limits. * `totalOiCap` (string) — Total open interest cap across the DEX. * `oiSzCapPerPerp` (string) — Open interest size cap per perpetual. * `maxTransferNtl` (string) — Maximum transfer notional amount. * `coinToOiCap` (array) — Array of `[coin, oiCap]` tuples giving the open interest cap per asset. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "perpDexLimits", "dex": "xyz"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # skip_ws=True avoids opening a WebSocket connection for this REST call info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The SDK has no dedicated perpDexLimits helper, so post the request directly. perp_dex_limits = info.post("/info", {"type": "perpDexLimits", "dex": "xyz"}) print(perp_dex_limits) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // apiUrl points the transport at a custom server (your Chainstack endpoint) const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const perpDexLimits = await info.perpDexLimits({ dex: "xyz" }); console.log(perpDexLimits); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "totalOiCap": "7000000000.0", "oiSzCapPerPerp": "20000000000.0", "maxTransferNtl": "3000000000.0", "coinToOiCap": [ ["xyz:AAPL", "100000000.0"], ["xyz:ALUMINIUM", "25000000.0"], ["xyz:AMD", "100000000.0"] ] } ``` ## Use case The `info` endpoint with `type: "perpDexLimits"` is useful for: * Checking the open interest caps of a builder-deployed perp DEX before opening positions * Monitoring per-asset open interest caps on a perp DEX * Building risk and deployment tools that validate against current market limits # perpDexStatus | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-perp-dex-status openapi/hyperliquid_node_api/hypercore_info/info_perp_dex_status.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "perpDexStatus" retrieves the current status of a specific perp DEX on Hyperliquid, including total net deposits. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "perpDexStatus"` retrieves the current status of a specific perp DEX on Hyperliquid, including total net deposits. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"perpDexStatus"`. * `dex` (string, required) — Perp DEX name. ## Response The response is an object containing: * `totalNetDeposit` (string) — Total net deposits in the perp DEX. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "perpDexStatus", "dex": "xyz"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The SDK has no dedicated perpDexStatus helper, so post the request directly. result = info.post("/info", {"type": "perpDexStatus", "dex": "xyz"}) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const result = await info.perpDexStatus({ dex: "xyz" }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "totalNetDeposit": "212751280.0536639988" } ``` ## Use case The `info` endpoint with `type: "perpDexStatus"` is useful for: * Monitoring the total value locked in a perp DEX * Building analytics dashboards that track DEX growth * Comparing deposit levels across different perp DEXes # perpDeployAuctionStatus | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-perpdeployauctionstatus openapi/hyperliquid_node_api/hypercore_info/info_perpdeployauctionstatus.json post /4f8d8f4040bdacd1577bff8058438274/info Reference docs for the perpDeployAuctionStatus JSON-RPC method on the Hyperliquid info blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > The `perpDeployAuctionStatus` call will return non-null when [HIP-3: Builder-Deployed Perpetuals](https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-3-builder-deployed-perpetuals) goes live on the mainnet. The `info` endpoint with `type: "perpDeployAuctionStatus"` retrieves the status information about perpetual contract deployment auctions on the Hyperliquid exchange. This endpoint provides timing information and gas-related data for the auction system. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"perpDeployAuctionStatus"` to retrieve perpetual deployment auction status. ## Response The response is an object containing auction timing and gas information: ### Timing information * `startTimeSeconds` (integer) — Auction start time in seconds since epoch * `durationSeconds` (integer) — Duration of the auction in seconds ### Gas information * `startGas` (string) — Starting gas amount for the auction * `currentGas` (string) — Current gas amount * `endGas` (string or null) — Ending gas amount (null if auction is ongoing) ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "perpDeployAuctionStatus"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) status = info.query_perp_deploy_auction_status() print(status) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const status = await info.perpDeployAuctionStatus(); console.log(status); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "startTimeSeconds": 1747656000, "durationSeconds": 111600, "startGas": "500.0", "currentGas": "500.0", "endGas": null } ``` ## Use case The `info` endpoint with `type: "perpDeployAuctionStatus"` is essential for applications that need to: * **Auction timing**: Monitor the timing and duration of perpetual deployment auctions * **Gas tracking**: Track gas amounts and changes during auction processes * **System monitoring**: Monitor the status of the auction system infrastructure * **Timeline calculation**: Calculate auction progress and remaining time * **Integration timing**: Plan system integrations around auction schedules * **Status reporting**: Report on auction system operational status * **Data analysis**: Analyze auction patterns and gas consumption trends * **Monitoring dashboards**: Display auction timing information in management interfaces This endpoint provides basic timing and gas information for the perpetual deployment auction system on Hyperliquid. # perpDexs | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-perpdexs openapi/hyperliquid_node_api/hypercore_info/info_perpdexs.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "perpDexs" retrieves information about all available perpetual DEXs (Decentralized Exchanges) on the Hyperliquid ecosystem. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. > [!NOTE] > > The `perpDexs` call will return non-null when [HIP-3: Builder-Deployed Perpetuals](https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-3-builder-deployed-perpetuals) goes live on the mainnet. The `info` endpoint with `type: "perpDexs"` retrieves information about all available perpetual DEXs (Decentralized Exchanges) on the Hyperliquid ecosystem. This endpoint provides basic identification and deployment information for each perpetual DEX instance. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"perpDexs"` to retrieve perpetual DEXs information. ## Response The response is an array of perpetual DEX information. Each element in the array can be: ### Default DEX (first element) * `null` — Represents the default/first perpetual DEX ### Additional DEXs For non-default DEXs, each object contains: * `name` (string) — Short name of the perpetual DEX * `full_name` (string) — Full descriptive name of the DEX * `deployer` (string) — Ethereum address of the DEX deployer in 42-character hexadecimal format * `oracle_updater` (string or null) — Address responsible for oracle updates, or null if not applicable ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "perpDexs"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) perp_dexs = info.perp_dexs() print(perp_dexs) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const perpDexs = await info.perpDexs(); console.log(perpDexs); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ null, { "name": "test", "full_name": "test dex", "deployer": "0x5e89b26d8d66da9888c835c9bfcc2aa51813e152", "oracle_updater": null } ] ``` ## Use case The `info` endpoint with `type: "perpDexs"` is essential for applications that need to: * **DEX enumeration**: List available perpetual DEX instances on Hyperliquid * **DEX identification**: Identify DEX names and deployment information * **Integration mapping**: Map DEX names to their deployer addresses for integration purposes * **System discovery**: Discover available trading venues within the Hyperliquid ecosystem * **Oracle tracking**: Identify which DEXs have oracle updaters and their addresses * **Deployment monitoring**: Track DEX deployments and their associated addresses * **Multi-DEX support**: Build applications that can work with multiple DEX instances * **Administration tools**: Create tools for managing and monitoring DEX instances This endpoint provides basic information about the perpetual DEX ecosystem on Hyperliquid, allowing applications to discover and identify available trading venues. # perpsAtOpenInterestCap | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-perps-at-open-interest-cap openapi/hyperliquid_node_api/hypercore_info/info_perps_at_open_interest_cap.json post /4f8d8f4040bdacd1577bff8058438274/info Reference docs for the perpsAtOpenInterestCap JSON-RPC method on the Hyperliquid info blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "perpsAtOpenInterestCap"` retrieves a list of perpetual contracts that have reached their open interest cap on the Hyperliquid network. This endpoint is crucial for understanding market capacity constraints and identifying contracts with maximum position exposure. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"perpsAtOpenInterestCap"` to retrieve perpetuals at open interest cap. ## Response The response is an array of perpetual contract symbols that have reached their open interest limit: * **Symbols array** (array of strings) — List of perpetual contract symbols at capacity * Each element is a string representing the contract symbol (e.g., "BADGER", "CANTO", "FTM") * Empty array indicates no contracts are currently at their open interest cap * Symbols represent the underlying asset of the perpetual contract ### Understanding open interest caps **Open interest limits:** * Maximum total position size allowed for a perpetual contract * Protects against excessive concentration risk and market manipulation * Dynamically adjusted based on market conditions and liquidity * Applied to the sum of all long and short positions **Market implications:** * Contracts at cap cannot accept new positions in the dominant direction * May still allow position reductions or opposite-direction trades * Indicates high market interest and potential volatility * Can affect price discovery and arbitrage opportunities **Risk management:** * Prevents excessive leverage concentration in single assets * Maintains market stability during high volatility periods * Protects the protocol from potential liquidation cascades * Ensures balanced exposure across different assets **Trading considerations:** * New positions may be rejected for capped contracts * Existing positions can still be closed or reduced * Alternative trading strategies may be required * Monitor for cap changes as market conditions evolve ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "perpsAtOpenInterestCap"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # The hyperliquid-python-sdk has no dedicated wrapper for this info type, # so post the request directly through the Info client. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) perps_at_cap = info.post("/info", {"type": "perpsAtOpenInterestCap"}) print(perps_at_cap) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const perpsAtCap = await info.perpsAtOpenInterestCap(); console.log(perpsAtCap); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} ["BADGER","CANTO","FTM","LOOM","PURR"] ``` ## Use case The `info` endpoint with `type: "perpsAtOpenInterestCap"` is essential for applications that need to: * **Trading platforms**: Display which contracts have position restrictions * **Risk management**: Monitor exposure limits across perpetual contracts * **Market analysis**: Identify high-demand contracts and market sentiment * **Order routing**: Avoid submitting orders for capacity-constrained contracts * **Portfolio management**: Adjust strategies based on available contract capacity * **Market making**: Understand liquidity constraints for automated strategies * **Compliance monitoring**: Track position limits for regulatory purposes * **User notifications**: Alert traders about contract availability changes * **Strategy optimization**: Adapt trading algorithms to capacity constraints * **Market research**: Analyze which assets are experiencing high demand * **Liquidity analysis**: Understand market depth and capacity utilization * **Position planning**: Plan entry and exit strategies around capacity limits * **Arbitrage detection**: Identify potential arbitrage opportunities from caps * **Risk assessment**: Evaluate concentration risk across available contracts This endpoint is particularly valuable for active traders managing large positions, algorithmic trading systems that need to avoid rejected orders, risk management systems monitoring exposure limits, and market analysis tools tracking demand patterns across perpetual contracts on the Hyperliquid network. # portfolio | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-portfolio openapi/hyperliquid_node_api/hypercore_info/info_portfolio.json post /info The info endpoint with type: "portfolio" retrieves comprehensive portfolio performance data for a specific user on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "portfolio"` retrieves comprehensive portfolio performance data for a specific user on the Hyperliquid exchange. This endpoint provides detailed historical performance metrics across multiple time periods, enabling thorough analysis of trading performance, account value progression, and volume activity. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"portfolio"` to retrieve portfolio data. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. ## Response The response is an array of portfolio performance data across different time periods. Each element is a tuple containing a time period identifier and the corresponding performance metrics. ### Response structure The response contains portfolio data for the following time periods: **Time period identifiers:** * `"day"` — Daily portfolio performance (last 24 hours) * `"week"` — Weekly portfolio performance (last 7 days) * `"month"` — Monthly portfolio performance (last 30 days) * `"allTime"` — All-time portfolio performance since account creation * `"perpDay"` — Daily perpetual trading performance * `"perpWeek"` — Weekly perpetual trading performance * `"perpMonth"` — Monthly perpetual trading performance * `"perpAllTime"` — All-time perpetual trading performance ### Performance metrics structure Each time period contains the following performance data: **Account value tracking:** * `accountValueHistory` — Array of \[timestamp, value] pairs showing account value progression over time * Timestamps are in milliseconds since Unix epoch * Values are represented as strings for precision **Profit and loss tracking:** * `pnlHistory` — Array of \[timestamp, pnl] pairs showing profit/loss progression over time * Timestamps are in milliseconds since Unix epoch * PnL values are represented as strings for precision * Shows cumulative realized and unrealized PnL **Volume metrics:** * `vlm` — Volume metric for the time period as a string * Represents total trading volume during the period ### Data interpretation **Account value history:** * Shows the total account value at specific timestamps * Includes both cash and position values * Useful for tracking overall portfolio growth * Data points are sampled at regular intervals **PnL history:** * Shows cumulative profit and loss at specific timestamps * Includes both realized and unrealized PnL * Positive values indicate profits, negative values indicate losses * Tracks performance relative to initial account value **Volume analysis:** * Total trading volume for the specified period * Helps assess trading activity levels * Can be used to calculate volume-based metrics ## Portfolio analysis insights ### Performance evaluation **Return analysis:** * Compare account value at different time points to calculate returns * Analyze PnL progression to understand profit/loss patterns * Use different time periods to assess short-term vs long-term performance **Risk assessment:** * Examine account value volatility through historical data * Analyze drawdown periods from PnL history * Compare performance across different time horizons **Activity analysis:** * Volume metrics indicate trading frequency and size * Compare volume across different periods to identify activity patterns * Assess relationship between volume and performance ### Time period comparison **Short-term performance:** * Daily and weekly data for recent performance analysis * Useful for tactical trading decisions * Shows immediate impact of recent trades **Long-term trends:** * Monthly and all-time data for strategic analysis * Helps identify long-term performance trends * Useful for portfolio allocation decisions **Perpetual vs spot trading:** * Separate metrics for perpetual contract performance * Enables analysis of different trading strategies * Helps optimize trading approach by instrument type ### Historical data usage **Trend identification:** * Use historical data points to identify performance trends * Analyze correlation between account value and market conditions * Identify periods of outperformance or underperformance **Performance attribution:** * Compare PnL across different time periods * Identify which periods contributed most to overall performance * Analyze consistency of returns over time ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "portfolio", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants # This method is public-only, so it uses the default Hyperliquid mainnet API. info = Info(constants.MAINNET_API_URL, skip_ws=True) portfolio = info.portfolio("0x31ca8395cf837de08b24da3f660e77761dfb974b") print(portfolio) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // This method is public-only, so the default transport targets the public Hyperliquid API. const transport = new HttpTransport(); const info = new InfoClient({ transport }); const portfolio = await info.portfolio({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(portfolio); ``` ## Example response ```json theme={"system"} [ [ "day", { "accountValueHistory": [ [ 1741886630493, "0.0" ], [ 1741895270493, "0.0" ] ], "pnlHistory": [ [ 1741886630493, "0.0" ], [ 1741895270493, "0.0" ] ], "vlm": "0.0" } ], ["week", { "accountValueHistory": [...], "pnlHistory": [...], "vlm": "..." }], ["month", { "accountValueHistory": [...], "pnlHistory": [...], "vlm": "..." }], ["allTime", { "accountValueHistory": [...], "pnlHistory": [...], "vlm": "..." }], ["perpDay", { "accountValueHistory": [...], "pnlHistory": [...], "vlm": "..." }], ["perpWeek", { "accountValueHistory": [...], "pnlHistory": [...], "vlm": "..." }], ["perpMonth", { "accountValueHistory": [...], "pnlHistory": [...], "vlm": "..." }], ["perpAllTime", { "accountValueHistory": [...], "pnlHistory": [...], "vlm": "..." }] ] ``` ## Use cases The `info` endpoint with `type: "portfolio"` is essential for applications that need to: * **Performance tracking**: Monitor portfolio performance across multiple time horizons * **Risk management**: Assess portfolio risk through historical volatility and drawdown analysis * **Portfolio analytics**: Generate comprehensive performance reports and analytics * **Investment research**: Analyze trading performance to inform investment decisions * **Backtesting validation**: Compare live performance against backtested strategies * **Client reporting**: Provide detailed performance reports to clients and stakeholders * **Performance attribution**: Attribute returns to specific time periods and trading strategies * **Benchmark comparison**: Compare portfolio performance against market benchmarks * **Trading strategy evaluation**: Assess the effectiveness of different trading approaches * **Regulatory compliance**: Maintain detailed performance records for regulatory requirements * **Academic research**: Study trading behavior and performance patterns * **Portfolio optimization**: Optimize portfolio allocation based on historical performance * **Risk-adjusted returns**: Calculate risk-adjusted performance metrics like Sharpe ratio * **Drawdown analysis**: Identify and analyze maximum drawdown periods * **Volume analysis**: Analyze trading volume patterns and their impact on performance * **Market timing analysis**: Evaluate market timing decisions through historical data * **Performance visualization**: Create charts and graphs showing portfolio progression * **Alert systems**: Set up alerts based on performance thresholds or trends * **Tax reporting**: Generate data for tax reporting and capital gains calculations * **Due diligence**: Conduct thorough analysis of trading track records * **Strategy development**: Develop new trading strategies based on historical performance data This endpoint provides comprehensive portfolio intelligence, enabling sophisticated analysis of trading performance and portfolio management on the Hyperliquid platform. # preTransferCheck | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-pre-transfer-check openapi/hyperliquid_node_api/hypercore_info/info_pre_transfer_check.json post /info The info endpoint with type: "preTransferCheck" pre-flights a transfer to a destination — sanction status, whether the destination exists, the fee, and transaction history. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "preTransferCheck"` pre-flights a transfer to a destination address. It returns whether the destination is sanctioned, whether it already exists on Hyperliquid, the fee that would apply, and whether the user has previously transacted. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"preTransferCheck"`. * `user` (string, required) — Destination address in 42-character hexadecimal format. * `source` (string, required) — Source address in 42-character hexadecimal format. ## Response Returns a pre-transfer check object: * `isSanctioned` (boolean) — Whether the destination address is sanctioned. * `userExists` (boolean) — Whether the destination already exists on Hyperliquid. * `fee` (string) — The transfer fee that would apply. * `userHasSentTx` (boolean) — Whether the user has previously sent a transaction. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "preTransferCheck", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", "source": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no dedicated preTransferCheck helper, so post the request directly. check = info.post("/info", { "type": "preTransferCheck", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", "source": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }) print(check) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const check = await info.preTransferCheck({ user: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", source: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }); console.log(check); ``` ## Example response ```json theme={"system"} { "isSanctioned": false, "userExists": true, "fee": "0.0", "userHasSentTx": true } ``` ## Use cases The `info` endpoint with `type: "preTransferCheck"` is useful for: * Pre-flighting a transfer before submitting it * Surfacing the applicable fee to the user before sending * Sanctions screening of a destination address # predictedFundings | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-predicted-fundings openapi/hyperliquid_node_api/hypercore_info/info_predicted_fundings.json post /info Reference docs for the predictedFundings JSON-RPC method on the Hyperliquid info blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "predictedFundings"` retrieves predicted funding rates for all perpetual contracts across different exchanges including Binance Perp, Hyperliquid Perp, and Bybit Perp. This endpoint provides comprehensive market data for cross-exchange arbitrage strategies, funding cost analysis, and market sentiment evaluation. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"predictedFundings"` to retrieve predicted funding rates across exchanges. ## Response The response returns an array where each element represents a trading asset and its funding predictions across different perpetual exchanges. ### Response structure Each asset entry contains: * **Asset symbol** (string) — The trading pair symbol (e.g., "BTC", "ETH", "SOL") * **Exchange data array** — Contains funding information for each available exchange: * **Exchange name** — Platform identifier ("BinPerp", "HlPerp", "BybitPerp") * **Funding details object**: * `fundingRate` (string) — The predicted funding rate as a decimal string * `nextFundingTime` (number) — Unix timestamp in milliseconds for the next funding payment * `null` — Indicates the asset is not available on that exchange ### Exchange types * **BinPerp** — Binance Perpetual futures * **HlPerp** — Hyperliquid Perpetual futures * **BybitPerp** — Bybit Perpetual futures ### Funding rate interpretation * **Positive rates** — Long positions pay short positions (bullish market sentiment) * **Negative rates** — Short positions pay long positions (bearish market sentiment) * **Rate magnitude** — Higher absolute values indicate stronger directional bias ### Use cases **Cross-exchange arbitrage** * Compare funding rates across exchanges for the same asset * Identify opportunities where funding rate differentials exceed trading costs **Market sentiment analysis** * Analyze funding rate trends to gauge market sentiment * Monitor extreme funding rates as potential reversal signals **Cost optimization** * Choose exchanges with favorable funding rates for long-term positions * Time position entries around funding payments **Trading strategy development** * Develop funding-based trading strategies * Backtest strategies using historical funding patterns ### Key features * **Real-time data** — Current predicted funding rates across major exchanges * **Comprehensive coverage** — Includes all major cryptocurrency perpetual contracts * **Cross-exchange comparison** — Easy comparison of funding costs between platforms * **Timing information** — Precise funding payment schedules for each exchange * **Market insights** — Understand market sentiment through funding rate analysis ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "predictedFundings"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no dedicated predictedFundings helper, so post the request directly. predicted_fundings = info.post("/info", {"type": "predictedFundings"}) print(predicted_fundings) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const predictedFundings = await info.predictedFundings(); console.log(predictedFundings); ``` ## Example response ```json theme={"system"} [ [ "AVAX", [ [ "BinPerp", { "fundingRate": "0.0001", "nextFundingTime": 1733961600000 } ], [ "HlPerp", { "fundingRate": "0.0000125", "nextFundingTime": 1733958000000 } ], [ "BybitPerp", { "fundingRate": "0.0001", "nextFundingTime": 1733961600000 } ] ] ] ] ``` ### Notes * Funding rates are predictions and may change before the actual funding time * Not all assets are available on all exchanges (indicated by `null` values) * Funding intervals vary by exchange (typically 1, 2, 4, or 8 hours) * Rates are expressed as decimal percentages (e.g., "0.0001" = 0.01%) # recentTrades | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-recent-trades openapi/hyperliquid_node_api/hypercore_info/info_recent_trades.json post /info The info endpoint with type: "recentTrades" retrieves the most recent public trades for a specific asset on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "recentTrades"` retrieves the most recent public trades for a specific asset on the Hyperliquid exchange. This endpoint provides real-time trade data including execution prices, quantities, and timing information, making it essential for market analysis, price discovery, and trading decision-making. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"recentTrades"` to retrieve recent trades. * `coin` (string, required) — Asset identifier (simple names like "BTC", "ETH" for perpetuals; spot format like "@107" for spot trades). ## Response The response is an array of recent trade objects, each representing a completed trade execution: ### Trade data structure Each trade object contains the following fields: **Core trade data:** * `px` — Trade price as a string for precision * `sz` — Trade size (quantity traded) as a string * `side` — Trade side: "A" for Ask/Sell, "B" for Bid/Buy * `time` — Trade timestamp in milliseconds * `tid` — Unique trade ID ### Data characteristics **Trade ordering:** * Trades are returned in reverse chronological order (most recent first) * Each trade represents a completed market transaction * Trade IDs are unique and sequential **Price and size precision:** * All prices and sizes are returned as strings to maintain precision * Precision depends on the asset's tick size and lot size requirements * No rounding or truncation is applied to the original trade data ### Trade interpretation **Trade sides:** * `"B"` (Bid/Buy) — Trade executed by a buyer (market buy or aggressive buy) * `"A"` (Ask/Sell) — Trade executed by a seller (market sell or aggressive sell) * Side indicates the direction of the aggressive order that initiated the trade **Market activity indicators:** * High frequency of trades indicates active market conditions * Large trade sizes may indicate institutional activity * Price progression shows market direction and momentum ### Asset identification **Perpetual contracts:** * Use simple asset names: "BTC", "ETH", "AVAX", "SOL" * Represent standard perpetual futures contracts **Spot markets:** * Use indexed format: "@107", "@1", etc. * Index corresponds to the spot pair position in the universe * Some assets may have remapped names in user interfaces ## Market data analysis ### Price discovery **Recent price action:** * Latest trades show current market pricing * Trade sequence reveals price movement direction * Price clustering indicates support/resistance levels **Volume analysis:** * Trade sizes indicate market participation levels * Consistent large trades may indicate institutional activity * Volume distribution shows market depth and liquidity ### Market sentiment **Buy/sell pressure:** * Predominance of "B" trades indicates buying pressure * Predominance of "A" trades indicates selling pressure * Balance of sides shows market equilibrium **Trade frequency:** * High trade frequency indicates active market conditions * Low frequency may indicate consolidation or low liquidity * Irregular patterns may indicate news or event-driven activity ## Real-time applications ### Trading signals **Momentum indicators:** * Consecutive trades in one direction indicate momentum * Price acceleration shows strengthening trends * Volume spikes may precede significant moves **Liquidity assessment:** * Trade frequency indicates market liquidity * Size distribution shows depth of market participation * Consistent execution suggests stable liquidity conditions ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "recentTrades", "coin": "BTC"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no dedicated recentTrades wrapper, so post the request directly. trades = info.post("/info", {"type": "recentTrades", "coin": "BTC"}) print(trades) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const trades = await info.recentTrades({ coin: "BTC" }); console.log(trades); ``` ## Use cases The `info` endpoint with `type: "recentTrades"` is essential for applications that need to: * **Real-time price feeds**: Display current market prices and recent trading activity * **Market analysis**: Analyze recent trading patterns, volume, and price movements * **Trading algorithms**: Make trading decisions based on recent market activity and momentum * **Price discovery**: Understand current market pricing and recent execution levels * **Liquidity assessment**: Evaluate market liquidity and trading activity levels * **Market surveillance**: Monitor trading activity for unusual patterns or market manipulation * **Trading interfaces**: Display recent trades in trading applications and market data feeds * **Technical analysis**: Analyze short-term price action and trading volume patterns * **Market making**: Assess recent trading activity to optimize bid-ask spreads and positioning * **Risk management**: Monitor recent market activity for risk assessment and position management * **Arbitrage detection**: Identify price discrepancies and trading opportunities across markets * **Market research**: Study trading behavior, market microstructure, and execution patterns * **Performance benchmarking**: Compare execution prices against recent market trades * **Compliance monitoring**: Track trading activity for regulatory compliance and reporting * **News impact analysis**: Assess market reaction to news events through trading activity * **Volatility analysis**: Study price volatility and trading intensity patterns * **Market timing**: Identify optimal entry and exit points based on recent trading activity * **Quantitative analysis**: Perform statistical analysis on recent trading data * **Academic research**: Study market dynamics, price formation, and trading behavior * **Customer analytics**: Analyze customer trading patterns relative to market activity This endpoint provides essential real-time market data, enabling comprehensive market analysis and trading decision-making on the Hyperliquid platform. # referral | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-referral openapi/hyperliquid_node_api/hypercore_info/info_referral.json post /info The info endpoint with type: "referral" retrieves comprehensive referral information for a specific user on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "referral"` retrieves comprehensive referral information for a specific user on the Hyperliquid exchange. This endpoint provides detailed data about referral relationships, rewards, volume metrics, and referrer status, enabling thorough analysis of referral program participation and performance. The example curl call is for the user `0x1442ad477ded1b0028b57621aa7b6f7eadb8f568` that used the AXIOM referral code `0x1cc34f6af34653c515b47a83e1de70ba9b0cda1f`. Here's an example `setReferrer` transaction [0xaa79d3a0073a73925828041e25e24a012300149a6f622a0727287dafe158fff5](https://hypurrscan.io/tx/0xaa79d3a0073a73925828041e25e24a012300149a6f622a0727287dafe158fff5). ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"referral"` to retrieve referral information. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. ## Response The response is a comprehensive referral information object containing details about the user's referral relationships, rewards, and referrer status. ### Response structure **Referral relationship:** * `referredBy` — Information about who referred this user (if applicable) * Contains referrer address and referral code used **User metrics:** * `cumVlm` — Cumulative volume traded by this user as a string * `unclaimedRewards` — Amount of unclaimed referral rewards as a string * `claimedRewards` — Amount of claimed referral rewards as a string * `builderRewards` — Builder rewards earned as a string **Referrer status:** * `referrerState` — State information if this user is a referrer * `rewardHistory` — Legacy reward history (now returned in nonFundingLedgerUpdate) ### Referral relationship structure The `referredBy` object contains: **Referrer information:** * `referrer` — Address of the user who made the referral * `code` — Referral code that was used for the referral This field is null if the user was not referred by anyone. ### Referrer state structure The `referrerState` object contains information if the user is acting as a referrer: **Referrer status:** * `stage` — Current stage of the referrer (e.g., "ready", "pending") * `data` — Detailed referrer information **Referrer data:** * `code` — This user's own referral code * `referralStates` — Array of users referred by this user ### Referred users structure Each entry in `referralStates` contains: **User identification:** * `user` — Address of the referred user * `timeJoined` — Timestamp when the user joined via referral (milliseconds) **Volume and activity:** * `cumVlm` — Cumulative volume traded by the referred user * `cumRewardedFeesSinceReferred` — Total fees that generated rewards since referral * `cumFeesRewardedToReferrer` — Total fees rewarded to the referrer from this user ### Reward system insights **Reward types:** * Unclaimed rewards represent pending rewards that can be claimed * Claimed rewards show historical rewards already received * Builder rewards are special rewards for ecosystem participation **Volume tracking:** * Cumulative volume shows total trading activity * Volume directly impacts reward calculations * Higher volume typically generates more rewards **Fee-based rewards:** * Rewards are generated from trading fees of referred users * Referrers receive a percentage of fees generated by their referrals * Fee rewards accumulate over time based on referral activity ## Referral program analysis ### Performance metrics **Referrer effectiveness:** * Number of successful referrals in `referralStates` array * Total volume generated by referred users * Total rewards earned from referral activity **User engagement:** * Individual referred user volume and activity * Time since referral to assess retention * Fee generation patterns over time **Reward optimization:** * Compare unclaimed vs claimed rewards * Analyze reward accumulation patterns * Track builder reward opportunities ### Network effects **Referral network growth:** * Track referral chain depth and breadth * Analyze referral code effectiveness * Monitor referral program adoption **Volume impact:** * Assess how referrals contribute to overall platform volume * Analyze volume patterns of referred vs non-referred users * Track volume growth over time since referral ### Reward distribution **Referrer rewards:** * Calculate total rewards earned as a referrer * Analyze reward per referral metrics * Track reward claim patterns **Referral rewards:** * Monitor rewards earned from being referred * Compare different referral sources * Analyze reward accumulation rates ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "referral", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # type: "referral" referral = info.query_referral_state("0x1442ad477ded1b0028b57621aa7b6f7eadb8f568") print(referral) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); // type: "referral" const referral = await info.referral({ user: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }); console.log(referral); ``` ## Example response ```json theme={"system"} { "referredBy": { "referrer": "0x5ac99df645f3414876c816caa18b2d234024b487", "code": "TESTNET" }, "cumVlm": "149428030.6628420055", "unclaimedRewards": "11.047361", "claimedRewards": "22.743781", "builderRewards": "0.027802", "referrerState": { "stage": "ready", "data": { "code": "TEST", "referralStates": [ { "cumVlm": "960652.017122", "cumRewardedFeesSinceReferred": "196.838825", "cumFeesRewardedToReferrer": "19.683748", "timeJoined": 1679425029416, "user": "0x11af2b93dcb3568b7bf2b6bd6182d260a9495728" }, { "cumVlm": "438278.672653", "cumRewardedFeesSinceReferred": "97.628107", "cumFeesRewardedToReferrer": "9.762562", "timeJoined": 1679423947882, "user": "0x3f69d170055913103a034a418953b8695e4e42fa" } ] } }, "rewardHistory": [] } ``` ## Use cases The `info` endpoint with `type: "referral"` is essential for applications that need to: * **Referral program management**: Track and manage referral program participation and performance * **Reward tracking**: Monitor referral rewards, claims, and accumulation patterns * **Network analysis**: Analyze referral networks and their growth patterns * **Performance analytics**: Generate reports on referral program effectiveness * **User engagement**: Track referred user activity and retention metrics * **Commission calculation**: Calculate referral commissions and fee sharing * **Compliance reporting**: Maintain records of referral relationships for regulatory purposes * **Marketing analytics**: Analyze the effectiveness of different referral codes and campaigns * **Volume attribution**: Attribute trading volume to specific referral sources * **Reward optimization**: Optimize reward structures based on referral performance data * **User onboarding**: Track user acquisition through referral programs * **Retention analysis**: Analyze retention rates of referred vs organic users * **Revenue sharing**: Implement revenue sharing programs based on referral data * **Gamification**: Create gamified referral experiences with leaderboards and achievements * **Fraud detection**: Detect potential referral fraud or abuse patterns * **Partner management**: Manage partnerships and affiliate relationships * **Customer lifetime value**: Calculate CLV impact of referral programs * **A/B testing**: Test different referral program structures and incentives * **Social trading**: Build social trading features based on referral relationships * **Community building**: Foster community growth through referral incentives * **Cross-platform integration**: Integrate referral data with external marketing tools * **Automated rewards**: Implement automated reward distribution systems * **Referral code management**: Manage and track custom referral codes * **Performance benchmarking**: Benchmark referral program performance against industry standards * **Tax reporting**: Generate tax-related documentation for referral rewards This endpoint provides comprehensive referral intelligence, enabling sophisticated analysis and management of referral programs and relationships on the Hyperliquid platform. # settledOutcome | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-settled-outcome openapi/hyperliquid_node_api/hypercore_info/info_settled_outcome.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "settledOutcome" retrieves the specification and settlement details for an outcome market by its outcome identifier. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "settledOutcome"` retrieves the specification and settlement details for an outcome market, identified by its `outcome` index. Outcome markets are binary (Yes/No) prediction markets on Hyperliquid. Use this method to read an outcome's definition and how it settled. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"settledOutcome"`. * `outcome` (integer, required) — The outcome identifier. ## Response Returns a settled outcome object, or `null` if the outcome is not settled. * `spec` (object) — The outcome specification: * `outcome` (integer) — The outcome identifier. * `name` (string) — The name of the outcome. * `description` (string) — The description of the outcome. * `sideSpecs` (array) — The side specifications, each with a `name` and an optional `token`. * `quoteToken` (string) — The quote token for this outcome. * `settleFraction` (string) — The settlement fraction. * `details` (string) — The settlement details. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "settledOutcome", "outcome": 0}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # skip_ws=True avoids opening a WebSocket connection for this REST call info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The SDK has no dedicated settledOutcome helper, so post the request directly. settled_outcome = info.post("/info", {"type": "settledOutcome", "outcome": 0}) print(settled_outcome) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // apiUrl points the transport at a custom server (your Chainstack endpoint) const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const settledOutcome = await info.settledOutcome({ outcome: 0 }); console.log(settledOutcome); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "spec": { "outcome": 0, "name": "Recurring", "description": "class:priceBinary|underlying:BTC|expiry:20260503-0600|targetPrice:78213|period:1d", "sideSpecs": [ { "name": "Yes" }, { "name": "No" } ], "quoteToken": "USDH" }, "settleFraction": "0.0", "details": "price:78212.4" } ``` ## Use cases The `info` endpoint with `type: "settledOutcome"` is useful for: * Reading the definition of an outcome market (underlying, target price, expiry) * Checking how an outcome settled and its settlement fraction * Building prediction-market dashboards and settlement reports # spotMetaAndAssetCtxs | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-spot-meta-and-asset-ctxs openapi/hyperliquid_node_api/hypercore_info/info_spot_meta_and_asset_ctxs.json post /info The info endpoint with type: "spotMetaAndAssetCtxs" retrieves comprehensive metadata and market contexts for all spot assets on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "spotMetaAndAssetCtxs"` retrieves comprehensive metadata and market contexts for all spot assets on the Hyperliquid exchange. This endpoint provides detailed information about token specifications, trading pairs, market prices, volumes, supply metrics, and EVM contract details for spot trading analysis and integration. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"spotMetaAndAssetCtxs"` to retrieve spot market metadata and asset contexts. ## Response The response returns an array with two elements: 1. **Token metadata array** — Detailed specifications for all spot tokens 2. **Market context array** — Real-time trading data and supply information ### Token Metadata Structure Each token metadata object contains: * `name` (string) — Token symbol/ticker (e.g., "PURR", "HYPE") * `szDecimals` (number) — Size decimals for trade sizing precision * `weiDecimals` (number) — Wei decimals for on-chain representation * `index` (number) — Unique index identifier for the token * `tokenId` (string) — Hexadecimal token identifier * `isCanonical` (boolean) — Whether this is the canonical version of the token * `evmContract` (object/null) — EVM contract details if bridged: * `address` (string) — Contract address on EVM chain * `evm_extra_wei_decimals` (number) — Additional decimal adjustment for EVM * `fullName` (string/null) — Full descriptive name of the token * `deployerTradingFeeShare` (string) — Fee share percentage for token deployer ### Market Context Structure Each market context object contains: * `prevDayPx` (string) — Previous day's closing price * `dayNtlVlm` (string) — Daily notional volume in USDC * `markPx` (string) — Current mark price * `midPx` (string) — Current mid price from order book * `circulatingSupply` (string) — Circulating token supply * `coin` (string) — Trading pair identifier (e.g., "PURR/USDC", "@150") * `totalSupply` (string) — Total token supply * `dayBaseVlm` (string) — Daily base asset volume ### Trading Pair Notation * **Named pairs** — Direct symbol notation (e.g., "PURR/USDC") * **Index pairs** — Reference by index (e.g., "@150" for token index 150) * **Base pair** — Most pairs trade against USDC as the quote currency ### Use Cases **Token Discovery and Analysis** * Browse all available spot tokens with complete metadata * Analyze token specifications and decimal configurations * Identify canonical vs non-canonical token versions **Market Data Analysis** * Monitor real-time prices and spreads across all spot pairs * Track daily trading volumes and price movements * Analyze supply metrics and market capitalization **Integration and Development** * Configure trading interfaces with proper decimal handling * Implement token bridges using EVM contract information * Build market scanners and analytics dashboards **Portfolio Management** * Track holdings across all spot assets * Calculate portfolio values using current mark prices * Monitor supply changes and dilution events ### Key Features * **Complete market coverage** — All spot assets and trading pairs * **Real-time data** — Current prices, volumes, and supply metrics * **Technical specifications** — Decimal precision and contract details * **Cross-chain support** — EVM contract information for bridged assets * **Fee transparency** — Deployer fee shares for each token ### Notes * Token indices provide a stable reference for programmatic access * Mark prices may differ from mid prices during volatile market conditions * Circulating supply may be less than total supply due to locked or vested tokens * EVM contract details enable cross-chain asset tracking and arbitrage * Daily volumes reset at UTC midnight * Some tokens may have null full names or EVM contracts if not applicable ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "spotMetaAndAssetCtxs"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants # spotMetaAndAssetCtxs is a public Hyperliquid method, so use the public mainnet API info = Info(constants.MAINNET_API_URL, skip_ws=True) # Posts {"type": "spotMetaAndAssetCtxs"} to /info meta, asset_ctxs = info.spot_meta_and_asset_ctxs() print(meta["tokens"][0]) print(asset_ctxs[0]) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // spotMetaAndAssetCtxs is a public Hyperliquid method, so use the default public transport const transport = new HttpTransport(); const info = new InfoClient({ transport }); // Posts { type: "spotMetaAndAssetCtxs" } to /info const [meta, assetCtxs] = await info.spotMetaAndAssetCtxs(); console.log(meta.tokens[0]); console.log(assetCtxs[0]); ``` # spotPairDeployAuctionStatus | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-spot-pair-deploy-auction-status openapi/hyperliquid_node_api/hypercore_info/info_spot_pair_deploy_auction_status.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "spotPairDeployAuctionStatus" retrieves the current status of the spot pair deployment auction, including timing, gas prices. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "spotPairDeployAuctionStatus"` retrieves the current status of the spot pair deployment auction, including timing, gas prices, and duration. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"spotPairDeployAuctionStatus"`. ## Response The response is an object containing: * `startTimeSeconds` (integer) — Auction start time as Unix timestamp in seconds. * `durationSeconds` (integer) — Total auction duration in seconds. * `startGas` (string) — Starting gas price for the auction. * `currentGas` (string) — Current gas price in the auction. * `endGas` (string or null) — Ending gas price, or `null` if the auction is still active. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "spotPairDeployAuctionStatus"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The Python SDK has no dedicated helper for this request type, # so post the request body directly through the inherited client. status = info.post("/info", {"type": "spotPairDeployAuctionStatus"}) print(status) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const status = await info.spotPairDeployAuctionStatus(); console.log(status); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "startTimeSeconds": 1771203600, "durationSeconds": 111600, "startGas": "500.0", "currentGas": "500.0", "endGas": null } ``` ## Use case The `info` endpoint with `type: "spotPairDeployAuctionStatus"` is useful for: * Monitoring the current gas auction for deploying spot pairs * Timing spot pair deployments for optimal gas prices * Building auction tracking interfaces for token deployers # spotClearinghouseState | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-spotclearinghousestate openapi/hyperliquid_node_api/hypercore_info/info_spotclearinghousestate.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "spotClearinghouseState" retrieves a user's token balances for spot trading on the Hyperliquid exchange. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "spotClearinghouseState"` retrieves a user's token balances for spot trading on the Hyperliquid exchange. This endpoint provides detailed information about available balances, held amounts in open orders, and entry notional values for all spot tokens in the user's account. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"spotClearinghouseState"` to retrieve user's spot token balances. * `user` (string, required) — Onchain address in 42-character hexadecimal format (e.g., `0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036`). ## Response The response contains detailed balance information: ### Balances An array of the user's spot token balances: * `coin` (string) — Token symbol (e.g., "USDC", "PURR"). * `token` (integer) — Token index identifier used internally by the exchange. * `hold` (string) — Amount currently on hold (locked in open orders or pending transactions). * `total` (string) — Total balance including both available and held amounts. * `entryNtl` (string) — Entry notional value, used for PnL calculations and tracking cost basis. ### Balance calculation The available balance for trading can be calculated as: ``` Available Balance = Total - Hold ``` For example, if `total` is "14.625485" and `hold` is "0.0", then the available balance is "14.625485". ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "spotClearinghouseState", "user": "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # skip_ws=True keeps this to a single info HTTP call info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # Posts {"type": "spotClearinghouseState", "user": address} spot_state = info.spot_user_state("0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036") print(spot_state) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // apiUrl points the transport at your custom server const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); // Posts {"type": "spotClearinghouseState", "user": "0x..."} const spotState = await info.spotClearinghouseState({ user: "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", }); console.log(spotState); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use case The `info` endpoint with `type: "spotClearinghouseState"` is essential for spot trading applications that need to: * Display user's current spot token balances * Calculate available balance for new orders (total - hold) * Show which tokens are locked in open orders * Track cost basis and entry values for portfolio management * Validate order placement against available balances * Build trading interfaces with real-time balance updates * Implement portfolio tracking and PnL calculations This endpoint is typically called frequently to keep trading interfaces updated with current balance information and ensure accurate order placement validation. # spotDeployState | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-spotdeploystate openapi/hyperliquid_node_api/hypercore_info/info_spotdeploystate.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "spotDeployState" returns the Spot Deploy Auction state. Available on Hyperliquid info via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "spotDeployState"` returns the Spot Deploy Auction state. It exposes per‑token genesis state and the current gas auction parameters used for spot token deployment. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"spotDeployState"` to retrieve spot deployment state information. * `user` (string, required) — Address in 42-character hexadecimal format. Can be zero address (`0x0000000000000000000000000000000000000000`) for global state information. ## Response The response is an object with two primary sections: ### States Array of token genesis entries. Each entry includes: * `token` (integer) — Token ID. * `spec` (object) — Token spec with: * `name` (string) * `szDecimals` (number) * `weiDecimals` (number) * `fullName` (string, optional) * `spots` (array of integers) — Spot pair indices associated with the token. * `maxSupply` (integer) * `hyperliquidityGenesisBalance` (string) * `totalGenesisBalanceWei` (string) * `userGenesisBalances` (array) — Tuples of `[address, balance]` in string form. * `existingTokenGenesisBalances` (array) — Tuples of `[tokenIndex, balance]` in string form. ### Gas auction Current auction parameters: * `startTimeSeconds` (integer) — Auction start time (seconds since epoch). * `durationSeconds` (integer) — Auction duration in seconds. * `startGas` (string) — Starting gas amount. * `currentGas` (string or null) — Current gas amount. * `endGas` (string, optional) — Ending gas amount when completed. ### Understanding spot token deployment **Deployment process:** * Users submit token deployment requests with specified parameters * Requests are queued and processed based on system capacity * Successful deployments create new tradeable spot tokens * Deployed tokens become available for spot trading **System parameters:** * **Deployment cost**: Fee required to deploy new tokens * **Supply limits**: Minimum and maximum token supply constraints * **Queue management**: Fair processing of deployment requests **Token lifecycle:** * **Request submission**: Users submit deployment parameters * **Queue processing**: Requests are processed in order * **Token creation**: Successful deployments create new tokens * **Trading availability**: Deployed tokens become tradeable ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "spotDeployState", "user": "0x0000000000000000000000000000000000000000"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # Posts {"type": "spotDeployState", "user": user} to /info state = info.query_spot_deploy_auction_status( "0x0000000000000000000000000000000000000000" ) print(state) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); // Posts {"type": "spotDeployState", "user": user} to the info endpoint const state = await info.spotDeployState({ user: "0x0000000000000000000000000000000000000000", }); console.log(state); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use case The `info` endpoint with `type: "spotDeployState"` is useful for: * **Auction monitoring**: Read current `gasAuction` timing and gas parameters. * **Genesis state inspection**: Review token `states` and genesis balances. * **Discovery**: Identify tokens and spot indices associated with upcoming or configured deployments. This endpoint is particularly valuable for token creators, market participants interested in new token launches, and platforms that need to monitor or facilitate spot token deployments on the Hyperliquid exchange. It provides essential visibility into the token deployment ecosystem and system operational status. # spotMeta | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-spotmeta openapi/hyperliquid_node_api/hypercore_info/info_spotmeta.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "spotMeta" retrieves spot metadata including tokens and universe for the Hyperliquid exchange. On Hyperliquid info. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "spotMeta"` retrieves spot metadata including tokens and universe for the Hyperliquid exchange. This endpoint provides essential information about available spot tokens, their specifications, and trading pairs. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"spotMeta"` to retrieve spot metadata. ## Response The response contains two main sections: ### Tokens An array of spot tokens with their metadata: * `name` (string) — The token name (e.g., "USDC", "PURR"). * `szDecimals` (integer) — Number of decimal places for size precision. * `weiDecimals` (integer) — Number of decimal places for wei representation. * `index` (integer) — Unique token index identifier. * `tokenId` (string) — Unique token identifier in hexadecimal format. * `isCanonical` (boolean) — Whether the token is canonical on the platform. * `evmContract` (object, nullable) — EVM contract data if applicable: `address` (string), `evm_extra_wei_decimals` (integer). * `fullName` (string, nullable) — Full descriptive name of the token. * `deployerTradingFeeShare` (string, optional) — Deployer trading fee share for this token. ### Universe An array of spot trading pairs: * `name` (string) — Trading pair name (e.g., "PURR/USDC", "@1"). * `tokens` (array) — Array of token indices that make up the trading pair. * `index` (integer) — Unique pair index identifier. * `isCanonical` (boolean) — Whether the trading pair is canonical. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "spotMeta"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) spot_meta = info.spot_meta() print(spot_meta) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const spotMeta = await info.spotMeta(); console.log(spotMeta); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use case The `info` endpoint with `type: "spotMeta"` is essential for spot trading applications that need to: * Display available spot tokens and their specifications * Validate trading pair configurations * Map token indices to human-readable names * Understand decimal precision for accurate calculations * Identify canonical vs non-canonical tokens and pairs * Build trading interfaces with proper token and pair data This metadata is typically cached and used throughout the application to ensure accurate token handling and trading pair validation for spot trading on Hyperliquid. # subAccounts2 | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-sub-accounts2 openapi/hyperliquid_node_api/hypercore_info/info_sub_accounts2.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "subAccounts2" returns a user's sub-accounts (V2), including each sub-account's per-DEX clearinghouse state and spot state. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "subAccounts2"` returns the sub-accounts owned by a user (the V2 response), including each sub-account's per-DEX clearinghouse state and spot state. Use it to enumerate and inspect the sub-accounts under a master account. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"subAccounts2"`. * `user` (string, required) — Master account address in 42-character hexadecimal format. ## Response Returns an array of the user's sub-accounts, or `null` if the user has none. Each sub-account contains: * `name` (string) — The sub-account name. * `subAccountUser` (string) — The sub-account address. * `master` (string) — The master account address. * `dexToClearinghouseState` (array) — `[dex, state]` tuples mapping each DEX (empty string for the main DEX) to its clearinghouse state. * `spotState` (object) — The sub-account's spot clearinghouse state. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "subAccounts2", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # skip_ws=True avoids opening a WebSocket connection for this REST call info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The SDK has no dedicated subAccounts2 helper, so post the request directly. sub_accounts = info.post("/info", { "type": "subAccounts2", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }) print(sub_accounts) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // apiUrl points the transport at a custom server (your Chainstack endpoint) const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const subAccounts = await info.subAccounts2({ user: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }); console.log(subAccounts); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} null ``` The example returns `null` because the queried address has no sub-accounts. An account with sub-accounts returns an array of sub-account objects as described above. ## Use cases The `info` endpoint with `type: "subAccounts2"` is useful for: * Enumerating the sub-accounts under a master account * Building dashboards that aggregate positions across sub-accounts * Portfolio tooling that needs each sub-account's clearinghouse and spot state # subAccounts | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-subaccounts openapi/hyperliquid_node_api/hypercore_info/info_subaccounts.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "subAccounts" retrieves information about all sub-accounts associated with a master account on the Hyperliquid exchange. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "subAccounts"` retrieves information about all sub-accounts associated with a master account on the Hyperliquid exchange. This endpoint provides comprehensive data about sub-account configurations, balances, positions, and trading activity for advanced account management and portfolio segregation. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"subAccounts"` to retrieve sub-accounts associated with a user. * `user` (string, required) — Address in 42-character hexadecimal format of the master account (e.g., `0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036`). ## Response The response is an array of sub-account objects with detailed information about each sub-account: ### Sub-account identification * `subAccountUser` (string) — Address of the sub-account in 42-character hexadecimal format * `name` (string) — Human-readable name assigned to the sub-account for identification * `master` (string) — Address of the master account (matches the requested user) ### Perpetuals account state * `clearinghouseState` (object) — Current perpetuals trading state of the sub-account: * `marginSummary` (object) — Overall margin information: * `accountValue` (string) — Total account value including unrealized profit and loss * `totalNtlPos` (string) — Total notional position value * `totalRawUsd` (string) — Total raw USD value in the account * `totalMarginUsed` (string) — Total margin currently allocated to open positions * `crossMarginSummary` (object) — Cross-margin specific information: * `accountValue` (string) — Account value for cross-margin calculations * `totalNtlPos` (string) — Total notional position value for cross-margin * `totalRawUsd` (string) — Total raw USD for cross-margin * `totalMarginUsed` (string) — Total margin used in cross-margin mode * `crossMaintenanceMarginUsed` (string) — Cross maintenance margin currently in use * `withdrawable` (string) — Amount available for withdrawal * `assetPositions` (array) — Array of current perpetuals positions (empty if no positions) * `time` (integer) — Timestamp of the state snapshot in milliseconds ### Spot account state * `spotState` (object) — Current spot trading state of the sub-account: * `balances` (array) — Array of spot token balances: * `coin` (string) — Token symbol (e.g., "USDC") * `token` (integer) — Token index identifier * `total` (string) — Total balance including held amounts * `hold` (string) — Amount currently on hold (locked in orders) * `entryNtl` (string) — Entry notional value for cost basis tracking ### Understanding sub-accounts **Sub-account benefits:** * **Portfolio segregation**: Separate trading strategies or asset classes * **Risk isolation**: Limit exposure by isolating positions across accounts * **Strategy organization**: Dedicated accounts for different trading approaches * **Performance tracking**: Monitor results of specific strategies independently **Account hierarchy:** * **Master account**: Primary account with overall control and access * **Sub-accounts**: Secondary accounts managed under the master account * **Unified management**: Master account can oversee all sub-account activities **Use cases:** * **Multi-strategy trading**: Different accounts for different trading strategies * **Risk management**: Isolate high-risk trades from conservative positions * **Team management**: Separate accounts for different team members or roles * **Asset segregation**: Dedicated accounts for specific asset classes ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "subAccounts", "user": "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) sub_accounts = info.query_sub_accounts("0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036") print(sub_accounts) ``` ```typescript TypeScript theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const subAccounts = await info.subAccounts({ user: "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", }); console.log(subAccounts); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ { "name": "Test", "subAccountUser": "0x035605fc2f24d65300227189025e90a0d947f16c", "master": "0x8c967e73e6b15087c42a10d344cff4c96d877f1d", "clearinghouseState": { "marginSummary": { "accountValue": "29.78001", "totalNtlPos": "0.0", "totalRawUsd": "29.78001", "totalMarginUsed": "0.0" }, "crossMarginSummary": { "accountValue": "29.78001", "totalNtlPos": "0.0", "totalRawUsd": "29.78001", "totalMarginUsed": "0.0" }, "crossMaintenanceMarginUsed": "0.0", "withdrawable": "29.78001", "assetPositions": [], "time": 1733968369395 }, "spotState": { "balances": [ { "coin": "USDC", "token": 0, "total": "0.22", "hold": "0.0", "entryNtl": "0.0" } ] } } ] ``` ## Use cases The `info` endpoint with `type: "subAccounts"` is essential for applications that need to: * **Portfolio management**: Monitor and manage multiple trading strategies across sub-accounts * **Risk oversight**: Track exposure and performance across segregated accounts * **Account administration**: Manage complex account structures for institutional users * **Consolidated reporting**: Generate comprehensive reports across all sub-accounts * **Strategy monitoring**: Track the performance of isolated trading strategies * **Team coordination**: Manage trading activities across different team members * **Asset allocation**: Understand capital distribution across sub-accounts * **Account consolidation**: View aggregated positions and balances across accounts * **Spot balance tracking**: Monitor spot token balances across all sub-accounts * **Perpetuals monitoring**: Track perpetuals positions and margin usage across accounts * **Withdrawal planning**: Check withdrawable amounts across all sub-accounts * **Multi-account analytics**: Perform analysis across segregated trading environments This endpoint provides comprehensive visibility into both perpetuals and spot trading states across all sub-accounts, making it essential for sophisticated account management and multi-strategy trading operations. # tokenDetails | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-token-details openapi/hyperliquid_node_api/hypercore_info/info_token_details.json post /info The info endpoint with type: "tokenDetails" retrieves comprehensive information about a specific token on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "tokenDetails"` retrieves comprehensive information about a specific token on the Hyperliquid exchange. This endpoint provides detailed supply metrics, pricing data, deployment information, and circulation details for in-depth token analysis and portfolio management. ## Example request ```shell cURL theme={"system"} curl -X POST https://api.hyperliquid.xyz/info \ -H "Content-Type: application/json" \ -d '{ "type": "tokenDetails", "tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054" }' ``` ```python hyperliquid-python-sdk theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants # tokenDetails has no dedicated method in hyperliquid-python-sdk, # so post the request body directly through the underlying client. info = Info(constants.MAINNET_API_URL, skip_ws=True) token_details = info.post( "/info", { "type": "tokenDetails", "tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054", }, ) print(token_details) ``` ```typescript @nktkas/hyperliquid theme={"system"} import * as hl from "@nktkas/hyperliquid"; const transport = new hl.HttpTransport(); const client = new hl.InfoClient({ transport }); const tokenDetails = await client.tokenDetails({ tokenId: "0x6d1e7cde53ba9467b783cb7c530ce054", }); console.log(tokenDetails); ``` ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"tokenDetails"` to retrieve token details. * `tokenId` (string, required) — The hexadecimal token identifier to query. This can be obtained from the `spotMetaAndAssetCtxs` endpoint. ## Response The response returns a detailed object with comprehensive token information: ### Token Identity * `name` (string) — Token symbol/ticker (e.g., "USDC", "PURR", "HYPE") ### Supply Metrics * `maxSupply` (string) — Maximum possible token supply (may be unlimited) * `totalSupply` (string) — Current total token supply in circulation * `circulatingSupply` (string) — Current circulating supply available for trading * `futureEmissions` (string) — Planned future token emissions ### Technical Specifications * `szDecimals` (number) — Size decimals for trade sizing precision * `weiDecimals` (number) — Wei decimals for on-chain representation ### Pricing Information * `midPx` (string) — Current mid price from order book * `markPx` (string) — Current mark price used for valuation * `prevDayPx` (string) — Previous day's closing price ### Deployment Information * `genesis` (object/null) — Genesis block information if applicable * `deployer` (string/null) — Address of the token deployer * `deployGas` (string/null) — Gas used for token deployment * `deployTime` (string/null) — ISO 8601 timestamp of deployment (e.g., "2024-06-05T10:50:59.434") ### Liquidity and Distribution * `seededUsdc` (string) — USDC seeded for initial liquidity * `nonCirculatingUserBalances` (array) — Non-circulating balances held by users ### Use Cases **Token Analysis** * Analyze token supply distribution and circulation metrics * Monitor price performance and historical data * Evaluate tokenomics and emission schedules **Portfolio Management** * Track detailed holdings and valuations * Calculate market capitalizations using accurate supply data * Monitor supply changes and dilution events **Trading and Investment** * Assess token liquidity through seeded USDC amounts * Analyze price stability and volatility patterns * Make informed trading decisions based on supply metrics **Development and Integration** * Configure applications with proper decimal handling * Implement token-specific logic based on deployment details * Build analytics dashboards with comprehensive token data ### Key Features * **Complete supply breakdown** — Max, total, and circulating supply with future emissions * **Real-time pricing** — Current mid and mark prices with historical reference * **Deployment transparency** — Creator information and deployment metadata * **Circulation analysis** — Detailed breakdown of non-circulating balances * **Technical precision** — Exact decimal specifications for accurate calculations ### Notes * Token IDs are unique hexadecimal identifiers obtained from market metadata * Circulating supply may differ from total supply due to locked or vested tokens * Mark prices are used for official valuations and may differ from mid prices * Deployment information may be null for canonical or pre-existing tokens * Future emissions indicate planned token releases that will increase total supply * Non-circulating balances represent tokens held but not available for trading ### Finding Token IDs To obtain token IDs for use with this endpoint: 1. Use the `spotMetaAndAssetCtxs` endpoint to get all token metadata 2. Extract the `tokenId` field from the desired token's metadata 3. Use that `tokenId` in this endpoint for detailed information # twapHistory | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-twap-history openapi/hyperliquid_node_api/hypercore_info/info_twap_history.json post /info The info endpoint with type: "twapHistory" returns a user's TWAP order history on Hyperliquid. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "twapHistory"` returns a user's TWAP (time-weighted average price) order history — each record's creation time, state, and current status. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"twapHistory"`. * `user` (string, required) — Address in 42-character hexadecimal format. ## Response Returns an array of TWAP history records. Each record contains: * `time` (integer) — Creation time of the record (seconds since epoch). * `state` (object) — The state of the TWAP order (coin, size, executed amounts, side, and related fields). * `status` (object) — The current status, for example `{"status": "finished"}`, `{"status": "activated"}`, `{"status": "terminated"}`, or `{"status": "error", "description": "..."}`. * `twapId` (integer) — The ID of the TWAP order. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "twapHistory", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no dedicated twapHistory helper, so post the request directly. twap_history = info.post("/info", { "type": "twapHistory", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }) print(twap_history) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const twapHistory = await info.twapHistory({ user: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }); console.log(twapHistory); ``` ## Example response ```json theme={"system"} [] ``` The example returns an empty array because the queried address has no TWAP history. An account with TWAP orders returns an array of history records as described above. ## Use cases The `info` endpoint with `type: "twapHistory"` is useful for: * Auditing a user's past TWAP orders * Trading dashboards that show TWAP execution history * Analyzing TWAP order outcomes over time # userAbstraction | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-abstraction openapi/hyperliquid_node_api/hypercore_info/info_user_abstraction.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "userAbstraction" retrieves a user's account abstraction configuration. Hyperliquid info via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userAbstraction"` retrieves a user's account abstraction configuration. Returns whether the user has the default or a custom abstraction setting. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userAbstraction"`. * `user` (string, required) — Onchain address in 42-character hexadecimal format. ## Response The response is a string representing the user's abstraction configuration (e.g., `"default"`). ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userAbstraction", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) abstraction = info.query_user_abstraction_state("0x31ca8395cf837de08b24da3f660e77761dfb974b") print(abstraction) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const abstraction = await info.userAbstraction({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(abstraction); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} "default" ``` ## Use case The `info` endpoint with `type: "userAbstraction"` is useful for: * Checking a user's account abstraction status before executing trades * Displaying the current abstraction configuration in user interfaces * Determining whether unified account features are enabled for a user # userBorrowLendInterest | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-borrow-lend-interest openapi/hyperliquid_node_api/hypercore_info/info_user_borrow_lend_interest.json post /info The info endpoint with type: "userBorrowLendInterest" returns a user's borrow/lend interest accrual records per token on Hyperliquid. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userBorrowLendInterest"` returns a user's borrow and lend interest accrual records, per token. Use it to track interest earned on supplied balances and paid on borrowed balances. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userBorrowLendInterest"`. * `user` (string, required) — Address in 42-character hexadecimal format. ## Response Returns an array of interest records. Each record contains: * `time` (integer) — Timestamp of the update (milliseconds since epoch). * `token` (string) — Token symbol (for example, `USDC`). * `borrow` (string) — Borrow interest amount. * `supply` (string) — Supply interest amount. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userBorrowLendInterest", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no dedicated userBorrowLendInterest helper, so post the request directly. interest = info.post("/info", { "type": "userBorrowLendInterest", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }) print(interest) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const interest = await info.userBorrowLendInterest({ user: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", }); console.log(interest); ``` ## Example response ```json theme={"system"} [ { "time": 1710000000000, "token": "USDC", "borrow": "0.0", "supply": "1.234567" } ] ``` ## Use cases The `info` endpoint with `type: "userBorrowLendInterest"` is useful for: * Tracking interest earned and paid over time * Accounting and tax reporting for borrow/lend activity * Analyzing the cost and yield of positions # userDexAbstraction | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-dex-abstraction openapi/hyperliquid_node_api/hypercore_info/info_user_dex_abstraction.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "userDexAbstraction" retrieves a user's DEX abstraction configuration. Hyperliquid info via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userDexAbstraction"` retrieves a user's DEX abstraction configuration. Returns the user's current abstraction setting for perp DEX interaction, or null if not set. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userDexAbstraction"`. * `user` (string, required) — Onchain address in 42-character hexadecimal format. ## Response The response is a string representing the user's DEX abstraction configuration, or `null` if no abstraction is set. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userDexAbstraction", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) result = info.query_user_dex_abstraction_state("0x31ca8395cf837de08b24da3f660e77761dfb974b") print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const result = await info.userDexAbstraction({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} null ``` ## Use case The `info` endpoint with `type: "userDexAbstraction"` is useful for: * Checking whether a user has DEX abstraction enabled * Determining which perp DEX a user's orders are routed to * Building interfaces that display the user's current DEX configuration # userFills | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-fills openapi/hyperliquid_node_api/hypercore_info/info_user_fills.json post /info The info endpoint with type: "userFills" retrieves a user's trading fills (executed orders) on the Hyperliquid exchange. On Hyperliquid info. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userFills"` retrieves a user's trading fills (executed orders) on the Hyperliquid exchange. This endpoint returns detailed information about completed trades, including prices, sizes, fees, and profit/loss data for both perpetual and spot markets. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userFills"` to retrieve user fills. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. * `aggregateByTime` (boolean, optional) — When true, partial fills are combined when a crossing order gets filled by multiple different resting orders. Resting orders filled by multiple crossing orders will not be aggregated. ## Response The response is an array of fill objects, with each fill containing detailed trade information: ### Fill data structure Each fill object contains the following fields: **Core trade data:** * `coin` — Asset identifier (simple names like "BTC", "ETH" for perpetuals; spot format like "@107" for spot trades) * `px` — Fill price as a string for precision * `sz` — Fill size (quantity traded) * `side` — Trade side: "A" for Ask/Sell, "B" for Bid/Buy * `time` — Fill timestamp in milliseconds * `oid` — Order ID that generated this fill * `tid` — Unique trade ID **Position and P\&L data:** * `startPosition` — Position size before this fill * `dir` — Trade direction (e.g., "Open Long", "Close Short", "Buy", "Sell") * `closedPnl` — Realized profit/loss from this fill * `crossed` — Boolean indicating if this was a crossing order **Fee and transaction data:** * `fee` — Trading fee paid for this fill * `feeToken` — Token used to pay the fee (typically "USDC") * `builderFee` — Builder fee (optional, only present if non-zero) * `hash` — Transaction hash for blockchain verification ### Asset identification **Perpetual contracts:** * Use simple asset names: "BTC", "ETH", "AVAX", "SOL" * Represent standard perpetual futures contracts **Spot markets:** * Use indexed format: "@107", "@1", etc. * Index corresponds to the spot pair position in the universe * Some assets may have remapped names in user interfaces ### Data limitations **Response limits:** * Maximum 2000 fills per response * Returns most recent fills first * For larger datasets, use pagination with `userFillsByTime` **Historical availability:** * Only the 10,000 most recent fills are available * Older fills are not accessible through this endpoint ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userFills", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) fills = info.user_fills("0x31ca8395cf837de08b24da3f660e77761dfb974b") print(fills) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const fills = await info.userFills({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(fills); ``` ## Example response ```json theme={"system"} [ { "closedPnl": "0.0", "coin": "AVAX", "crossed": false, "dir": "Open Long", "hash": "0xa166e3fa63c25663024b03f2e0da011a00307e4017465df020210d3d432e7cb8", "oid": 90542681, "px": "18.435", "side": "B", "startPosition": "26.86", "sz": "93.53", "time": 1681222254710, "fee": "0.01", "feeToken": "USDC", "builderFee": "0.01", "tid": 118906512037719 }, { "coin": "@107", "px": "18.62041381", "sz": "43.84", "side": "A", "time": 1735969713869, "startPosition": "10659.65434798", "dir": "Sell", "closedPnl": "8722.988077", "hash": "0x2222138cc516e3fe746c0411dd733f02e60086f43205af2ae37c93f6a792430b", "oid": 59071663721, "crossed": true, "fee": "0.304521", "tid": 907359904431134, "feeToken": "USDC" } ] ``` ## Use cases The `info` endpoint with `type: "userFills"` is essential for applications that need to: * **Trade history tracking**: Display complete trading history with detailed fill information * **Portfolio analysis**: Calculate realized profits/losses and trading performance metrics * **Fee analysis**: Track trading costs and fee optimization strategies * **Position reconciliation**: Verify position changes against executed fills * **Tax reporting**: Generate detailed trade records for tax compliance * **Risk management**: Monitor trading patterns and position sizing * **Performance attribution**: Analyze trading performance by asset, time period, or strategy * **Audit trails**: Maintain comprehensive records of all trading activity * **Trading analytics**: Build dashboards showing fill patterns and execution quality * **Compliance reporting**: Generate reports for regulatory requirements * **Account reconciliation**: Match fills against expected order executions * **Strategy backtesting**: Use historical fill data to validate trading strategies * **Fee optimization**: Analyze fee structures and optimize trading costs * **Position tracking**: Monitor how positions change over time through fills * **Market impact analysis**: Study the effect of order execution on prices This endpoint provides comprehensive trade execution data that forms the foundation for most trading-related analytics, reporting, and portfolio management functions in trading applications built on the Hyperliquid platform. # userFillsByTime | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-fills-by-time openapi/hyperliquid_node_api/hypercore_info/info_user_fills_by_time.json post /info The info endpoint with type: "userFillsByTime" retrieves a user's trading fills (executed orders) within a specific time range on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userFillsByTime"` retrieves a user's trading fills (executed orders) within a specific time range on the Hyperliquid exchange. This endpoint provides time-filtered access to detailed trade execution data, making it ideal for historical analysis and reporting. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userFillsByTime"` to retrieve user fills by time range. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. * `startTime` (integer, required) — Start time in milliseconds, inclusive. * `endTime` (integer, optional) — End time in milliseconds, inclusive. Defaults to current time. * `aggregateByTime` (boolean, optional) — When true, partial fills are combined when a crossing order gets filled by multiple different resting orders. Resting orders filled by multiple crossing orders will not be aggregated. ## Response The response is an array of fill objects within the specified time range, with each fill containing detailed trade information: ### Fill data structure Each fill object contains the following fields: **Core trade data:** * `coin` — Asset identifier (simple names like "BTC", "ETH" for perpetuals; spot format like "@107" for spot trades) * `px` — Fill price as a string for precision * `sz` — Fill size (quantity traded) * `side` — Trade side: "A" for Ask/Sell, "B" for Bid/Buy * `time` — Fill timestamp in milliseconds * `oid` — Order ID that generated this fill * `tid` — Unique trade ID **Position and P\&L data:** * `startPosition` — Position size before this fill * `dir` — Trade direction (e.g., "Open Long", "Close Short", "Buy", "Sell") * `closedPnl` — Realized profit/loss from this fill * `crossed` — Boolean indicating if this was a crossing order **Fee and transaction data:** * `fee` — Trading fee paid for this fill * `feeToken` — Token used to pay the fee (typically "USDC") * `builderFee` — Builder fee (optional, only present if non-zero) * `hash` — Transaction hash for blockchain verification ### Time range filtering **Time parameters:** * `startTime` — Inclusive start timestamp in milliseconds * `endTime` — Inclusive end timestamp (optional, defaults to current time) * Time range can span any period within the available data window **Data availability:** * Only the 10,000 most recent fills are available * Older fills beyond this limit are not accessible * Maximum 2000 fills returned per response ### Pagination and limits **Response limits:** * Maximum 2000 fills per response * Results are ordered by time (most recent first) * For larger time ranges, multiple requests may be needed **Pagination strategy:** * Use the `time` field of the last returned fill as the new `endTime` * Subtract 1 millisecond to avoid duplicate results * Continue until no more fills are returned ### Asset identification **Perpetual contracts:** * Use simple asset names: "BTC", "ETH", "AVAX", "SOL" * Represent standard perpetual futures contracts **Spot markets:** * Use indexed format: "@107", "@1", etc. * Index corresponds to the spot pair position in the universe * Some assets may have remapped names in user interfaces ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userFillsByTime", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b", "startTime": 1754380000000, "endTime": 1754380200000}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants # userFillsByTime is available on the public Hyperliquid API only info = Info(constants.MAINNET_API_URL, skip_ws=True) fills = info.user_fills_by_time( "0x31ca8395cf837de08b24da3f660e77761dfb974b", 1754380000000, # startTime (ms) 1754380200000, # endTime (ms) ) print(fills) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; // userFillsByTime is available on the public Hyperliquid API only const transport = new hl.HttpTransport(); const info = new hl.InfoClient({ transport }); const fills = await info.userFillsByTime({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", startTime: 1754380000000, // ms endTime: 1754380200000, // ms }); console.log(fills); ``` ## Example response ```json theme={"system"} [ { "closedPnl": "0.0", "coin": "AVAX", "crossed": false, "dir": "Open Long", "hash": "0xa166e3fa63c25663024b03f2e0da011a00307e4017465df020210d3d432e7cb8", "oid": 90542681, "px": "18.435", "side": "B", "startPosition": "26.86", "sz": "93.53", "time": 1681222254710, "fee": "0.01", "feeToken": "USDC", "builderFee": "0.01", "tid": 118906512037719 }, { "coin": "@107", "px": "18.62041381", "sz": "43.84", "side": "A", "time": 1735969713869, "startPosition": "10659.65434798", "dir": "Sell", "closedPnl": "8722.988077", "hash": "0x2222138cc516e3fe746c0411dd733f02e60086f43205af2ae37c93f6a792430b", "oid": 59071663721, "crossed": true, "fee": "0.304521", "tid": 907359904431134, "feeToken": "USDC" } ] ``` ## Use cases The `info` endpoint with `type: "userFillsByTime"` is essential for applications that need to: * **Historical analysis**: Analyze trading patterns and performance over specific time periods * **Reporting and analytics**: Generate time-based trading reports and performance metrics * **Tax compliance**: Extract trading data for specific tax periods and jurisdictions * **Audit trails**: Maintain detailed records of trading activity within defined timeframes * **Performance attribution**: Analyze trading performance by specific time periods or market conditions * **Risk analysis**: Study trading behavior during specific market events or volatility periods * **Strategy backtesting**: Use historical fill data to validate trading strategies over time * **Compliance reporting**: Generate regulatory reports for specific reporting periods * **Portfolio reconciliation**: Match fills against expected executions within time windows * **Fee analysis**: Calculate trading costs and analyze fee patterns over time * **Market impact studies**: Analyze execution quality during specific market conditions * **Trading pattern analysis**: Identify trends and patterns in trading behavior over time * **Data synchronization**: Keep external systems synchronized with trading activity * **Custom dashboards**: Build time-filtered views of trading activity and performance * **Automated reporting**: Generate scheduled reports for specific time periods This endpoint is particularly valuable for applications requiring precise time-based filtering of trading data, enabling detailed historical analysis and comprehensive reporting capabilities for trading activities on the Hyperliquid platform. # userFunding | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-funding openapi/hyperliquid_node_api/hypercore_info/info_user_funding.json post /info The info endpoint with type: "userFunding" retrieves the funding history for a specific user account on the Hyperliquid exchange. On Hyperliquid info. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userFunding"` retrieves the funding history for a specific user account on the Hyperliquid exchange. This endpoint provides detailed information about funding payments and receipts across all perpetual positions over time, allowing users to track their funding costs and analyze trading performance. > [!NOTE] > > This endpoint is similar to [userNonFundingLedgerUpdates](/reference/hyperliquid-info-user-non-funding-ledger-updates) — both query the Hyperliquid `info` API with different `type` values and return structurally similar arrays. The key difference is in the output content: this page returns only funding events, while [userNonFundingLedgerUpdates](/reference/hyperliquid-info-user-non-funding-ledger-updates) returns all other balance‑affecting ledger updates (deposits, withdrawals, transfers, liquidations, spot activity), excluding funding. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userFunding"` to retrieve user funding history. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. * `startTime` (integer, required) — Start time in milliseconds, inclusive. * `endTime` (integer, optional) — End time in milliseconds, inclusive. Defaults to current time. ## Response The response is an array of funding events, chronologically ordered from oldest to newest. Each event represents a funding payment or receipt for a specific perpetual position. ### Response structure Each funding event contains: **Event metadata:** * `time` — Unix timestamp in milliseconds when the funding occurred * `hash` — Transaction hash (typically zero hash for funding events) **Funding details:** * `delta.type` — Always "funding" for funding events * `delta.coin` — Asset symbol (e.g., "BTC", "ETH", "SOL") * `delta.usdc` — Funding amount in USDC (negative = paid, positive = received) * `delta.szi` — Position size at the time of funding * `delta.fundingRate` — Funding rate applied for this period ### Data interpretation **Funding payments vs receipts:** * Negative `usdc` values indicate funding paid (user is long in a positive funding environment or short in negative funding) * Positive `usdc` values indicate funding received (user is short in positive funding environment or long in negative funding) **Position correlation:** * `szi` shows the position size when funding was applied * Positive `szi` indicates long positions, negative indicates short positions * Larger absolute position sizes result in proportionally larger funding payments/receipts **Funding rate analysis:** * `fundingRate` represents the rate charged per unit of time * Positive rates favor shorts (longs pay shorts) * Negative rates favor longs (shorts pay longs) * Rates are typically charged every 8 hours (3 times daily) ## Trading insights ### Funding cost analysis **Position cost calculation:** * Total funding cost = sum of all `usdc` values for a specific asset * Daily funding impact = funding payments/receipts over 24-hour periods * Funding rate trends indicate market sentiment and position bias **Strategy optimization:** * High funding costs may indicate overleverage or poor market timing * Consistent funding receipts suggest alignment with market structure * Funding patterns help optimize position entry and exit timing ### Market sentiment indicators **Funding rate patterns:** * Consistently positive funding rates indicate bullish bias (demand for long positions) * Negative funding rates suggest bearish sentiment (demand for short positions) * Extreme funding rates often coincide with market turning points **Position management:** * Monitor funding costs relative to trading profits * Consider position size adjustments during high funding periods * Use funding rate changes as market sentiment indicators ## Historical analysis ### Performance tracking **Funding efficiency:** * Track funding costs as percentage of trading profits * Compare funding expenses across different assets * Identify optimal position sizing for funding cost management **Market timing:** * Analyze funding rate cycles for entry/exit optimization * Monitor funding rate extremes for reversal signals * Use historical funding patterns for strategy backtesting ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "type": "userFunding", "user": "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", "startTime": 1681923833000, "endTime": 1681924833000 }' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) result = info.user_funding_history( user="0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", startTime=1681923833000, endTime=1681924833000, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const result = await info.userFunding({ user: "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", startTime: 1681923833000, endTime: 1681924833000, }); console.log(result); ``` ## Example response ```json theme={"system"} [ { "delta": { "coin": "ETH", "fundingRate": "0.0000417", "szi": "49.1477", "type": "funding", "usdc": "-3.625312" }, "hash": "0xa166e3fa63c25663024b03f2e0da011a00307e4017465df020210d3d432e7cb8", "time": 1681222254710 } ] ``` ## Use cases The `info` endpoint with `type: "userFunding"` is essential for applications that need to: * **Trading analytics**: Analyze funding costs and their impact on trading performance * **Portfolio management**: Track funding expenses across multiple positions and assets * **Strategy optimization**: Optimize position sizing and timing based on funding patterns * **Performance attribution**: Separate trading gains from funding costs for accurate measurement * **Tax reporting**: Calculate funding payments and receipts for tax documentation * **Risk management**: Monitor funding costs as part of overall trading risk assessment * **Market analysis**: Study funding rate patterns to understand market sentiment * **Algorithm development**: Incorporate funding costs into automated trading strategies * **Position management**: Make informed decisions about position holds based on funding trends * **Cost analysis**: Compare funding efficiency across different trading strategies * **Backtesting**: Include historical funding costs in strategy backtesting and simulation * **Reporting tools**: Generate comprehensive trading reports including funding breakdowns This endpoint provides comprehensive funding history with time-based filtering, enabling detailed analysis of funding costs and their impact on trading performance in perpetual futures markets. # userNonFundingLedgerUpdates | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-non-funding-ledger-updates openapi/hyperliquid_node_api/hypercore_info/info_user_non_funding_ledger_updates.json post /info The info endpoint with type: "userNonFundingLedgerUpdates" retrieves comprehensive ledger updates for a specific user account on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userNonFundingLedgerUpdates"` retrieves comprehensive ledger updates for a specific user account on the Hyperliquid exchange, excluding funding payments. This endpoint provides detailed information about all account activities including deposits, withdrawals, transfers, liquidations, spot trading, and other balance-affecting operations. > [!NOTE] > > This endpoint is similar to [userFunding](/reference/hyperliquid-info-user-funding) — both query the Hyperliquid `info` API with different `type` values and return arrays of time‑ordered events. The difference is in scope: [userFunding](/reference/hyperliquid-info-user-funding) returns only funding events, whereas this page returns non‑funding ledger updates (deposits, withdrawals, transfers, liquidations, spot activity). ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userNonFundingLedgerUpdates"` to retrieve user ledger updates. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. * `startTime` (integer, required) — Start time in milliseconds, inclusive. * `endTime` (integer, optional) — End time in milliseconds, inclusive. Defaults to current time. ## Response The response is an array of ledger update events, chronologically ordered from oldest to newest. Each event represents a balance-affecting operation on the user's account, excluding funding payments and receipts. ### Response structure Each ledger update event contains: **Event metadata:** * `time` — Unix timestamp in milliseconds when the event occurred * `hash` — Transaction hash for blockchain transactions **Update details (`delta` object):** * `type` — Type of ledger update (see transaction types below) * Additional fields specific to each transaction type ### Transaction types **Deposit operations:** * `deposit` — USDC deposits into the account * `usdc` — Amount deposited in USDC **Withdrawal operations:** * `withdraw` — USDC withdrawals from the account * `usdc` — Amount withdrawn in USDC * `nonce` — Unique nonce for the withdrawal * `fee` — Withdrawal fee in USDC **Account transfers:** * `accountClassTransfer` — Transfers between spot and perpetual accounts * `usdc` — Amount transferred in USDC * `toPerp` — Boolean indicating transfer direction (true = to perp, false = to spot) **Spot trading operations:** * `spotTransfer` — Spot token transfers between accounts * `token` — Token symbol being transferred * `amount` — Token amount transferred * `usdcValue` — USD value of the transfer * `user` — Source user address * `destination` — Destination address * `fee` — Transfer fee * `nativeTokenFee` — Native token fee * `nonce` — Transfer nonce (if applicable) * `spotGenesis` — Initial token allocations or airdrops * `token` — Token symbol * `amount` — Token amount received **Sub-account operations:** * `subAccountTransfer` — Transfers between main account and sub-accounts * `usdc` — Amount transferred * `user` — Source account address * `destination` — Destination account address **Internal transfers:** * `internalTransfer` — Internal transfers within the exchange * `usdc` — Amount transferred * `user` — Source user * `destination` — Destination user * `fee` — Transfer fee **Liquidation events:** * `liquidation` — Forced position closures due to insufficient margin * `liquidatedNtlPos` — Total notional value liquidated * `accountValue` — Account value at liquidation * `leverageType` — Leverage type (Cross/Isolated) * `liquidatedPositions` — Array of liquidated positions with coin and size **Staking operations:** * `cStakingTransfer` — Consensus staking deposits and withdrawals * `token` — Staked token symbol * `amount` — Amount staked/unstaked * `isDeposit` — Boolean indicating if it's a deposit or withdrawal ### Data interpretation **Balance tracking:** * Each event shows the exact change to account balances * USDC amounts can be positive (credits) or negative (debits) * Events are sequential and can be used to reconstruct account history **Transfer analysis:** * `accountClassTransfer` events move funds between spot and perpetual trading * `spotTransfer` events involve token movements between different addresses * `subAccountTransfer` events manage funds across account hierarchies **Risk events:** * `liquidation` events indicate margin calls and forced position closures * Track liquidation frequency and sizes for risk assessment * Account value at liquidation shows remaining equity ## Account management insights ### Fund flow analysis **Deposit and withdrawal patterns:** * Track net fund flows to understand capital deployment * Monitor withdrawal patterns for liquidity planning * Analyze deposit timing relative to market movements **Cross-margin management:** * Monitor transfers between spot and perpetual accounts * Optimize capital allocation across trading strategies * Track margin efficiency and utilization ### Trading activity reconstruction **Complete transaction history:** * Combine with funding data for comprehensive P\&L analysis * Track all balance-affecting operations in chronological order * Identify trading patterns and account usage **Portfolio management:** * Monitor spot token accumulation and distribution * Track staking activities and rewards * Analyze sub-account fund management ### Risk monitoring **Liquidation analysis:** * Identify liquidation triggers and patterns * Monitor account health leading to liquidations * Analyze liquidation impact on overall portfolio **Operational risk:** * Track withdrawal frequencies and amounts * Monitor account class transfers for strategy changes * Identify unusual transfer patterns or activities ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{ "type": "userNonFundingLedgerUpdates", "user": "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", "startTime": 1681923833000, "endTime": 1681924833000 }' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) updates = info.user_non_funding_ledger_updates( user="0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", startTime=1681923833000, endTime=1681924833000, ) print(updates) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const updates = await info.userNonFundingLedgerUpdates({ user: "0x2ba553d9f990a3b66b03b2dc0d030dfc1c061036", startTime: 1681923833000, endTime: 1681924833000, }); console.log(updates); ``` ## Example response ```json theme={"system"} [ { "delta": { "type": "deposit", "usdc": "1000.0" }, "hash": "0x1234567890abcdef1234567890abcdef12345678", "time": 1681923833000 }, { "delta": { "type": "withdraw", "usdc": "-500.0", "nonce": 12345, "fee": "1.0" }, "hash": "0xabcdef1234567890abcdef1234567890abcdef12", "time": 1681924000000 } ] ``` ## Use cases The `info` endpoint with `type: "userNonFundingLedgerUpdates"` is essential for applications that need to: * **Account reconciliation**: Track all balance changes and verify account states * **Portfolio analytics**: Analyze complete fund flows and asset movements * **Trading analysis**: Reconstruct trading activity and performance metrics * **Risk management**: Monitor liquidations and account health indicators * **Compliance reporting**: Generate comprehensive transaction reports for regulatory requirements * **Tax calculation**: Track all taxable events including transfers and liquidations * **Audit trails**: Maintain complete records of all account activities * **Liquidity analysis**: Monitor deposit and withdrawal patterns * **Capital allocation**: Analyze fund movements between different trading strategies * **Performance attribution**: Separate trading gains from deposits and withdrawals * **Sub-account management**: Track fund flows across account hierarchies * **Financial reporting**: Generate detailed financial statements and reports * **Automated reconciliation**: Build systems to automatically verify account balances * **Anomaly detection**: Identify unusual transaction patterns or activities This endpoint provides comprehensive account activity data with time-based filtering, enabling detailed analysis of all non-funding balance changes and portfolio tracking in the Hyperliquid ecosystem. # userRole | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-role openapi/hyperliquid_node_api/hypercore_info/info_user_role.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "userRole" retrieves the role and account type information for a specific user on the Hyperliquid network. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userRole"` retrieves the role and account type information for a specific user on the Hyperliquid network. This endpoint is essential for understanding the account structure, permissions, and relationships within the Hyperliquid ecosystem. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userRole"` to retrieve user role information. * `user` (string, required) — User address in 42-character hexadecimal format (e.g., `0x31ca8395cf837de08b24da3f660e77761dfb974b`). ## Response The response varies based on the user's role type. All responses include a `role` field, with additional `data` for certain role types: ### Role types #### User ```json theme={"system"} {"role": "user"} ``` * **role** (string) — Standard user account with full trading capabilities #### Agent ```json theme={"system"} {"role": "agent", "data": {"user": "0x..."}} ``` * **role** (string) — Agent account that acts on behalf of another user * **data.user** (string) — Address of the user this agent represents #### Vault ```json theme={"system"} {"role": "vault"} ``` * **role** (string) — Vault account for collective asset management #### Sub-account ```json theme={"system"} {"role": "subAccount", "data": {"master": "0x..."}} ``` * **role** (string) — Sub-account linked to a master account * **data.master** (string) — Address of the master account #### Missing ```json theme={"system"} {"role": "missing"} ``` * **role** (string) — Unrecognized or non-existent account ### Understanding user roles **User accounts:** * Standard individual or institutional trading accounts * Full access to trading, staking, and governance features * Independent account management and permissions **Agent accounts:** * Authorized to act on behalf of other users * Used for automated trading, portfolio management, or custody services * Maintains connection to the principal user account **Vault accounts:** * Collective investment vehicles or fund structures * May have multiple participants or beneficiaries * Often used for institutional asset management **Sub-accounts:** * Linked to a master account for organizational purposes * Useful for separating trading strategies or risk management * Master account typically maintains oversight and control **Missing accounts:** * Address exists but has no recognized role or activity * May indicate new, inactive, or invalid addresses * Useful for validation and error handling ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userRole", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # Pass your full Chainstack endpoint as the base URL. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) result = info.user_role(user="0x31ca8395cf837de08b24da3f660e77761dfb974b") print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // apiUrl points the transport at your full Chainstack endpoint. const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const result = await info.userRole({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(result); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} {"role": "user"} ``` ## Use case The `info` endpoint with `type: "userRole"` is essential for applications that need to: * **Access control**: Determine appropriate permissions and features for different account types * **User interface**: Customize UI elements based on account role and capabilities * **Compliance monitoring**: Track different account types for regulatory requirements * **Risk management**: Apply role-specific risk controls and limits * **Agent verification**: Validate agent relationships and authorization * **Sub-account management**: Display master-sub account hierarchies * **Vault operations**: Identify collective investment accounts and structures * **Account validation**: Verify account existence and type before operations * **Automated systems**: Route operations based on account role and permissions * **Reporting systems**: Categorize accounts for analytics and compliance * **Customer support**: Understand account structure for troubleshooting * **Onboarding flows**: Guide users through role-specific setup processes * **API integration**: Implement role-based API access and functionality * **Audit trails**: Track activities across different account types This endpoint is particularly valuable for institutional platforms managing multiple account types, compliance systems requiring role-based monitoring, automated trading systems that need to understand account relationships, and applications providing differentiated services based on user roles on the Hyperliquid network. # userToMultiSigSigners | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-to-multi-sig-signers openapi/hyperliquid_node_api/hypercore_info/info_user_to_multi_sig_signers.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "userToMultiSigSigners" retrieves the list of multi-signature signers associated with a specific user on the Hyperliquid network. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userToMultiSigSigners"` retrieves the list of multi-signature signers associated with a specific user on the Hyperliquid network. This endpoint is essential for understanding the multi-signature setup and authorized signers for a user's account. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userToMultiSigSigners"` to retrieve multi-signature signers. * `user` (string, required) — User address in 42-character hexadecimal format (e.g., `0x3710f20c8a8b2781D878105Fbd61CC0c20fE0411`). ## Response The response is an array of signer addresses associated with the user's multi-signature setup: * **Signers array** (array of strings) — List of authorized signer addresses * Each element is a hexadecimal address string * Represents accounts that can participate in multi-signature operations * Empty array indicates no multi-signature setup or no authorized signers ### Understanding multi-signature signers **Multi-signature functionality:** * Allows multiple parties to control a single account or execute transactions * Enhances security by requiring multiple approvals for sensitive operations * Commonly used for institutional accounts, DAOs, and shared custody arrangements * Signers are pre-authorized addresses that can participate in multi-sig operations **Signer management:** * Users can add or remove signers through governance or administrative functions * Each signer has equal authority unless specified otherwise in the multi-sig contract * Threshold requirements may apply (e.g., 2-of-3, 3-of-5 signatures required) * Signer changes typically require existing signer approval **Security considerations:** * Multi-signature setups reduce single points of failure * Distributed control among trusted parties or devices * Protection against key compromise or loss * Enhanced compliance for institutional requirements ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userToMultiSigSigners", "user": "0x3710f20c8a8b2781D878105Fbd61CC0c20fE0411"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) signers = info.query_user_to_multi_sig_signers("0x3710f20c8a8b2781D878105Fbd61CC0c20fE0411") print(signers) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const signers = await info.userToMultiSigSigners({ user: "0x3710f20c8a8b2781D878105Fbd61CC0c20fE0411", }); console.log(signers); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use case The `info` endpoint with `type: "userToMultiSigSigners"` is essential for applications that need to: * **Account management**: Display authorized signers for multi-signature accounts * **Security auditing**: Verify current signer configurations and permissions * **Transaction preparation**: Identify required signers before initiating multi-sig transactions * **Compliance monitoring**: Track authorized parties for regulatory requirements * **Access control**: Validate signer permissions for administrative operations * **Multi-sig wallets**: Show users their current signer setup and configurations * **Institutional dashboards**: Monitor multi-signature arrangements across accounts * **Risk management**: Assess security posture through signer distribution * **Governance systems**: Identify voting participants in multi-sig governance * **Backup verification**: Ensure proper backup signer arrangements * **Onboarding flows**: Guide new users through multi-signature setup * **Permission management**: Track and manage signer roles and responsibilities * **Emergency procedures**: Identify available signers for urgent operations * **Audit trails**: Document signer changes and account modifications This endpoint is particularly valuable for institutional users managing complex multi-signature arrangements, security-conscious traders using multi-sig wallets, compliance teams tracking authorized signers, and applications providing comprehensive account security management on the Hyperliquid network. # userTwapSliceFills | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-twap-slice-fills openapi/hyperliquid_node_api/hypercore_info/info_user_twap_slice_fills.json post /info The info endpoint with type: "userTwapSliceFills" retrieves a user's TWAP (Time-Weighted Average Price) slice fills on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userTwapSliceFills"` retrieves a user's TWAP (Time-Weighted Average Price) slice fills on the Hyperliquid exchange. This endpoint provides detailed information about individual fill executions that result from TWAP order slicing, enabling analysis of TWAP execution quality and performance. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userTwapSliceFills"` to retrieve TWAP slice fills. * `user` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. ## Response The response is an array of TWAP slice fill objects, each containing detailed fill information and the associated TWAP order ID: ### Response structure Each TWAP slice fill object contains: * `fill` — Detailed fill information object (similar to regular fills) * `twapId` — Unique identifier for the TWAP order that generated this slice fill ### Fill data structure The `fill` object contains comprehensive execution information: **Core trade data:** * `coin` — Asset identifier (simple names like "BTC", "ETH" for perpetuals; spot format like "@107" for spot trades) * `px` — Fill price as a string for precision * `sz` — Fill size (quantity traded) * `side` — Trade side: "A" for Ask/Sell, "B" for Bid/Buy * `time` — Fill timestamp in milliseconds * `oid` — Order ID that generated this fill * `tid` — Unique trade ID **Position and P\&L data:** * `startPosition` — Position size before this fill * `dir` — Trade direction (e.g., "Open Long", "Close Short", "Buy", "Sell") * `closedPnl` — Realized profit/loss from this fill * `crossed` — Boolean indicating if this was a crossing order **Fee and transaction data:** * `fee` — Trading fee paid for this fill * `feeToken` — Token used to pay the fee (typically "USDC") * `hash` — Transaction hash (TWAP fills have a hash of all zeros) **TWAP-specific characteristics:** * `twapId` — Links this fill to the parent TWAP order * `hash` — Always "0x0000000000000000000000000000000000000000000000000000000000000000" for TWAP fills ### Data limits and ordering **Response limits:** * Maximum 2000 most recent TWAP slice fills * Results are ordered by time (most recent first) * Older fills beyond the 2000 limit are not accessible **TWAP execution context:** * Each fill represents one slice execution from a larger TWAP order * Multiple fills can share the same `twapId` if they're from the same TWAP order * Fills show the actual execution details of TWAP slicing strategy ## TWAP order execution ### Understanding TWAP fills **TWAP strategy:** * Large orders are broken into smaller slices executed over time * Each slice execution generates a separate fill record * Goal is to achieve time-weighted average pricing and reduce market impact **Slice execution:** * Individual slices are executed as market conditions allow * Each slice fill shows actual execution price and quantity * Timing and sizing depend on TWAP algorithm parameters **Execution tracking:** * `twapId` groups all fills from the same TWAP order * Sequential fills show the progression of TWAP execution * Total execution = sum of all fills with the same `twapId` ### TWAP vs regular fills **Key differences:** * TWAP fills have zero hash (`0x000...000`) * TWAP fills are linked by `twapId` * TWAP fills represent algorithmic execution slices * Regular fills represent direct order executions ### Asset identification **Perpetual contracts:** * Use simple asset names: "BTC", "ETH", "AVAX", "SOL" * Represent standard perpetual futures contracts **Spot markets:** * Use indexed format: "@107", "@1", etc. * Index corresponds to the spot pair position in the universe * Some assets may have remapped names in user interfaces ## Performance analysis ### TWAP execution metrics **Price performance:** * Compare individual slice prices to market benchmarks * Analyze price improvement or slippage across slices * Calculate volume-weighted average price (VWAP) achieved **Timing analysis:** * Review execution timing across the TWAP duration * Analyze market impact of individual slices * Assess execution completion rate and timing **Cost analysis:** * Sum fees across all slices for total execution cost * Compare TWAP execution costs to alternative strategies * Analyze fee efficiency of slice sizing ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userTwapSliceFills", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants # This method is available only on the public Hyperliquid API. info = Info(constants.MAINNET_API_URL, skip_ws=True) fills = info.user_twap_slice_fills("0x31ca8395cf837de08b24da3f660e77761dfb974b") print(fills) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; // This method is available only on the public Hyperliquid API. const transport = new hl.HttpTransport(); const info = new hl.InfoClient({ transport }); const fills = await info.userTwapSliceFills({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(fills); ``` ## Example response ```json theme={"system"} [ { "fill": { "closedPnl": "0.0", "coin": "AVAX", "crossed": true, "dir": "Open Long", "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", "oid": 90542681, "px": "18.435", "side": "B", "startPosition": "26.86", "sz": "93.53", "time": 1681222254710, "fee": "0.01", "feeToken": "USDC", "tid": 118906512037719 }, "twapId": 3156 } ] ``` ## Use cases The `info` endpoint with `type: "userTwapSliceFills"` is essential for applications that need to: * **TWAP execution analysis**: Analyze the performance and efficiency of TWAP order executions * **Algorithmic trading evaluation**: Assess the effectiveness of TWAP strategies and parameters * **Market impact studies**: Study how TWAP slicing affects market impact and execution quality * **Performance attribution**: Attribute trading performance to specific TWAP execution strategies * **Cost analysis**: Calculate total execution costs and analyze fee efficiency across TWAP slices * **Execution quality monitoring**: Monitor TWAP execution quality and identify optimization opportunities * **Risk management**: Assess execution risk and market impact of large order strategies * **Strategy optimization**: Optimize TWAP parameters based on historical execution data * **Compliance reporting**: Generate detailed reports on algorithmic execution for regulatory requirements * **Client reporting**: Provide institutional clients with detailed TWAP execution reports * **Academic research**: Study algorithmic execution strategies and market microstructure * **Quantitative analysis**: Perform statistical analysis on TWAP execution patterns and outcomes * **Benchmark comparison**: Compare TWAP execution against other execution strategies * **Market timing analysis**: Analyze optimal timing for TWAP execution initiation * **Liquidity analysis**: Study market liquidity patterns through TWAP execution data * **Trading system development**: Build and optimize TWAP execution algorithms * **Portfolio management**: Track large order executions as part of portfolio management * **Execution consulting**: Provide execution analysis and optimization services * **Regulatory compliance**: Maintain detailed records of algorithmic execution for audit purposes * **Performance reporting**: Generate comprehensive execution performance reports for stakeholders This endpoint provides specialized data for analyzing TWAP execution performance, enabling sophisticated analysis of algorithmic trading strategies and execution quality on the Hyperliquid platform. # userTwapSliceFillsByTime | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-user-twap-slice-fills-by-time openapi/hyperliquid_node_api/hypercore_info/info_user_twap_slice_fills_by_time.json post /info The info endpoint with type: "userTwapSliceFillsByTime" returns a user's TWAP slice fills within a time range on Hyperliquid. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userTwapSliceFillsByTime"` returns a user's TWAP slice fills within a time range. Each TWAP order executes as a series of slices; this method returns those individual slice fills filtered by time. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userTwapSliceFillsByTime"`. * `user` (string, required) — Address in 42-character hexadecimal format. * `startTime` (integer, required) — Start time in milliseconds since epoch (inclusive). * `endTime` (integer, optional) — End time in milliseconds since epoch (inclusive). Defaults to the current time. ## Response Returns an array of TWAP slice fills. Each entry is a fill record (with the standard fill fields such as `coin`, `px`, `sz`, `side`, `time`, and `hash`) paired with the TWAP order ID it belongs to. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userTwapSliceFillsByTime", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", "startTime": 0}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no dedicated userTwapSliceFillsByTime helper, so post the request directly. fills = info.post("/info", { "type": "userTwapSliceFillsByTime", "user": "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", "startTime": 0, }) print(fills) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const fills = await info.userTwapSliceFillsByTime({ user: "0x1442ad477ded1b0028b57621aa7b6f7eadb8f568", startTime: 0, }); console.log(fills); ``` ## Example response ```json theme={"system"} [] ``` The example returns an empty array because the queried address has no TWAP slice fills in the range. An account with TWAP executions returns an array of slice fills as described above. ## Use cases The `info` endpoint with `type: "userTwapSliceFillsByTime"` is useful for: * Reconciling TWAP executions within a specific time window * Backtesting and performance analysis of TWAP strategies * Reporting on slice-level execution detail # userFees | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-userfees openapi/hyperliquid_node_api/hypercore_info/info_userfees.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "userFees" retrieves detailed information about a user's fee structure and trading costs on the Hyperliquid exchange. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userFees"` retrieves detailed information about a user's fee structure and trading costs on the Hyperliquid exchange. This endpoint provides comprehensive fee information including base rates, VIP tiers, discounts, and effective rates after all reductions are applied. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userFees"` to retrieve user's fee structure. * `user` (string, required) — Address in 42-character hexadecimal format (e.g., `0x31ca8395cf837de08b24da3f660e77761dfb974b`). ## Response The response is a comprehensive object containing detailed fee information, schedules, and user-specific rates: ### Daily volume data * `dailyUserVlm` (array) — Array of daily volume data objects: * `date` (string) — Date in YYYY-MM-DD format * `userCross` (string) — User's cross-margin trading volume for the day * `userAdd` (string) — User's isolated/add margin trading volume for the day * `exchange` (string) — Total exchange volume for the day ### Fee schedule structure * `feeSchedule` (object) — Complete fee schedule information: * `cross` (string) — Base cross-margin taker fee rate * `add` (string) — Base isolated/add margin maker fee rate * `spotCross` (string) — Base spot cross-margin taker fee rate * `spotAdd` (string) — Base spot isolated/add margin maker fee rate * `tiers` (object) — Tiered fee structures: * `vip` (array) — VIP tier configurations with volume cutoffs and reduced rates * `mm` (array) — Market maker tier configurations * `referralDiscount` (string) — Base referral discount percentage * `stakingDiscountTiers` (array) — Staking-based discount tiers ### User-specific rates * `userCrossRate` (string) — User's effective cross-margin trading rate (after discounts) * `userAddRate` (string) — User's effective isolated/add margin trading rate (after discounts) * `userSpotCrossRate` (string) — User's effective spot cross-margin trading rate * `userSpotAddRate` (string) — User's effective spot isolated/add margin trading rate ### Applied discounts and benefits * `activeReferralDiscount` (string) — Currently active referral discount percentage * `activeStakingDiscount` (object) — Currently active staking discount: * `bpsOfMaxSupply` (string) — Basis points of max supply being staked * `discount` (string) — Discount percentage applied ### Trial and rewards * `trial` (object/null) — Trial program information (if applicable) * `feeTrialReward` (string) — Rewards from fee trial programs * `nextTrialAvailableTimestamp` (integer/null) — When next trial becomes available ### Staking information * `stakingLink` (object) — Staking relationship information: * `type` (string) — Type of staking link (e.g., "tradingUser") * `stakingUser` (string) — Address of the linked staking user ### Understanding fee structure **Cross vs Add (Isolated) margin:** * **Cross margin**: Uses entire account balance as collateral for positions * **Add/Isolated margin**: Each position has dedicated collateral * **Cross rates**: Generally higher due to increased risk sharing * **Add rates**: Lower rates to encourage isolated position management **Fee types:** * **Perpetuals fees**: `userCrossRate` and `userAddRate` for perpetual futures * **Spot fees**: `userSpotCrossRate` and `userSpotAddRate` for spot trading * **Market maker rebates**: Negative fees for qualifying market makers **Discount mechanisms:** * **Referral discounts**: Reductions based on referral program participation * **Staking discounts**: Fee reductions based on token staking amounts * **VIP tiers**: Volume-based fee reductions with specific cutoffs * **Trial programs**: Temporary fee benefits and rewards **Rate calculation:** * Base rates are modified by applicable discounts * User-specific rates show final effective rates after all benefits * Staking discounts based on percentage of maximum token supply held ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userFees", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python hyperliquid-python-sdk theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) user_fees = info.user_fees("0x31ca8395cf837de08b24da3f660e77761dfb974b") print(user_fees) ``` ```typescript @nktkas/hyperliquid theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const client = new InfoClient({ transport }); const userFees = await client.userFees({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(userFees); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "dailyUserVlm": [ { "date": "2025-05-23", "userCross": "0.0", "userAdd": "0.0", "exchange": "2852367.0770729999" } ], "feeSchedule": { "cross": "0.00045", "add": "0.00015", "spotCross": "0.0007", "spotAdd": "0.0004", "tiers": { "vip": [ { "ntlCutoff": "5000000.0", "cross": "0.0004", "add": "0.00012", "spotCross": "0.0006", "spotAdd": "0.0003" } ], "mm": [ { "makerFractionCutoff": "0.005", "add": "-0.00001" } ] }, "referralDiscount": "0.04", "stakingDiscountTiers": [ { "bpsOfMaxSupply": "0.0001", "discount": "0.05" } ] }, "userCrossRate": "0.000315", "userAddRate": "0.000105", "userSpotCrossRate": "0.00049", "userSpotAddRate": "0.00028", "activeReferralDiscount": "0.0", "trial": null, "feeTrialReward": "0.0", "nextTrialAvailableTimestamp": null, "stakingLink": { "type": "tradingUser", "stakingUser": "0x54c049d9c7d3c92c2462bf3d28e083f3d6805061" }, "activeStakingDiscount": { "bpsOfMaxSupply": "4.7577998927", "discount": "0.3" } } ``` ## Use cases The `info` endpoint with `type: "userFees"` is essential for applications that need to: * **Trading cost calculation**: Accurately estimate trading costs using user-specific rates * **Fee optimization**: Help users understand how to reduce fees through staking or referrals * **Profitability analysis**: Calculate net profits after accounting for actual trading fees * **Volume analysis**: Track daily trading volumes and exchange activity patterns * **Discount tracking**: Monitor active staking and referral discounts * **Trading strategy optimization**: Factor accurate fees into algorithmic trading decisions * **Fee dashboards**: Display current fee structure, discounts, and potential savings * **Cross vs isolated margin decisions**: Compare fee structures for different margin types * **Performance reporting**: Include accurate fee costs in trading performance analysis * **Institutional reporting**: Provide comprehensive fee transparency for clients * **Strategy backtesting**: Include realistic fee models in historical analysis * **Staking optimization**: Understand staking requirements for maximum fee discounts This endpoint provides comprehensive fee information including base rates, user-specific discounts, volume data, and staking benefits, enabling accurate cost calculations and optimization strategies on the Hyperliquid platform. # userRateLimit | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-userratelimit openapi/hyperliquid_node_api/hypercore_info/info_userratelimit.json post /4f8d8f4040bdacd1577bff8058438274/info Reference for the userRateLimit JSON-RPC method on the Hyperliquid info blockchain via Chainstack nodes. Parameters, response, and examples included. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userRateLimit"` retrieves detailed information about a user's API rate limit configuration and current usage on the Hyperliquid exchange. This endpoint provides essential information for managing API usage, avoiding rate limiting, and optimizing request patterns for high-frequency applications. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userRateLimit"` to retrieve user's rate limit information. * `user` (string, required) — Address in 42-character hexadecimal format (e.g., `0x31ca8395cf837de08b24da3f660e77761dfb974b`). ## Response The response is an object containing comprehensive rate limit information for the user: ### Rate limit configuration * `userAddress` (string) — Address of the user (matches the requested user) * `rateLimitTier` (string) — Rate limit tier assigned to the user: * `"standard"` — Basic rate limits for regular users * `"premium"` — Enhanced limits for active traders * `"vip"` — Highest limits for institutional or high-volume users ### Limit specifications * `requestsPerSecond` (integer, optional) — Maximum allowed requests per second * `requestsPerMinute` (integer, optional) — Maximum allowed requests per minute * `requestsPerHour` (integer, optional) — Maximum allowed requests per hour * `burstLimit` (integer, optional) — Maximum burst requests allowed in short periods ### Current usage tracking * `currentUsage` (object, optional) — Real-time API usage statistics: * `requestsLastSecond` (integer) — Number of requests made in the last second * `requestsLastMinute` (integer) — Number of requests made in the last minute * `requestsLastHour` (integer) — Number of requests made in the last hour ### Reset timing * `resetTimes` (object, optional) — When rate limit counters reset: * `nextSecondReset` (integer) — Timestamp when per-second counter resets (milliseconds) * `nextMinuteReset` (integer) — Timestamp when per-minute counter resets (milliseconds) * `nextHourReset` (integer) — Timestamp when per-hour counter resets (milliseconds) ### Status information * `isThrottled` (boolean, optional) — Whether the user is currently being rate limited * `upgradeAvailable` (boolean, optional) — Whether higher rate limit tiers are available ### Understanding rate limits **Rate limit tiers:** * **Standard**: Basic limits suitable for manual trading and light automation * **Premium**: Enhanced limits for active algorithmic trading * **VIP**: Highest limits for institutional users and market makers **Time windows:** * **Per-second**: Short-term burst protection * **Per-minute**: Medium-term usage control * **Per-hour**: Long-term usage management **Usage optimization:** * Monitor current usage to stay within limits * Implement backoff strategies when approaching limits * Use reset times to optimize request timing * Consider rate limit upgrades for high-frequency needs ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userRateLimit", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) rate_limit = info.user_rate_limit(user="0x31ca8395cf837de08b24da3f660e77761dfb974b") print(rate_limit) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const rateLimit = await info.userRateLimit({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(rateLimit); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use case The `info` endpoint with `type: "userRateLimit"` is essential for applications that need to: * **API usage optimization**: Monitor and optimize API request patterns to avoid rate limiting * **Request scheduling**: Plan API calls based on current usage and reset times * **Error handling**: Implement proper backoff strategies when rate limits are reached * **Performance monitoring**: Track API usage patterns and identify optimization opportunities * **Capacity planning**: Understand rate limit constraints for application design * **Automated trading**: Ensure trading bots operate within API usage limits * **High-frequency systems**: Optimize request timing for maximum throughput * **Usage dashboards**: Display real-time API usage and remaining capacity * **Tier management**: Monitor rate limit tier status and upgrade availability * **Compliance monitoring**: Ensure applications stay within assigned usage limits * **Load balancing**: Distribute API calls across time windows to maximize usage * **System health**: Monitor for rate limiting issues in production systems * **Development planning**: Design applications with rate limit considerations * **Cost optimization**: Balance API usage with performance requirements This endpoint is particularly valuable for algorithmic trading systems, high-frequency applications, and any automated system that makes frequent API calls to the Hyperliquid exchange. It enables proactive rate limit management and helps prevent service disruptions due to API throttling. # userVaultEquities | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-uservaultequities openapi/hyperliquid_node_api/hypercore_info/info_uservaultequities.json post /4f8d8f4040bdacd1577bff8058438274/info Reference docs for the userVaultEquities JSON-RPC method on the Hyperliquid info blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "userVaultEquities"` retrieves detailed information about a user's equity positions across all vaults they have invested in on the Hyperliquid exchange. This endpoint provides comprehensive investment tracking including current values, profit/loss metrics, and withdrawal details for each vault position. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"userVaultEquities"` to retrieve user's vault equity positions. * `user` (string, required) — Address in 42-character hexadecimal format (e.g., `0x2b804617c6f63c040377e95bb276811747006f4b`). ## Response The response is an array of vault equity objects representing each vault investment: ### Response structure Each vault equity object contains: * `vaultAddress` (string) — Contract address of the vault (42-character hexadecimal) * `equity` (string) — Current total equity value of the user's position in USD ### Understanding vault equity **Equity value:** * Represents the current total value of the user's investment in the vault * Reflects real-time value based on vault performance and share pricing * Calculated internally by the vault based on the user's share ownership **Investment tracking:** * Each vault investment is tracked separately by vault address * Equity values change based on vault performance and market conditions * Values are denominated in USD for easy comparison and analysis **Vault identification:** * Vault addresses uniquely identify each vault investment * Can be used with other endpoints (like `vaultDetails`) to get additional vault information * Allows tracking of investments across multiple vault positions ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "userVaultEquities", "user": "0x2b804617c6f63c040377e95bb276811747006f4b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) equities = info.user_vault_equities("0x2b804617c6f63c040377e95bb276811747006f4b") print(equities) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const equities = await info.userVaultEquities({ user: "0x2b804617c6f63c040377e95bb276811747006f4b", }); console.log(equities); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ { "vaultAddress": "0xdfc24b077bc1425ad1dea75bcb6f8158e10df303", "equity": "742500.082809" } ] ``` ## Use cases The `info` endpoint with `type: "userVaultEquities"` is essential for applications that need to: * **Portfolio tracking**: Monitor all vault investments and their current values * **Investment overview**: Get a consolidated view of vault equity positions * **Investment dashboards**: Display vault portfolio summaries with current values * **Asset allocation analysis**: Understand distribution of investments across vaults * **Investment reporting**: Generate reports showing current vault equity values * **Portfolio rebalancing**: Make informed decisions about vault allocations based on current equity * **Investment interfaces**: Build user-friendly vault portfolio management tools * **Performance monitoring**: Track vault investment values over time * **Tax reporting**: Track current investment values for tax purposes * **Vault comparison**: Compare equity values across different vault investments * **Risk assessment**: Evaluate exposure and concentration across vault positions * **Investment analytics**: Analyze vault investment distribution and values This endpoint provides a simple but essential overview of a user's vault investment portfolio, showing current equity values for each vault position. For more detailed vault information (such as performance metrics, shares, or withdrawal details), combine this data with other vault-related endpoints like `vaultDetails`. # validatorL1Votes | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-validator-l1-votes openapi/hyperliquid_node_api/hypercore_info/info_validator_l1_votes.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "validatorL1Votes" retrieves information about validator participation in L1 governance voting on the Hyperliquid network. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "validatorL1Votes"` retrieves information about validator participation in L1 governance voting on the Hyperliquid network. This endpoint provides data about validator voting behavior, consensus participation, and governance engagement. > [!NOTE] > > This request type is supported by the Hyperliquid info server and by Chainstack's node endpoint, but it is not documented in the upstream Hyperliquid docs. The response shape may evolve. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"validatorL1Votes"` to retrieve validator L1 voting information. ## Response The response is an array of validator voting information, including their L1 governance participation: ### Validator voting data * `validator` (string) — Validator address in hexadecimal format * `votingPower` (string) — Validator's voting power in the consensus mechanism * `l1Votes` (array) — List of L1 votes cast by the validator: * `proposalId` (string) — Unique identifier for the governance proposal * `vote` (string) — Vote cast by the validator ("yes", "no", or "abstain") * `timestamp` (integer) — Timestamp when the vote was cast * `blockHeight` (integer) — Block height at which the vote was recorded * `participationRate` (string) — Validator's participation rate in L1 governance ### Understanding L1 governance voting **L1 governance structure:** * Validators participate in network governance through on-chain voting * Proposals affect protocol parameters, upgrades, and network policies * Voting power typically correlates with validator stake and delegation * Consensus mechanisms ensure democratic decision-making **Voting mechanics:** * Validators can vote "yes", "no", or "abstain" on proposals * Voting periods have specific timeframes for participation * Votes are recorded on-chain for transparency and immutability * Participation rates indicate validator engagement in governance **Governance importance:** * Ensures decentralized decision-making for protocol evolution * Validators represent delegator interests in governance decisions * High participation rates indicate healthy network governance * Voting history provides transparency for delegator selection **Consensus participation:** * Validators with higher voting power have greater influence * Participation rates affect proposal outcomes and legitimacy * Regular voting demonstrates validator commitment to network health * Abstentions may indicate neutral positions or technical issues ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "validatorL1Votes"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # hyperliquid-python-sdk has no typed helper for this type, so post the # raw info request through the generic API.post method. votes = info.post("/info", {"type": "validatorL1Votes"}) print(votes) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const votes = await info.validatorL1Votes(); console.log(votes); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ { "validator": "0x5ac9e4e1b3b8d3d8d5f1a9f7d2b1c4e5f6a7b8c9", "votingPower": "123456.0", "l1Votes": [ { "proposalId": "HYP-1", "vote": "yes", "timestamp": 1732320000000, "blockHeight": 123456 } ], "participationRate": "0.95" } ] ``` ## Use case The `info` endpoint with `type: "validatorL1Votes"` is essential for applications that need to: * **Governance dashboards**: Display validator voting history and participation rates * **Delegator tools**: Help users choose validators based on governance engagement * **Compliance monitoring**: Track validator participation for regulatory requirements * **Network analysis**: Analyze governance participation patterns and trends * **Validator research**: Evaluate validator commitment to network governance * **Proposal tracking**: Monitor voting progress and validator positions * **Transparency tools**: Provide public access to governance voting records * **Risk assessment**: Evaluate network governance health and participation * **Automated alerts**: Notify stakeholders about important governance votes * **Academic research**: Study decentralized governance patterns and effectiveness * **Audit systems**: Verify validator behavior and governance participation * **Delegation strategies**: Optimize delegation based on governance alignment * **Network health monitoring**: Track overall governance participation rates * **Validator comparison**: Compare governance engagement across validators This endpoint is particularly valuable for delegators selecting validators based on governance participation, governance researchers analyzing voting patterns, compliance systems tracking validator behavior, and applications providing comprehensive validator analytics and transparency tools on the Hyperliquid network. # validatorSummaries | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-validator-summaries openapi/hyperliquid_node_api/hypercore_info/info_validator_summaries.json post /info The info endpoint with type: "validatorSummaries" returns summaries for all validators — stake, status, commission, and uptime/APR stats. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "validatorSummaries"` returns summaries for all validators on Hyperliquid — including delegated stake, active/jailed status, commission, and uptime and predicted-APR statistics. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"validatorSummaries"`. ## Response Returns an array of validator summaries. Each summary includes fields such as: * `validator` (string) — The validator address. * `signer` (string) — The validator signer address. * `name` (string) — The validator name. * `description` (string) — The validator description. * `stake` (integer) — Total stake delegated to the validator (in wei). * `isJailed` (boolean) — Whether the validator is jailed. * `isActive` (boolean) — Whether the validator is active. * `commission` (string) — The validator commission rate. * `stats` (array) — Per-period (`day`, `week`, `month`) uptime fraction and predicted APR. ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "validatorSummaries"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no dedicated validatorSummaries helper, so post the request directly. validators = info.post("/info", {"type": "validatorSummaries"}) print(validators) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport(); const info = new InfoClient({ transport }); const validators = await info.validatorSummaries(); console.log(validators); ``` ## Example response ```json theme={"system"} [ { "validator": "0x000000000056f99d36b6f2e0c51fd41496bbacb8", "signer": "0x0000000008b0b558419582041f85740344ae8fde", "name": "ValiDAO", "description": "The People's Validator", "nRecentBlocks": 1, "stake": 658880656542139, "isJailed": false, "unjailableAfter": null, "isActive": true, "commission": "0.04", "stats": [ ["day", { "uptimeFraction": "1.0", "predictedApr": "0.0215529962", "nSamples": 1440 }] ] } ] ``` ## Use cases The `info` endpoint with `type: "validatorSummaries"` is useful for: * Staking dashboards that list validators and their APRs * Helping delegators choose a validator by stake, commission, and uptime * Monitoring validator status and network health # vaultDetails | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-vault-details openapi/hyperliquid_node_api/hypercore_info/info_vault_details.json post /info The info endpoint with type: "vaultDetails" retrieves comprehensive information about a specific vault on the Hyperliquid exchange. > [!NOTE] > > You can only use this endpoint on the official Hyperliquid public API. It is not available through Chainstack, as the open-source node implementation does not support it yet. See [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "vaultDetails"` retrieves comprehensive information about a specific vault on the Hyperliquid exchange. This endpoint provides detailed vault metrics including portfolio performance, follower information, leader details, and vault configuration settings, enabling thorough analysis of vault operations and performance. > [!NOTE] > > **Data freshness and availability** > > This `vaultDetails` endpoint provides the most recent data for any specific vault. For broader vault discovery: > > * The [`vaultSummaries`](/reference/hyperliquid-info-vaultsummaries) endpoint returns vaults that are less than 2 hours old > * For hourly snapshots of all vaults, use `https://stats-data.hyperliquid.xyz/Mainnet/vaults` > > See also: [`vaultSummaries`](/reference/hyperliquid-info-vaultsummaries) for browsing all available vaults. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"vaultDetails"` to retrieve vault details. * `vaultAddress` (string, required) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. * `user` (string, optional) — Address in 42-character hexadecimal format; e.g. 0x0000000000000000000000000000000000000000. ## Response The response is a comprehensive vault details object containing all relevant information about the vault's configuration, performance, and participants: ### Response structure **Basic vault information:** * `name` — The vault's display name * `vaultAddress` — The vault's blockchain address * `leader` — Address of the vault leader/manager * `description` — Detailed description of the vault's strategy and purpose **Performance metrics:** * `portfolio` — Array of portfolio performance data across different time periods * `apr` — Annual percentage return as a decimal * `leaderFraction` — Fraction of the vault controlled by the leader * `leaderCommission` — Commission rate charged by the leader **Follower information:** * `followers` — Array of follower objects with detailed equity and performance data * `followerState` — State information for followers (null if not applicable) **Vault configuration:** * `maxDistributable` — Maximum amount that can be distributed * `maxWithdrawable` — Maximum amount that can be withdrawn * `isClosed` — Boolean indicating if the vault is closed to new deposits * `allowDeposits` — Boolean indicating if new deposits are allowed * `alwaysCloseOnWithdraw` — Boolean indicating if positions are always closed on withdrawal **Vault relationships:** * `relationship` — Object describing parent/child vault relationships ### Portfolio data structure The `portfolio` array contains performance data across different time periods: **Time period identifiers:** * `"day"` — Daily performance data * `"week"` — Weekly performance data * `"month"` — Monthly performance data * `"allTime"` — All-time performance data * `"perpDay"` — Daily perpetual trading performance * `"perpWeek"` — Weekly perpetual trading performance * `"perpMonth"` — Monthly perpetual trading performance * `"perpAllTime"` — All-time perpetual trading performance **Performance metrics for each period:** * `accountValueHistory` — Array of \[timestamp, value] pairs showing account value over time * `pnlHistory` — Array of \[timestamp, pnl] pairs showing profit/loss over time * `vlm` — Volume metric for the period ### Follower data structure Each follower object contains: **Identity and equity:** * `user` — Follower's wallet address * `vaultEquity` — Current equity value in the vault * `pnl` — Current profit and loss * `allTimePnl` — All-time profit and loss since joining **Timing information:** * `daysFollowing` — Number of days the user has been following the vault * `vaultEntryTime` — Timestamp when the user entered the vault (milliseconds) * `lockupUntil` — Timestamp until which funds are locked (milliseconds) ### Vault relationships The `relationship` object describes vault hierarchies: **Parent vault:** * `type: "parent"` — Indicates this vault has child vaults * `data.childAddresses` — Array of child vault addresses **Child vault:** * `type: "child"` — Indicates this vault has a parent vault * `data.parentAddress` — Parent vault address ## Vault management insights ### Performance analysis **Return metrics:** * APR provides annualized return rate for comparison * Portfolio history enables trend analysis across timeframes * PnL history shows profit/loss progression over time **Risk assessment:** * Leader fraction indicates concentration risk * Follower distribution shows vault popularity and trust * Lockup periods indicate capital commitment levels ### Operational metrics **Liquidity management:** * `maxDistributable` shows available funds for distribution * `maxWithdrawable` indicates immediate withdrawal capacity * Deposit settings control new capital inflows **Strategy execution:** * Description provides insight into vault strategy * Portfolio performance across timeframes shows execution quality * Volume metrics indicate trading activity levels ### Follower analytics **Participation patterns:** * Days following shows follower retention * Entry times reveal growth patterns * Lockup periods indicate commitment levels **Performance distribution:** * Individual PnL shows follower experience * Equity distribution reveals vault composition * All-time PnL indicates long-term performance ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "vaultDetails", "vaultAddress": "0xdfc24b077bc1425ad1dea75bcb6f8158e10df303"}' \ https://api.hyperliquid.xyz/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info from hyperliquid.utils import constants # This endpoint is public-only, so use the default Hyperliquid mainnet URL. info = Info(constants.MAINNET_API_URL, skip_ws=True) # The SDK has no dedicated vaultDetails helper, so post the request directly. # This sends the same {"type": "vaultDetails", ...} body as the curl above. result = info.post( "/info", { "type": "vaultDetails", "vaultAddress": "0xdfc24b077bc1425ad1dea75bcb6f8158e10df303", }, ) print(result) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; // This endpoint is public-only, so use the default public transport. const transport = new HttpTransport(); const info = new InfoClient({ transport }); const result = await info.vaultDetails({ vaultAddress: "0xdfc24b077bc1425ad1dea75bcb6f8158e10df303", }); console.log(result); ``` ## Example response ```json theme={"system"} { "name": "Test", "vaultAddress": "0xdfc24b077bc1425ad1dea75bcb6f8158e10df303", "leader": "0x677d831aef5328190852e24f13c46cac05f984e7", "description": "This community-owned vault provides liquidity to Hyperliquid through multiple market making strategies, performs liquidations, and accrues platform fees.", "portfolio": [ [ "day", { "accountValueHistory": [ [ 1734397526634, "329265410.90790099" ] ], "pnlHistory": [ [ 1734397526634, "0.0" ] ], "vlm": "0.0" } ], [ "week", { "accountValueHistory": [...], "pnlHistory": [...], "vlm": "..." } ] ], "apr": 0.36387129259090006, "followerState": null, "leaderFraction": 0.0007904828725729887, "leaderCommission": 0, "followers": [ { "user": "0x005844b2ffb2e122cf4244be7dbcb4f84924907c", "vaultEquity": "714491.71026243", "pnl": "3203.43026143", "allTimePnl": "79843.74476743", "daysFollowing": 388, "vaultEntryTime": 1700926145201, "lockupUntil": 1734824439201 } ], "maxDistributable": 94856870.164485, "maxWithdrawable": 742557.680863, "isClosed": false, "relationship": { "type": "parent", "data": { "childAddresses": [ "0x010461c14e146ac35fe42271bdc1134ee31c703a", "0x2e3d94f0562703b25c83308a05046ddaf9a8dd14", "0x31ca8395cf837de08b24da3f660e77761dfb974b" ] } }, "allowDeposits": true, "alwaysCloseOnWithdraw": false } ``` ## Use cases The `info` endpoint with `type: "vaultDetails"` is essential for applications that need to: * **Vault analysis**: Analyze vault performance, strategy effectiveness, and risk metrics * **Investment research**: Research vault opportunities and compare different vault strategies * **Portfolio management**: Track vault investments and monitor performance across holdings * **Risk assessment**: Evaluate vault risk through leader concentration and follower distribution * **Performance attribution**: Attribute returns to specific vault strategies and time periods * **Due diligence**: Conduct thorough analysis before investing in or following a vault * **Competitive analysis**: Compare vault performance against benchmarks and competitors * **Follower tracking**: Monitor follower behavior, retention, and satisfaction metrics * **Liquidity analysis**: Assess vault liquidity through withdrawal limits and deposit settings * **Strategy evaluation**: Evaluate the effectiveness of different vault trading strategies * **Regulatory compliance**: Maintain detailed records of vault operations for compliance purposes * **Client reporting**: Generate comprehensive reports for institutional clients and investors * **Academic research**: Study vault economics, follower behavior, and strategy performance * **Market making analysis**: Analyze market making vault performance and liquidity provision * **Yield farming optimization**: Optimize yield farming strategies through vault performance data * **Risk management**: Implement risk controls based on vault metrics and performance history * **Investment advisory**: Provide investment advice based on comprehensive vault analysis * **Platform development**: Build vault management and analysis tools for traders and investors * **Performance benchmarking**: Establish performance benchmarks for vault strategies * **Capital allocation**: Make informed decisions about capital allocation across different vaults This endpoint provides comprehensive vault intelligence, enabling sophisticated analysis of vault operations, performance, and investment opportunities on the Hyperliquid platform. # vaultSummaries | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-vaultsummaries openapi/hyperliquid_node_api/hypercore_info/info_vaultsummaries.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "vaultSummaries" retrieves summary information for all available vaults on the Hyperliquid exchange. On Hyperliquid info. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "vaultSummaries"` retrieves summary information for all available vaults on the Hyperliquid exchange. This endpoint provides vault metadata including names, addresses, TVL, and relationship information for all vaults in the system. > [!NOTE] > > **Data availability** > > This endpoint returns all available vaults in the system. If the response is an empty array, it indicates that no vaults are currently deployed on the network. > > For detailed information about a specific vault, use the [`vaultDetails`](/reference/hyperliquid-info-vault-details) endpoint. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"vaultSummaries"` to retrieve vault summaries. ## Response The response is an array of vault summary objects containing the following fields: ### Vault identification * `name` (string) — Name of the vault * `vaultAddress` (string) — Vault's blockchain address (42-character hexadecimal) * `leader` (string) — Address of the vault leader/manager ### Vault metrics * `tvl` (string) — Total Value Locked in the vault * `isClosed` (boolean) — Whether the vault is closed to new deposits * `createTimeMillis` (number) — Timestamp when vault was created (in milliseconds since Unix epoch) ### Relationship information * `relationship` (object) — Vault relationship structure * `type` (string) — Type of vault relationship: `"normal"`, `"child"`, or `"parent"` * `data` (object, optional) — Additional relationship data * `childAddresses` (array of strings) — If parent vault, contains addresses of child vaults ### Understanding vault relationships **Vault types:** * **Normal vaults**: Standard standalone vaults * **Parent vaults**: Vaults that manage or control other child vaults * **Child vaults**: Vaults that are controlled by a parent vault **Vault status:** * **Open vaults**: `isClosed: false` - accepting new deposits * **Closed vaults**: `isClosed: true` - not accepting new deposits ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "vaultSummaries"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # The SDK has no dedicated vaultSummaries helper, so post the request type directly. vault_summaries = info.post("/info", {"type": "vaultSummaries"}) print(vault_summaries) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport, InfoClient } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const info = new InfoClient({ transport }); const vaultSummaries = await info.vaultSummaries(); console.log(vaultSummaries); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} [ { "name": "Alpha Strategy Vault", "vaultAddress": "0xdfc24b077bc1425ad1dea75bcb6f8158e10df303", "leader": "0x677d831aef5328190852e24f13c46cac05f984e7", "tvl": "1500000.50", "isClosed": false, "relationship": { "type": "normal" }, "createTimeMillis": 1704067200000 }, { "name": "Beta Fund", "vaultAddress": "0x789abc123def456ghi789jkl012mno345pqr678", "leader": "0x012def456ghi789jkl012mno345pqr678stu901", "tvl": "5000000.25", "isClosed": false, "relationship": { "type": "parent", "data": { "childAddresses": [ "0xaaa111bbb222ccc333ddd444eee555fff666777", "0x888999aaabbbcccdddeeefff000111222333444" ] } }, "createTimeMillis": 1704153600000 } ] ``` ## Use case The `info` endpoint with `type: "vaultSummaries"` is essential for applications that need to: * **Vault discovery**: Browse all available vaults in the system * **TVL analysis**: Monitor total value locked across different vaults * **Vault status**: Check which vaults are open or closed for deposits * **Relationship mapping**: Understand parent-child vault relationships * **Vault listing**: Build interfaces showing all available vaults * **Leader tracking**: Identify vault managers and their addresses * **Historical analysis**: Track when vaults were created * **Vault hierarchies**: Map complex vault structures with parent-child relationships * **System overview**: Get a complete picture of all vaults in the ecosystem * **Integration points**: Feed vault data into portfolio management systems * **Monitoring systems**: Track new vault deployments and closures * **Data aggregation**: Collect comprehensive vault metadata for analysis This endpoint provides the foundational data needed to understand the complete vault ecosystem on Hyperliquid, serving as the primary discovery mechanism for all available vaults. # webData2 | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-web-data2 openapi/hyperliquid_node_api/hypercore_info/info_web_data2.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "webData2" returns aggregate information about a user for frontend consumption. Hyperliquid info via Chainstack. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "webData2"` returns aggregate information about a user for frontend consumption. It is primarily a convenience aggregation used by web clients. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"webData2"`. * `user` (string, required) — Address in 42-character hexadecimal format (e.g., `0x31ca8395cf837de08b24da3f660e77761dfb974b`). ## Response The response is a large object with user‑centric aggregates for frontend use (exact fields may change). Typical categories include: * user overview and balances * open orders and positions summaries * recent fills and funding * selected UI/runtime configuration for the client ### Understanding web data aggregation **Data optimization:** * Combines multiple data sources into a single efficient endpoint * Reduces the number of API calls required for web applications * Provides pre-aggregated statistics for improved performance * Optimized for frontend consumption and display **Real-time updates:** * Market data reflects current trading conditions * Network statistics show live platform activity * UI configuration allows for dynamic feature management * Data freshness ensures accurate user experience **Frontend integration:** * Structured for easy consumption by web applications * Includes display-ready formatted data * Supports responsive UI updates and notifications * Enables comprehensive dashboard implementations **Performance benefits:** * Single endpoint reduces latency and bandwidth usage * Pre-calculated aggregations improve response times * Efficient data structure minimizes parsing overhead * Optimized for high-frequency frontend updates ## Example request ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "webData2"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # In the official Python SDK, webData2 is a WebSocket subscription, # not an HTTP info method, so the WebSocket manager must stay enabled. info = Info("YOUR_CHAINSTACK_ENDPOINT") user = "0x31ca8395cf837de08b24da3f660e77761dfb974b" info.subscribe({"type": "webData2", "user": user}, print) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import * as hl from "@nktkas/hyperliquid"; const transport = new hl.HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); const client = new hl.InfoClient({ transport }); const data = await client.webData2({ user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(data); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Use case The `info` endpoint with `type: "webData2"` is essential for applications that need to: * **Trading dashboards**: Display comprehensive market overview and statistics * **Web applications**: Provide aggregated data for frontend interfaces * **Market summaries**: Show platform-wide trading activity and metrics * **Real-time displays**: Update UI components with current market conditions * **Performance monitoring**: Track network health and user activity * **Feature management**: Control UI features based on configuration settings * **Mobile applications**: Efficiently load dashboard data with minimal requests * **Analytics interfaces**: Display aggregated platform statistics * **Status pages**: Show platform health and maintenance information * **Portfolio overviews**: Provide market context for user positions * **Trading terminals**: Display comprehensive market data in trading interfaces * **Public displays**: Show platform statistics for marketing and transparency * **API optimization**: Reduce multiple endpoint calls with single aggregated request * **Caching strategies**: Implement efficient data caching for web applications This endpoint is particularly valuable for web developers building comprehensive trading interfaces, mobile app developers optimizing for performance, dashboard creators needing aggregated platform data, and any application requiring efficient access to multiple data categories in a single API call on the Hyperliquid network. # webData3 | Hyperliquid info Source: https://docs.chainstack.com/reference/hyperliquid-info-web-data3 openapi/hyperliquid_node_api/hypercore_info/info_web_data3.json post /4f8d8f4040bdacd1577bff8058438274/info The info endpoint with type: "webData3" retrieves a comprehensive user state summary including account state, perp dex states, and metadata. > [!NOTE] > > This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see [Hyperliquid methods](/docs/hyperliquid-methods) for the full availability breakdown. The `info` endpoint with `type: "webData3"` retrieves a comprehensive user state summary including account state, perp dex states, and metadata. This is a combined endpoint that returns data useful for rendering trading interfaces. ## Parameters ### Request body * `type` (string, required) — The request type. Must be `"webData3"`. * `user` (string, required) — Onchain address in 42-character hexadecimal format. ## Response The response is an object containing: * `userState` (object) — User account state including agent address, cumulative ledger, server time, and vault status. * `perpDexStates` (array) — Array of perp dex state objects with total vault equity and perps at open interest cap. ## Example request The Hyperliquid SDKs do not expose a dedicated `webData3` info method, so the examples below post the request through each SDK's generic info request path. ```shell Shell theme={"system"} curl -X POST \ -H "Content-Type: application/json" \ -d '{"type": "webData3", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b"}' \ https://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/info ``` ```python Python (hyperliquid-python-sdk) theme={"system"} from hyperliquid.info import Info # Skip the WebSocket connection for a one-off request. info = Info("YOUR_CHAINSTACK_ENDPOINT", skip_ws=True) # No typed webData3 method exists, so post the raw request to /info. response = info.post( "/info", { "type": "webData3", "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b", }, ) print(response) ``` ```typescript TypeScript (@nktkas/hyperliquid) theme={"system"} import { HttpTransport } from "@nktkas/hyperliquid"; const transport = new HttpTransport({ apiUrl: "YOUR_CHAINSTACK_ENDPOINT" }); // No typed webData3 method exists, so send the raw request to the info endpoint. const response = await transport.request("info", { type: "webData3", user: "0x31ca8395cf837de08b24da3f660e77761dfb974b", }); console.log(response); ``` > [!NOTE] > > **Use your own endpoint in your code.** The code examples use a placeholder Chainstack endpoint (YOUR\_CHAINSTACK\_ENDPOINT) — replace it with your own Hyperliquid node endpoint from the [Chainstack console](https://console.chainstack.com/). The curl above uses a shared public endpoint for quick checks only; do not use it in production. ## Example response ```json theme={"system"} { "userState": { "agentAddress": null, "agentValidUntil": null, "cumLedger": "111469349.66", "serverTime": 1771204901501, "isVault": true, "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b" }, "perpDexStates": [ { "totalVaultEquity": "0.0", "perpsAtOpenInterestCap": [ "CANTO", "FTM" ] } ] } ``` ## Use case The `info` endpoint with `type: "webData3"` is useful for trading applications that need to: * Render comprehensive trading dashboards with a single API call * Display user account state alongside perp dex information * Check which perpetuals are at open interest capacity * Retrieve server time for clock synchronization # isBlockhashValid | Solana Source: https://docs.chainstack.com/reference/isblockhashvalid openapi/solana_node_api/isBlockhashValid.json POST /83e0d6661ee0488e22e167c18818cb38 The Solana isBlockhashValid method checks whether a given blockhash is still valid. Available on Solana via Chainstack JSON-RPC nodes. The Solana `isBlockhashValid` method checks whether a given blockhash is still valid. This method is useful for determining if a previously fetched blockhash can still be used for transaction submission. It helps prevent the use of expired blockhashes, which would cause transactions to fail. ## Parameters * `blockhash` — the blockhash to validate, as a base-58 encoded string. * `commitment` — (optional) the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. ## Response * `result` — a boolean indicating whether the blockhash is still valid: * `true` — the blockhash is valid and can be used for transaction submission. * `false` — the blockhash is no longer valid and should not be used. ## Use case A practical use case for `isBlockhashValid` is in transaction retry logic. When a transaction fails due to network issues, an application can use this method to check if the original blockhash is still valid before retrying the transaction. If the blockhash is no longer valid, the application knows it needs to fetch a new blockhash before retrying. # logsSubscribe | Solana Source: https://docs.chainstack.com/reference/logssubscribe-solana Subscribe to real-time Solana log messages with logsSubscribe. Receive WebSocket notifications whenever new program logs are generated on-chain. ## Parameters * `filter` - An object or string specifying the type of logs to receive: * `"all"` - Subscribe to all logs (string) * `{mentions: []}` - An array of public keys as base-58 encoded strings. Returns notifications only when the specified account or program ID is mentioned in the log. * `` - (optional) The level of commitment desired when querying state. Default is "finalized". ## Response * `result` - An integer subscription id (needed to unsubscribe) * `subscription` - An object containing log information: * `signature` - The transaction signature as base-58 encoded string * `err` - Error if transaction failed, null if transaction succeeded * `logs` - Array of string log messages ## `logsSubscribe` code examples This example subscribes to all logs from the [pump.fun program](https://solscan.io/account/6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P). > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"logsSubscribe","params":[{"mentions":["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"]},{"commitment":"finalized"}]} ``` ## Use case The `logsSubscribe` method in Solana is used to subscribe to the network and receive real-time updates on new log messages. This can be useful in various real-world scenarios, including: * Debugging and monitoring: Developers can use this method to receive instant notifications about log messages related to their applications, helping them debug issues and monitor performance in real-time. * Event tracking: dApps can utilize this method to track specific events or actions on the blockchain by subscribing to logs mentioning particular accounts or programs. * Analytics: Blockchain analytics services can use this subscription to gather real-time data about specific program executions, helping to provide insights into the usage and behavior of various Solana programs. * Notification systems: Wallet providers or other services can use this method to notify users about specific events or transactions related to their accounts. By subscribing to the `logsSubscribe` method, applications can receive instant updates about new log messages, allowing them to react quickly to events on the Solana blockchain and provide a more responsive user experience. # logsUnsubscribe | Solana Source: https://docs.chainstack.com/reference/logsunsubscribe-solana Cancel a Solana logs subscription with logsUnsubscribe. Stop receiving real-time WebSocket notifications for program log messages on the network. See also [logsSubscribe | Solana](/reference/logssubscribe-solana). ## Parameters * `integer` - The ID of the subscription that you want to cancel. ## Response * `boolean` - A boolean value indicating if the subscription was removed successfully. `true` if removed successfully, `false` if not. ## `logsUnsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection. You can use a WebSocket client library for your programming language of choice to interact with Solana's WebSocket API. ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"logsUnsubscribe","params":[0]} ``` # Accounts info | Monad Source: https://docs.chainstack.com/reference/monad-accounts-info-rpc-methods The accounts info JSON RPC methods allow developers to access account-specific information on Monad, including balances, transaction counts, and contract code. Retrieving account information from the Monad blockchain can be done by using the following methods: > [card] [eth\_getBalance](/reference/monad-getbalance) > [card] [eth\_getTransactionCount](/reference/monad-gettransactioncount) > [card] [eth\_getCode](/reference/monad-getcode) > [card] [eth\_getStorageAt](/reference/monad-getstorageat) This information can be used for: * **Balance checking** — query account balances for wallets and smart contracts. * **Nonce management** — retrieve transaction counts for proper transaction ordering. * **Contract verification** — check if an address contains smart contract code. * **DApp development** — integrate account data into decentralized applications for user dashboards and transaction management. # Blocks info | Monad Source: https://docs.chainstack.com/reference/monad-blocks-rpc-methods Monad block information JSON-RPC methods — query block details including transactions, timestamps, block height, and headers on the Monad network. Retrieving block information from the Monad blockchain can be done by using the following methods: > [card] [eth\_blockNumber](/reference/monad-getblocknumber) > [card] [eth\_getBlockByNumber](/reference/monad-getblockbynumber) > [card] [eth\_getBlockByHash](/reference/monad-getblockbyhash) > [card] [eth\_getBlockTransactionCountByHash](/reference/monad-getblocktransactioncountbyhash) > [card] [eth\_getBlockTransactionCountByNumber](/reference/monad-getblocktransactioncountbynumber) > [card] [eth\_newBlockFilter](/reference/monad-newblockfilter) This information can be used to track the state of the blockchain and for: * **Blockchain analysis** — insights into the overall activity on the network, such as the number of processed transactions and average transaction fees, can be obtained. * **Block explorers** — web-based tools that provide access to information on a blockchain, including block data, transactions, and addresses, allowing users to search, view, and analyze the network's activity. * **Smart contract analysis** — monitoring the activity of specific smart contracts. * **DApp development** — block information can be utilized in the development of decentralized applications. # Chain info | Monad Source: https://docs.chainstack.com/reference/monad-chain-data-rpc-methods Monad chain data JSON-RPC methods — retrieve the chain ID for transaction signing, replay protection, and other chain-specific network information. Retrieving chain information from the Monad blockchain can be done by using the following methods: > [card] [eth\_chainId](/reference/monad-getchainid) > [card] [eth\_syncing](/reference/monad-syncing) This information can be used for: * **Transaction signing** — ensure transactions are signed with the correct chain ID for replay protection. * **Network identification** — verify the connected network (mainnet vs testnet). * **Multi-chain applications** — correctly route transactions in applications supporting multiple networks. # Client information | Monad Source: https://docs.chainstack.com/reference/monad-client-data-rpc-methods The client information JSON RPC methods allow developers to access node and network information on Monad, including client version and network status. Retrieving client and network information from the Monad blockchain can be done by using the following methods: > [card] [web3\_clientVersion](/reference/monad-clientversion) > [card] [net\_version](/reference/monad-netversion) > [card] [net\_listening](/reference/monad-listening) This information can be used for: * **Node monitoring** — verify client version and network connectivity status. * **Diagnostics** — troubleshoot connection issues and verify node health. * **Version management** — ensure applications are compatible with the node version. # web3_clientVersion | Monad Source: https://docs.chainstack.com/reference/monad-clientversion openapi/monad_node_api/client_info/web3_clientVersion.json POST / Monad API method that returns the current client version. This method is useful for identifying the node implementation and version you are connected to. Monad API method that returns the current client version. This method is useful for identifying the node implementation and version you are connected to. ## Parameters * `none` ## Response * `result` — a string representing the current client version. ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getClientVersion() { const clientVersion = await provider.send("web3_clientVersion", []); console.log(`Client version: ${clientVersion}`); } getClientVersion(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) client_version = web3.client_version print(f'Client version: {client_version}') ``` ## Use case A practical use case for `web3_clientVersion` is implementing compatibility checks in your application to ensure the connected node supports required features, or logging node information for debugging and monitoring purposes. # eth_createAccessList | Monad Source: https://docs.chainstack.com/reference/monad-createaccesslist openapi/monad_node_api/execute_transactions/eth_createAccessList.json POST / Monad API method that creates an EIP-2930 access list for a transaction. Reference for eth_createAccessList on Monad via Chainstack. Monad API method that creates an EIP-2930 access list for a transaction. This method returns a list of addresses and storage keys that the transaction will access, which can be used to reduce gas costs for subsequent executions. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > Monad nodes, including Archive nodes, keep state only for a rolling window of recent blocks, several days long. For a block older than that window, this method returns `-32602` `Block requested not found`. See the Monad note on [Networks](/docs/protocols-networks). ## Parameters * `object` — the transaction call object: * `from` (optional) — address the transaction is sent from * `to` — address the transaction is directed to * `gas` (optional) — gas provided for the call * `gasPrice` (optional) — gas price for the call * `value` (optional) — value sent with the call * `data` (optional) — hash of the method signature and encoded parameters * `quantity|tag` — the block number as a hexadecimal string, or block tag (`latest`, `earliest`, `pending`). ## Response * `result` — an object containing: * `accessList` — array of access list entries, each with: * `address` — the address that will be accessed * `storageKeys` — array of storage keys that will be accessed * `gasUsed` — the estimated gas used with the access list ## `eth_createAccessList` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function createAccessList() { const tx = { to: "0x...", // Contract address data: "0x..." // Encoded function call }; const result = await provider.send("eth_createAccessList", [tx, "latest"]); console.log("Access list:", result.accessList); console.log(`Estimated gas: ${parseInt(result.gasUsed, 16)}`); } createAccessList(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) tx = { 'to': '0x...', # Contract address 'data': '0x...' # Encoded function call } result = web3.eth.create_access_list(tx, 'latest') print(f'Access list: {result.accessList}') print(f'Estimated gas: {result.gasUsed}') ``` ## Use case A practical use case for `eth_createAccessList` is optimizing gas costs for complex smart contract interactions by pre-declaring the state that will be accessed, which reduces the cold access gas penalties introduced in EIP-2929. # Debug and Trace | Monad Source: https://docs.chainstack.com/reference/monad-debug-trace-rpc-methods The debug and trace JSON RPC methods allow developers to trace transaction execution and analyze contract calls on Monad with detailed step-by-step information. Tracing and debugging on the Monad blockchain can be done by using the following methods: > [card] [debug\_traceTransaction](/reference/monad-tracetransaction) > [card] [debug\_traceBlockByNumber](/reference/monad-traceblockbynumber) > [card] [debug\_traceBlockByHash](/reference/monad-traceblockbyhash) > [card] [debug\_traceCall](/reference/monad-tracecall) These methods support multiple tracers including: * **callTracer** — traces all calls made during execution * **prestateTracer** — returns the state accessed during execution * **stateDiffTracer** — returns state changes made during execution This information can be used for: * **Debugging** — understand why transactions failed or behaved unexpectedly. * **Security analysis** — analyze contract interactions and fund flows. * **Gas optimization** — identify expensive operations in contract execution. * **Forensics** — trace transaction paths for audit and compliance. # eth_estimateGas | Monad Source: https://docs.chainstack.com/reference/monad-estimategas openapi/monad_node_api/gas_data/eth_estimateGas.json POST / Monad API method that generates and returns an estimate of how much gas is necessary to allow the transaction to complete. On Monad. Monad API method that generates and returns an estimate of how much gas is necessary to allow the transaction to complete. The transaction will not be added to the blockchain. > [!NOTE] > > **Monad-specific behavior**: Does not accept EIP-4844 (blob) transaction type, as EIP-4844 is not supported on Monad. > [!NOTE] > > Monad nodes, including Archive nodes, keep state only for a rolling window of recent blocks, several days long. For a block older than that window, this method returns `-32602` `Block requested not found`. See the Monad note on [Networks](/docs/protocols-networks). ## Parameters * `object` — the transaction call object: * `from` (optional) — address the transaction is sent from * `to` — address the transaction is directed to * `gas` (optional) — gas provided for the call * `gasPrice` (optional) — gas price for the call * `value` (optional) — value sent with the call * `data` (optional) — hash of the method signature and encoded parameters ## Response * `result` — the estimated amount of gas needed for the transaction, encoded as hexadecimal. ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function estimateGas() { // Estimate gas for name() call on Wrapped Monad (WMON) contract const gasEstimate = await provider.estimateGas({ to: "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541", data: "0x06fdde03" // name() }); console.log(`Estimated gas: ${gasEstimate.toString()}`); } estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Estimate gas for name() call on Wrapped Monad (WMON) contract gas_estimate = web3.eth.estimate_gas({ 'to': '0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541', 'data': '0x06fdde03' # name() }) print(f'Estimated gas: {gas_estimate}') ``` ## Use case A practical use case for `eth_estimateGas` is providing accurate gas estimates to users before they confirm transactions, preventing failed transactions due to insufficient gas. # eth_call | Monad Source: https://docs.chainstack.com/reference/monad-ethcall openapi/monad_node_api/execute_transactions/eth_call.json POST / Monad API method that executes a new message call immediately without creating a transaction on the blockchain. Monad via Chainstack. Monad API method that executes a new message call immediately without creating a transaction on the blockchain. This is useful for reading data from smart contracts or simulating transactions. > [!NOTE] > > **Monad-specific behavior**: > > * State is available only for a rolling window of recent blocks, several days long, on every node including Archive nodes. For a block older than that window, the call returns `-32602` `Block requested not found`. See the Monad note on [Networks](/docs/protocols-networks). > * Does not accept EIP-4844 (blob) transaction type, as EIP-4844 is not supported on Monad. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `object` — the transaction call object: * `from` (optional) — address the transaction is sent from * `to` — address the transaction is directed to * `gas` (optional) — gas provided for the call * `gasPrice` (optional) — gas price for the call * `value` (optional) — value sent with the call * `data` (optional) — hash of the method signature and encoded parameters * `quantity or tag` — integer block number, or the string `latest`, `earliest`, or `pending` ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `result` — the return value of the executed contract call. ## `eth_call` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function callContract() { // Call name() on Wrapped Monad (WMON) contract const result = await provider.call({ to: "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541", data: "0x06fdde03" // name() }); console.log(result); } callContract(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Call name() on Wrapped Monad (WMON) contract result = web3.eth.call({ 'to': '0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541', 'data': '0x06fdde03' # name() }) print(result.hex()) ``` ## Use case A practical use case for `eth_call` is reading data from smart contracts, such as token balances, contract state variables, or simulating complex transactions before sending them. # Executing transactions | Monad Source: https://docs.chainstack.com/reference/monad-execution-rpc-methods Monad transaction execution JSON-RPC methods — call smart contracts, simulate transactions, send raw transactions, and estimate gas on the Monad network. Executing and simulating transactions on the Monad blockchain can be done by using the following methods: > [card] [eth\_call](/reference/monad-ethcall) > [card] [eth\_sendRawTransaction](/reference/monad-sendrawtransaction) > [card] [eth\_createAccessList](/reference/monad-createaccesslist) > [card] [eth\_simulateV1](/reference/monad-simulatev1) These methods can be used for: * **Smart contract interaction** — execute read-only calls to smart contracts without creating a transaction on the blockchain. * **Transaction simulation** — test transaction execution before broadcasting to ensure expected behavior. * **Transaction broadcasting** — send signed transactions to the network for processing and inclusion in blocks. * **DApp development** — integrate transaction execution capabilities into decentralized applications. # eth_feeHistory | Monad Source: https://docs.chainstack.com/reference/monad-feehistory openapi/monad_node_api/gas_data/eth_feeHistory.json POST / Monad API method that returns historical gas information, allowing you to track trends over time. Monad API method that returns historical gas information. Monad API method that returns historical gas information, allowing you to track trends over time. This method is essential for building sophisticated gas estimation strategies based on recent network conditions. > [!NOTE] > > **Monad-specific behavior**: Currently returns default values. This is temporary and will be updated with actual historical data in the future. ## Parameters * `blockCount` — the number of blocks in the requested range (1 to 1024). * `newestBlock` — the highest block of the requested range as a hexadecimal string or block tag. * `rewardPercentiles` — an array of floating point values between 0 and 100 to sample the effective priority fees. ## Response * `result` — an object containing: * `oldestBlock` — the oldest block in the range * `baseFeePerGas` — array of base fees per gas for each block * `gasUsedRatio` — array of gas used ratios for each block * `reward` — array of arrays containing the requested percentile values of effective priority fees ## `eth_feeHistory` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getFeeHistory() { const feeHistory = await provider.send("eth_feeHistory", ["0x4", "latest", [25, 50, 75]]); console.log(`Oldest block: ${parseInt(feeHistory.oldestBlock, 16)}`); console.log("Base fees:", feeHistory.baseFeePerGas.map(fee => ethers.formatUnits(fee, "gwei") + " gwei")); console.log("Gas used ratios:", feeHistory.gasUsedRatio); } getFeeHistory(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) fee_history = web3.eth.fee_history(4, 'latest', [25, 50, 75]) print(f'Oldest block: {fee_history.oldestBlock}') print(f'Base fees: {[web3.from_wei(fee, "gwei") for fee in fee_history.baseFeePerGas]} gwei') print(f'Gas used ratios: {fee_history.gasUsedRatio}') ``` ## Use case A practical use case for `eth_feeHistory` is building a gas price oracle that analyzes historical trends to predict optimal gas prices, or creating dashboards that visualize network congestion over time. # Gas data | Monad Source: https://docs.chainstack.com/reference/monad-gas-data-rpc-methods The gas data JSON RPC methods allow developers to access gas-related information on Monad, including current gas prices and gas estimation for transactions. Retrieving gas data from the Monad blockchain can be done by using the following methods: > [card] [eth\_gasPrice](/reference/monad-getgasprice) > [card] [eth\_estimateGas](/reference/monad-estimategas) > [card] [eth\_maxPriorityFeePerGas](/reference/monad-maxpriorityfeepergas) > [card] [eth\_feeHistory](/reference/monad-feehistory) This information can be used for: * **Transaction cost estimation** — calculate the expected cost of transactions before submission. * **Gas price monitoring** — track current network gas prices for optimal transaction timing. * **DApp UX optimization** — provide users with accurate fee estimates in decentralized applications. * **Budget management** — ensure sufficient funds for transaction execution. # eth_getBalance | Monad Source: https://docs.chainstack.com/reference/monad-getbalance openapi/monad_node_api/accounts_info/eth_getBalance.json POST / Monad API method that returns the balance of the account at the given address. The balance is returned in wei, the smallest unit of MON. Monad API method that returns the balance of the account at the given address. The balance is returned in wei, the smallest unit of MON. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > Monad nodes, including Archive nodes, keep state only for a rolling window of recent blocks, several days long. For a block older than that window, this method returns `-32602` `Block requested not found`. See the Monad note on [Networks](/docs/protocols-networks). ## Parameters * `data` — the 20-byte address to check for balance. * `quantity or tag` — integer block number, or the string `latest`, `earliest`, or `pending`. ## Response * `result` — the balance of the account in wei, encoded as hexadecimal. ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getBalance() { const address = "0x0000000000000000000000000000000000001000"; const balance = await provider.getBalance(address); console.log(`Balance: ${ethers.formatEther(balance)} MON`); } getBalance(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) address = "0x0000000000000000000000000000000000001000" balance = web3.eth.get_balance(address) print(f'Balance: {web3.from_wei(balance, "ether")} MON') ``` ## Use case A practical use case for `eth_getBalance` is displaying user wallet balances in DApps or checking if an account has sufficient funds before sending a transaction. # eth_getBlockByHash | Monad Source: https://docs.chainstack.com/reference/monad-getblockbyhash openapi/monad_node_api/blocks_info/eth_getBlockByHash.json POST / Monad API method that returns information about a block by block hash. Monad developers can call eth_getBlockByHash via Chainstack. Monad API method that returns information about a block by block hash. This method allows you to retrieve detailed information about a specific block using its unique hash identifier. ## Parameters * `data` — the 32-byte hash of the block. * `boolean` — if `true`, returns the full transaction objects; if `false`, returns only the hashes of the transactions. ## Response * `result` — the block object, or `null` when no block was found. See [eth\_getBlockByNumber](/reference/monad-getblockbynumber) for the full response structure. ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getBlockByHash() { const blockHash = "0x936a2bd4e1248949fe93cc4ee34a3a19a5e77a823788e40610d953ad6bc44c2a"; const block = await provider.getBlock(blockHash); console.log(block); } getBlockByHash(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0x936a2bd4e1248949fe93cc4ee34a3a19a5e77a823788e40610d953ad6bc44c2a" block = web3.eth.get_block(block_hash) print(block) ``` ## Use case A practical use case for `eth_getBlockByHash` is verifying block data when you have a specific block hash from a transaction receipt or external source. # eth_getBlockByNumber | Monad Source: https://docs.chainstack.com/reference/monad-getblockbynumber openapi/monad_node_api/blocks_info/eth_getBlockByNumber.json POST / Monad API method that returns information about a block by block number. This method allows you to retrieve detailed information about a specific block. Monad API method that returns information about a block by block number. This method allows you to retrieve detailed information about a specific block, including its transactions. ## Parameters * `quantity or tag` — the integer of a block number encoded as hexadecimal, or the string `latest`, `earliest`, or `pending`. * `boolean` — if `true`, returns the full transaction objects; if `false`, returns only the hashes of the transactions. ## Response * `result` — the block object, or `null` when no block was found: * `number` — the block number * `hash` — the block hash * `parentHash` — hash of the parent block * `nonce` — hash of the proof-of-work * `sha3Uncles` — SHA3 of the uncles data * `logsBloom` — the bloom filter for the logs * `transactionsRoot` — the root of the transaction trie * `stateRoot` — the root of the final state trie * `receiptsRoot` — the root of the receipts trie * `miner` — the address of the beneficiary * `difficulty` — integer of the difficulty * `totalDifficulty` — total difficulty of the chain until this block * `extraData` — extra data field * `size` — size of this block in bytes * `gasLimit` — maximum gas allowed in this block * `gasUsed` — total gas used by all transactions * `timestamp` — the unix timestamp for when the block was collated * `transactions` — array of transaction objects or hashes * `uncles` — array of uncle hashes ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getBlock() { const block = await provider.getBlock("latest"); console.log(block); } getBlock(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block = web3.eth.get_block('latest') print(block) ``` ## Use case A practical use case for `eth_getBlockByNumber` is retrieving block data for analysis, such as examining transaction volume, gas usage patterns, or timestamps for specific blocks. # eth_blockNumber | Monad Source: https://docs.chainstack.com/reference/monad-getblocknumber openapi/monad_node_api/blocks_info/eth_blockNumber.json POST / Monad API method that returns the latest block number of the blockchain. Monad developers can call eth_blockNumber via Chainstack. Monad API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number, encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getBlockNumber() { const blockNumber = await provider.getBlockNumber(); console.log(`Latest block: ${blockNumber}`); } getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.block_number) ``` ## Use case A practical use case for `eth_blockNumber` is tracking the current state of the blockchain. Applications can use the current block number to determine time-sensitive operations, such as calculating when a time-locked contract will become executable or monitoring network progress. # eth_getBlockReceipts | Monad Source: https://docs.chainstack.com/reference/monad-getblockreceipts openapi/monad_node_api/transaction_info/eth_getBlockReceipts.json POST / Monad API method that returns all transaction receipts for a given block. Reference for eth_getBlockReceipts on Monad via Chainstack. Monad API method that returns all transaction receipts for a given block. This method is more efficient than calling `eth_getTransactionReceipt` for each transaction individually when you need all receipts from a block. ## Parameters * `quantity|tag` — the block number as a hexadecimal string, or one of the following block tags: * `latest` — the most recent block in the canonical chain * `earliest` — the genesis block * `pending` — the pending state/transactions ## Response * `result` — an array of transaction receipt objects, each containing: * `transactionHash` — hash of the transaction * `transactionIndex` — index position of the transaction * `blockHash` — hash of the block containing this transaction * `blockNumber` — number of the block containing this transaction * `from` — address of the sender * `to` — address of the receiver * `cumulativeGasUsed` — total gas used in the block up to this transaction * `gasUsed` — gas used by this transaction * `contractAddress` — contract address created, if any * `logs` — array of log objects generated by this transaction * `logsBloom` — bloom filter for the logs * `status` — `1` (success) or `0` (failure) ## `eth_getBlockReceipts` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getBlockReceipts() { const blockNumber = "latest"; // Or use hex like "0x1234" const receipts = await provider.send("eth_getBlockReceipts", [blockNumber]); console.log(`Found ${receipts.length} receipts`); receipts.forEach((receipt, i) => { console.log(`Transaction ${i}: ${receipt.transactionHash}, Gas used: ${parseInt(receipt.gasUsed, 16)}`); }); } getBlockReceipts(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Using raw RPC call receipts = web3.provider.make_request('eth_getBlockReceipts', ['latest']) for receipt in receipts['result']: print(f"Transaction: {receipt['transactionHash']}, Gas used: {int(receipt['gasUsed'], 16)}") ``` ## Use case A practical use case for `eth_getBlockReceipts` is building analytics tools that need to process all transaction outcomes in a block, such as calculating total gas usage, counting successful vs. failed transactions, or extracting all events emitted in a block. # eth_getBlockTransactionCountByHash | Monad Source: https://docs.chainstack.com/reference/monad-getblocktransactioncountbyhash openapi/monad_node_api/blocks_info/eth_getBlockTransactionCountByHash.json POST / Monad API method that returns the number of transactions in a block matching the given block hash. Use it on Monad via Chainstack. Monad API method that returns the number of transactions in a block matching the given block hash. This method is useful for quickly determining the transaction count without retrieving the entire block data. ## Parameters * `data` — the 32-byte hash of the block. ## Response * `result` — the number of transactions in the block, encoded as hexadecimal. ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getBlockTransactionCount() { const blockHash = "0x936a2bd4e1248949fe93cc4ee34a3a19a5e77a823788e40610d953ad6bc44c2a"; const count = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); console.log(`Transaction count: ${parseInt(count, 16)}`); } getBlockTransactionCount(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0x936a2bd4e1248949fe93cc4ee34a3a19a5e77a823788e40610d953ad6bc44c2a" count = web3.eth.get_block_transaction_count(block_hash) print(f'Transaction count: {count}') ``` ## Use case A practical use case for `eth_getBlockTransactionCountByHash` is monitoring network activity by tracking transaction counts in specific blocks, or validating block data integrity when you have a known block hash. # eth_getBlockTransactionCountByNumber | Monad Source: https://docs.chainstack.com/reference/monad-getblocktransactioncountbynumber openapi/monad_node_api/blocks_info/eth_getBlockTransactionCountByNumber.json POST / Monad API method that returns the number of transactions in a block matching the given block number. Use it on Monad via Chainstack. Monad API method that returns the number of transactions in a block matching the given block number. This method allows you to quickly check the transaction count for any block by its number. ## Parameters * `quantity|tag` — the block number as a hexadecimal string, or one of the following block tags: * `latest` — the most recent block in the canonical chain * `earliest` — the genesis block * `pending` — the pending state/transactions ## Response * `result` — the number of transactions in the block, encoded as hexadecimal. ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getBlockTransactionCount() { const blockNumber = "latest"; // Or use hex like "0x1234" const count = await provider.send("eth_getBlockTransactionCountByNumber", [blockNumber]); console.log(`Transaction count: ${parseInt(count, 16)}`); } getBlockTransactionCount(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) count = web3.eth.get_block_transaction_count('latest') print(f'Transaction count: {count}') ``` ## Use case A practical use case for `eth_getBlockTransactionCountByNumber` is analyzing network throughput over time by sampling transaction counts across multiple blocks, which helps understand network congestion patterns. # eth_chainId | Monad Source: https://docs.chainstack.com/reference/monad-getchainid openapi/monad_node_api/chain_info/eth_chainId.json POST / Monad API method that returns the chain ID of the current network. eth_chainId JSON-RPC method available on the Monad blockchain via Chainstack. Monad API method that returns the chain ID of the current network. The chain ID is used for transaction signing to prevent replay attacks across different networks. ## Parameters * `none` ## Response * `result` — the current chain ID as a hexadecimal string: * Monad Mainnet: `0x8f` (143) * Monad Testnet: `0x279f` (10143) ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getChainId() { const network = await provider.getNetwork(); console.log(`Chain ID: ${network.chainId}`); } getChainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) chain_id = web3.eth.chain_id print(f'Chain ID: {chain_id}') ``` ## Use case A practical use case for `eth_chainId` is verifying that your application is connected to the correct network before sending transactions, which is essential for preventing accidental transactions on the wrong network. # eth_getCode | Monad Source: https://docs.chainstack.com/reference/monad-getcode openapi/monad_node_api/accounts_info/eth_getCode.json POST / Monad API method that returns the code at a given address. This is used to check if an address is a smart contract and retrieve its bytecode. Monad API method that returns the code at a given address. This is used to check if an address is a smart contract and retrieve its bytecode. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > Monad nodes, including Archive nodes, keep state only for a rolling window of recent blocks, several days long. For a block older than that window, this method returns `-32602` `Block requested not found`. See the Monad note on [Networks](/docs/protocols-networks). ## Parameters * `data` — the 20-byte address. * `quantity or tag` — integer block number, or the string `latest`, `earliest`, or `pending`. ## Response * `result` — the code from the given address. Returns `0x` for externally owned accounts (EOAs). ## `eth_getCode` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getCode() { const address = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541"; const code = await provider.getCode(address); if (code === "0x") { console.log("Address is an EOA (not a contract)"); } else { console.log(`Contract bytecode: ${code}`); } } getCode(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) address = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541" code = web3.eth.get_code(address) if code == b'': print("Address is an EOA (not a contract)") else: print(f'Contract bytecode: {code.hex()}') ``` ## Use case A practical use case for `eth_getCode` is verifying whether an address is a smart contract before interacting with it, which helps prevent errors and potential security issues. # eth_gasPrice | Monad Source: https://docs.chainstack.com/reference/monad-getgasprice openapi/monad_node_api/gas_data/eth_gasPrice.json POST / Monad API method that returns the current gas price in wei. This value is determined by the network and represents the median gas price of recent blocks. Monad API method that returns the current gas price in wei. This value is determined by the network and represents the median gas price of recent blocks. ## Parameters * `none` ## Response * `result` — the current gas price in wei, encoded as hexadecimal. ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getGasPrice() { const feeData = await provider.getFeeData(); console.log(`Gas price: ${ethers.formatUnits(feeData.gasPrice, "gwei")} gwei`); } getGasPrice(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) gas_price = web3.eth.gas_price print(f'Gas price: {web3.from_wei(gas_price, "gwei")} gwei') ``` ## Use case A practical use case for `eth_gasPrice` is calculating transaction costs before sending transactions, allowing users to see estimated fees in their wallet or DApp interface. # eth_getLogs | Monad Source: https://docs.chainstack.com/reference/monad-getlogs openapi/monad_node_api/logs_and_events/eth_getLogs.json POST / Monad API method that returns an array of all logs matching a given filter object. This is essential for tracking events emitted by smart contracts. Monad API method that returns an array of all logs matching a given filter object. This is essential for tracking events emitted by smart contracts. > [!WARNING] > > **Monad-specific behavior**: `eth_getLogs` has a maximum block range (typically 1000 blocks). Because Monad blocks are much larger than Ethereum blocks, use small block ranges (1-10 blocks) for optimal performance. Requests with longer ranges can take a long time or time out. ## Parameters * `object` — the filter options: * `fromBlock` (optional) — integer block number, or `latest`, `earliest`, `pending` * `toBlock` (optional) — integer block number, or `latest`, `earliest`, `pending` * `address` (optional) — contract address or list of addresses from which logs should originate * `topics` (optional) — array of 32-byte data topics * `blockhash` (optional) — restricts logs to a single block with the given hash ## Response * `result` — array of log objects, or an empty array if nothing has changed: * `removed` — `true` when the log was removed due to chain reorganization * `logIndex` — integer of the log index position in the block * `transactionIndex` — integer of the transaction's index position * `transactionHash` — hash of the transaction that generated this log * `blockHash` — hash of the block containing this log * `blockNumber` — block number containing this log * `address` — address from which this log originated * `data` — non-indexed arguments of the log * `topics` — array of indexed log arguments ## `eth_getLogs` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getLogs() { const logs = await provider.getLogs({ fromBlock: "latest", toBlock: "latest", address: "0x..." // Optional: filter by contract address }); console.log(logs); } getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) logs = web3.eth.get_logs({ 'fromBlock': 'latest', 'toBlock': 'latest', 'address': '0x...' # Optional: filter by contract address }) for log in logs: print(log) ``` ## Use case A practical use case for `eth_getLogs` is tracking specific events from smart contracts, such as token transfers, swaps, or any custom events defined in your contracts. # eth_getStorageAt | Monad Source: https://docs.chainstack.com/reference/monad-getstorageat openapi/monad_node_api/accounts_info/eth_getStorageAt.json POST / Monad API method that returns the value from a storage position at a given address. Available on Monad via Chainstack JSON-RPC nodes. Monad API method that returns the value from a storage position at a given address. This method allows you to read the raw storage of smart contracts, which is useful for debugging and analyzing contract state. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > Monad nodes, including Archive nodes, keep state only for a rolling window of recent blocks, several days long. For a block older than that window, this method returns `-32602` `Block requested not found`. See the Monad note on [Networks](/docs/protocols-networks). ## Parameters * `data` — the 20-byte address of the storage. * `quantity` — the position in the storage as a hexadecimal string. * `quantity|tag` — the block number as a hexadecimal string, or one of the following block tags: * `latest` — the most recent block in the canonical chain * `earliest` — the genesis block * `pending` — the pending state/transactions ## Response * `result` — the value at this storage position, encoded as a 32-byte hexadecimal string. ## `eth_getStorageAt` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getStorageAt() { const contractAddress = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541"; const position = "0x0"; // Storage slot 0 const value = await provider.getStorage(contractAddress, position); console.log(`Storage value at position 0: ${value}`); } getStorageAt(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) contract_address = "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541" value = web3.eth.get_storage_at(contract_address, 0) print(f'Storage value at position 0: {value.hex()}') ``` ## Use case A practical use case for `eth_getStorageAt` is reading private or internal state variables from smart contracts that are not exposed through public getter functions, or verifying that a contract's storage layout matches expected values during audits. # Monad API reference: high-throughput EVM JSON-RPC Source: https://docs.chainstack.com/reference/monad-getting-started Use the Monad JSON-RPC API on Chainstack to deploy nodes, send transactions, and build on the high-throughput EVM-compatible layer-1 with MonadBFT. ## What is the Monad protocol? Monad is a high-performance EVM-compatible Layer 1 blockchain designed for massive scalability. It achieves up to 10,000 transactions per second with 1-second block times and near-instant finality through innovations like parallel execution, optimistic concurrency, and the MonadBFT consensus mechanism. Monad is fully compatible with existing Ethereum tooling at the bytecode level, so Solidity contracts, developer tools, and libraries work without modification. Find useful Monad tools in the [Monad tooling](/docs/monad-tooling) section. ## What is the Monad API? The Monad API allows developers to communicate with the Monad blockchain to build applications. Since Monad is EVM-compatible, it uses the standard Ethereum JSON-RPC API. An application must connect to a Monad RPC node to read data from and send transactions to the Monad blockchain. When communicating with a Monad RPC node, the client implements a JSON-RPC specification, a communication protocol allowing one to make remote calls and execute them as if they were made locally. ## How to start using the Monad API You need access to a Monad RPC node to use the Monad API. Follow these steps to sign up on Chainstack, deploy a Monad RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now, you can connect to the Monad blockchain and use the Monad API to build. ## Network information | Network | Chain ID | Native token | | - | - | - | | Mainnet | 143 | MON | | Testnet | 10143 | MON | > [!NOTE] > > **See also** > > [Build better with Monad](https://chainstack.com/build-better-with-monad/) # eth_getTransactionByBlockHashAndIndex | Monad Source: https://docs.chainstack.com/reference/monad-gettransactionbyblockhashandindex openapi/monad_node_api/transaction_info/eth_getTransactionByBlockHashAndIndex.json POST / Monad API method that returns information about a transaction by block hash and transaction index position. Chainstack Monad reference. Monad API method that returns information about a transaction by block hash and transaction index position. This method allows you to retrieve a specific transaction from a block when you know both the block hash and the transaction's position within that block. ## Parameters * `data` — the 32-byte hash of the block. * `quantity` — the transaction index position within the block, encoded as hexadecimal. ## Response * `result` — the transaction object, or `null` when no transaction was found: * `blockHash` — hash of the block containing this transaction * `blockNumber` — number of the block containing this transaction * `from` — address of the sender * `gas` — gas provided by the sender * `gasPrice` — gas price in wei * `hash` — hash of the transaction * `input` — the data sent along with the transaction * `nonce` — number of transactions made by the sender * `to` — address of the receiver * `transactionIndex` — integer of the transaction's index position * `value` — value transferred in wei * `v`, `r`, `s` — signature values ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getTransactionByIndex() { const blockHash = "0x936a2bd4e1248949fe93cc4ee34a3a19a5e77a823788e40610d953ad6bc44c2a"; const index = "0x0"; // First transaction const tx = await provider.send("eth_getTransactionByBlockHashAndIndex", [blockHash, index]); console.log(tx); } getTransactionByIndex(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0x936a2bd4e1248949fe93cc4ee34a3a19a5e77a823788e40610d953ad6bc44c2a" tx = web3.eth.get_transaction_by_block(block_hash, 0) print(tx) ``` ## Use case A practical use case for `eth_getTransactionByBlockHashAndIndex` is iterating through all transactions in a block when analyzing block contents, or retrieving specific transactions based on their execution order. # eth_getTransactionByBlockNumberAndIndex | Monad Source: https://docs.chainstack.com/reference/monad-gettransactionbyblocknumberandindex openapi/monad_node_api/transaction_info/eth_getTransactionByBlockNumberAndIndex.json POST / Monad API method that returns information about a transaction by block number and transaction index position. Monad via Chainstack. Monad API method that returns information about a transaction by block number and transaction index position. This method allows you to retrieve a specific transaction using the block number and the transaction's position within that block. ## Parameters * `quantity|tag` — the block number as a hexadecimal string, or one of the following block tags: * `latest` — the most recent block in the canonical chain * `earliest` — the genesis block * `pending` — the pending state/transactions * `quantity` — the transaction index position within the block, encoded as hexadecimal. ## Response * `result` — the transaction object, or `null` when no transaction was found: * `blockHash` — hash of the block containing this transaction * `blockNumber` — number of the block containing this transaction * `from` — address of the sender * `gas` — gas provided by the sender * `gasPrice` — gas price in wei * `hash` — hash of the transaction * `input` — the data sent along with the transaction * `nonce` — number of transactions made by the sender * `to` — address of the receiver * `transactionIndex` — integer of the transaction's index position * `value` — value transferred in wei * `v`, `r`, `s` — signature values ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getTransactionByIndex() { const blockNumber = "latest"; // Or use hex like "0x1234" const index = "0x0"; // First transaction const tx = await provider.send("eth_getTransactionByBlockNumberAndIndex", [blockNumber, index]); console.log(tx); } getTransactionByIndex(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) tx = web3.eth.get_transaction_by_block('latest', 0) print(tx) ``` ## Use case A practical use case for `eth_getTransactionByBlockNumberAndIndex` is building block explorers that allow users to browse transactions sequentially within blocks, or analyzing the first or last transactions in recent blocks. # eth_getTransactionByHash | Monad Source: https://docs.chainstack.com/reference/monad-gettransactionbyhash openapi/monad_node_api/transaction_info/eth_getTransactionByHash.json POST / Monad API method that returns information about a transaction by transaction hash. eth_getTransactionByHash on Monad via Chainstack. Monad API method that returns information about a transaction by transaction hash. This method allows you to retrieve detailed information about a specific transaction. > [!NOTE] > > **Monad-specific behavior**: This method does not return pending transactions. It will only return transactions that have been included in a block. If you query for a transaction that is still in the mempool, the method will return `null`. ## Parameters * `data` — the 32-byte hash of the transaction. ## Response * `result` — the transaction object, or `null` when no transaction was found: * `blockHash` — hash of the block containing this transaction * `blockNumber` — number of the block containing this transaction * `from` — address of the sender * `gas` — gas provided by the sender * `gasPrice` — gas price in wei * `hash` — hash of the transaction * `input` — the data sent along with the transaction * `nonce` — number of transactions made by the sender * `to` — address of the receiver * `transactionIndex` — integer of the transaction's index position * `value` — value transferred in wei * `v`, `r`, `s` — signature values ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getTransaction() { const txHash = "0x8e00e28d3a51bcae537f7dd2dbeed7678ab67f5a63a0c3b3962c5c804fd98047"; const tx = await provider.getTransaction(txHash); console.log(tx); } getTransaction(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) tx_hash = "0x8e00e28d3a51bcae537f7dd2dbeed7678ab67f5a63a0c3b3962c5c804fd98047" tx = web3.eth.get_transaction(tx_hash) print(tx) ``` ## Use case A practical use case for `eth_getTransactionByHash` is retrieving transaction details for display in a DApp or for verifying transaction parameters. # eth_getTransactionCount | Monad Source: https://docs.chainstack.com/reference/monad-gettransactioncount openapi/monad_node_api/accounts_info/eth_getTransactionCount.json POST / Monad API method that returns the number of transactions sent from an address. Reference for eth_getTransactionCount on Monad via Chainstack. Monad API method that returns the number of transactions sent from an address. This is also known as the account nonce, which is used to prevent transaction replay and ensure proper transaction ordering. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > Monad nodes, including Archive nodes, keep state only for a rolling window of recent blocks, several days long. For a block older than that window, this method returns `-32602` `Block requested not found`. See the Monad note on [Networks](/docs/protocols-networks). ## Parameters * `data` — the 20-byte address. * `quantity or tag` — integer block number, or the string `latest`, `earliest`, or `pending`. ## Response * `result` — the number of transactions sent from the address, encoded as hexadecimal. ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getTransactionCount() { const address = "0x45f106ee4b44d0fe347faca381ee35d3300a8473"; const nonce = await provider.getTransactionCount(address); console.log(`Transaction count: ${nonce}`); } getTransactionCount(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) address = "0x45f106ee4b44d0fe347faca381ee35d3300a8473" nonce = web3.eth.get_transaction_count(address) print(f'Transaction count: {nonce}') ``` ## Use case A practical use case for `eth_getTransactionCount` is obtaining the correct nonce when manually constructing transactions, ensuring they are processed in the correct order. # eth_getTransactionReceipt | Monad Source: https://docs.chainstack.com/reference/monad-gettransactionreceipt openapi/monad_node_api/transaction_info/eth_getTransactionReceipt.json POST / Monad API method that returns the receipt of a transaction by transaction hash. eth_getTransactionReceipt on Monad via Chainstack. Monad API method that returns the receipt of a transaction by transaction hash. Transaction receipts contain information about the execution of a transaction, including gas used, logs emitted, and status. ## Parameters * `data` — the 32-byte hash of the transaction. ## Response * `result` — the transaction receipt object, or `null` when no receipt was found: * `transactionHash` — hash of the transaction * `transactionIndex` — integer of the transaction's index position * `blockHash` — hash of the block containing this transaction * `blockNumber` — number of the block containing this transaction * `from` — address of the sender * `to` — address of the receiver * `cumulativeGasUsed` — total gas used when this transaction was executed * `gasUsed` — gas used by this specific transaction * `contractAddress` — address of the created contract (if any) * `logs` — array of log objects * `logsBloom` — bloom filter for light clients * `status` — `1` (success) or `0` (failure) ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getReceipt() { const txHash = "0x8e00e28d3a51bcae537f7dd2dbeed7678ab67f5a63a0c3b3962c5c804fd98047"; const receipt = await provider.getTransactionReceipt(txHash); console.log(receipt); } getReceipt(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) tx_hash = "0x8e00e28d3a51bcae537f7dd2dbeed7678ab67f5a63a0c3b3962c5c804fd98047" receipt = web3.eth.get_transaction_receipt(tx_hash) print(receipt) ``` ## Use case A practical use case for `eth_getTransactionReceipt` is verifying transaction success and extracting event logs emitted during execution. # net_listening | Monad Source: https://docs.chainstack.com/reference/monad-listening openapi/monad_node_api/client_info/net_listening.json POST / Monad API method that returns true if the client is actively listening for network connections. Available on Monad via Chainstack. Monad API method that returns `true` if the client is actively listening for network connections. This method is useful for checking the node's network connectivity status. ## Parameters * `none` ## Response * `result` — `true` if the client is listening for connections, `false` otherwise. ## `net_listening` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function checkListening() { const isListening = await provider.send("net_listening", []); console.log(`Node is listening: ${isListening}`); } checkListening(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) is_listening = web3.net.listening print(f'Node is listening: {is_listening}') ``` ## Use case A practical use case for `net_listening` is implementing health checks in your infrastructure to verify that nodes are properly connected to the network and able to receive peer connections. # Logs & events | Monad Source: https://docs.chainstack.com/reference/monad-logs-rpc-methods Monad logs and events JSON-RPC methods — query event logs emitted by smart contracts to track state changes and monitor on-chain contract activity. Retrieving logs and events from the Monad blockchain can be done by using the following methods: > [card] [eth\_getLogs](/reference/monad-getlogs) For real-time log streaming, use `eth_subscribe("logs", {...})` over a WebSocket connection. See the [event-monitoring tutorial](/docs/monad-tutorial-event-monitoring) for end-to-end examples in JavaScript and Python. This information can be used for: * **Event monitoring** — track specific events emitted by smart contracts. * **Historical data analysis** — query past events for analytics and reporting. * **DApp state synchronization** — keep application state in sync with on-chain events. * **Indexing services** — build off-chain indexes of on-chain data for faster querying. # eth_maxPriorityFeePerGas | Monad Source: https://docs.chainstack.com/reference/monad-maxpriorityfeepergas openapi/monad_node_api/gas_data/eth_maxPriorityFeePerGas.json POST / Monad API method that returns a suggestion for the max priority fee per gas (tip) to use in EIP-1559 transactions. Monad via Chainstack. Monad API method that returns a suggestion for the max priority fee per gas (tip) to use in EIP-1559 transactions. This method helps you determine an appropriate tip to incentivize validators to include your transaction. > [!NOTE] > > **Monad-specific behavior**: Currently returns a hardcoded suggested fee of 2 gwei. This is temporary and will be updated with dynamic fee estimation in the future. ## Parameters * `none` ## Response * `result` — the suggested max priority fee per gas in wei, encoded as hexadecimal. ## `eth_maxPriorityFeePerGas` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getMaxPriorityFee() { const maxPriorityFee = await provider.send("eth_maxPriorityFeePerGas", []); console.log(`Max priority fee: ${parseInt(maxPriorityFee, 16)} wei`); console.log(`Max priority fee: ${ethers.formatUnits(maxPriorityFee, "gwei")} gwei`); } getMaxPriorityFee(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) max_priority_fee = web3.eth.max_priority_fee print(f'Max priority fee: {max_priority_fee} wei') print(f'Max priority fee: {web3.from_wei(max_priority_fee, "gwei")} gwei') ``` ## Use case A practical use case for `eth_maxPriorityFeePerGas` is dynamically setting the priority fee for EIP-1559 transactions to ensure competitive inclusion times while avoiding overpaying during low-congestion periods. # net_version | Monad Source: https://docs.chainstack.com/reference/monad-netversion openapi/monad_node_api/client_info/net_version.json POST / Monad API method that returns the current network ID. net_version JSON-RPC method available on the Monad blockchain via Chainstack. Monad API method that returns the current network ID. This method helps identify which network the node is connected to, which is important for ensuring transactions are signed for the correct network. ## Parameters * `none` ## Response * `result` — a string representing the current network ID. ## `net_version` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function getNetworkVersion() { const networkVersion = await provider.send("net_version", []); console.log(`Network ID: ${networkVersion}`); } getNetworkVersion(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) network_id = web3.net.version print(f'Network ID: {network_id}') ``` ## Use case A practical use case for `net_version` is validating that your application is connected to the expected network before sending transactions, preventing accidental transactions on the wrong network. # eth_newBlockFilter | Monad Source: https://docs.chainstack.com/reference/monad-newblockfilter openapi/monad_node_api/blocks_info/eth_newBlockFilter.json POST / Monad API method that creates a filter in the node to notify when a new block arrives. Use eth_getFilterChanges to poll for new blocks. Monad API method that creates a filter in the node to notify when a new block arrives. Use `eth_getFilterChanges` to poll for new blocks. > [!WARNING] > > Monad does not implement `eth_newBlockFilter` or the other filter methods. Chainstack nodes and Monad's public RPC both return `-32601` `Method not found`. To receive new blocks, subscribe with `eth_subscribe("newHeads")` or `eth_subscribe("monadNewHeads")` over WebSocket — see [monadNewHeads](/reference/monad-subscribe-monadnewheads). ## Parameters * `none` ## Response * `result` — the filter ID, used to poll for new blocks with `eth_getFilterChanges`. ## `eth_newBlockFilter` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function createBlockFilter() { const filterId = await provider.send("eth_newBlockFilter", []); console.log("Filter ID:", filterId); // Poll for new blocks setInterval(async () => { const changes = await provider.send("eth_getFilterChanges", [filterId]); if (changes.length > 0) { console.log("New blocks:", changes); } }, 2000); } createBlockFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 import time node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Create the block filter filter_id = web3.eth.filter('latest').filter_id print(f'Filter ID: {filter_id}') # Poll for new blocks while True: changes = web3.eth.get_filter_changes(filter_id) if changes: print(f'New blocks: {changes}') time.sleep(2) ``` ## Use case A practical use case for `eth_newBlockFilter` is building applications that need to react to new blocks as they are mined, such as block explorers, monitoring tools, or HTTP-only clients that can't hold a persistent WebSocket connection. For real-time streaming, prefer `eth_subscribe("newHeads")` over WebSocket — see the [event-monitoring tutorial](/docs/monad-tutorial-event-monitoring). # eth_newPendingTransactionFilter | Monad Source: https://docs.chainstack.com/reference/monad-newpendingtransactionfilter openapi/monad_node_api/transaction_info/eth_newPendingTransactionFilter.json POST / Monad API method that creates a filter in the node to notify when new pending transactions arrive. Use it on Monad via Chainstack. Monad API method that creates a filter in the node to notify when new pending transactions arrive. Use `eth_getFilterChanges` to poll for pending transaction hashes. > [!WARNING] > > Monad does not implement `eth_newPendingTransactionFilter` or the other filter methods. Chainstack nodes and Monad's public RPC both return `-32601` `Method not found`. The `newPendingTransactions` WebSocket subscription is not available on Monad either. ## Parameters * `none` ## Response * `result` — the filter ID, used to poll for pending transactions with `eth_getFilterChanges`. ## `eth_newPendingTransactionFilter` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function createPendingTxFilter() { const filterId = await provider.send("eth_newPendingTransactionFilter", []); console.log("Filter ID:", filterId); // Poll for pending transactions setInterval(async () => { const txHashes = await provider.send("eth_getFilterChanges", [filterId]); if (txHashes.length > 0) { console.log("Pending transactions:", txHashes); } }, 1000); } createPendingTxFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 import time node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Create the pending transaction filter filter_id = web3.eth.filter('pending').filter_id print(f'Filter ID: {filter_id}') # Poll for pending transactions while True: tx_hashes = web3.eth.get_filter_changes(filter_id) if tx_hashes: print(f'Pending transactions: {[tx.hex() for tx in tx_hashes]}') time.sleep(1) ``` ## Use case A practical use case for `eth_newPendingTransactionFilter` is building mempool monitoring tools, MEV bots, or applications that need to track pending transactions before they are included in blocks. # eth_sendRawTransaction | Monad Source: https://docs.chainstack.com/reference/monad-sendrawtransaction openapi/monad_node_api/execute_transactions/eth_sendRawTransaction.json POST / Monad API method that submits a pre-signed transaction for broadcast to the network. eth_sendRawTransaction on Monad via Chainstack. Monad API method that submits a pre-signed transaction for broadcast to the network. This method is used to send signed transactions, which can transfer value or interact with smart contracts. > [!WARNING] > > This method creates a transaction that alters the state of the blockchain. It requires MON tokens to pay for gas fees. > [!NOTE] > > **Monad-specific behavior**: > > * May not immediately reject transactions with a nonce gap or insufficient gas balance. Due to asynchronous execution, the RPC server may not have the latest account state, so these transactions are initially allowed as they may become valid during block creation. > * Does not accept EIP-4844 (blob) transaction type, as EIP-4844 is not supported on Monad. ## Parameters * `data` — the signed transaction data (typically signed with a library like ethers.js or web3.py). ## Response * `result` — the 32-byte transaction hash, or the zero hash if the transaction is not yet available. ## `eth_sendRawTransaction` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); const wallet = new ethers.Wallet("PRIVATE_KEY", provider); async function sendTransaction() { const tx = await wallet.sendTransaction({ to: "0x...", // Recipient address value: ethers.parseEther("0.01") }); console.log(`Transaction hash: ${tx.hash}`); await tx.wait(); console.log("Transaction confirmed"); } sendTransaction(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Build and sign transaction tx = { 'nonce': web3.eth.get_transaction_count('YOUR_ADDRESS'), 'to': '0x...', # Recipient address 'value': web3.to_wei(0.01, 'ether'), 'gas': 21000, 'gasPrice': web3.eth.gas_price, 'chainId': 143 # Monad mainnet } signed_tx = web3.eth.account.sign_transaction(tx, 'PRIVATE_KEY') tx_hash = web3.eth.send_raw_transaction(signed_tx.raw_transaction) print(f'Transaction hash: {tx_hash.hex()}') ``` ## Use case A practical use case for `eth_sendRawTransaction` is sending MON transfers or interacting with smart contracts after signing transactions offline or in a secure environment. # eth_simulateV1 | Monad Source: https://docs.chainstack.com/reference/monad-simulatev1 openapi/monad_node_api/execute_transactions/eth_simulateV1.json POST / Monad API method that simulates a sequence of calls across one or more blocks with state and block overrides. Reference for eth_simulateV1 on Monad via Chainstack. Monad API method that simulates a sequence of calls across one or more blocks on top of the state of a chosen block, without creating transactions on the blockchain. Each simulated block can override account state and block fields, and with `traceTransfers` the result reports every native MON transfer as a `Transfer` log. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `object` — the simulation request object: * `blockStateCalls` — an array of simulated blocks, executed in order on top of the base block. Each entry contains: * `blockOverrides` (optional) — block fields to override for this simulated block, such as `number`, `time`, `gasLimit`, or `feeRecipient`. * `stateOverrides` (optional) — account state to override for the simulation, keyed by address. Each entry can set `balance`, `nonce`, `code`, `state`, or `stateDiff`. * `calls` — an array of call objects executed in sequence, each with `from`, `to`, and optionally `gas`, `value`, `data`, `nonce`, `maxFeePerGas`, and `maxPriorityFeePerGas`. * `traceTransfers` (optional) — when `true`, every native MON transfer is returned as a `Transfer` log emitted by `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`. Defaults to `false`. * `validation` (optional) — enables transaction checks, such as rejecting a `maxFeePerGas` below the block base fee. Defaults to `true`, and `false` is not supported on Monad. * `quantity or tag` — the base block: a block number in hex, a block hash, or `latest`, `safe`, `finalized`, or `pending`. ## Response * `result` — an array with one object per simulated block. Each object contains the block header fields (`number`, `hash`, `parentHash`, `timestamp`, `gasLimit`, `gasUsed`, `baseFeePerGas`, `miner`, `logsBloom`, and the trie roots), `transactions` as an array of transaction hashes, and: * `calls` — an array with one result per call: * `status` — `0x1` if the call succeeded, `0x0` if it failed. * `returnData` — the data returned by the call. * `gasUsed` — the gas charged for the call. On Monad, this value equals the gas limit of the call. * `logs` — the logs emitted by the call, including a `Transfer` log for each native MON transfer when `traceTransfers` is `true`. * `error` — present when the call failed, with a `message` describing the failure. ## Monad-specific behavior On Monad, `eth_simulateV1` behaves as follows: * `gasUsed` reports the gas limit of each call, not the gas consumed, because [Monad charges the gas limit](https://docs.monad.xyz/developer-essentials/gas-pricing) of a transaction. A call without `gas` runs with the node's default call gas limit and reports that limit as `gasUsed`. Set `gas` on each call to get a representative value. * Validation is always on. Omitting `validation` behaves as `true`, and `"validation": false` returns a `-32000` error stating that this mode is not supported yet. Calls without fee fields are accepted. * The [Monad reserve balance](https://docs.monad.xyz/developer-essentials/reserve-balance) rule applies inside the simulation. A value transfer that leaves the sender below the 10 MON reserve balance reverts with `execution reverted`, except for the sender's first transaction in the reserve window (an emptying transaction). Set the sender's `balance` in `stateOverrides` well above 10 MON when one account makes several value transfers. * `returnFullTransactions` is not supported. `transactions` always contains transaction hashes. * The base block must be one whose state the node holds. `earliest` returns the `-32602` error `Block requested not found`. ## Request limits A single `eth_simulateV1` request on Chainstack Monad nodes accepts: * Up to 256 simulated blocks in `blockStateCalls`. * Up to 2,000 calls across all simulated blocks. * Up to 1,600,000,000 gas in total, summed over the gas limits of all calls. A call without `gas` counts at the node's default call gas limit. A request over any of these limits returns a `-32000` error that states the limit, for example `Too many calls to simulate: 2001, maximum allowed is 2000`. ## Example: simulate a native MON transfer The code examples on this page simulate a transfer of 1 MON from `0x5d20879655df3c1e04ab111af0009ad650e762f0` to `0x000000000000000000000000000000000000dEaD` on top of the latest block. The `stateOverrides` entry sets the sender's balance to 100 MON (`0x56bc75e2d63100000`) for the simulation only, so the result does not depend on the account's live balance. The call sets `gas` to 21,000 (`0x5208`), the intrinsic gas of a plain MON transfer. The simulated call returns `status` `0x1`, `gasUsed` `0x5208`, and one `Transfer` log for the native MON movement: * `address` — `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`, the pseudo-address that `traceTransfers` uses for native transfers. * `topics` — the `Transfer(address,address,uint256)` event signature, then the sender and the recipient. * `data` — the amount, `1000000000000000000` wei (1 MON). ## `eth_simulateV1` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); const sender = "0x5d20879655df3c1e04ab111af0009ad650e762f0"; const recipient = "0x000000000000000000000000000000000000dEaD"; async function simulateTransfer() { const [block] = await provider.send("eth_simulateV1", [ { blockStateCalls: [ { stateOverrides: { [sender]: { balance: "0x56bc75e2d63100000" } }, // 100 MON calls: [ { from: sender, to: recipient, gas: "0x5208", // 21,000 value: "0xde0b6b3a7640000" // 1 MON } ] } ], traceTransfers: true }, "latest" ]); const [result] = block.calls; console.log(`Simulated block: ${parseInt(block.number, 16)}`); console.log(`Status: ${result.status}, gas used: ${parseInt(result.gasUsed, 16)}`); for (const log of result.logs) { console.log(`Transfer log from ${log.address}: ${BigInt(log.data)} wei`); } } simulateTransfer(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) sender = "0x5d20879655df3c1e04ab111af0009ad650e762f0" recipient = "0x000000000000000000000000000000000000dEaD" response = web3.provider.make_request("eth_simulateV1", [ { "blockStateCalls": [ { "stateOverrides": {sender: {"balance": "0x56bc75e2d63100000"}}, # 100 MON "calls": [ { "from": sender, "to": recipient, "gas": "0x5208", # 21,000 "value": "0xde0b6b3a7640000" # 1 MON } ] } ], "traceTransfers": True }, "latest" ]) block = response["result"][0] result = block["calls"][0] print(f"Simulated block: {int(block['number'], 16)}") print(f"Status: {result['status']}, gas used: {int(result['gasUsed'], 16)}") for log in result["logs"]: print(f"Transfer log from {log['address']}: {int(log['data'], 16)} wei") ``` ## Use case A practical use case for `eth_simulateV1` is previewing a multi-step flow before signing it, such as a token approval followed by a swap. The calls in one request run in sequence against shared state, so each call sees the effects of the calls before it, and `stateOverrides` funds the sender without touching a real account. # eth_subscribe monadNewHeads | Monad Source: https://docs.chainstack.com/reference/monad-subscribe-monadnewheads Subscribe to Monad block headers at the Proposed state via WebSocket — earlier than standard newHeads — with blockId and commitState for lifecycle tracking. Monad API method that streams new block headers as soon as a block reaches the `Proposed` state in Monad's consensus lifecycle — typically about one second earlier than standard `eth_subscribe("newHeads")`, which currently fires at the `Voted` state. Each notification includes two Monad-specific fields, `blockId` and `commitState`, that let you follow a block through every stage of its lifecycle: `Proposed` → `Voted` → `Finalized` → `Verified`. > [!NOTE] > > Monad's real-time data behavior, including the commit state at which standard `newHeads` fires, is still evolving. For the authoritative spec, see the Monad [WebSocket guide](https://docs.monad.xyz/reference/websockets) and [block states](https://docs.monad.xyz/monad-arch/consensus/block-states) documentation. This subscription is unique to Monad. It is available over WebSocket on Chainstack [Global Nodes](/docs/global-elastic-node) for Monad Mainnet and Testnet. ## Parameters * `string` — the subscription type, `monadNewHeads` in this case. ## Response * `subscription` — the subscription ID. Each notification delivers a block object with the following fields. Monad-specific fields: * `blockId` — a 32-byte identifier unique to this specific block proposal. A proposal with the same `number` but a different `blockId` is a different proposal. Use this to deduplicate per-proposal when the same block number is emitted multiple times as its `commitState` advances. * `commitState` — the current state of the block in Monad's commit lifecycle. One of `Proposed`, `Voted`, `Finalized`, or `Verified`. Standard block header fields: * `number` — the block number, encoded as hexadecimal. * `hash` — the block hash. * `parentHash` — hash of the previous block. * `sha3Uncles` — hash of the list of uncles included in the block. Always `0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347` on Monad. * `logsBloom` — the bloom filter for the logs of the block. * `transactionsRoot` — the root of the transaction trie of the block. * `stateRoot` — the root of the final state trie of the block. * `receiptsRoot` — the root of the receipts trie of the block. * `miner` — the address of the block proposer. * `difficulty` — always `0x0` on Monad (proof-of-stake-based consensus). * `totalDifficulty` — always `0x0` on Monad. * `extraData` — arbitrary data field set by the proposer. * `size` — the size of this block in bytes, encoded as hexadecimal. * `gasLimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasUsed` — the total gas used by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was proposed, encoded as hexadecimal. * `mixHash` — the previous RANDAO value. * `nonce` — always `0x0000000000000000` on Monad. * `baseFeePerGas` — EIP-1559 base fee per gas, encoded as hexadecimal. * `withdrawalsRoot` — Merkle root of withdrawals. * `blobGasUsed` — cumulative blob gas used, EIP-4844. * `excessBlobGas` — excess blob gas. * `parentBeaconBlockRoot` — parent beacon block root. * `requestsHash` — EIP-7685 execution requests hash. > [!NOTE] > > The same block number will appear multiple times as its `commitState` advances — typically once each for `Proposed`, `Voted`, `Finalized`, and `Verified`. Blocks may also skip intermediate states and go directly to `Finalized` when consensus outpaces execution. Failed proposals are abandoned implicitly — there is no explicit failure notification. > > Deduplicate by `blockId` if you need one event per unique proposal, or by the pair `(number, commitState)` if you care about lifecycle transitions. ## `eth_subscribe("monadNewHeads")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WSS_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["monadNewHeads"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const CHAINSTACK_WSS_ENDPOINT = 'YOUR_CHAINSTACK_WSS_ENDPOINT'; const ws = new WebSocket(CHAINSTACK_WSS_ENDPOINT); ws.on('open', () => { ws.send(JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['monadNewHeads'], })); }); ws.on('message', (raw) => { const msg = JSON.parse(raw); if (msg.method === 'eth_subscription') { const { blockId, commitState, number, hash } = msg.params.result; console.log(`block=${number} state=${commitState} hash=${hash} blockId=${blockId}`); } else { console.log(msg); } }); ``` ```python python theme={"system"} import asyncio import json import websockets CHAINSTACK_WSS_ENDPOINT = "YOUR_CHAINSTACK_WSS_ENDPOINT" async def subscribe(): async with websockets.connect(CHAINSTACK_WSS_ENDPOINT) as ws: await ws.send(json.dumps({ "id": 1, "jsonrpc": "2.0", "method": "eth_subscribe", "params": ["monadNewHeads"], })) async for raw in ws: msg = json.loads(raw) if msg.get("method") == "eth_subscription": r = msg["params"]["result"] print(f"block={r['number']} state={r['commitState']} hash={r['hash']} blockId={r['blockId']}") else: print(msg) asyncio.run(subscribe()) ``` This streams block headers for every commit-state transition of every block. Expect multiple notifications per block number. ### Example notification ```json theme={"system"} { "blockId": "0x712a75695b4fe4aa4e791d99f1720a5667d7ca941cd899c836794baaebf47b15", "commitState": "Finalized", "number": "0x3f066f5", "hash": "0xf172f316048de9fc1de260b60d5b3fc68509610250ffe1b697de80f21b505e0b", "parentHash": "0x47a93e2327ee38cb4c8f558656ccb7bd77a36cb7fb42915d6aa13deaa72c0da0", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "miner": "0xc0d840d90176302cdc9e301325d997892995f32f", "stateRoot": "0x16bbec97f83d928b061597f326c552d5fbf6073935dc6bded573ff64f04a87f7", "transactionsRoot": "0x72b8192ca5716a769aaa924dc91e7a0198d92b1c3251bc7ddf02eb12a38ed882", "receiptsRoot": "0x4bca83851a34e54dca1213108332ac23fc403c900c6f013648bd37dfe35494a8", "logsBloom": "0x00000000000000000000001000000040000200000000000000000000000000000000000001000000000000000000000000000000000000000000200000000000000000000000000000002000000000002000000000008080000000000000000000000020000014000000004000000020840000000100010000000000000000010000000800000000004000000000000000000400000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000100000000000000000000000000", "difficulty": "0x0", "totalDifficulty": "0x0", "gasLimit": "0x8f0d180", "gasUsed": "0xdb236", "timestamp": "0x6ab8d8d7", "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", "mixHash": "0xf9e8490d5fed8a46d698bfe1be9b0b60a9d1f3a932d7f860a413865cb63d905c", "nonce": "0x0000000000000000", "baseFeePerGas": "0x174876e800", "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "blobGasUsed": "0x0", "excessBlobGas": "0x0", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "requestsHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "size": "0x320" } ``` A typical stream for a single block, in order of arrival, might look like: ``` block=0x3f066f5 state=Proposed blockId=0x712a75695b4fe4aa4e791d99f1720a5667d7ca941cd899c836794baaebf47b15 block=0x3f066f5 state=Voted blockId=0x712a75695b4fe4aa4e791d99f1720a5667d7ca941cd899c836794baaebf47b15 block=0x3f066f5 state=Finalized blockId=0x712a75695b4fe4aa4e791d99f1720a5667d7ca941cd899c836794baaebf47b15 block=0x3f066f5 state=Verified blockId=0x712a75695b4fe4aa4e791d99f1720a5667d7ca941cd899c836794baaebf47b15 ``` Send `eth_unsubscribe` with the returned subscription ID to close the stream. ## `monadNewHeads` vs `newHeads` Both subscription types work on Chainstack Monad nodes. Pick based on what you optimize for. * `eth_subscribe("newHeads")` — fires once per block, at the `Voted` state. Closest to the semantics of Ethereum's `newHeads`. Use this when you want one event per canonical block and do not need visibility into earlier states. * `eth_subscribe("monadNewHeads")` — fires at every state transition, starting at `Proposed`. Gives you block data roughly one second earlier than `newHeads`, plus the `blockId` and `commitState` fields. Use this when latency matters or when you need to react to speculative state. For broader event-monitoring patterns on Monad — including `eth_subscribe("logs", {...})` and HTTP polling alternatives — see [Monad: Monitoring events and transactions](/docs/monad-tutorial-event-monitoring). ## Use case A practical use case for `eth_subscribe("monadNewHeads")` is any latency-sensitive application that benefits from acting on speculatively-executed block state before it reaches the `Voted` state: * Trading and MEV. Searchers and takers can observe new blocks at the `Proposed` state to trigger off-chain logic roughly one second sooner than they could with `newHeads`, while still reconciling against later states (`Voted`, `Finalized`) for correctness. * Pre-confirmation UX. Wallets, explorers, and dashboards can surface "tentative" block data immediately on `Proposed`, then upgrade the display as the same `blockId` advances through `Voted` → `Finalized`, giving users end-to-end visibility into Monad's commit lifecycle. * Infrastructure and consensus monitoring. Node operators and analytics platforms can track the exact cadence and distribution of block states to detect execution lag, skipped intermediate states, or abandoned proposals in real time. # eth_syncing | Monad Source: https://docs.chainstack.com/reference/monad-syncing openapi/monad_node_api/chain_info/eth_syncing.json POST / Monad API method that returns an object with data about the sync status or false if the node is fully synced. Monad via Chainstack. Monad API method that returns an object with data about the sync status or `false` if the node is fully synced. This method is useful for monitoring the synchronization progress of your node. ## Parameters * `none` ## Response * `result` — returns `false` if the node is not syncing, or an object with sync status data: * `startingBlock` — the block at which the import started * `currentBlock` — the current block being processed * `highestBlock` — the estimated highest block ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function checkSyncStatus() { const syncStatus = await provider.send("eth_syncing", []); if (syncStatus === false) { console.log("Node is fully synced"); } else { console.log(`Syncing: ${parseInt(syncStatus.currentBlock, 16)} / ${parseInt(syncStatus.highestBlock, 16)}`); } } checkSyncStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) sync_status = web3.eth.syncing if sync_status is False: print('Node is fully synced') else: print(f'Syncing: {sync_status.currentBlock} / {sync_status.highestBlock}') ``` ## Use case A practical use case for `eth_syncing` is implementing health checks in your application to ensure the node is fully synced before processing transactions or queries that require the latest chain state. # debug_traceBlockByHash | Monad Source: https://docs.chainstack.com/reference/monad-traceblockbyhash openapi/monad_node_api/debug_and_trace/debug_traceBlockByHash.json POST / Monad API method that returns traces for all transactions in a block specified by block hash. debug_traceBlockByHash on Monad via Chainstack. Monad API method that returns traces for all transactions in a block specified by block hash. This method is useful for analyzing all transaction executions within a specific block when you have its hash. > [!WARNING] > > **Monad-specific behavior**: > > * The trace options object parameter **must be explicitly provided**. Unlike standard EVM clients where this parameter is optional, Monad RPC will return an error (`-32602 Invalid params`) if the parameter is omitted. Always include the trace options, even if empty (`{}`). > * When an empty trace options object `{}` is provided, Monad defaults to `callTracer` instead of struct logs, because Monad does not currently support opcode-level struct logs at the VM level. ## Parameters * `data` — the 32-byte hash of the block. * `object` (optional) — the tracer options: * `tracer` — the tracer to use (e.g., `callTracer`, `prestateTracer`) * `tracerConfig` — configuration options for the tracer * `timeout` — timeout for the trace operation ## Response * `result` — an array of trace results, one for each transaction in the block. The structure of each trace depends on the tracer used. ## `debug_traceBlockByHash` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function traceBlock() { const blockHash = "0x936a2bd4e1248949fe93cc4ee34a3a19a5e77a823788e40610d953ad6bc44c2a"; const traces = await provider.send("debug_traceBlockByHash", [ blockHash, { tracer: "callTracer" } ]); console.log(`Traced ${traces.length} transactions`); traces.forEach((trace, i) => { console.log(`Transaction ${i}:`, trace.result?.type || 'unknown'); }); } traceBlock(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0x936a2bd4e1248949fe93cc4ee34a3a19a5e77a823788e40610d953ad6bc44c2a" traces = web3.provider.make_request('debug_traceBlockByHash', [block_hash, {'tracer': 'callTracer'}]) print(f'Traced {len(traces["result"])} transactions') for i, trace in enumerate(traces['result']): print(f'Transaction {i}: {trace.get("result", {}).get("type", "unknown")}') ``` ## Use case A practical use case for `debug_traceBlockByHash` is investigating specific blocks identified through external sources (like block explorers or incident reports) to understand the exact execution flow of all transactions they contain. # debug_traceBlockByNumber | Monad Source: https://docs.chainstack.com/reference/monad-traceblockbynumber openapi/monad_node_api/debug_and_trace/debug_traceBlockByNumber.json POST / Monad API method that returns traces for all transactions in a block specified by block number. Available on Monad via Chainstack. Monad API method that returns traces for all transactions in a block specified by block number. This method is useful for analyzing all transaction executions within a single block. > [!WARNING] > > **Monad-specific behavior**: > > * The trace options object parameter **must be explicitly provided**. Unlike standard EVM clients where this parameter is optional, Monad RPC will return an error (`-32602 Invalid params`) if the parameter is omitted. Always include the trace options, even if empty (`{}`). > * When an empty trace options object `{}` is provided, Monad defaults to `callTracer` instead of struct logs, because Monad does not currently support opcode-level struct logs at the VM level. ## Parameters * `quantity|tag` — the block number as a hexadecimal string, or one of the following block tags: * `latest` — the most recent block in the canonical chain * `earliest` — the genesis block * `pending` — the pending state/transactions * `object` (optional) — the tracer options: * `tracer` — the tracer to use (e.g., `callTracer`, `prestateTracer`) * `tracerConfig` — configuration options for the tracer * `timeout` — timeout for the trace operation ## Response * `result` — an array of trace results, one for each transaction in the block. The structure of each trace depends on the tracer used. ## `debug_traceBlockByNumber` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function traceBlock() { const blockNumber = "latest"; // Or use hex like "0x1234" const traces = await provider.send("debug_traceBlockByNumber", [ blockNumber, { tracer: "callTracer" } ]); console.log(`Traced ${traces.length} transactions`); traces.forEach((trace, i) => { console.log(`Transaction ${i}:`, trace.result?.type || 'unknown'); }); } traceBlock(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) traces = web3.provider.make_request('debug_traceBlockByNumber', ['latest', {'tracer': 'callTracer'}]) print(f'Traced {len(traces["result"])} transactions') for i, trace in enumerate(traces['result']): print(f'Transaction {i}: {trace.get("result", {}).get("type", "unknown")}') ``` ## Use case A practical use case for `debug_traceBlockByNumber` is building block analysis tools that need to understand the complete execution flow of all transactions in a block, such as MEV detection or security monitoring systems. # debug_traceCall | Monad Source: https://docs.chainstack.com/reference/monad-tracecall openapi/monad_node_api/debug_and_trace/debug_traceCall.json POST / Monad API method that traces a call without creating a transaction on the blockchain. Available on Monad via Chainstack JSON-RPC nodes. Monad API method that traces a call without creating a transaction on the blockchain. This method is similar to `eth_call` but returns detailed execution traces, making it useful for debugging contract calls before sending actual transactions. > [!WARNING] > > **Monad-specific behavior**: > > * The trace options object parameter **must be explicitly provided**. Unlike standard EVM clients where this parameter is optional, Monad RPC will return an error (`-32602 Invalid params`) if the parameter is omitted. Always include the trace options, even if empty (`{}`). > * When an empty trace options object `{}` is provided, Monad defaults to `callTracer` instead of struct logs, because Monad does not currently support opcode-level struct logs at the VM level. > [!NOTE] > > Monad nodes, including Archive nodes, keep state only for a rolling window of recent blocks, several days long. For a block older than that window, this method returns `-32602` `Block requested not found`. See the Monad note on [Networks](/docs/protocols-networks). ## Parameters * `object` — the transaction call object: * `from` (optional) — address the transaction is sent from * `to` — address the transaction is directed to * `gas` (optional) — gas provided for the call * `gasPrice` (optional) — gas price for the call * `value` (optional) — value sent with the call * `data` (optional) — hash of the method signature and encoded parameters * `quantity|tag` — the block number as a hexadecimal string, or block tag (`latest`, `earliest`, `pending`). * `object` (optional) — the tracer options: * `tracer` — the tracer to use (e.g., `callTracer`, `prestateTracer`) * `tracerConfig` — configuration options for the tracer * `timeout` — timeout for the trace operation ## Response * `result` — the trace result object. The structure depends on the tracer used: * For `callTracer`: * `type` — the call type (CALL, CREATE, etc.) * `from` — sender address * `to` — recipient address * `value` — value transferred * `gas` — gas provided * `gasUsed` — gas used * `input` — call data * `output` — return data * `calls` — nested calls ## `debug_traceCall` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function traceCall() { // Trace name() call on Wrapped Monad (WMON) contract const tx = { to: "0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541", data: "0x06fdde03" // name() }; const trace = await provider.send("debug_traceCall", [ tx, "latest", { tracer: "callTracer" } ]); console.log("Call trace:", JSON.stringify(trace, null, 2)); } traceCall(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Trace name() call on Wrapped Monad (WMON) contract tx = { 'to': '0xFb8bf4c1CC7a94c73D209a149eA2AbEa852BC541', 'data': '0x06fdde03' # name() } trace = web3.provider.make_request('debug_traceCall', [tx, 'latest', {'tracer': 'callTracer'}]) print(f'Call trace: {trace["result"]}') ``` ## Use case A practical use case for `debug_traceCall` is simulating complex contract interactions before sending transactions to understand the execution path, identify potential failures, and verify expected behavior without spending gas. # debug_traceTransaction | Monad Source: https://docs.chainstack.com/reference/monad-tracetransaction openapi/monad_node_api/debug_and_trace/debug_traceTransaction.json POST / Monad API method that returns a detailed trace of a transaction's execution. This method provides step-by-step execution information. Monad API method that returns a detailed trace of a transaction's execution. This method provides step-by-step execution information, which is essential for debugging failed transactions and understanding contract behavior. > [!WARNING] > > **Monad-specific behavior**: > > * The trace options object parameter **must be explicitly provided**. Unlike standard EVM clients where this parameter is optional, Monad RPC will return an error (`-32602 Invalid params`) if the parameter is omitted. Always include the trace options, even if empty (`{}`). > * When an empty trace options object `{}` is provided, Monad defaults to `callTracer` instead of struct logs, because Monad does not currently support opcode-level struct logs at the VM level. ## Parameters * `data` — the 32-byte hash of the transaction to trace. * `object` (optional) — the tracer options: * `tracer` — the tracer to use (e.g., `callTracer`, `prestateTracer`) * `tracerConfig` — configuration options for the tracer * `timeout` — timeout for the trace operation ## Response * `result` — the trace result object. The structure depends on the tracer used: * For `callTracer`: * `type` — the call type (CALL, CREATE, etc.) * `from` — sender address * `to` — recipient address * `value` — value transferred * `gas` — gas provided * `gasUsed` — gas used * `input` — call data * `output` — return data * `calls` — nested calls ## `debug_traceTransaction` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const provider = new ethers.JsonRpcProvider("CHAINSTACK_NODE_URL"); async function traceTransaction() { const txHash = "0x8e00e28d3a51bcae537f7dd2dbeed7678ab67f5a63a0c3b3962c5c804fd98047"; const trace = await provider.send("debug_traceTransaction", [ txHash, { tracer: "callTracer" } ]); console.log("Transaction trace:", JSON.stringify(trace, null, 2)); } traceTransaction(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) tx_hash = "0x8e00e28d3a51bcae537f7dd2dbeed7678ab67f5a63a0c3b3962c5c804fd98047" trace = web3.provider.make_request('debug_traceTransaction', [tx_hash, {'tracer': 'callTracer'}]) print(f'Transaction trace: {trace["result"]}') ``` ## Use case A practical use case for `debug_traceTransaction` is debugging failed transactions to identify the exact point of failure, or analyzing the complete call graph of complex multi-contract interactions. # Transactions info | Monad Source: https://docs.chainstack.com/reference/monad-transactions-rpc-methods Monad transaction info JSON-RPC methods — retrieve transaction details including sender, recipient, value, gas used, status, and receipts on the network. Retrieving transaction information from the Monad blockchain can be done by using the following methods: > [card] [eth\_getTransactionByHash](/reference/monad-gettransactionbyhash) > [card] [eth\_getTransactionReceipt](/reference/monad-gettransactionreceipt) > [card] [eth\_getTransactionByBlockHashAndIndex](/reference/monad-gettransactionbyblockhashandindex) > [card] [eth\_getTransactionByBlockNumberAndIndex](/reference/monad-gettransactionbyblocknumberandindex) > [card] [eth\_getBlockReceipts](/reference/monad-getblockreceipts) > [card] [eth\_newPendingTransactionFilter](/reference/monad-newpendingtransactionfilter) This information can be used to: * **Transaction tracking** — monitor the status and details of specific transactions on the network. * **Receipt verification** — confirm transaction execution and access logs, gas usage, and status. * **DApp development** — integrate transaction data into decentralized applications for user feedback and state management. * **Analytics** — analyze transaction patterns and network usage. # net_listening | Polygon Source: https://docs.chainstack.com/reference/netlistening openapi/polygon_node_api/client_info/net_listening.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns a boolean value indicating whether the client is currently listening for network connections or not. Polygon API method that returns a boolean value indicating whether the client is currently listening for network connections or not. The `net_listening` method can be useful in many scenarios where it is important to monitor the status of a Polygon client's network connection to ensure that it is working properly. ## Parameters * `none` ## Response * `boolean` — a boolean value that indicates whether or not a node is currently actively seeking peer connections. `true` if the client is actively listening for connections, `false` if not. ## `net_listening` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const isListening = async () => { const listen = await provider.send("net_listening"); console.log(listen); } isListening() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) is_listening = web3.provider.make_request('net_listening', []) print(is_listening) ``` ## Use case A method like `net_listening` can be used to monitor the network status of the Polygon node a developer is connecting to. Imagine a blockchain explorer where users can search for transactions, addresses, and other information on the Polygon blockchain. In order to provide accurate information to its users, the website needs to be able to monitor the current status of its connection. Here is an example of this implementation using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Create a function to monitor the node status and display information to users async function monitorNetworkStatus() { try { // Check if the client is currently listening for network connections const isListening = await provider.send("net_listening", []); // Get the number of peers currently connected to the client const peerCount = await provider.send("net_peerCount", []); // Log the network status information to the console console.log(`Network status: ${isListening ? 'Listening' : 'Not listening'} | Peers: ${peerCount}`); } catch (error) { // Handle any errors that might occur console.error(`Error monitoring network status: ${error}`); } } // Call the monitorNetworkStatus function at regular intervals to continuously monitor the network status setInterval(monitorNetworkStatus, 5000); // Monitor every 5 seconds ``` The `monitorNetworkStatus` function defined in this code uses the `net_listening` method to check whether the Polygon client is currently listening for network connections, and the [net\_peerCount](/reference/peercount) method to get the number of peers currently connected to the client. The function logs this information to the console, which could be displayed to the user in a user-friendly format on the blockchain explorer website. By calling this function at regular intervals using the `setInterval` function, the website can continuously monitor the node's status and provide users with up-to-date information about the overall health of the node. # eth_newFilter | Polygon Source: https://docs.chainstack.com/reference/newfilter openapi/polygon_node_api/logs_and_events/eth_newFilter.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that generates a filter object based on the filter parameters. Available on Polygon via Chainstack JSON-RPC nodes. Polygon API method that generates a filter object based on the filter parameters. It returns a filter ID, which can be used to retrieve the results of the filter using the [eth\_getFilterChanges](/reference/getfilterchanges) method. By creating a filter for specific events, developers can receive notifications when those events occur and use them to trigger actions in their applications. ## Parameters * `object` — the filter parameters: * `fromBlock` — (optional, default: `latest`) integer that specifies the starting block number from which the logs should be fetched. * `toBlock` — (optional, default: `latest`) integer that specifies the ending block number until which the logs should be fetched. * `address` — (optional) the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — (optional) an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `blockhash` — (optional) the hash of the specific block. Limits logs to a specific block with a 32-byte hash value. It takes precedence over `fromBlock` and `toBlock`. > [!NOTE] > > **Possible tags forfromBlockandtoBlock** > > * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block > * `earliest` — the earliest available or genesis block > * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `result` — a hexadecimal string representing the ID of the newly created filter > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/getfilterchanges) to retrieve updated logs * [eth\_uninstallFilter](/reference/uninstallfilter) to remove the filter ## `eth_newFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/polygon-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }; const createFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) filter = { 'toBlock': 'latest', 'address': '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', 'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } def create_logs_filter(filter_object): try: logs_filter = web3.eth.filter(filter_object) return logs_filter except Exception as e: print(e) logs_filter = create_logs_filter(filter) filter_id = logs_filter.filter_id print(f'New filter ID: {filter_id}') ``` ## Use case You can use `eth_newFilter` to create a filter for a specific action on a smart contract, for example, to monitor the transfer transactions from the [Wrapped ETH token](https://polygonscan.com/token/0x7ceb23fd6bc0add59e62ac25578270cff1b9f619). The idea is to create a filter using the `eth_newFilter` method to monitor an ERC-20 smart contract, WETH in this case. The following filter parameters are selected: ```javascript filter_params theme={"system"} const filter = { // Poll the latest block available toBlock: 'latest', // WETH smart contract address address: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619', // Signature of the Transfer event. topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], }; ``` The program is then polling the filter every 2 seconds to check for any new changes using the `eth_getFilterChanges` method. Here is the implementation using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const filter = { toBlock: 'latest', address: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], }; const createAndPollFilter = async () => { try { const filterId = await provider.send('eth_newFilter', [filter]); console.log(`The filter ID is: ${filterId}`); // the filter ID returned by eth_newFilter // Poll the filter every 30 seconds setInterval(async () => { const filterChanges = await provider.send('eth_getFilterChanges', [filterId]); if (filterChanges.length === 0) { console.log('No new logs since the last poll.'); } else { console.log(filterChanges); // the filter changes returned by eth_getFilterChanges } }, 2000); // 2 seconds in milliseconds return filterId; } catch (error) { console.log(error); } }; createAndPollFilter(); ``` Here's a breakdown of how the code works: The `createAndPollFilter` function is an asynchronous function that creates and polls a filter for the WETH smart contract on Polygon. The `eth_newFilter` method creates a new filter with the specified parameters. In this case, it passes in the filter object as the filter parameter. The method returns the ID of the newly created filter, which is then logged into the console. The `setInterval` method is used to poll the filter every 2 seconds. It takes a function as the first argument and an interval in milliseconds as the second argument. The function passed to `setInterval` is an asynchronous function that calls `eth_getFilterChanges` with the filter ID returned by `eth_newFilter`. This method returns an array of all the changes to the filter since the last time it was polled. If there are no changes, it logs "No new logs since the last poll." to the console. If there are changes, it logs the filter changes to the console. The `createAndPollFilter` function returns the filter ID, and if there are any errors thrown while creating or polling the filter, the catch block logs the error to the console. # API errors reference Source: https://docs.chainstack.com/reference/node-api-errors-reference Complete reference of node API error codes you may encounter on Chainstack — error explanations, common causes, and step-by-step troubleshooting solutions. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). ## HTTP status codes | Code | Message | Solution | | - | - | - | | 400 | Bad Request | Your request is invalid. Make sure your request is properly formatted and this is a valid request. | | 401 | Authorization Required | The credentials you provide to access your node endpoint are absent or incorrect. See how to [know your node credentials](https://docs.chainstack.com/docs/manage-your-node#view-node-access-and-credentials). | | 404 | Not Found | [Contact support](https://support.chainstack.com/) to check if you are accessing the correct node and if the node is healthy. | | 404 | DEPTH\_ZERO\_SELF\_SIGNED\_CERT | [Contact support](https://support.chainstack.com/) to check if the gateway is healthy. | | 413 | Request Entity Too Large | The default limit of the request body size is 1 MB. Reduce the request body size. | | 429 | Too Many Requests | You hit the default node [HTTP connections limit](https://docs.chainstack.com/docs/handle-real-time-data-using-websockets-with-javascript-and-python#http-and-websocket-connection-limitations). Reduce the number of requests. | | 429 | You've exceeded the RPS limit available on the current plan | You hit the [rate limit on your plan](https://docs.chainstack.com/docs/pricing-introduction#rate-limits). Reduce the request rate or upgrade to a higher plan. See also [RPS plan limits](https://docs.chainstack.com/docs/rps-plan-limits). | | 429 | You've reached your monthly quota of Request Units (RUs) | You exhausted the monthly RU quota on your plan. [Enable extra usage](https://console.chainstack.com/user/settings/billing) to continue beyond the quota or upgrade for more RUs. See also [Quotas](https://docs.chainstack.com/docs/quotas). | | 499 | No Response From Node | Increase the client-side time-out. [Contact support](https://support.chainstack.com/) if the issue persists. | | 500 | Internal Server Error | [Contact support](https://support.chainstack.com/) to check if you are accessing the correct node and if the gateway is healthy. | | 500 | ECONNREFUSED | [Contact support](https://support.chainstack.com/). | | 502 | Bad Gateway | [Contact support](https://support.chainstack.com/) to check if your node is experiencing resource constraints or if the node is healthy. | | 503 | Service Temporarily Unavailable | [Contact support](https://support.chainstack.com/) to check if your node is experiencing resource constraints. | | 504 | Gateway Time-Out | [Contact support](https://support.chainstack.com/) to check if your node is experiencing resource constraints. Or consider getting a [dedicated gateway](https://docs.chainstack.com/docs/trader-node#dedicated-gateways). | ## WebSocket status codes | Code | Message | Solution | | - | - | - | | 1006 | Socket Error: read ECONNRESET | [Implement a reconnect](https://docs.chainstack.com/docs/handle-real-time-data-using-websockets-with-javascript-and-python) in your code or get a [dedicated gateway](https://docs.chainstack.com/docs/trader-node#dedicated-gateways). | | 1009 | MESSAGE\_TOO\_BIG | The incoming WebSocket message exceeded the maximum allowed size. Increase or disable `max_size` in your WebSocket client configuration. | ## JSON-RPC status codes | Code | Message | Solution | | - | - | - | | -32700 | Parse Error | Your JSON cannot be parsed on the server side. Make sure your JSON is properly formatted. | | -32600 | Invalid Request | Your JSON-RPC request is not properly formed. See [node API reference](https://docs.chainstack.com/reference/chainstack-platform-api-list-all-nodes). | | -32601 | Method Not Found | The method you are trying to call is not available on the node. See also [API namespaces](https://support.chainstack.com/hc/en-us/articles/360024806051). | | -32602 | Invalid Params | Your request has invalid parameters. See [node API reference](https://docs.chainstack.com/reference/chainstack-platform-api-list-all-nodes). | | -32603 | Internal Error | A JSON-RPC error that means your request cannot be processed and usually has a -32000 error with details on why the request cannot be processed. | | -32005 | Rate Limit Exceeded | You exceeded the RPS limit or monthly RU quota on your plan. The `message` field contains details on which limit was hit. The response may include a `data.try_again_in` field indicating when to retry. See [RPS plan limits](https://docs.chainstack.com/docs/rps-plan-limits) and [Quotas](https://docs.chainstack.com/docs/quotas). | | -32000 | Server Error | The implementation-defined error on the server. The node implementation shows why it cannot process the request. For example, see [EIP-1474](https://eips.ethereum.org/EIPS/eip-1474). | # Optimism API reference: op-reth JSON-RPC Source: https://docs.chainstack.com/reference/optimism-api-reference Optimism API reference for Chainstack nodes — Global and Trader Nodes both run op-reth. Covers the supported JSON-RPC methods and namespaces. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). This page provides an overview of the available methods on the Chainstack Optimism nodes. Go ahead and try the interactive examples as well. Chainstack Optimism [Global Nodes](/docs/global-elastic-node) and [Trader Nodes](/docs/trader-node) both run [op-reth](https://github.com/paradigmxyz/reth), so method availability is the same across tiers. Debug and trace methods require [debug and trace APIs](/docs/debug-and-trace-apis) to be enabled on the node. > [!NOTE] > > The list may not be comprehensive but serves as guidance. In general, if a method is available in `op-reth` and it's an `eth_` method, then it's available on your Chainstack node. > > If there's a method missing, you are unsure, or just have any questions, [talk to us](https://support.chainstack.com/hc/en-us/requests/new). We love talking to developers . | Method | Availability | Comments | | - | - | - | | eth\_getBlockByHash | | | | eth\_getBlockByNumber | | | | eth\_getBlockTransactionCountByHash | | | | eth\_getBlockTransactionCountByNumber | | | | eth\_getUncleCountByBlockHash | | | | eth\_getUncleCountByBlockNumber | | | | eth\_getBlockReceipts | | | | eth\_chainId | | | | eth\_syncing | | | | eth\_blockNumber | | | | eth\_accounts | | Returns an empty list because Chainstack doesn't store accounts. | | eth\_call | | | | eth\_callMany | | | | eth\_estimateGas | | | | eth\_createAccessList | | | | eth\_gasPrice | | | | eth\_feeHistory | | | | eth\_maxPriorityFeePerGas | | | | eth\_newFilter | | | | eth\_newBlockFilter | | | | eth\_newPendingTransactionFilter | | Subsequent `eth_getFilterChanges` returns transactions as the sequencer includes them in [subblocks](/docs/flashblocks-on-optimism), because the [Optimism mempool is private](https://docs.optimism.io/op-stack/protocol/differences#mempool-rules). | | eth\_uninstallFilter | | | | eth\_getFilterChanges | | | | eth\_getFilterLogs | | | | eth\_getLogs | | | | eth\_getBalance | | | | eth\_getStorageAt | | | | eth\_getTransactionCount | | | | eth\_getCode | | | | eth\_getProof | | | | eth\_getTransactionByHash | | | | eth\_getRawTransactionByHash | | | | eth\_getTransactionByBlockHashAndIndex | | | | eth\_getRawTransactionByBlockHashAndIndex | | | | eth\_getTransactionByBlockNumberAndIndex | | | | eth\_getRawTransactionByBlockNumberAndIndex | | | | eth\_getTransactionReceipt | | | | eth\_subscribe | | | | eth\_unsubscribe | | | | eth\_sendRawTransaction | | | | eth\_sendRawTransactionSync | | Sync version of `eth_sendRawTransaction` defined by [EIP-7966](https://eips.ethereum.org/EIPS/eip-7966). Returns the receipt instead of just the hash. | | web3\_clientVersion | | | | web3\_sha3 | | | | net\_listening | | | | net\_peerCount | | Always returns `0x0` on op-reth. The p2p networking is done on `op-node`, which does not have an endpoint exposed. See also [OP issue 5745](https://github.com/ethereum-optimism/optimism/issues/5745). | | net\_version | | | | debug\_traceBlockByHash | | Enable [debug and trace APIs](/docs/debug-and-trace-apis). | | debug\_traceBlockByNumber | | Enable [debug and trace APIs](/docs/debug-and-trace-apis). | | debug\_traceTransaction | | Enable [debug and trace APIs](/docs/debug-and-trace-apis). | | debug\_traceCall | | Enable [debug and trace APIs](/docs/debug-and-trace-apis). | | debug\_traceCallMany | | Enable [debug and trace APIs](/docs/debug-and-trace-apis). | | debug\_getModifiedAccountsByNumber | | Enable [debug and trace APIs](/docs/debug-and-trace-apis). | | debug\_getModifiedAccountsByHash | | Enable [debug and trace APIs](/docs/debug-and-trace-apis). | | debug\_storageRangeAt | | Not supported on Reth (returns `null`). Use [`eth_getStorageAt`](/docs/using-eth_getstorageat-instead-of-debug_storagerangeat-on-reth) instead. | | admin\_nodeInfo | Can be enabled on a [Dedicated Node](/docs/dedicated-node). | | | admin\_peers | Can be enabled on a [Dedicated Node](/docs/dedicated-node). | | | admin\_addPeer | Can be enabled on a [Dedicated Node](/docs/dedicated-node). | | op-reth also supports the `trace_` namespace (for example, `trace_block` and `trace_transaction`); these require [debug and trace APIs](/docs/debug-and-trace-apis) to be enabled. See the [Base methods](/docs/base-methods) reference for the full op-reth method set, which is shared with Optimism. # eth_blockNumber | Optimism Source: https://docs.chainstack.com/reference/optimism-blocknumber openapi/optimism_node_api/eth_blockNumber.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method that returns the latest block number of the blockchain. Reference for eth_blockNumber on Optimism via Chainstack. Optimism API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0x69B5B` means that the block is block 432987 in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to, encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. ## Use case A possible use case for the `eth_blockNumber` method in Optimism is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. # eth_call | Optimism Source: https://docs.chainstack.com/reference/optimism-call openapi/optimism_node_api/eth_call.json POST /efb0a5eccd2caa5135eb54eba6f7f300 The example is a call to the poolinfo of the Gamma MasterChef contract. eth_call JSON-RPC method available on the Optimism blockchain via Chainstack. The example is a call to the `poolinfo` of the [Gamma MasterChef contract](https://optimistic.etherscan.io/address/0xc7846d1bc4d8bcf7c45a7c998b77ce9b3c904365#readContract). Optimism API method `eth_call` executes a smart contract function call directly and returns the result without requiring a transaction or changing the state on the blockchain. This method is widely used for reading data from smart contracts. ## Parameters * `transactionObject` — an object containing transaction parameters, including: * `to` — the address of the contract to call. * `data` — the call data, typically including the function signature and parameters. * `blockParameter` — a string specifying the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the blockchain state to use for the call. Most commonly, `"latest"` is used. ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `result` — the result of the call, encoded as a hexadecimal string. The format of the result depends on the function called. ## Use case The `eth_call` method is essential for: * DApps that need to retrieve state or compute derived values from smart contracts without making a transaction. * Wallets and interfaces that display contract information, such as token balances or the state of a decentralized finance (DeFi) contract. * Automated scripts and bots that monitor contract states or conditions on the blockchain. # eth_callMany | Optimism Source: https://docs.chainstack.com/reference/optimism-callmany openapi/optimism_node_api/eth_callMany.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_callMany executes a list of transaction bundles without creating transactions on the blockchain. On Optimism. Optimism API method `eth_callMany` executes a list of transaction bundles without creating transactions on the blockchain. This method is particularly useful for simulating transactions to understand their potential outcomes. It supports simulating transactions at an intermediate state of a block, simulating multiple transactions with sequential dependencies, and overwriting block headers. This examples simulates the transfer of 100 USDT to an address in block 116318465 at transaction position 1 in the block and does the balance check of the address for the USDT, all in one call. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * **BUNDLES** (array, required): A list of bundles where each bundle contains: * **transactions** (array): An array of transaction objects with parameters similar to `eth_call`. * **blockOverride** (object, optional): Overrides for the execution context block header, including `blockNumber`, `blockHash`, `coinbase`, `timestamp`, `difficulty`, `gasLimit`, and `baseFee`. * **SIMULATION CONTEXT** (object, required): The context for the simulation, specifying the `blockNumber or Tag` and `transactionIndex`. * **STATE OVERRIDES** (object, optional): An optional mapping for state overrides, following the `eth_call` standard. * **TIMEOUT** (integer, optional): The maximum running time for the simulations in milliseconds, defaulting to 5000 milliseconds. ## Response The method returns a list of lists containing either the return value of the executed contract call or the reverted error code. * **array** - All the return/error values * **value** - The value if was successful * **error** - The error if the transaction failed ## Use case The `eth_callMany` method is essential for: * Developers who need to simulate the outcome of transactions at a specific block height without making actual state changes. * Applications that require understanding the sequential dependencies between multiple transactions. * Services that need to test the impact of block header changes on transaction execution. # eth_chainId | Optimism Source: https://docs.chainstack.com/reference/optimism-chainid openapi/optimism_node_api/eth_chainId.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_chainId retrieves the current chain ID, which is a unique identifier for the blockchain network the node is connected to. Optimism API method `eth_chainId` retrieves the current chain ID, which is a unique identifier for the blockchain network the node is connected to. The chain ID is crucial for transaction signing to prevent replay attacks across different chains. ## Parameters * `none` — This method does not require any parameters. ## Response * `result` — the current chain ID, encoded as a hexadecimal string. For example, the chain ID for Optimism Mainnet is `0xa`. ## Use case The `eth_chainId` method is essential for: * Wallets and applications that sign transactions, as the chain ID is used as part of the signing process to ensure transactions are only valid on the intended chain. * Applications that interact with multiple blockchain networks and need to confirm the network they are communicating with. # web3_clientVersion | Optimism Source: https://docs.chainstack.com/reference/optimism-clientversion openapi/optimism_node_api/web3_clientVersion.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method web3_clientVersion retrieves the version string of the Optimism client the node is running. Optimism via Chainstack. Optimism API method `web3_clientVersion` retrieves the version string of the Optimism client the node is running. This method is useful for identifying the client and its version, which can be important for compatibility and debugging purposes. ## Parameters This method does not require any parameters. ## Response * `result` — the version string of the Optimism client, which typically includes the client name, version, network, platform, and other build information. ## Use case The `web3_clientVersion` method is essential for: * Developers and system administrators needing to verify the client version for compatibility with dApps or network protocols. * Services that monitor and report on the distribution of client versions across the network. * Tools that automate interactions with Optimism nodes and require version checks to ensure proper operation. # eth_createAccessList | Optimism Source: https://docs.chainstack.com/reference/optimism-createaccesslist openapi/optimism_node_api/eth_createAccessList.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_createAccessList generates an access list for a transaction based on specific default values. On Optimism. Optimism API method `eth_createAccessList` generates an access list for a transaction based on specific default values. An access list includes addresses and storage keys the transaction intends to access, facilitating more efficient gas usage. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `transactionObject` — an object containing transaction parameters with default values: * `from` — the address of the sender. * `data` — the data being sent with the transaction. * `blockParameter` — specifies the blockchain state to use for generating the access list, with a default value of `"pending"`. ## Response * `result` — an object containing: * `accessList` — the generated access list, detailing addresses and storage keys the transaction will access. * `gasUsed` — the estimated gas used to execute the transaction with the generated access list. ## Use case The `eth_createAccessList` method is crucial for: * Preparing transactions that optimize gas usage by explicitly stating which addresses and storage keys will be accessed. * Developers aiming to reduce transaction costs and improve efficiency on the Optimism network. * Transactions that comply with EIP-2930, which introduces access lists as a transaction type to specify state access. This method provides a practical approach to optimizing transaction execution and gas usage on the Optimism blockchain by pre-determining the access list. # eth_estimateGas | Optimism Source: https://docs.chainstack.com/reference/optimism-estimategas openapi/optimism_node_api/eth_estimateGas.json POST /efb0a5eccd2caa5135eb54eba6f7f300 The eth_estimateGas method estimates the amount of gas required to execute a transaction. eth_estimateGas on Optimism via Chainstack. # `eth_estimateGas` RPC Method The `eth_estimateGas` method estimates the amount of gas required to execute a transaction. This is useful for understanding how much gas might be consumed by a transaction before actually sending it. The estimation is not always accurate but provides a good approximation. ## Request To use `eth_estimateGas`, send a POST request with a JSON RPC call in the body. ## Parameters A single object containing: * **from** (`string`): (optional) The address the transaction is sent from. * **to** (`string`): The address the transaction is directed to. * **gas** (`string`): (optional) The gas limit provided by the sender. * **gasPrice** (`string`): (optional) The gas price for the transaction. * **value** (`string`): (optional) The value transferred for the transaction in Wei. * **data** (`string`): (optional) The data sent along with the transaction. ## Response The method returns the estimated gas required as a hexadecimal number. * **result** (`string`): The estimated gas required for the transaction. ## Use case The `eth_estimateGas` method is essential for: * Developers to estimate transaction costs before sending them. * Wallets and interfaces that calculate transaction fees. * Optimizing smart contract interactions by understanding gas requirements. # eth_feeHistory | Optimism Source: https://docs.chainstack.com/reference/optimism-feehistory openapi/optimism_node_api/eth_feeHistory.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_feeHistory retrieves historical gas fee data, providing insights into gas price trends and block utilization over time. Optimism API method `eth_feeHistory` retrieves historical gas fee data, providing insights into gas price trends and block utilization over time. This method is valuable for estimating transaction fees and understanding network congestion. ## Parameters * `blockCount` — the number of blocks to include in the history. * `newestBlock` — the newest block (by number or one of the strings `"earliest"`, `"latest"`, or `"pending"`) to include in the history. * `rewardPercentiles` — an array of percentiles to calculate effective priority fees over the requested blocks. ## Response * `result` — an object containing: * `oldestBlock` — the oldest block number in the history. * `baseFeePerGas` — an array of base fees per gas for each block. * `gasUsedRatio` — an array of ratios of gas used to gas limit for each block. * `reward` — an array of arrays containing the priority fees at the specified percentiles for each block. ## Use case The `eth_feeHistory` method is essential for: * Wallets and interfaces providing users with data-driven gas price suggestions. * Applications and services monitoring Optimism network congestion and optimizing transaction fee estimates. * Analysts studying Optimism gas fee trends and block space utilization. # eth_gasPrice | Optimism Source: https://docs.chainstack.com/reference/optimism-gasprice openapi/optimism_node_api/eth_gasPrice.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_gasPrice retrieves the current gas price, which is crucial for executing transactions on the Optimism network. Optimism API method `eth_gasPrice` retrieves the current gas price, which is crucial for executing transactions on the Optimism network. The gas price is determined by the demand for transaction processing and the supply of miners willing to process transactions. ## Parameters * `none` — This method does not require any parameters. ## Response * `result` — the current gas price, encoded as a hexadecimal string. This value fluctuates based on network activity and miner demand. ## Use case The `eth_gasPrice` method is essential for: * Users and applications preparing to send transactions, enabling them to set an appropriate gas price to ensure timely transaction processing. * Wallets and interfaces that provide gas price suggestions to optimize for cost and confirmation time. * Developers and analysts monitoring the Optimism network's congestion and transaction costs. This method provides a straightforward way to access the current gas price, helping users and applications interact with the Optimism blockchain more effectively by estimating transaction costs accurately. # eth_getBalance | Optimism Source: https://docs.chainstack.com/reference/optimism-getbalance openapi/optimism_node_api/eth_getBalance.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getBalance retrieves the balance of an account at a given block. eth_getBalance on Optimism via Chainstack. Optimism API method `eth_getBalance` retrieves the balance of an account at a given block. This method is essential for applications that need to display or utilize the current balance of an Ethereum address. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to get the balance for. * `block` — the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the state to consider. The default is `"latest"`. ## Response * `result` — the balance of the account in Wei, as a hexadecimal string. ## Use case The `eth_getBalance` method is essential for: * Wallets and financial applications that track and display user balances. * DApps that perform actions based on the account balance. * Services that monitor account balances for thresholds, changes, or unusual activity. # eth_getBlockByHash | Optimism Source: https://docs.chainstack.com/reference/optimism-getblockbyhash openapi/optimism_node_api/eth_getBlockByHash.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method that retrieves a block's information by its hash. The block hash is a unique identifier for each block in the blockchain. Optimism API method that retrieves a block's information by its hash. The block hash is a unique identifier for each block in the blockchain, represented as a hexadecimal number. ## Parameters * `hash` — the hash of the block to retrieve, as a hexadecimal string. * `fullTransactionObjects` — a boolean indicating whether to return full transaction objects or only their hashes. If `true`, full transaction objects are returned; if `false`, only the hashes of the transactions are returned. ## Response * `result` — an object containing the block's information, including the block number, timestamp, transactions, and more. If `fullTransactionObjects` is `true`, this will include detailed information about each transaction in the block. ## Use case The `eth_getBlockByHash` method is useful for applications that need to retrieve specific blocks from the blockchain, such as block explorers, wallets, or applications that process or display block information. For example, a block explorer might use this method to display detailed information about a block and its transactions to users. # eth_getBlockByNumber | Optimism Source: https://docs.chainstack.com/reference/optimism-getblockbynumber openapi/optimism_node_api/eth_getBlockByNumber.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method that retrieves a block's information by its number. The block number is a sequential identifier for each block in the blockchain. Optimism API method that retrieves a block's information by its number. The block number is a sequential identifier for each block in the blockchain. ## Parameters * `blockNumber` — the number of the block to retrieve, encoded as a hexadecimal string. For example, `0x6f5f828` represents block number 116783144. * `fullTransactionObjects` — a boolean indicating whether to return full transaction objects or only their hashes. If `true`, full transaction objects are returned; if `false`, only the hashes of the transactions are returned. ## Response * `result` — an object containing the block's information, including the block number, timestamp, transactions, and more. If `fullTransactionObjects` is `true`, this will include detailed information about each transaction in the block. ## Use case The `eth_getBlockByNumber` method is useful for applications that need to retrieve specific blocks from the blockchain based on their block number. This can include block explorers, wallets, or any application that requires detailed information about a particular block or its transactions. # eth_getBlockReceipts | Optimism Source: https://docs.chainstack.com/reference/optimism-getblockreceipts openapi/optimism_node_api/eth_getBlockReceipts.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getBlockReceipts retrieves all transaction receipts for a specified block. Use it on Optimism via Chainstack. Optimism API method `eth_getBlockReceipts` retrieves all transaction receipts for a specified block. This method enables applications to access detailed information about every transaction within a block, including outcomes, gas used, and event logs. ## Parameters * `blockIdentifier` — a string that specifies the block number in hexadecimal format or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating which block's receipts to retrieve. For instance, using `"latest"` fetches the receipts for the most recent block. ## Response * `result` — an array of objects, each representing a transaction receipt within the specified block. Key fields in each receipt include: * `transactionHash` — the hash of the transaction. * `transactionIndex` — the transaction's index position within the block. * `blockHash` — the hash of the block containing the transaction. * `blockNumber` — the number of the block containing the transaction. * `from` — the address of the sender. * `to` — the address of the receiver (`null` for contract creation transactions). * `cumulativeGasUsed` — the total amount of gas used in the block up until this transaction. * `gasUsed` — the amount of gas used by this transaction. * `contractAddress` — the contract address created by the transaction, if any. * `logs` — an array of log objects generated by the transaction. * `status` — the status of the transaction (`0x1` for success, `0x0` for failure). ## Use case The `eth_getBlockReceipts` method is crucial for applications requiring comprehensive transactional data from a block, such as: * Analyzing gas consumption and efficiency across transactions. * Monitoring smart contract interactions and logging for decentralized applications (DApps). * Verifying transaction execution outcomes for auditing and security analysis. This method offers a granular view of block transactions, facilitating detailed analysis and monitoring of transaction outcomes, gas usage, and smart contract events on the Optimism blockchain. # eth_getBlockTransactionCountByHash | Optimism Source: https://docs.chainstack.com/reference/optimism-getblocktransactioncountbyhash openapi/optimism_node_api/eth_getBlockTransactionCountByHash.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method that retrieves the number of transactions in a block, identified by its hash. Use it on Optimism via Chainstack. Optimism API method that retrieves the number of transactions in a block, identified by its hash. This method provides a way to know how many transactions were included in a specific block without retrieving the full block data. ## Parameters * `hash` — the hash of the block for which the transaction count is being queried, as a hexadecimal string. ## Response * `result` — the number of transactions in the specified block, encoded as a hexadecimal string. This count includes all transactions that were part of the block when it was mined. ## Use case The `eth_getBlockTransactionCountByHash` method is particularly useful for applications that need to quickly assess the volume of transactions within specific blocks. For instance, analytics platforms may use this information to analyze network activity over time or to identify blocks that contain an unusually high or low number of transactions. # eth_getBlockTransactionCountByNumber | Optimism Source: https://docs.chainstack.com/reference/optimism-getblocktransactioncountbynumber openapi/optimism_node_api/eth_getBlockTransactionCountByNumber.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method that retrieves the number of transactions in a block, identified by its number. Chainstack Optimism reference. Optimism API method that retrieves the number of transactions in a block, identified by its number. This method allows for understanding the transaction volume within a specific block without needing to load the entire block's data. ## Parameters * `blockNumber` — the number of the block for which the transaction count is being queried, encoded as a hexadecimal string. ## Response * `result` — the number of transactions in the specified block, encoded as a hexadecimal string. This includes all transactions that were part of the block at the time it was mined. ## Use case The `eth_getBlockTransactionCountByNumber` method is useful for applications that monitor blockchain activity, such as wallets, block explorers, or analytics platforms. It can help these applications quickly gauge the level of activity within specific blocks or over a range of blocks. # eth_getCode | Optimism Source: https://docs.chainstack.com/reference/optimism-getcode openapi/optimism_node_api/eth_getCode.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getCode retrieves the code stored at a specific address. Available on Optimism via Chainstack JSON-RPC nodes. Optimism API method `eth_getCode` retrieves the code stored at a specific address. This method is typically used to inspect the bytecode of smart contracts on the Optimism blockchain. This example retrieves the bytecode of the [Chainlink token](https://optimistic.etherscan.io/address/0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6) on the Optimism Mainnet. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to get the code from. * `block` — the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the state to consider. The default is `"latest"`. ## Response * `result` — the code from the specified address, returned as a hexadecimal string. ## Use case The `eth_getCode` method is essential for: * Developers and auditors who need to verify the deployed bytecode of smart contracts. * DApps that interact with contracts dynamically based on their code. * Tools and services that monitor and analyze contract deployments and updates. # eth_getFilterChanges | Optimism Source: https://docs.chainstack.com/reference/optimism-getfilterchanges openapi/optimism_node_api/eth_getFilterChanges.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getFilterChanges retrieves the changes of a filter since it was last accessed. Chainstack Optimism reference. Optimism API method `eth_getFilterChanges` retrieves the changes of a filter since it was last accessed. This method is crucial for applications that need to monitor specific events or blocks on the Optimism blockchain. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter | Optimism](/reference/newfilter) > * [eth\_newBlockFilter | Optimism](/reference/optimism-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `filterId` — the ID of the filter whose changes are being queried. This ID is returned by filter creation methods such as `eth_newBlockFilter`, or `eth_newFilter`. For this example, a random value `"0x4a2b3c"` is used. ## Response * `result` — an array of changes. For log filters, this will be an array of log entries. For block filters, it will be an array of block hashes. For pending transaction filters, it will be an array of transaction hashes. Each log entry object contains details such as the log index, transaction index, transaction hash, block hash, block number, address, data, and topics. ## Use case The `eth_getFilterChanges` method is essential for: * DApps that need to react to new events on the blockchain, such as token transfers or contract interactions. * Services that monitor the blockchain for security or compliance reasons, needing to track specific transactions or blocks. * Wallets and exchanges that require real-time information about transactions or blocks for processing or notification purposes. # eth_getFilterLogs | Optimism Source: https://docs.chainstack.com/reference/optimism-getfilterlogs openapi/optimism_node_api/eth_getFilterLogs.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getFilterLogs retrieves all logs matching a previously created filter. Available on Optimism via Chainstack. Optimism API method `eth_getFilterLogs` retrieves all logs matching a previously created filter. This method is essential for applications that need to fetch historical log data based on specific criteria. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterLogs` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/ethereum-newfilter) > * [eth\_newBlockFilter](/reference/ethereum-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `filterId` — the ID of the filter for which logs are being fetched. This ID is returned by filter creation methods such as `eth_newBlockFilter`, or `eth_newFilter`. For this example, a random value `"0x5a6b7c"` is used. ## Response * `result` — an array of log objects. Each log object contains details such as whether the log was removed (due to a chain reorganization), log index, transaction index, transaction hash, block hash, block number, address from which the log originated, data contained in the log, and topics associated with the log. ## Use case The `eth_getFilterLogs` method is essential for: * DApps that require access to historical log data for analysis or display purposes. * Services that monitor smart contract interactions over time, needing to retrieve all past events that match certain criteria. * Analytical tools that process and visualize blockchain data based on logs from specific contracts or transactions. # eth_getLogs | Optimism Source: https://docs.chainstack.com/reference/optimism-getlogs openapi/optimism_node_api/eth_getLogs.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getLogs retrieves logs matching specific criteria, such as block range, address, and topics. Optimism via Chainstack. Optimism API method `eth_getLogs` retrieves logs matching specific criteria, such as block range, address, and topics. This method is crucial for applications that need to fetch log data for analysis, display, or monitoring smart contract events. The example track [Chainlink token transfers](https://optimistic.etherscan.io/token/0x350a791bfc2c21f9ed5d10980dad2e2638ffa7f6). ## Parameters * `filterObject` — an object containing filter options: * `fromBlock` — the starting block to search for logs. In this example, it's set to `"latest"`. * `address` — the contract address to fetch logs from. * `topics` — an array of topics to match against the logs. ## Response * `result` — an array of log objects. Each log object contains details such as whether the log was removed (due to a chain reorganization), log index, transaction index, transaction hash, block hash, block number, address from which the log originated, data contained in the log, and topics associated with the log. ## Use case The `eth_getLogs` method is essential for: * DApps that require access to historical log data for analysis or display purposes. * Services that monitor smart contract interactions over time, needing to retrieve all past events that match certain criteria. * Analytical tools that process and visualize blockchain data based on logs from specific contracts or transactions. # debug_getModifiedAccountsByHash | Optimism Source: https://docs.chainstack.com/reference/optimism-getmodifiedaccountsbyhash openapi/optimism_node_api/debug_getModifiedAccountsByHash.json POST /efb0a5eccd2caa5135eb54eba6f7f300 The debug_getModifiedAccountsByHash RPC method retrieves a list of accounts that were modified in. debug_getModifiedAccountsByHash on Optimism via Chainstack. The `debug_getModifiedAccountsByHash` RPC method retrieves a list of accounts that were modified in a specific block, identified by the block's hash. This method is useful for developers and analysts who need to track changes to the state of accounts within a particular block for debugging or analytical purposes. ## Request To use `debug_getModifiedAccountsByHash`, you should send a POST request with a JSON RPC call in the body. ## Parameters * **Block Hash** (`string`): The hash of the block for which to retrieve the list of modified accounts. ## Response The response will be an array of account addresses (as strings) that were modified in the specified block. * **result** (`array`): The addresses of the accounts modified in the specified block. ## Use case The `debug_getModifiedAccountsByHash` method is essential for: * Developers debugging contract interactions within specific blocks. * Analysts tracking state changes for forensic or audit purposes. * Tools and services monitoring the Optimism state for changes to specific accounts within a block. # debug_getModifiedAccountsByNumber | Optimism Source: https://docs.chainstack.com/reference/optimism-getmodifiedaccountsbynumber openapi/optimism_node_api/debug_getModifiedAccountsByNumber.json POST /efb0a5eccd2caa5135eb54eba6f7f300 The debug_getModifiedAccountsByNumber RPC method retrieves a list of accounts that were. debug_getModifiedAccountsByNumber on Optimism via Chainstack. The `debug_getModifiedAccountsByNumber` RPC method retrieves a list of accounts that were modified between two specified block numbers. This method is invaluable for developers and analysts looking to track changes to the state of accounts across specific blocks for debugging or analytical purposes. ## Request To use `debug_getModifiedAccountsByNumber`, you should send a POST request with a JSON RPC call in the body. ## Parameters * **Start Block** (`string`): The block number (in hexadecimal) from which to start tracking changes. * **End Block** (`string`): The block number (in hexadecimal) up to which to track changes. ## Response The response will be an array of account addresses (as strings) that were modified between the two specified blocks. * **result** (`array`): The addresses of the accounts modified between the specified blocks. ## Use case The `debug_getModifiedAccountsByNumber` method is essential for: * Developers debugging contract interactions over specific blocks. * Analysts tracking state changes for forensic or audit purposes. * Tools and services monitoring the Optimism state for changes to specific accounts. # eth_getProof | Optimism Source: https://docs.chainstack.com/reference/optimism-getproof openapi/optimism_node_api/eth_getProof.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getProof retrieves the account and storage values of the specified account, including the Merkle-proof. On Optimism. Optimism API method `eth_getProof` retrieves the account and storage values of the specified account, including the Merkle-proof. This method is used to verify the data without needing the entire state on the client side. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). > [!NOTE] > > On Global Nodes, this method returns a proof for the latest block only. A block number behind the tip returns `-32602` `distance to target block exceeds maximum proof window`. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). ## Parameters * `address` — the address of the account. * `storageKeys` — an array of storage keys to prove. * `block` — the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the state to consider. The default is `"latest"`. ## Response * `result` — an object containing the account proof, balance, code hash, nonce, storage hash, and storage proof for each requested storage key. ## Use case The `eth_getProof` method is essential for: * Applications that need to verify account or contract state without syncing the entire blockchain. * Services that provide cryptographic proofs of specific data to third parties. * Tools that audit the state of accounts or contracts at specific block heights. # eth_getRawTransactionByBlockHashAndIndex | Optimism Source: https://docs.chainstack.com/reference/optimism-getrawtransactionbyblockhashandindex openapi/optimism_node_api/eth_getRawTransactionByBlockHashAndIndex.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getRawTransactionByBlockHashAndIndex retrieves the raw transaction data as a hexadecimal string for a transaction in a specific block. Optimism API method `eth_getRawTransactionByBlockHashAndIndex` retrieves the raw transaction data as a hexadecimal string for a transaction in a specific block, identified by the block hash and the transaction's index position within that block. This method is useful for obtaining the complete transaction data, including its signature, without needing the transaction hash. ## Parameters * `blockHash` — the hash of the block containing the transaction. * `transactionIndex` — the index position of the transaction in the block, specified as a hexadecimal. The example uses `"0x1"`. ## Response * `result` — the raw transaction data as a hexadecimal string. ## Use case The `eth_getRawTransactionByBlockHashAndIndex` method is essential for: * Services that need to verify or inspect the complete transaction data and signature. * Wallets and exchanges that require the raw transaction for auditing or processing purposes. * Developers and tools that analyze transactions at a low level for security, performance, or other research purposes. # eth_getRawTransactionByBlockNumberAndIndex | Optimism Source: https://docs.chainstack.com/reference/optimism-getrawtransactionbyblocknumberandindex openapi/optimism_node_api/eth_getRawTransactionByBlockNumberAndIndex.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Reference docs for the eth_getRawTransactionByBlockNumberAndIndex JSON-RPC method on the Optimism blockchain, available via Chainstack JSON-RPC nodes. Optimism API method `eth_getRawTransactionByBlockNumberAndIndex` retrieves the raw transaction data as a hexadecimal string for a transaction in a specific block, identified by the block number and the transaction's index position within that block. This method is useful for obtaining the complete transaction data, including its signature, without needing the transaction hash. ## Parameters * `blockNumber` — the number of the block. * `transactionIndex` — the index position of the transaction in the block, specified as a hexadecimal. The example uses `"0x1"`. ## Response * `result` — the raw transaction data as a hexadecimal string. ## Use case The `eth_getRawTransactionByBlockNumberAndIndex` method is essential for: * Services that need to verify or inspect the complete transaction data and signature. * Wallets and exchanges that require the raw transaction for auditing or processing purposes. * Developers and tools that analyze transactions at a low level for security, performance, or other research purposes. # eth_getRawTransactionByHash | Optimism Source: https://docs.chainstack.com/reference/optimism-getrawtransactionbyhash openapi/optimism_node_api/eth_getRawTransactionByHash.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getRawTransactionByHash retrieves the raw transaction data as a hexadecimal string for a given transaction hash. Optimism API method `eth_getRawTransactionByHash` retrieves the raw transaction data as a hexadecimal string for a given transaction hash. This method is useful for obtaining the complete transaction data, including its signature. ## Parameters * `transactionHash` — the hash of the transaction to retrieve. ## Response * `result` — the raw transaction data as a hexadecimal string. ## Use case The `eth_getRawTransactionByHash` method is essential for: * Services that need to verify or inspect the complete transaction data and signature. * Wallets and exchanges that require the raw transaction for auditing or processing purposes. * Developers and tools that analyze transactions at a low level for security, performance, or other research purposes. # eth_getStorageAt | Optimism Source: https://docs.chainstack.com/reference/optimism-getstorageat openapi/optimism_node_api/eth_getStorageAt.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getStorageAt retrieves the value from a specific storage position of a contract at a given address. On Optimism. Optimism API method `eth_getStorageAt` retrieves the value from a specific storage position of a contract at a given address. This method is useful for inspecting the state of smart contracts at specific points in their execution or at a particular block. This example makes a call to slot 7 of the [USDC contract](https://optimistic.etherscan.io/token/0x0b2c639c533813f4aa9d7837caf62653d097ff85) on the Optimism Mainnet. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the contract to access. * `position` — the storage position to inspect, given as a hexadecimal value. * `block` — the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the state to consider. The default is `"latest"`. ## Response * `result` — the value from the specified storage position at the given address, returned as a hexadecimal string. ## Use case The `eth_getStorageAt` method is essential for: * Developers and auditors who need to verify the state and behavior of smart contracts by inspecting their storage. * DApps that require access to contract state that is not exposed through contract methods. * Tools and services that monitor contract state for changes, updates, or anomalies. # debug_storageRangeAt | Optimism Source: https://docs.chainstack.com/reference/optimism-getstoragerangeat openapi/optimism_node_api/debug_storageRangeAt.json POST /efb0a5eccd2caa5135eb54eba6f7f300 The debug_storageRangeAt RPC method retrieves storage entries from a contract at a specific block. Use it on Optimism via Chainstack. The `debug_storageRangeAt` RPC method retrieves storage entries from a contract at a specific block. This method is useful for developers and analysts who need to inspect the state of a contract's storage at a particular point in time. ## Request To use `debug_storageRangeAt`, send a POST request with a JSON RPC call in the body. ## Parameters * **Block Hash** (`string`): The hash of the block. * **Transaction Index** (`integer`): The index of the transaction in the block. * **Contract Address** (`string`): The address of the contract. * **Storage Key** (`string`): The starting point of the storage to inspect. * **Max Results** (`integer`): The maximum number of storage entries to return. ## Response The response includes storage entries within the specified range and a flag indicating if the storage snapshot is complete. * **storage** (`object`): A map of storage entries. * **complete** (`boolean`): Indicates whether the snapshot of storage is complete. ## Use case The `debug_storageRangeAt` method is essential for: * Developers debugging smart contracts by inspecting storage states at specific blocks. * Analysts conducting forensic analysis of contract interactions. * Tools and services that provide insights into contract state changes over time. # eth_getTransactionByBlockHashAndIndex | Optimism Source: https://docs.chainstack.com/reference/optimism-gettransactionbyblockhashandindex openapi/optimism_node_api/eth_getTransactionByBlockHashAndIndex.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Reference docs for the eth_getTransactionByBlockHashAndIndex JSON-RPC method on the Optimism blockchain, available via Chainstack JSON-RPC nodes. Optimism API method `eth_getTransactionByBlockHashAndIndex` retrieves information about a transaction by specifying the block hash and the transaction's index position within the block. This method is useful for fetching specific transactions when the block hash and index are known. ## Parameters * `blockHash` — the hash of the block. * `transactionIndex` — the index position of the transaction in the block, specified as a hexadecimal. The example uses `"0x1"`. ## Response * `result` — an object containing details about the transaction, including the block hash, block number, from address, gas used, gas price, hash, input data, nonce, to address, transaction index, value transferred, and the signature components (v, r, s). ## Use case The `eth_getTransactionByBlockHashAndIndex` method is essential for: * Applications that need to retrieve specific transactions from a known block for analysis or display. * Services that monitor blocks and transactions for security, compliance, or other operational reasons. * Tools that reconstruct transaction sequences within blocks for auditing or forensic purposes. # eth_getTransactionByBlockNumberAndIndex | Optimism Source: https://docs.chainstack.com/reference/optimism-gettransactionbyblocknumberandindex openapi/optimism_node_api/eth_getTransactionByBlockNumberAndIndex.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Reference docs for the eth_getTransactionByBlockNumberAndIndex JSON-RPC method on the Optimism blockchain, available via Chainstack JSON-RPC nodes. Optimism API method `eth_getTransactionByBlockNumberAndIndex` retrieves information about a transaction by specifying the block number and the transaction's index position within the block. This method is useful for fetching specific transactions when the block number and index are known. ## Parameters * `blockNumber` — the number of the block. * `transactionIndex` — the index position of the transaction in the block, specified as a hexadecimal. The example uses `"0x1"`. ## Response * `result` — an object containing details about the transaction, including the block hash, block number, from address, gas used, gas price, hash, input data, nonce, to address, transaction index, value transferred, and the signature components (v, r, s). ## Use case The `eth_getTransactionByBlockNumberAndIndex` method is essential for: * Applications that need to retrieve specific transactions from a known block for analysis or display. * Services that monitor blocks and transactions for security, compliance, or other operational reasons. * Tools that reconstruct transaction sequences within blocks for auditing or forensic purposes. # eth_getTransactionByHash | Optimism Source: https://docs.chainstack.com/reference/optimism-gettransactionbyhash openapi/optimism_node_api/eth_getTransactionByHash.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getTransactionByHash retrieves information about a transaction given its hash. Chainstack Optimism reference. Optimism API method `eth_getTransactionByHash` retrieves information about a transaction given its hash. This method is crucial for tracking transaction details and status. ## Parameters * `transactionHash` — the hash of the transaction to retrieve. ## Response * `result` — an object containing details about the transaction, including the block hash, block number, from address, gas used, gas price, hash, input data, nonce, to address, transaction index, value transferred, and the signature components (v, r, s). ## Use case The `eth_getTransactionByHash` method is essential for: * Wallets and exchanges that track transactions to update user balances and display transaction statuses. * DApps that need to verify transaction execution or retrieve transaction inputs for processing. * Services that monitor the Optimism network for specific transaction patterns or activities. # eth_getTransactionCount | Optimism Source: https://docs.chainstack.com/reference/optimism-gettransactioncount openapi/optimism_node_api/eth_getTransactionCount.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Reference docs for the eth_getTransactionCount JSON-RPC method on the Optimism blockchain, available via Chainstack JSON-RPC nodes. Optimism API method `eth_getTransactionCount` retrieves the number of transactions sent from a specified address, often used to determine the nonce for the next transaction from that address. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address to get the transaction count for. In this example. * `block` — the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"`, indicating the state to consider. The default is `"latest"`. ## Response * `result` — the number of transactions sent from the specified address, returned as a hexadecimal string. ## Use case The `eth_getTransactionCount` method is essential for: * Wallets and transaction services that need to calculate the nonce for new transactions. * Applications that monitor account activity, including outgoing transactions. * Tools that analyze the history and behavior of accounts on the Ethereum blockchain. # eth_getTransactionReceipt | Optimism Source: https://docs.chainstack.com/reference/optimism-gettransactionreceipt openapi/optimism_node_api/eth_getTransactionReceipt.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_getTransactionReceipt retrieves the receipt of a transaction by its hash. Use it on Optimism via Chainstack. Optimism API method `eth_getTransactionReceipt` retrieves the receipt of a transaction by its hash. This receipt includes details such as the transaction's outcome (status), gas used, and logs generated by the transaction. This method is crucial for confirming transaction execution and for interacting with smart contracts. ## Parameters * `transactionHash` — the hash of the transaction for which the receipt is being requested. ## Response * `Transaction receipt` — the receipt object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the contract address created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects this transaction generates, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the recipient's address of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `l1Fee` — this field represents the cost incurred for executing the transaction on the Ethereum mainnet (Layer 1). It is calculated by multiplying the `l1GasPrice` by the `l1GasUsed`. * `l1FeeScalar` — a scaling factor applied to the actual gas consumption on the Ethereum mainnet (Layer 1). This value is used to determine the dynamic costs associated with the transaction. Setting to 1 does not alter the Layer 1 gas usage. * `l1GasPrice` — the price of gas, measured in Wwei, that the transaction is willing to pay for execution on the Ethereum mainnet (Layer 1). * `l1GasUsed` — the total amount of gas units the transaction consumes during its execution on the Ethereum mainnet (Layer 1). The `l1Fee`, `l1FeeScalar`, `l1GasPrice`, and `l1GasUsed` fields are specific to Optimism, an Optimistic Rollup solution for scaling Ethereum. These fields provide information about the transaction's interaction with the Layer 1 (Ethereum mainnet) chain, which is necessary for the security and validity of the Optimistic Rollup. ## Use case The `eth_getTransactionReceipt` method is essential for: * DApps that need to confirm the outcome of a transaction, especially after interacting with smart contracts. * Services that monitor transactions for confirmation, gas usage, and event logging. * Tools that analyze the efficiency and effects of transactions on the Optimism network. # eth_getUncleCountByBlockHash | Optimism Source: https://docs.chainstack.com/reference/optimism-getunclecountbyblockhash openapi/optimism_node_api/eth_getUncleCountByBlockHash.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method that retrieves the number of uncles in a block, identified by its hash. eth_getUncleCountByBlockHash on Optimism via Chainstack. Optimism API method that retrieves the number of uncles in a block, identified by its hash. Uncles are blocks that are not part of the main chain but are still valid and have a relation to a block on the main chain. ## Parameters * `hash` — the hash of the block for which the uncle count is being queried, as a hexadecimal string. ## Response * `result` — the number of uncles in the specified block, encoded as a hexadecimal string. This count includes all uncles that were referenced by the block when it was mined. ## Use case The `eth_getUncleCountByBlockHash` method is useful for applications that analyze blockchain security and decentralization aspects, such as block explorers or network analysis tools. It can help these applications understand the level of block competition and the rate at which uncles are included in the blockchain. # eth_getUncleCountByBlockNumber | Optimism Source: https://docs.chainstack.com/reference/optimism-getunclecountbyblocknumber openapi/optimism_node_api/eth_getUncleCountByBlockNumber.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method that retrieves the number of uncles in a block, identified by its number. Available on Optimism via Chainstack. Optimism API method that retrieves the number of uncles in a block, identified by its number. Uncles are blocks that are not part of the main blockchain but are still valid and have a relationship to a block on the main chain. ## Parameters * `blockNumber` — the number of the block for which the uncle count is being queried, encoded as a hexadecimal string. ## Response * `result` — the number of uncles in the specified block, encoded as a hexadecimal string. This includes all uncles that were referenced by the block at the time it was sealed. ## Use case The `eth_getUncleCountByBlockNumber` method is important for applications that monitor the health and security of the blockchain, such as network analysis tools or block explorers. It provides insights into the level of competition among miners and the overall inclusiveness of the blockchain towards valid blocks that did not make it to the main chain. # net_listening | Optimism Source: https://docs.chainstack.com/reference/optimism-listening openapi/optimism_node_api/net_listening.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method net_listening checks if the client is actively listening for network connections. Chainstack Optimism reference. Optimism API method `net_listening` checks if the client is actively listening for network connections. This method is useful for diagnosing network issues or confirming the node's network status. ## Parameters This method does not require any parameters. ## Response * `result` — a boolean value indicating whether the client is actively listening for network connections. ## Use case The `net_listening` method is essential for: * Network administrators and node operators to verify the operational status of their Optimism client. * Developers and services that need to ensure their node is correctly configured to participate in the network. * Automated systems that monitor the health and connectivity of Optimism nodes. # eth_maxPriorityFeePerGas | Optimism Source: https://docs.chainstack.com/reference/optimism-maxpriorityfeepergas openapi/optimism_node_api/eth_maxPriorityFeePerGas.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_maxPriorityFeePerGas queries the current maximum priority fee per gas. Available on Optimism via Chainstack. Optimism API method `eth_maxPriorityFeePerGas` queries the current maximum priority fee per gas. This method is particularly useful in periods of high network congestion to estimate the appropriate priority fee for a transaction. ## Parameters This method does not require any parameters. ## Response * `result` — the current maximum priority fee per gas, returned as a hexadecimal string. ## Use case The `eth_maxPriorityFeePerGas` method is essential for: * Wallets and transaction builders that dynamically adjust transaction fees to ensure timely inclusion in a block. * Users and services looking to estimate transaction costs more accurately during periods of network congestion. * Developers and analysts monitoring Optimism network fee dynamics. # eth_newBlockFilter | Optimism Source: https://docs.chainstack.com/reference/optimism-newblockfilter openapi/optimism_node_api/eth_newBlockFilter.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_newBlockFilter creates a new filter object that notifies subscribers when a new block is added to the blockchain. Optimism API method `eth_newBlockFilter` creates a new filter object that notifies subscribers when a new block is added to the blockchain. This method is particularly useful for applications that need to stay updated with the latest blocks for processing or monitoring purposes. ## Parameters * `none` — This method does not require any parameters. ## Response * `result` — the ID of the created filter. This ID is used to identify the filter in subsequent calls to methods like `eth_getFilterChanges` or `eth_getFilterLogs` to retrieve updates about new blocks. ## Use case The `eth_newBlockFilter` method is essential for: * Applications that perform actions triggered by the addition of new blocks, such as updating a user interface with the latest transactions or block information. * Services that monitor the blockchain for security or analytical purposes, needing real-time data on block additions. * Wallets and exchanges that track new blocks to confirm transactions. # eth_newFilter | Optimism Source: https://docs.chainstack.com/reference/optimism-newfilter openapi/optimism_node_api/eth_newFilter.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_newFilter creates a new filter object for monitoring log entries that match specified criteria. On Optimism. Optimism API method `eth_newFilter` creates a new filter object for monitoring log entries that match specified criteria. This method is crucial for applications that need to track events or changes in smart contracts. The example tracks all transfers of the [Chainlink token](https://optimistic.etherscan.io/token/0x350a791bfc2c21f9ed5d10980dad2e2638ffa7f6). Once you install the filter, track the changes with [eth\_getFilterChanges](/reference/getfilterchanges). ## Parameters * `filterObject` — an object containing filter options: * `fromBlock` — (optional) the block number (in hexadecimal) or one of the strings `"earliest"`, `"latest"`, or `"pending"` from where to start filtering. Default is `"latest"`. * `address` — (optional) an address or a list of addresses to only get logs from specific contracts. * `topics` — (optional) an array of topics to filter for log entries. Each event signature generated by the EVM logging mechanism has a unique topic. ## Response * `result` — the ID of the created filter. This ID is used to identify and manage the filter with other filter methods like `eth_getFilterChanges` or `eth_getFilterLogs`. ## Use case The `eth_newFilter` method is essential for: * DApps that need to react to specific events emitted by smart contracts, such as token transfers or contract state changes. * Wallets and services that provide users with notifications based on contract interactions. * Developers and analysts monitoring specific contracts or topics for changes or patterns. # eth_newPendingTransactionFilter | Optimism Source: https://docs.chainstack.com/reference/optimism-newpendingtransactionfilter openapi/optimism_node_api/eth_newPendingTransactionFilter.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method that creates a filter in the node to notify when new pending transactions are received. Chainstack Optimism reference. Optimism API method that creates a filter in the node to notify when new pending transactions are received. To check if the state has changed, call eth\_getFilterChanges with the filter ID returned by this method. > [!NOTE] > > On Optimism Mainnet, this filter reports transactions as the sequencer includes them in subblocks, because the mempool is [private to the sequencer](/docs/mempool-configuration). For the pre-confirmed transactions and state, poll the `pending` block tag as described in [Track real-time transactions on Optimism with Subblocks](/docs/optimism-streaming-transactions-flashblocks). ## Parameters * `none` ## Response * `result` — a string representing the filter ID. This ID should be used in subsequent calls to eth\_getFilterChanges to retrieve new pending transactions. ## Use case The `eth_newPendingTransactionFilter` method is useful for applications that need to monitor the mempool for new transactions. Common use cases include: * Building transaction monitoring tools * Creating real-time transaction tracking interfaces * Implementing MEV (Maximal Extractable Value) strategies * Monitoring specific addresses for pending transactions On Optimism Mainnet, `eth_subscribe("newPendingTransactions")` and `eth_newPendingTransactionFilter` report transactions as the sequencer includes them in subblocks, not from a public mempool, because the mempool is private to the sequencer. They suit tracking transactions as they are pre-confirmed, but not strategies that need to see a transaction before the sequencer includes it. To read the pre-confirmed transactions and state, poll `eth_getBlockByNumber` with the `pending` tag as described in [Track real-time transactions on Optimism with Subblocks](/docs/optimism-streaming-transactions-flashblocks). Note: After creating the filter, you need to poll `eth_getFilterChanges` periodically to get updates. Filters timeout if they're not used for a period of time, so applications should be prepared to recreate them if necessary. # eth_sendRawTransaction | Optimism Source: https://docs.chainstack.com/reference/optimism-sendrawtransaction openapi/optimism_node_api/eth_sendRawTransaction.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_sendRawTransaction allows for the submission of a pre-signed transaction to the Optimism network for broadcast. Optimism API method `eth_sendRawTransaction` allows for the submission of a pre-signed transaction to the Optimism network for broadcast. This method is crucial for executing transactions from externally owned accounts without relying on Optimism node wallets for signing. ## Parameters * `signedTransactionData` — the signed transaction data in hexadecimal format. This data includes the transaction details such as nonce, gas price, gas limit, to address, value, data, and the signature. ## Response * `result` — the hash of the successfully submitted transaction. ## Use case The `eth_sendRawTransaction` method is essential for: * Wallets and other applications that sign transactions offline or in a secure environment and then submit them to the network. * Services that batch or automate transaction submissions. * Users and developers requiring control over the transaction signing process for security reasons. # eth_sendRawTransactionSync | Optimism Source: https://docs.chainstack.com/reference/optimism-sendrawtransactionsync openapi/optimism_node_api/eth_sendRawTransactionSync.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method that submits a signed transaction and blocks until the transaction receipt is returned or the timeout expires. Optimism API method that submits a signed transaction and blocks until the transaction receipt is returned or the timeout expires. This is a synchronous version of [`eth_sendRawTransaction`](/reference/optimism-sendrawtransaction) — instead of returning just the transaction hash, it waits for the transaction to be included in a block and returns the full receipt. This method is defined by [EIP-7966](https://eips.ethereum.org/EIPS/eip-7966) and is available on op-reth builds that include the EIP-7966 implementation. > [!NOTE] > > Optimism's \~2-second block time means sync calls usually return a receipt well within typical timeouts. Pick a `timeout` that fits your application — the request returns once a receipt is available or once the timeout expires. ## Parameters * `signedTransactionData` — the signed transaction data in hexadecimal format. Includes nonce, gas price, gas limit, recipient address, value, data, and signature. * `timeout` (optional) — maximum wait time in milliseconds, passed as a **decimal integer** per EIP-7966 (e.g., `5000`). If omitted, the node uses its configured default. Note that Polygon (Bor) uses a different encoding — see the [Polygon equivalent](/reference/polygon-sendrawtransactionsync). ## Response * `result` — the full transaction receipt object once the transaction is included in a block, with the same fields as [`eth_getTransactionReceipt`](/reference/optimism-gettransactionreceipt). If the timeout expires before inclusion, the node returns an error. ## `eth_sendRawTransactionSync` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const sendTransactionSync = async () => { const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); const tx = { to: "0xRecipientAddress", value: ethers.parseEther("0"), chainId: 10, }; // Sign the transaction const signedTx = await wallet.signTransaction(tx); // Send synchronously — blocks until receipt is returned or timeout expires const receipt = await provider.send( "eth_sendRawTransactionSync", [signedTx, 5000] ); console.log(`Confirmed in block: ${receipt.blockNumber}`); console.log(`Status: ${receipt.status}`); }; sendTransactionSync(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) account = web3.eth.account.from_key("YOUR_PRIVATE_KEY") tx = { 'nonce': web3.eth.get_transaction_count(account.address), 'to': "0xRecipientAddress", 'value': 0, 'gas': 21000, 'gasPrice': web3.eth.gas_price, 'chainId': 10, # Optimism mainnet } signed = account.sign_transaction(tx) # Send synchronously with a 5-second timeout (decimal integer per EIP-7966) receipt = web3.provider.make_request( "eth_sendRawTransactionSync", [signed.raw_transaction.hex(), 5000] ) print(f"Receipt: {receipt['result']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_sendRawTransactionSync", "params": ["SIGNED_TX_DATA", 5000], "id": 1}' ``` ## When to use Use `eth_sendRawTransactionSync` instead of `eth_sendRawTransaction` when: * You need the receipt immediately and want to avoid polling with `eth_getTransactionReceipt`. * You are building synchronous request/response flows (payments, bots, simulations). * You want to simplify transaction submission logic by getting the result in a single call. For fire-and-forget scenarios or when submitting many transactions in parallel, use [`eth_sendRawTransaction`](/reference/optimism-sendrawtransaction). # web3_sha3 | Optimism Source: https://docs.chainstack.com/reference/optimism-sha3 openapi/optimism_node_api/web3_sha3.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method web3_sha3 computes the Keccak-256 hash of the given data. Available on Optimism via Chainstack JSON-RPC nodes. Optimism API method `web3_sha3` computes the Keccak-256 hash of the given data. This method is a utility function often used in Optimism for generating hashes of arbitrary data, which is a common requirement for various cryptographic operations. This example computes Keccak-256 for the value `Chainstack`. `Chainstack` in hex: `0x436861696E737461636B`. ## Parameters * `data` — the data to hash, typically provided as a hexadecimal string. ## Response * `result` — the Keccak-256 hash of the input data, returned as a hexadecimal string. ## Use case The `web3_sha3` method is essential for: * Generating identifiers or hashes for on-chain data storage or referencing. * Creating cryptographic proofs that require a hash of data. * Developers needing to perform off-chain hash calculations that match the Optimism network's hashing algorithm. # eth_subscribe logs | Optimism Source: https://docs.chainstack.com/reference/optimism-subscribelogs Subscribe to real-time Optimism event logs via WebSocket. Receive notifications whenever new logs matching your specified filter criteria are emitted. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) the event filter options: * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about topics and event logs. ## Response * `subscription` — the subscription ID. * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_subscribe("logs")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_subscribe", "params": ["logs", {"address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewLogs() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['logs', {"address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewLogs(); ``` This will generate a continuous stream of data displaying new event logs matching the filter. Use [eth\_unsubscribe](/reference/ethereum-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("logs")` method in Optimism is used to subscribe to the logs generated by smart contracts on the blockchain. These logs contain information about events that have occurred on the blockchain, such as details of a smart contract function and outputs of transactions. A practical and real-world scenario for using the `eth_subscribe("logs")` method could be in the context of a decentralized exchange (DEX). In the context of a DEX, trades are executed using smart contracts that run on the Optimism blockchain. When a trade is executed on a DEX, a smart contract is called to process the transaction. This smart contract generates a log that contains information about the trade, such as the amount of cryptocurrency being traded, the parties involved, and so on. By using the `eth_subscribe("logs")` method, a user or application can subscribe to the logs generated by the DEX's smart contracts. This can be useful for tracking trades in real time, monitoring the performance of the DEX, and generating reports on trading activity. For example, a trader might use the `eth_subscribe("logs")` method to monitor the logs generated by a DEX in order to identify trends in trading activity. They might look for patterns in the volume and price of trades, which could help them make more informed trading decisions. Similarly, an analytics platform might use the `eth_subscribe("logs")` method to collect data on trading activity across multiple DEXs. By analyzing this data, they could generate insights into the performance of different DEXs, which could be useful for investors and traders looking to make informed decisions about where to trade. # eth_subscribe newHeads | Optimism Source: https://docs.chainstack.com/reference/optimism-subscribenewheads Subscribe to new Optimism block headers via WebSocket. Receive real-time notifications whenever a new block is added to the Optimism blockchain. Optimism API method allowing developers to receive real-time notifications regarding new block headers on the Optimism blockchain; it sends notifications whenever a new block is added. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `newHeads` in this case. ## Response * `subscription` — the subscription ID. * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Optimism nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Optimism blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been sealed in the Optimism network since the inception of the network. It measures the overall security and integrity of the Optimism network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `eth_subscribe("newHeads")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewBlocks() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewBlocks(); ``` This will generate a continuous stream of data displaying new block headers as they are added to the chain. Use [eth\_unsubscribe | Optimism](/reference/optimism-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newHeads")` method can be useful in a variety of real-world scenarios, such as: * Real-time monitoring of blockchain activity. Companies or individuals who rely on the Optimism network for their business can use `eth_subscribe("newHeads")` to receive real-time notifications about new blocks being added to the chain. This can help them keep track of important transactions, such as high-value trades, in real time and respond quickly to any issues that may arise. * Decentralized Finance (DeFi) applications. DeFi applications, such as lending platforms, can use `eth_subscribe("newHeads")` to monitor the blockchain for new blocks that might contain important data, such as changes in interest rates or new lending opportunities. * Smart contract automation. Developers can use `eth_subscribe("newHeads")` to trigger automated smart contract actions whenever a new block is added to the chain. For example, a smart contract could automatically execute an action order whenever a new block is added that meets certain criteria. # eth_syncing | Optimism Source: https://docs.chainstack.com/reference/optimism-syncing openapi/optimism_node_api/eth_syncing.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_syncing checks if the node is currently syncing with the blockchain. Available on Optimism via Chainstack. Optimism API method `eth_syncing` checks if the node is currently syncing with the blockchain. It provides details about the sync progress if the node is syncing. ## Parameters * `none` — This method does not require any parameters. ## Response * `result` — an object containing syncing details if the node is syncing; otherwise, `false`. The object includes: * `startingBlock` — the block number where the sync started. * `currentBlock` — the block number where the node is currently synced to. * `highestBlock` — the highest block number known in the chain that the node is syncing towards. If the node is not syncing, the result will be `false`. ## Use case The `eth_syncing` method is useful for: * Monitoring tools and services that track the sync status of Optimism nodes. * Developers and users who want to check if a node is fully synced before making transactions or deploying contracts. # debug_traceBlockByNumber | Optimism Source: https://docs.chainstack.com/reference/optimism-traceblockbynumber openapi/optimism_node_api/debug_traceBlockByNumber.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Reference docs for the debug_traceBlockByNumber JSON-RPC method on the Optimism blockchain, available via Chainstack JSON-RPC nodes. The `debug_traceBlockByNumber` RPC method is used to retrieve detailed execution traces for all transactions included in a block, identified by the block's number. This method is particularly useful for developers and analysts who need to deeply understand transaction execution and state changes within a specific block. ## Request To use `debug_traceBlockByNumber`, send a POST request with a JSON RPC call in the body. ## Parameters * **Block Number** (`string`): The number of the block for which to retrieve the execution traces, in hexadecimal format. ## Response The response includes detailed execution traces for all transactions in the specified block. * **result** (`array`): An array of execution traces for each transaction in the block. Each trace provides comprehensive details about the execution, including calls, state changes, and gas usage. ## Use case The `debug_traceBlockByNumber` method is essential for: * Developers debugging complex interactions within a block. * Analysts conducting forensic analysis of block transactions. * Tools and services that provide insights into the Ethereum Virtual Machine (EVM) execution and state changes. # debug_traceCall | Optimism Source: https://docs.chainstack.com/reference/optimism-tracecall openapi/optimism_node_api/debug_traceCall.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Executes a call and retrieves a detailed execution trace of the call, similar to a transaction but without. debug_traceCall on Optimism via Chainstack. Executes a call and retrieves a detailed execution trace of the call, similar to a transaction but without creating a transaction on the blockchain. This method is crucial for developers and analysts who need to understand the execution flow of a call, including all the operations (opcodes) executed, gas usage, and the call's outcome. ## Request To use `debug_traceCall`, send a POST request with a JSON RPC call in the body. ## Parameters * **from** (`string`): The address the call is sent from. * **to** (`string`): The address the call is directed to. * **gasPrice** (`string`): The gas price for the call. * **data** (`string`): The data sent along with the call. ## Response The response includes a detailed execution trace of the call, providing insights into each step of the call's execution. * **result** (`object`): The detailed execution trace, including: * **structLogs** (`array`): An array of execution steps (opcodes) taken by the call. * **gas** (`integer`): The gas provided by the call. * **returnValue** (`string`): The return value of the call, if any. * **gasUsed** (`integer`): The total gas used by the call. * **failed** (`boolean`): Indicates whether the call failed. ## Use case The `debug_traceCall` method is essential for: * Developers debugging smart contract interactions. * Analysts conducting forensic analysis of calls. * Tools and services that provide insights into Ethereum Virtual Machine (EVM) execution. # debug_traceCallMany | Optimism Source: https://docs.chainstack.com/reference/optimism-tracecallmany openapi/optimism_node_api/debug_traceCallMany.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Executes a list of bundles (a bundle is a collection of transactions), without creating transactions on the. debug_traceCallMany on Optimism via Chainstack. Executes a list of bundles (a bundle is a collection of transactions), without creating transactions on the blockchain. Returns their traces. This method is akin to `eth_callMany` but provides detailed execution traces for each call, making it invaluable for debugging complex interactions involving multiple transactions. ## Request To use `debug_traceCallMany`, send a POST request with a JSON RPC call in the body. ## Parameters * **transactions** (`array`): An array of transaction objects to be executed. Each transaction object includes: * **from** (`string`): The address the transaction is sent from (optional). * **to** (`string`): The address the transaction is directed to. * **gas** (`string`): The gas limit for the transaction. * **gasPrice** (`string`): The gas price for the transaction. * **data** (`string`): The data sent along with the transaction. * **blockOverride** (`object`): Optional overrides for the execution context block header, including: * **blockNumber** (`string`): Block number for the execution context. * **Simulation Context** (`object`): The context for the simulation, including block number and transaction index. * **State Overrides** (`object`): Optional state overrides for the execution context. ## Response The response includes detailed execution traces for each call in the bundles, providing insights into each step of the transactions' execution. * **result** (`array`): An array of execution traces for each call, including: * **structLogs** (`array`): An array of execution steps (opcodes) taken by the call. * **gas** (`integer`): The gas provided by the call. * **returnValue** (`string`): The return value of the call, if any. * **gasUsed** (`integer`): The total gas used by the call. * **failed** (`boolean`): Indicates whether the call failed. ## Use case The `debug_traceCallMany` method is essential for: * Developers debugging complex interactions involving multiple transactions. * Analysts conducting forensic analysis of transaction sequences. * Tools and services that provide insights into Ethereum Virtual Machine (EVM) execution across multiple calls. # debug_traceTransaction | Optimism Source: https://docs.chainstack.com/reference/optimism-tracetransaction openapi/optimism_node_api/debug_traceTransaction.json POST /efb0a5eccd2caa5135eb54eba6f7f300 The debug_traceTransaction RPC method retrieves a detailed execution trace of a transaction, identified by. debug_traceTransaction on Optimism via Chainstack. The `debug_traceTransaction` RPC method retrieves a detailed execution trace of a transaction, identified by the transaction's hash. This method is crucial for developers and analysts who need to understand the execution flow of a transaction, including all the operations (opcodes) executed, gas usage, and the transaction's outcome. ## Request To use `debug_traceTransaction`, send a POST request with a JSON RPC call in the body. ## Parameters * **Transaction Hash** (`string`): The hash of the transaction to trace. ## Response The response includes a detailed execution trace of the transaction, providing insights into each step of the transaction's execution. * **result** (`object`): The detailed execution trace, including: * **structLogs** (`array`): An array of execution steps (opcodes) taken by the transaction. * **gas** (`integer`): The gas provided by the transaction. * **returnValue** (`string`): The return value of the transaction, if any. * **gasUsed** (`integer`): The total gas used by the transaction. * **failed** (`boolean`): Indicates whether the transaction failed. ## Use case The `debug_traceTransaction` method is essential for: * Developers debugging smart contract interactions. * Analysts conducting forensic analysis of transactions. * Tools and services that provide insights into Ethereum Virtual Machine (EVM) execution. # eth_uninstallFilter | Optimism Source: https://docs.chainstack.com/reference/optimism-uninstallfilter openapi/optimism_node_api/eth_uninstallFilter.json POST /efb0a5eccd2caa5135eb54eba6f7f300 Optimism API method eth_uninstallFilter removes a previously installed filter, stopping further notifications. Optimism via Chainstack. Optimism API method `eth_uninstallFilter` removes a previously installed filter, stopping further notifications. This method is crucial for managing resources and avoiding unnecessary data processing from filters that are no longer needed. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter | Optimism](/reference/newfilter) > * [eth\_newBlockFilter | Optimism](/reference/optimism-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `filterId` — the ID of the filter to be uninstalled. This ID is returned by filter creation methods such as `eth_newBlockFilter`, or `eth_newFilter`. For this example, a random value `"0x1a2b3c"` is used. ## Response * `result` — a boolean indicating the success of the filter removal. `true` if the filter was successfully uninstalled; otherwise, `false`. ## Use case The `eth_uninstallFilter` method is essential for: * Applications that dynamically create and remove filters based on user interaction or specific conditions to manage system resources efficiently. * Services that need to clean up filters to prevent processing outdated or irrelevant data. * Developers who are testing or debugging Optimism filters and need to reset their filter setup. # eth_unsubscribe | Optimism Source: https://docs.chainstack.com/reference/optimism-unsubscribe Cancel any active Optimism WebSocket subscription with eth_unsubscribe. Pass the subscription ID to stop receiving real-time event notifications. When a client subscribes to a particular event, the Optimism node will send updates as soon as they occur. However, in some cases, the client may no longer be interested in receiving updates for a particular event. The `eth_unsubscribe` method can be used to cancel the following subscriptions: > [card] [eth\_subscribe('logs')](/reference/optimism-subscribelogs) > [card] [eth\_subscribe('newHeads')](/reference/optimism-subscribenewheads) ## Parameters * `string` — the ID of the subscription that you want to cancel ## Response * `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `eth_unsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > "jsonrpc":"2.0","id": 1, "method": "eth_unsubscribe", "params": ["0x7529626735859f32962b10b19ee2e3eb"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function removeSubscription() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_unsubscribe', params: ['0x9de3135dd3dd6c6b99bd104061a5af4f'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } removeSubscription(); ``` This will remove the subscription matching the ID passed as a parameter. # net_peerCount | Polygon Source: https://docs.chainstack.com/reference/peercount openapi/polygon_node_api/client_info/net_peerCount.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the number of peers connected to the node. Call net_peerCount on Polygon through Chainstack nodes. Polygon API method that returns the number of peers connected to the node. This method can be useful for developers who want to monitor the connectivity of their node and ensure that it is functioning as expected. By checking the number of connected peers, developers can verify that their node is receiving updates and is properly synchronized with the network. ## Parameters * `none` ## Response * `quantity` — an integer value indicating the current number of peers connected to the node ## `net_peerCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const peersCount = async () => { const peers = await provider.send("net_peerCount"); console.log(`Peers connected: ${peers}`); } peersCount() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) peers = web3.provider.make_request('net_peerCount', []) print(peers) ``` ## Use case One practical use case for the `net_peerCount` method is to monitor the number of peers connected to a node and take some action if the number drops below a certain threshold. For example, a developer might want to send a notification to check the node if the peer count is low for more than 10 minutes. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const MIN_PEER_COUNT = 5; // Set the minimum number of peers. let lowPeerCountTime = null; // Track the time when peer count first dropped below the threshold. /** * Checks the number of connected peers and sends a notification if the peer count has been low for more than 10 minutes. */ function checkPeerCount() { provider.send("net_peerCount").then((peerCountHex) => { const peerCount = Number(peerCountHex); // net_peerCount returns a hex string; convert it to a number. console.log(`Number of connected peers: ${peerCount}`); if (peerCount < MIN_PEER_COUNT) { // Peer count has dropped below the threshold. if (lowPeerCountTime === null) { // This is the first time we've seen a low peer count. lowPeerCountTime = Date.now(); console.log(`Low peer count detected at ${new Date(lowPeerCountTime)}.`); } else if (Date.now() - lowPeerCountTime > 10 * 60 * 1000) { // Peer count has been low for more than 10 minutes, send a notification. console.log('Sending notification to check the node.'); // TODO: Add code here to send a notification, e.g. via email or SMS. } } else { // Peer count is above the threshold. lowPeerCountTime = null; } }); } // Check the peer count once when the script starts. checkPeerCount(); // Check the peer count every minute. setInterval(checkPeerCount, 60 * 1000); ``` This code defines a function called `checkPeerCount` that retrieves the current number of connected peers by sending the `net_peerCount` RPC request with `provider.send`. Since `net_peerCount` returns a hexadecimal string, the function converts it to a number with `Number()`. The function then checks whether the peer count is below a minimum threshold (defined as `MIN_PEER_COUNT`), and takes appropriate action based on the result. If the peer count is below the threshold and this is the first time the count has dropped below the threshold, the function records the current time and logs a message to the console. If the peer count remains below the threshold for more than 10 minutes, the function logs a message to the console indicating that a notification should be sent and optionally sends a notification via email or SMS. If the peer count is above the threshold, the function resets the `lowPeerCountTime` variable to null. By running the `checkPeerCount` function periodically, we can ensure that we are notified if the peer count drops below a certain threshold and stays low for an extended period of time, which can indicate issues with the node's connectivity. # Accounts info | Plasma Source: https://docs.chainstack.com/reference/plasma-accounts-info-rpc-methods Plasma account information JSON-RPC methods — retrieve account balances, nonces, storage values, and contract bytecode for addresses on the Plasma network. Retrieving account information from the Plasma blockchain can be done using the following methods: > [card] [eth\_getBalance](/reference/plasma-eth-getbalance) > [card] [eth\_getCode](/reference/plasma-eth-getcode) > [card] [eth\_getStorageAt](/reference/plasma-eth-getstorageat) > [card] [eth\_getTransactionCount](/reference/plasma-eth-gettransactioncount) > [card] [eth\_getProof](/reference/plasma-eth-getproof) > [card] [eth\_getAccount](/reference/plasma-eth-getaccount) This information can be used for: * **Wallet applications** — display user balances and transaction counts * **Smart contract interaction** — read contract storage and bytecode * **Account monitoring** — track account state changes over time * **DApp development** — integrate account data into decentralized applications # Blocks info | Plasma Source: https://docs.chainstack.com/reference/plasma-blocks-info-rpc-methods Plasma block information JSON-RPC methods — query block details including transactions, timestamps, block height, and header data on the Plasma blockchain. Retrieving block information from the Plasma blockchain can be done using the following methods: > [card] [eth\_blockNumber](/reference/plasma-eth-blocknumber) > [card] [eth\_getBlockByNumber](/reference/plasma-eth-getblockbynumber) > [card] [eth\_getBlockByHash](/reference/plasma-eth-getblockbyhash) > [card] [eth\_getBlockTransactionCountByNumber](/reference/plasma-eth-getblocktransactioncountbynumber) > [card] [eth\_getBlockTransactionCountByHash](/reference/plasma-eth-getblocktransactioncountbyhash) > [card] [eth\_getBlockReceipts](/reference/plasma-eth-getblockreceipts) > [card] [eth\_getUncleCountByBlockNumber](/reference/plasma-eth-getunclecountbyblocknumber) > [card] [eth\_getUncleCountByBlockHash](/reference/plasma-eth-getunclecountbyblockhash) > [card] [eth\_getUncleByBlockNumberAndIndex](/reference/plasma-eth-getunclebyblocknumberandindex) > [card] [eth\_getUncleByBlockHashAndIndex](/reference/plasma-eth-getunclebyblockhashandindex) > [card] [eth\_getHeaderByNumber](/reference/plasma-eth-getheaderbynumber) > [card] [eth\_getHeaderByHash](/reference/plasma-eth-getheaderbyhash) This information can be used for: * **Blockchain analysis** — insights into the overall network activity * **Block explorers** — web-based tools to view and analyze blockchain data * **Smart contract analysis** — track contract activity at specific blocks * **DApp development** — utilize block information in decentralized applications # Chain info | Plasma Source: https://docs.chainstack.com/reference/plasma-chain-info-rpc-methods The chain info JSON-RPC methods allow developers to access blockchain-level information such as chain ID, sync status, and protocol version. Retrieving chain information from the Plasma blockchain can be done using the following methods: > [card] [eth\_chainId](/reference/plasma-eth-chainid) > [card] [eth\_syncing](/reference/plasma-eth-syncing) > [card] [eth\_protocolVersion](/reference/plasma-eth-protocolversion) > [card] [eth\_accounts](/reference/plasma-eth-accounts) > [card] [eth\_hashrate](/reference/plasma-eth-hashrate) This information can be used for: * **Network identification** — verify the correct blockchain network * **Sync monitoring** — track node synchronization status * **Protocol compatibility** — check client version and protocol support * **Multi-chain applications** — identify and switch between networks # Client info | Plasma Source: https://docs.chainstack.com/reference/plasma-client-info-rpc-methods The client info JSON-RPC methods allow developers to access information about the Plasma node client, including version, network ID, and peer connections. Retrieving client information from the Plasma node can be done using the following methods: > [card] [web3\_clientVersion](/reference/plasma-web3-clientversion) > [card] [web3\_sha3](/reference/plasma-web3-sha3) > [card] [net\_version](/reference/plasma-net-version) > [card] [net\_listening](/reference/plasma-net-listening) > [card] [net\_peerCount](/reference/plasma-net-peercount) This information can be used for: * **Node monitoring** — check client version and peer count * **Network diagnostics** — verify network connectivity * **Compatibility checks** — ensure client version compatibility * **Infrastructure management** — monitor node health and status # Debug and trace | Plasma Source: https://docs.chainstack.com/reference/plasma-debug-and-trace-rpc-methods The debug and trace JSON-RPC methods provide advanced debugging capabilities for developers, including transaction tracing, call tracing, and raw data access. Debugging and tracing on the Plasma blockchain can be done using the following methods: > [card] [debug\_getRawHeader](/reference/plasma-debug-getrawheader) > [card] [debug\_getRawBlock](/reference/plasma-debug-getrawblock) > [card] [debug\_getRawTransaction](/reference/plasma-debug-getrawtransaction) > [card] [debug\_getRawReceipts](/reference/plasma-debug-getrawreceipts) > [card] [debug\_traceCall](/reference/plasma-debug-tracecall) > [card] [trace\_call](/reference/plasma-trace-call) > [card] [trace\_callMany](/reference/plasma-trace-callmany) > [card] [trace\_replayBlockTransactions](/reference/plasma-trace-replayblocktransactions) > [card] [trace\_block](/reference/plasma-trace-block) > [card] [trace\_get](/reference/plasma-trace-get) This information can be used for: * **Transaction debugging** — analyze transaction execution in detail * **Smart contract analysis** — trace contract calls and state changes * **Error investigation** — identify and fix transaction failures * **Performance optimization** — analyze gas usage and execution paths # debug_getRawBlock | Plasma Source: https://docs.chainstack.com/reference/plasma-debug-getrawblock openapi/plasma_node_api/debug_and_trace/debug_getRawBlock.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the RLP-encoded block. quantity or tag — the block number in hex format or block tag (latest, earliest, pending, safe. Plasma API method that returns the RLP-encoded block. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `data` — the RLP-encoded block ## `debug_getRawBlock` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getRawBlock", [ "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getRawBlock", "params": [], "id": 1 }' ``` # debug_getRawHeader | Plasma Source: https://docs.chainstack.com/reference/plasma-debug-getrawheader openapi/plasma_node_api/debug_and_trace/debug_getRawHeader.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the RLP-encoded header. quantity or tag — the block number in hex format or block tag (latest, earliest, pending, safe. Plasma API method that returns the RLP-encoded header. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `data` — the RLP-encoded header ## `debug_getRawHeader` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getRawHeader", [ "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getRawHeader", "params": [], "id": 1 }' ``` # debug_getRawReceipts | Plasma Source: https://docs.chainstack.com/reference/plasma-debug-getrawreceipts openapi/plasma_node_api/debug_and_trace/debug_getRawReceipts.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the RLP-encoded receipts for a block. quantity or tag — the block number in hex format or block tag (latest, earliest, pending. Plasma API method that returns the RLP-encoded receipts for a block. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `data` — the RLP-encoded receipts ## `debug_getRawReceipts` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getRawReceipts", [ "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getRawReceipts", "params": [], "id": 1 }' ``` # debug_getRawTransaction | Plasma Source: https://docs.chainstack.com/reference/plasma-debug-getrawtransaction openapi/plasma_node_api/debug_and_trace/debug_getRawTransaction.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the RLP-encoded transaction. data — the hash of the transaction data — the RLP-encoded transaction. On Plasma. Plasma API method that returns the RLP-encoded transaction. ## Parameters * `data` — the hash of the transaction ## Response * `data` — the RLP-encoded transaction ## `debug_getRawTransaction` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getRawTransaction", [ "0xTRANSACTION_HASH" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getRawTransaction", "params": [], "id": 1 }' ``` # debug_traceCall | Plasma Source: https://docs.chainstack.com/reference/plasma-debug-tracecall openapi/plasma_node_api/debug_and_trace/debug_traceCall.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that runs an eth_call within the context of the given block execution and returns trace results. Plasma via Chainstack. Plasma API method that runs an eth\_call within the context of the given block execution and returns trace results. ## Parameters * `object` — the transaction call object * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `object` — (optional) tracer configuration options ## Response * `object` — the trace result ## `debug_traceCall` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_traceCall", [ { to: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", data: "0x" }, "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_traceCall", "params": [], "id": 1 }' ``` # eth_accounts | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-accounts openapi/plasma_node_api/chain_info/eth_accounts.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns a list of addresses owned by the client. Chainstack supports eth_accounts on Plasma JSON-RPC nodes. Plasma API method that returns a list of addresses owned by the client. ## Parameters None ## Response * `array` — an array of addresses owned by the client ## `eth_accounts` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_accounts", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1 }' ``` # eth_blobBaseFee | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-blobbasefee openapi/plasma_node_api/gas_data/eth_blobBaseFee.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the base fee for blob gas in Wei. eth_blobBaseFee JSON-RPC method available on the Plasma blockchain via Chainstack. Plasma API method that returns the base fee for blob gas in Wei. ## Parameters None ## Response * `quantity` — the current blob base fee in Wei ## `eth_blobBaseFee` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_blobBaseFee", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_blobBaseFee", "params": [], "id": 1 }' ``` # eth_blockNumber | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-blocknumber openapi/plasma_node_api/blocks_info/eth_blockNumber.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the number of the most recent block. eth_blockNumber JSON-RPC method available on the Plasma blockchain via Chainstack. Plasma API method that returns the number of the most recent block. ## Parameters None ## Response * `quantity` — the current block number the client is on ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_blockNumber", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1 }' ``` # eth_call | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-call openapi/plasma_node_api/execute_transactions/eth_call.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that executes a new message call immediately without creating a transaction on the blockchain. Plasma via Chainstack. Plasma API method that executes a new message call immediately without creating a transaction on the blockchain. ## Parameters * `object` — the transaction call object with fields like `from`, `to`, `gas`, `gasPrice`, `value`, `data` * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `data` — the return value of the executed contract ## `eth_call` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_call", [ { to: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", data: "0x" }, "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_call", "params": [], "id": 1 }' ``` # eth_chainId | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-chainid openapi/plasma_node_api/chain_info/eth_chainId.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the chain ID of the current network. eth_chainId JSON-RPC method available on the Plasma blockchain via Chainstack. Plasma API method that returns the chain ID of the current network. ## Parameters None ## Response * `quantity` — the chain ID ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_chainId", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_chainId", "params": [], "id": 1 }' ``` # eth_createAccessList | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-createaccesslist openapi/plasma_node_api/execute_transactions/eth_createAccessList.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that creates an EIP-2930 access list for a transaction. Reference for eth_createAccessList on Plasma via Chainstack. Plasma API method that creates an EIP-2930 access list for a transaction. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `object` — the transaction call object * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — an object with `accessList` and `gasUsed` ## `eth_createAccessList` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_createAccessList", [ { to: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", data: "0x" }, "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_createAccessList", "params": [], "id": 1 }' ``` # eth_estimateGas | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-estimategas openapi/plasma_node_api/execute_transactions/eth_estimateGas.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that generates and returns an estimate of how much gas is necessary for the transaction to complete. Plasma via Chainstack. Plasma API method that generates and returns an estimate of how much gas is necessary for the transaction to complete. ## Parameters * `object` — the transaction call object with fields like `from`, `to`, `gas`, `gasPrice`, `value`, `data` ## Response * `quantity` — the amount of gas used ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_estimateGas", [ { to: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" } ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_estimateGas", "params": [], "id": 1 }' ``` # eth_feeHistory | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-feehistory openapi/plasma_node_api/gas_data/eth_feeHistory.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns historical gas information for a range of blocks. Available on Plasma via Chainstack JSON-RPC nodes. Plasma API method that returns historical gas information for a range of blocks. ## Parameters * `quantity` — number of blocks in the requested range (between 1 and 1024) * `quantity or tag` — the highest block of the requested range * `array` — array of percentile values (between 0 and 100) ## Response * `object` — an object with fee history including `baseFeePerGas`, `gasUsedRatio`, and `reward` ## `eth_feeHistory` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_feeHistory", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_feeHistory", "params": [], "id": 1 }' ``` # eth_gasPrice | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-gasprice openapi/plasma_node_api/gas_data/eth_gasPrice.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the current price per gas in Wei. eth_gasPrice JSON-RPC method available on the Plasma blockchain via Chainstack. Plasma API method that returns the current price per gas in Wei. ## Parameters None ## Response * `quantity` — the current gas price in Wei ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_gasPrice", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_gasPrice", "params": [], "id": 1 }' ``` # eth_getAccount | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getaccount openapi/plasma_node_api/accounts_info/eth_getAccount.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the account information for a given address. Reference for eth_getAccount on Plasma via Chainstack. Plasma API method that returns the account information for a given address. ## Parameters * `address` — the address of the account * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — account object with balance, nonce, codeHash, and storageRoot ## `eth_getAccount` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getAccount", [ "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getAccount", "params": [], "id": 1 }' ``` # eth_getBalance | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getbalance openapi/plasma_node_api/accounts_info/eth_getBalance.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the balance of a specific account in Wei, the smallest unit of ether. Use it on Plasma via Chainstack. Plasma API method that returns the balance of a specific account in Wei, the smallest unit of ether. ## Parameters * `address` — the address to check the balance of * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `quantity` — the integer value of the current balance in Wei ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBalance", [ "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBalance", "params": [], "id": 1 }' ``` # eth_getBlockByHash | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getblockbyhash openapi/plasma_node_api/blocks_info/eth_getBlockByHash.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns information about a block by block hash. data — the hash of the block boolean — if true, returns full transaction objects. Plasma API method that returns information about a block by block hash. ## Parameters * `data` — the hash of the block * `boolean` — if `true`, returns full transaction objects; if `false`, returns only transaction hashes ## Response * `object` — a block object, or `null` when no block was found ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBlockByHash", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBlockByHash", "params": [], "id": 1 }' ``` # eth_getBlockByNumber | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getblockbynumber openapi/plasma_node_api/blocks_info/eth_getBlockByNumber.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns information about a block by block number. quantity or tag — the block number in hex format or block tag (latest, earliest. Plasma API method that returns information about a block by block number. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `boolean` — if `true`, returns full transaction objects; if `false`, returns only transaction hashes ## Response * `object` — a block object, or `null` when no block was found ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBlockByNumber", [ "latest", false ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBlockByNumber", "params": [], "id": 1 }' ``` # eth_getBlockReceipts | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getblockreceipts openapi/plasma_node_api/blocks_info/eth_getBlockReceipts.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns all transaction receipts for a given block. quantity or tag — the block number in hex format or block tag (latest, earliest. Plasma API method that returns all transaction receipts for a given block. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `array` — an array of receipt objects for all transactions in the block ## `eth_getBlockReceipts` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBlockReceipts", [ "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBlockReceipts", "params": [], "id": 1 }' ``` # eth_getBlockTransactionCountByHash | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getblocktransactioncountbyhash openapi/plasma_node_api/blocks_info/eth_getBlockTransactionCountByHash.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the number of transactions in a block by block hash. data — the hash of the block. Plasma via Chainstack. Plasma API method that returns the number of transactions in a block by block hash. ## Parameters * `data` — the hash of the block ## Response * `quantity` — the number of transactions in the block ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBlockTransactionCountByHash", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByHash", "params": [], "id": 1 }' ``` # eth_getBlockTransactionCountByNumber | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getblocktransactioncountbynumber openapi/plasma_node_api/blocks_info/eth_getBlockTransactionCountByNumber.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the number of transactions in a block by block number. eth_getBlockTransactionCountByNumber on Plasma via Chainstack. Plasma API method that returns the number of transactions in a block by block number. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `quantity` — the number of transactions in the block ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBlockTransactionCountByNumber", [ "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByNumber", "params": [], "id": 1 }' ``` # eth_getCode | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getcode openapi/plasma_node_api/accounts_info/eth_getCode.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the bytecode of a smart contract at a given address. Available on Plasma via Chainstack JSON-RPC nodes. Plasma API method that returns the bytecode of a smart contract at a given address. ## Parameters * `address` — the contract address * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `data` — the bytecode at the given address ## `eth_getCode` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getCode", [ "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getCode", "params": [], "id": 1 }' ``` # eth_getHeaderByHash | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getheaderbyhash openapi/plasma_node_api/blocks_info/eth_getHeaderByHash.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the block header by block hash. data — the hash of the block object — the block header object. On Plasma. Plasma API method that returns the block header by block hash. ## Parameters * `data` — the hash of the block ## Response * `object` — the block header object ## `eth_getHeaderByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getHeaderByHash", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getHeaderByHash", "params": [], "id": 1 }' ``` # eth_getHeaderByNumber | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getheaderbynumber openapi/plasma_node_api/blocks_info/eth_getHeaderByNumber.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the block header by block number. quantity or tag — the block number in hex format or block tag (latest, earliest, pending. Plasma API method that returns the block header by block number. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — the block header object ## `eth_getHeaderByNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getHeaderByNumber", [ "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getHeaderByNumber", "params": [], "id": 1 }' ``` # eth_getLogs | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getlogs openapi/plasma_node_api/logs_and_events/eth_getLogs.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns an array of all logs matching a given filter object. Available on Plasma via Chainstack JSON-RPC nodes. Plasma API method that returns an array of all logs matching a given filter object. ## Parameters * `object` — the filter options with fields like `fromBlock`, `toBlock`, `address`, `topics`, `blockHash` ## Response * `array` — an array of log objects ## `eth_getLogs` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getLogs", [ { fromBlock: "latest", toBlock: "latest" } ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getLogs", "params": [], "id": 1 }' ``` # eth_getProof | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getproof openapi/plasma_node_api/accounts_info/eth_getProof.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the account and storage values including the Merkle-proof. Available on Plasma via Chainstack JSON-RPC nodes. Plasma API method that returns the account and storage values including the Merkle-proof. > [!NOTE] > > On Global Nodes, this method returns a proof for the latest block only. A block number behind the tip returns `-32602` `distance to target block exceeds maximum proof window`. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). ## Parameters * `address` — the address of the account * `array` — an array of storage keys to generate proofs for * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — account object with balance, codeHash, nonce, storageHash, accountProof, and storageProof ## `eth_getProof` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getProof", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getProof", "params": [], "id": 1 }' ``` # eth_getRawTransactionByBlockHashAndIndex | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getrawtransactionbyblockhashandindex openapi/plasma_node_api/transaction_info/eth_getRawTransactionByBlockHashAndIndex.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the raw transaction data by block hash and transaction index. Available on Plasma via Chainstack JSON-RPC nodes. Plasma API method that returns the raw transaction data by block hash and transaction index. ## Parameters * `data` — the hash of the block * `quantity` — the transaction index position ## Response * `data` — the raw transaction data, or `null` when no transaction was found ## `eth_getRawTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getRawTransactionByBlockHashAndIndex", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getRawTransactionByBlockHashAndIndex", "params": [], "id": 1 }' ``` # eth_getRawTransactionByBlockNumberAndIndex | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getrawtransactionbyblocknumberandindex openapi/plasma_node_api/transaction_info/eth_getRawTransactionByBlockNumberAndIndex.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the raw transaction data by block number and transaction index. Available on Plasma via Chainstack. Plasma API method that returns the raw transaction data by block number and transaction index. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `quantity` — the transaction index position ## Response * `data` — the raw transaction data, or `null` when no transaction was found ## `eth_getRawTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getRawTransactionByBlockNumberAndIndex", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getRawTransactionByBlockNumberAndIndex", "params": [], "id": 1 }' ``` # eth_getRawTransactionByHash | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getrawtransactionbyhash openapi/plasma_node_api/transaction_info/eth_getRawTransactionByHash.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the raw transaction data by transaction hash. data — the hash of the transaction. Plasma via Chainstack. Plasma API method that returns the raw transaction data by transaction hash. ## Parameters * `data` — the hash of the transaction ## Response * `data` — the raw transaction data, or `null` when no transaction was found ## `eth_getRawTransactionByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getRawTransactionByHash", [ "0xTRANSACTION_HASH" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getRawTransactionByHash", "params": [], "id": 1 }' ``` # eth_getStorageAt | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getstorageat openapi/plasma_node_api/accounts_info/eth_getStorageAt.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the value from a storage position at a given address. Available on Plasma via Chainstack JSON-RPC nodes. Plasma API method that returns the value from a storage position at a given address. ## Parameters * `address` — the address to get storage from * `quantity` — the position in the storage * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `data` — the value at this storage position ## `eth_getStorageAt` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getStorageAt", [ "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "0x0", "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getStorageAt", "params": [], "id": 1 }' ``` # eth_getTransactionByBlockHashAndIndex | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-gettransactionbyblockhashandindex openapi/plasma_node_api/transaction_info/eth_getTransactionByBlockHashAndIndex.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns information about a transaction by block hash and transaction index position. Plasma via Chainstack. Plasma API method that returns information about a transaction by block hash and transaction index position. ## Parameters * `data` — the hash of the block * `quantity` — the transaction index position ## Response * `object` — a transaction object, or `null` when no transaction was found ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionByBlockHashAndIndex", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionByBlockHashAndIndex", "params": [], "id": 1 }' ``` # eth_getTransactionByBlockNumberAndIndex | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-gettransactionbyblocknumberandindex openapi/plasma_node_api/transaction_info/eth_getTransactionByBlockNumberAndIndex.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns information about a transaction by block number and transaction index position. Plasma via Chainstack. Plasma API method that returns information about a transaction by block number and transaction index position. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `quantity` — the transaction index position ## Response * `object` — a transaction object, or `null` when no transaction was found ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionByBlockNumberAndIndex", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionByBlockNumberAndIndex", "params": [], "id": 1 }' ``` # eth_getTransactionByHash | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-gettransactionbyhash openapi/plasma_node_api/transaction_info/eth_getTransactionByHash.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns information about a transaction by transaction hash. data — the hash of the transaction. On Plasma. Plasma API method that returns information about a transaction by transaction hash. ## Parameters * `data` — the hash of the transaction ## Response * `object` — a transaction object, or `null` when no transaction was found ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionByHash", [ "0xTRANSACTION_HASH" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", "params": [], "id": 1 }' ``` # eth_getTransactionBySenderAndNonce | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-gettransactionbysenderandnonce openapi/plasma_node_api/transaction_info/eth_getTransactionBySenderAndNonce.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the transaction information by sender address and nonce. address — the sender address quantity — the transaction nonce. Plasma API method that returns the transaction information by sender address and nonce. ## Parameters * `address` — the sender address * `quantity` — the transaction nonce ## Response * `object` — a transaction object, or `null` when no transaction was found ## `eth_getTransactionBySenderAndNonce` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionBySenderAndNonce", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionBySenderAndNonce", "params": [], "id": 1 }' ``` # eth_getTransactionCount | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-gettransactioncount openapi/plasma_node_api/accounts_info/eth_getTransactionCount.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the number of transactions sent from an address (nonce). eth_getTransactionCount on Plasma via Chainstack. Plasma API method that returns the number of transactions sent from an address (nonce). ## Parameters * `address` — the address to get the transaction count from * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `quantity` — the number of transactions sent from this address ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionCount", [ "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionCount", "params": [], "id": 1 }' ``` # eth_getTransactionReceipt | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-gettransactionreceipt openapi/plasma_node_api/transaction_info/eth_getTransactionReceipt.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the receipt of a transaction by transaction hash. data — the hash of the transaction. Plasma via Chainstack. Plasma API method that returns the receipt of a transaction by transaction hash. ## Parameters * `data` — the hash of the transaction ## Response * `object` — a transaction receipt object, or `null` when no receipt was found ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionReceipt", [ "0xTRANSACTION_HASH" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionReceipt", "params": [], "id": 1 }' ``` # eth_getUncleByBlockHashAndIndex | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getunclebyblockhashandindex openapi/plasma_node_api/blocks_info/eth_getUncleByBlockHashAndIndex.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns information about an uncle by block hash and uncle index position. Use it on Plasma via Chainstack. Plasma API method that returns information about an uncle by block hash and uncle index position. ## Parameters * `data` — the hash of the block * `quantity` — the uncle index position ## Response * `object` — a block object, or `null` when no uncle was found ## `eth_getUncleByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleByBlockHashAndIndex", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleByBlockHashAndIndex", "params": [], "id": 1 }' ``` # eth_getUncleByBlockNumberAndIndex | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getunclebyblocknumberandindex openapi/plasma_node_api/blocks_info/eth_getUncleByBlockNumberAndIndex.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns information about an uncle by block number and uncle index position. Use it on Plasma via Chainstack. Plasma API method that returns information about an uncle by block number and uncle index position. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `quantity` — the uncle index position ## Response * `object` — a block object, or `null` when no uncle was found ## `eth_getUncleByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleByBlockNumberAndIndex", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleByBlockNumberAndIndex", "params": [], "id": 1 }' ``` # eth_getUncleCountByBlockHash | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getunclecountbyblockhash openapi/plasma_node_api/blocks_info/eth_getUncleCountByBlockHash.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the number of uncles in a block by block hash. data — the hash of the block quantity — the number of uncles in the block. Plasma API method that returns the number of uncles in a block by block hash. ## Parameters * `data` — the hash of the block ## Response * `quantity` — the number of uncles in the block ## `eth_getUncleCountByBlockHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleCountByBlockHash", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleCountByBlockHash", "params": [], "id": 1 }' ``` # eth_getUncleCountByBlockNumber | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-getunclecountbyblocknumber openapi/plasma_node_api/blocks_info/eth_getUncleCountByBlockNumber.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the number of uncles in a block by block number. eth_getUncleCountByBlockNumber on Plasma via Chainstack. Plasma API method that returns the number of uncles in a block by block number. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `quantity` — the number of uncles in the block ## `eth_getUncleCountByBlockNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleCountByBlockNumber", [ "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleCountByBlockNumber", "params": [], "id": 1 }' ``` # eth_hashrate | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-hashrate openapi/plasma_node_api/chain_info/eth_hashrate.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the number of hashes per second that the node is mining with. eth_hashrate on Plasma via Chainstack. Plasma API method that returns the number of hashes per second that the node is mining with. ## Parameters None ## Response * `quantity` — the number of hashes per second ## `eth_hashrate` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_hashrate", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_hashrate", "params": [], "id": 1 }' ``` # eth_maxPriorityFeePerGas | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-maxpriorityfeepergas openapi/plasma_node_api/gas_data/eth_maxPriorityFeePerGas.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the current max priority fee per gas in Wei. Reference for eth_maxPriorityFeePerGas on Plasma via Chainstack. Plasma API method that returns the current max priority fee per gas in Wei. ## Parameters None ## Response * `quantity` — the current max priority fee per gas in Wei ## `eth_maxPriorityFeePerGas` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_maxPriorityFeePerGas", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_maxPriorityFeePerGas", "params": [], "id": 1 }' ``` # eth_newBlockFilter | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-newblockfilter openapi/plasma_node_api/filter_handling/eth_newBlockFilter.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that creates a filter in the node to notify when a new block arrives. eth_newBlockFilter on Plasma via Chainstack. Plasma API method that creates a filter in the node to notify when a new block arrives. ## Parameters None ## Response * `quantity` — a filter ID ## `eth_newBlockFilter` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_newBlockFilter", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_newBlockFilter", "params": [], "id": 1 }' ``` # eth_newFilter | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-newfilter openapi/plasma_node_api/filter_handling/eth_newFilter.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that creates a filter object based on filter options to notify when the state changes (logs). Plasma via Chainstack. Plasma API method that creates a filter object based on filter options to notify when the state changes (logs). ## Parameters * `object` — the filter options with fields like `fromBlock`, `toBlock`, `address`, `topics` ## Response * `quantity` — a filter ID ## `eth_newFilter` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_newFilter", [ { fromBlock: "latest", toBlock: "latest" } ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_newFilter", "params": [], "id": 1 }' ``` # eth_newPendingTransactionFilter | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-newpendingtransactionfilter openapi/plasma_node_api/filter_handling/eth_newPendingTransactionFilter.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that creates a filter in the node to notify when new pending transactions arrive. Use it on Plasma via Chainstack. Plasma API method that creates a filter in the node to notify when new pending transactions arrive. ## Parameters None ## Response * `quantity` — a filter ID ## `eth_newPendingTransactionFilter` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_newPendingTransactionFilter", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_newPendingTransactionFilter", "params": [], "id": 1 }' ``` # eth_protocolVersion | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-protocolversion openapi/plasma_node_api/chain_info/eth_protocolVersion.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the current Ethereum protocol version. Call eth_protocolVersion on Plasma through Chainstack nodes. Plasma API method that returns the current Ethereum protocol version. ## Parameters None ## Response * `string` — the current Ethereum protocol version ## `eth_protocolVersion` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_protocolVersion", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_protocolVersion", "params": [], "id": 1 }' ``` # eth_simulateV1 | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-simulatev1 openapi/plasma_node_api/execute_transactions/eth_simulateV1.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that simulates transaction execution. eth_simulateV1 JSON-RPC method available on the Plasma blockchain via Chainstack. Plasma API method that simulates transaction execution. ## Parameters * `object` — the simulation configuration object * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — the simulation result ## `eth_simulateV1` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_simulateV1", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_simulateV1", "params": [], "id": 1 }' ``` # eth_syncing | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-syncing openapi/plasma_node_api/chain_info/eth_syncing.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns an object with data about the sync status or false. Available on Plasma via Chainstack JSON-RPC nodes. Plasma API method that returns an object with data about the sync status or `false`. ## Parameters None ## Response * `object|boolean` — an object with sync status data or `false` when not syncing ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_syncing", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1 }' ``` # eth_uninstallFilter | Plasma Source: https://docs.chainstack.com/reference/plasma-eth-uninstallfilter openapi/plasma_node_api/filter_handling/eth_uninstallFilter.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that uninstalls a filter with the given ID. quantity — the filter ID boolean — true if the filter was successfully uninstalled. Plasma API method that uninstalls a filter with the given ID. ## Parameters * `quantity` — the filter ID ## Response * `boolean` — `true` if the filter was successfully uninstalled, `false` otherwise ## `eth_uninstallFilter` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_uninstallFilter", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_uninstallFilter", "params": [], "id": 1 }' ``` # Execute transactions | Plasma Source: https://docs.chainstack.com/reference/plasma-execute-transactions-rpc-methods Plasma transaction execution JSON-RPC methods — simulate eth_call executions and estimate gas costs without broadcasting transactions to the Plasma blockchain. Executing and simulating transactions on the Plasma blockchain can be done using the following methods: > [card] [eth\_call](/reference/plasma-eth-call) > [card] [eth\_estimateGas](/reference/plasma-eth-estimategas) > [card] [eth\_createAccessList](/reference/plasma-eth-createaccesslist) > [card] [eth\_simulateV1](/reference/plasma-eth-simulatev1) This information can be used for: * **Gas estimation** — calculate required gas before sending transactions * **Transaction simulation** — test transaction execution without state changes * **Smart contract testing** — verify contract behavior before deployment * **DApp optimization** — optimize transaction parameters for cost efficiency # Filter handling | Plasma Source: https://docs.chainstack.com/reference/plasma-filter-handling-rpc-methods The filter handling JSON-RPC methods allow developers to create and manage filters for monitoring blockchain events such as new blocks and logs. Managing filters on the Plasma blockchain can be done using the following methods: > [card] [eth\_newFilter](/reference/plasma-eth-newfilter) > [card] [eth\_newBlockFilter](/reference/plasma-eth-newblockfilter) > [card] [eth\_newPendingTransactionFilter](/reference/plasma-eth-newpendingtransactionfilter) > [card] [eth\_uninstallFilter](/reference/plasma-eth-uninstallfilter) This information can be used for: * **Event monitoring** — track specific smart contract events * **Block notifications** — receive updates when new blocks are mined * **Real-time applications** — build responsive DApps with live data * **Log filtering** — efficiently query blockchain logs # Gas data | Plasma Source: https://docs.chainstack.com/reference/plasma-gas-data-rpc-methods The gas data JSON-RPC methods allow developers to access current gas prices and historical fee information on the Plasma blockchain. Retrieving gas and fee information from the Plasma blockchain can be done using the following methods: > [card] [eth\_gasPrice](/reference/plasma-eth-gasprice) > [card] [eth\_maxPriorityFeePerGas](/reference/plasma-eth-maxpriorityfeepergas) > [card] [eth\_feeHistory](/reference/plasma-eth-feehistory) > [card] [eth\_blobBaseFee](/reference/plasma-eth-blobbasefee) This information can be used for: * **Fee estimation** — calculate optimal gas prices for transactions * **Cost analysis** — analyze historical gas price trends * **Wallet applications** — display current network fee information * **Transaction optimization** — choose appropriate gas parameters # Plasma API reference: Reth-based EVM JSON-RPC quickstart Source: https://docs.chainstack.com/reference/plasma-getting-started Use the Plasma JSON-RPC API on Chainstack to deploy nodes, send transactions, and build on the Reth-powered EVM-compatible Ethereum sidechain. ## What is the Plasma protocol? Plasma is an EVM-compatible blockchain built on Ethereum, designed to provide a high-performance, scalable platform for decentralized applications. Plasma utilizes Reth (Rust Ethereum) as its execution client, offering robust performance and reliability for developers building on the network. ## What is the Plasma API? The Plasma API allows developers to communicate with the Plasma blockchain to build applications. An application must connect to a Plasma RPC node to read data from and send transactions to the Plasma blockchain. When communicating with a Plasma RPC node, the client implements a JSON-RPC specification, a communication protocol allowing one to make remote calls and execute them as if they were made locally. ## How to start using the Plasma API You need access to a Plasma RPC node to use the Plasma API. The Plasma mainnet RPC endpoint is available at: ``` https://plasma-mainnet.core.chainstack.com/be84184cd12967be5a65d06651c60085 ``` Now, you can connect to the Plasma blockchain and use the Plasma API to build. ## Supported RPC methods Plasma supports standard Ethereum JSON-RPC methods as well as debug and trace APIs: * **Accounts info** — retrieve account balances, code, storage, and transaction counts * **Blocks info** — access block data, headers, and transaction counts * **Transaction info** — query transaction details and receipts * **Execute transactions** — simulate and estimate gas for transactions * **Gas data** — get current gas prices and fee history * **Chain info** — access chain ID, sync status, and protocol version * **Filter handling** — create and manage event filters * **Logs and events** — query blockchain logs * **Client info** — get client version and network information * **Debug and trace** — access advanced debugging and tracing capabilities # Logs and events | Plasma Source: https://docs.chainstack.com/reference/plasma-logs-and-events-rpc-methods Plasma logs and events JSON-RPC methods — query smart contract event logs, filter by topics and address, and track on-chain activity on the Plasma network. Retrieving logs and events from the Plasma blockchain can be done using the following methods: > [card] [eth\_getLogs](/reference/plasma-eth-getlogs) This information can be used for: * **Event tracking** — monitor specific smart contract events * **Historical analysis** — analyze past contract interactions * **DApp integration** — display contract events in applications * **Audit and compliance** — track on-chain activity for verification # net_listening | Plasma Source: https://docs.chainstack.com/reference/plasma-net-listening openapi/plasma_node_api/client_info/net_listening.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns true if the client is actively listening for network connections. Available on Plasma via Chainstack. Plasma API method that returns `true` if the client is actively listening for network connections. ## Parameters None ## Response * `boolean` — `true` when listening, otherwise `false` ## `net_listening` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("net_listening", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "net_listening", "params": [], "id": 1 }' ``` # net_peerCount | Plasma Source: https://docs.chainstack.com/reference/plasma-net-peercount openapi/plasma_node_api/client_info/net_peerCount.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the number of peers currently connected to the client. Available on Plasma via Chainstack JSON-RPC nodes. Plasma API method that returns the number of peers currently connected to the client. ## Parameters None ## Response * `quantity` — the number of connected peers ## `net_peerCount` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("net_peerCount", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "net_peerCount", "params": [], "id": 1 }' ``` # net_version | Plasma Source: https://docs.chainstack.com/reference/plasma-net-version openapi/plasma_node_api/client_info/net_version.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the current network ID. net_version JSON-RPC method available on the Plasma blockchain via Chainstack. Plasma API method that returns the current network ID. ## Parameters None ## Response * `string` — the current network ID ## `net_version` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("net_version", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "net_version", "params": [], "id": 1 }' ``` # trace_block | Plasma Source: https://docs.chainstack.com/reference/plasma-trace-block openapi/plasma_node_api/debug_and_trace/trace_block.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns traces created at the given block. quantity or tag — the block number in hex format or block tag (latest, earliest, pending. Plasma API method that returns traces created at the given block. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `array` — an array of trace objects ## `trace_block` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("trace_block", [ "latest" ]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "trace_block", "params": [], "id": 1 }' ``` # trace_call | Plasma Source: https://docs.chainstack.com/reference/plasma-trace-call openapi/plasma_node_api/debug_and_trace/trace_call.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that executes the given call and returns execution traces. object — the transaction call object array — array of trace types (trace. Plasma API method that executes the given call and returns execution traces. ## Parameters * `object` — the transaction call object * `array` — array of trace types (`trace`, `vmTrace`, `stateDiff`) * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — the trace result ## `trace_call` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("trace_call", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "trace_call", "params": [], "id": 1 }' ``` # trace_callMany | Plasma Source: https://docs.chainstack.com/reference/plasma-trace-callmany openapi/plasma_node_api/debug_and_trace/trace_callMany.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that performs multiple call traces on top of the same block. Reference for trace_callMany on Plasma via Chainstack. Plasma API method that performs multiple call traces on top of the same block. ## Parameters * `array` — array of transaction call objects with trace types * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `array` — an array of trace results ## `trace_callMany` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("trace_callMany", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "trace_callMany", "params": [], "id": 1 }' ``` # trace_get | Plasma Source: https://docs.chainstack.com/reference/plasma-trace-get openapi/plasma_node_api/debug_and_trace/trace_get.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns trace at the given position. data — the hash of the transaction array — array of trace indices. On Plasma. Plasma API method that returns trace at the given position. ## Parameters * `data` — the hash of the transaction * `array` — array of trace indices ## Response * `object` — the trace object ## `trace_get` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("trace_get", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "trace_get", "params": [], "id": 1 }' ``` # trace_replayBlockTransactions | Plasma Source: https://docs.chainstack.com/reference/plasma-trace-replayblocktransactions openapi/plasma_node_api/debug_and_trace/trace_replayBlockTransactions.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that replays all transactions in a block and returns the requested traces. trace_replayBlockTransactions on Plasma via Chainstack. Plasma API method that replays all transactions in a block and returns the requested traces. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `array` — array of trace types (`trace`, `vmTrace`, `stateDiff`) ## Response * `array` — an array of trace results for each transaction ## `trace_replayBlockTransactions` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("trace_replayBlockTransactions", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "trace_replayBlockTransactions", "params": [], "id": 1 }' ``` # Transaction info | Plasma Source: https://docs.chainstack.com/reference/plasma-transaction-info-rpc-methods Plasma transaction info JSON-RPC methods — retrieve transaction data, receipts, and confirmation status for monitoring on-chain activity on the Plasma network. Retrieving transaction information from the Plasma blockchain can be done using the following methods: > [card] [eth\_getTransactionByHash](/reference/plasma-eth-gettransactionbyhash) > [card] [eth\_getTransactionReceipt](/reference/plasma-eth-gettransactionreceipt) > [card] [eth\_getTransactionByBlockNumberAndIndex](/reference/plasma-eth-gettransactionbyblocknumberandindex) > [card] [eth\_getTransactionByBlockHashAndIndex](/reference/plasma-eth-gettransactionbyblockhashandindex) > [card] [eth\_getRawTransactionByHash](/reference/plasma-eth-getrawtransactionbyhash) > [card] [eth\_getRawTransactionByBlockNumberAndIndex](/reference/plasma-eth-getrawtransactionbyblocknumberandindex) > [card] [eth\_getRawTransactionByBlockHashAndIndex](/reference/plasma-eth-getrawtransactionbyblockhashandindex) > [card] [eth\_getTransactionBySenderAndNonce](/reference/plasma-eth-gettransactionbysenderandnonce) This information can be used for: * **Transaction tracking** — monitor transaction status and confirmations * **Receipt verification** — verify transaction execution and event logs * **Wallet applications** — display transaction history and details * **DApp development** — integrate transaction data into applications # web3_clientVersion | Plasma Source: https://docs.chainstack.com/reference/plasma-web3-clientversion openapi/plasma_node_api/client_info/web3_clientVersion.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns the current client version. web3_clientVersion JSON-RPC method available on the Plasma blockchain via Chainstack. Plasma API method that returns the current client version. ## Parameters None ## Response * `string` — the current client version ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("web3_clientVersion", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "web3_clientVersion", "params": [], "id": 1 }' ``` # web3_sha3 | Plasma Source: https://docs.chainstack.com/reference/plasma-web3-sha3 openapi/plasma_node_api/client_info/web3_sha3.json POST /be84184cd12967be5a65d06651c60085 Plasma API method that returns Keccak-256 (not the standardized SHA3-256) of the given data. data — the data to convert into a SHA3 hash. Plasma API method that returns Keccak-256 (not the standardized SHA3-256) of the given data. ## Parameters * `data` — the data to convert into a SHA3 hash ## Response * `data` — the SHA3 result of the given string ## `web3_sha3` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("web3_sha3", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Make the call # Add your parameters here # result = web3.eth.(...) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "web3_sha3", "params": [], "id": 1 }' ``` # Getting started with the Chainstack Platform API Source: https://docs.chainstack.com/reference/platform-api-getting-started Get started with the Chainstack Platform API to manage projects, networks, and nodes programmatically. Learn authentication, base URL, and request format. The Chainstack platform API allows you to manage your infrastructure programmatically. The API has two versions: * **v2 (recommended)** — manage your organization, deployment options, projects, nodes, node access rules, and node usage reports. * **v1** — manage organizations, projects, networks, nodes, and faucets. ## Create API key You must create an API key to authenticate your requests to the Chainstack API. To create an API key: **Step 1** Go to the [API keys](https://console.chainstack.com/user/settings/api-keys) section. **Step 2** Click **Create key**. **Step 3** Type in a name for the key and click **Next**. **Step 4** Copy or write down the created API key. > [!WARNING] > > The API key value is only shown once and cannot be retrieved. Make sure you keep the API key secure. ## Delete API key You can delete your existing API keys. To delete an API key: **Step 1** Go to the [API keys](https://console.chainstack.com/user/settings/api-keys) section. **Step 2** Click the edit icon next to the key you want to delete. **Step 3** Click **Delete**. ## Errors Every error response has the same JSON body: ```json theme={"system"} { "error": { "code": "invalid", "message": "A human-readable description of the error.", "fields": { "name": ["This field is required."] } } } ``` The `fields` object appears only on validation errors. Nested fields are flattened with dots — for example, `nodes.0.configuration.archive`. | Status | `code` | Meaning | | - | - | - | | `400` | `invalid` | The request body or parameters failed validation. | | `401` | `not_authenticated` | The request has no `Authorization: Bearer YOUR_CHAINSTACK_API_KEY` header. | | `401` | `authentication_failed` | The API key is invalid, deleted, or expired. | | `403` | `permission_denied` | The action isn't allowed. Every write also returns this code while the platform is under maintenance — retry later — or while your organization has outstanding invoices. The message says which. | | `403` | `protected` | The resource can't be deleted yet — for example, a project that still has nodes. | | `403` | `quota_exceeded` | Your plan doesn't include what you requested — for example, a deployment option or a feature. | | `403` | `extra_usage_required` | The resource needs pay-as-you-go enabled on the **Billing** page. | | `404` | `not_found` | The resource doesn't exist or belongs to another organization. | | `405` | `method_not_allowed` | The method isn't supported — for example, `PUT`. Use `PATCH` to update a resource. | | `415` | `unsupported_media_type` | The request body isn't JSON on an endpoint that accepts only JSON, such as node access rules. | | `429` | `throttled` | Too many requests. Wait the number of seconds in the `Retry-After` header before retrying. | # Accounts info | Polygon Source: https://docs.chainstack.com/reference/polygon-accounts-info-rpc-methods Polygon account information JSON-RPC methods — retrieve balances, nonces, contract code, and storage data for accounts on the Polygon PoS network. Retrieving account information from the Polygon blockchain can be done by using the following methods: > [card] [eth\_getTransactionCount | Polygon](/reference/gettransactioncount) > [card] [eth\_getBalance | Polygon](/reference/getbalance) > [card] [eth\_getCode | Polygon](/reference/getcode) > [card] [eth\_getStorageAt | Polygon](/reference/getstorageat) # Blocks info | Polygon Source: https://docs.chainstack.com/reference/polygon-blocks-rpc-methods Polygon block information JSON-RPC methods — query block details including transactions, timestamps, height, headers, and uncle counts on Polygon PoS. Retrieving block information from the Polygon blockchain can be done by using the following methods: > [card] [eth\_blockNumber | Polygon](/reference/polygon-getblocknumber) > [card] [eth\_getBlockByHash | Polygon](/reference/getblockbyhash) > [card] [eth\_getBlockByNumber | Polygon](/reference/polygon-getblockbynumber) > [card] [eth\_getBlockTransactionCountByHash | Polygon](/reference/polygon-getblocktransactioncountbyhash) > [card] [eth\_getBlockTransactionCountByNumber | Polygon](/reference/polygon-getblocktransactioncountbynumber) > [card] [eth\_newBlockFilter | Polygon](/reference/polygon-newblockfilter) This information can be used to track the state of the blockchain and for: * **Blockchain analysis** — insights into the overall activity on the network, such as the number of processed transactions and average transaction fees, can be obtained. * **Block explores** — web-based tools that provide access to information on a blockchain, including block data, transactions, and addresses, allowing users to search, view, and analyze the network's activity. * **Smart contract analysis** — the activity of specific smart contracts * **DApp development** — block information can be utilized in the development of decentralized applications. # bor_getAuthor | Polygon Source: https://docs.chainstack.com/reference/polygon-borgetauthor openapi/polygon_node_api/bor/bor_getAuthor.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method bor_getAuthor returns the validator address that produced a given block. On Polygon via Chainstack. Polygon API method `bor_getAuthor` returns the address of the validator that produced (signed) a given block on the Bor consensus layer. ## Parameters * `block` — the block number in hexadecimal (for example, `0x561294b`), or a block hash. ## Response * `result` — the address of the validator that authored the block. ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x0e94b9b3fabd95338b8b23c36caae1d640e1339f" } ``` ## Use case `bor_getAuthor` is used by explorers and analytics tools to attribute each block to the validator that produced it on Polygon. # bor_getCurrentProposer | Polygon Source: https://docs.chainstack.com/reference/polygon-borgetcurrentproposer openapi/polygon_node_api/bor/bor_getCurrentProposer.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method bor_getCurrentProposer returns the address of the current block proposer on the Bor consensus layer. On Polygon via Chainstack. Polygon API method `bor_getCurrentProposer` returns the address of the validator that is the current block proposer on the Bor consensus layer. This method takes no parameters. ## Parameters This method takes no parameters. ## Response * `result` — the address of the current block proposer. ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x0e94b9b3fabd95338b8b23c36caae1d640e1339f" } ``` ## Use case `bor_getCurrentProposer` is used by validator dashboards and monitoring tools to track which validator is currently proposing blocks on Polygon. # bor_getCurrentValidators | Polygon Source: https://docs.chainstack.com/reference/polygon-borgetcurrentvalidators openapi/polygon_node_api/bor/bor_getCurrentValidators.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method bor_getCurrentValidators returns the current validator set seen by the Bor consensus engine. On Polygon via Chainstack. Polygon API method `bor_getCurrentValidators` returns the current validator set as seen by the Bor consensus engine for the active span. This method takes no parameters. ## Parameters This method takes no parameters. ## Response * `result` — an array of validator objects: * `ID` — the validator ID. * `signer` — the validator's signer address. * `power` — the validator's voting power. * `accum` — the validator's proposer priority (accumulator) used for proposer selection. ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": [{ "ID": 0, "signer": "0x0e94b9b3fabd95338b8b23c36caae1d640e1339f", "power": 69637778, "accum": 0 }] } ``` ## Use case `bor_getCurrentValidators` is used by staking dashboards and explorers to display the active validator set and each validator's voting power on Polygon. # bor_getRootHash | Polygon Source: https://docs.chainstack.com/reference/polygon-borgetroothash openapi/polygon_node_api/bor/bor_getRootHash.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method bor_getRootHash returns the root hash for a given block range, used for checkpointing to Ethereum. On Polygon via Chainstack. Polygon API method `bor_getRootHash` returns the root hash computed over a given block range. Bor uses these range root hashes when checkpointing Polygon state to Ethereum. ## Parameters * `from` — the start block number (integer). * `to` — the end block number (integer). The range is capped by the node's configured limit. ## Response * `result` — the root hash for the requested block range, as a hexadecimal string without the `0x` prefix. ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "684c2efeba6e8b27d1fd5082424107a53dfc16f54731908ba96c0c76c598b46a" } ``` ## Use case `bor_getRootHash` is used by checkpoint and bridge tooling to reconstruct or verify the block-range root hashes that Bor submits to Ethereum. # bor_getSignersAtHash | Polygon Source: https://docs.chainstack.com/reference/polygon-borgetsignersathash openapi/polygon_node_api/bor/bor_getSignersAtHash.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method bor_getSignersAtHash returns the authorized signers at a given block hash. On Polygon via Chainstack. Polygon API method `bor_getSignersAtHash` returns the list of authorized signers at a given block hash on the Bor consensus layer. ## Parameters * `hash` — the block hash to query. ## Response * `result` — an array of signer addresses authorized at the given block. ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": ["0x0e94b9b3fabd95338b8b23c36caae1d640e1339f"] } ``` ## Use case `bor_getSignersAtHash` is used to audit which validators were authorized to sign at a specific point in the chain's history on Polygon. # Chain info | Polygon Source: https://docs.chainstack.com/reference/polygon-chain-data-rpc-methods Polygon PoS chain data RPC methods for querying network details like chain ID, syncing status, and protocol version through Chainstack endpoints. For example, if a developer wants to build a DApp on Polygon, they would use these methods to get information about the network they are connecting to. They could use the chain ID information to make sure they are connecting to the right network and to ensure the security of their DApp; for example, distinguishing between the main network and a test network. Retrieving chain information from the Polygon blockchain can be done by using the following methods: > [card] [eth\_chainId | Polygon](/reference/chainid) > [card] [eth\_syncing | Polygon](/reference/syncing) # clearSubscriptions | Polygon Source: https://docs.chainstack.com/reference/polygon-clearsubscriptions Use eth_clearSubscriptions on Polygon via ethers.js to remove all active WebSocket subscriptions from your Chainstack node connection at once. The subscriptions available using the ethers.js `provider.on` method are: > [card] [subscribe('newBlockHeaders')](/reference/polygon-subscribenewblockheaders) > [card] [subscribe('pendingTransactions')](/reference/polygon-subscribependingtransactions) > [card] [subscribe('logs')](/reference/polygon-subscribelogs) > [card] [subscribe('syncing')](/reference/polygon-subscribesyncing) ## Parameters `boolean` — keep the [subscribe("syncing")](/reference/polygon-subscribesyncing) subscription if `true`. ## Response `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `clearSubscriptions` code example ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function clearSubscriptions() { try { await provider.removeAllListeners(); console.log("Subscriptions were canceled successfully"); return process.exit(1) } catch (error) { console.error("Failed to cancel subscriptions:", error); return process.exit(1) } } clearSubscriptions() ``` ## Use case A way to use the `clearSubscriptions` method is to place it in a timer to clear all of the subscriptions after a pre-determined about. The following code is an example using the [subscribe("newBlockHeaders")](/reference/polygon-subscribenewblockheaders) method, which will be stopped and removed after one minute using `clearSubscriptions`: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToNewBlocks() { try { // Listen for the 'block' event, emitted on every new block provider.on('block', handleNewBlock); console.log("Subscribed to new blocks"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs the received block number async function handleNewBlock(blockNumber) { const block = await provider.getBlock(blockNumber); console.log(block); } async function clearSubscriptions() { try { await provider.removeAllListeners(); console.log("Subscriptions were canceled successfully"); return process.exit(1) } catch (error) { console.error("Failed to cancel subscriptions:", error); return process.exit(1) } } async function main() { subscribeToNewBlocks(); // Run clearSubscriptions() once after 60 seconds setTimeout(clearSubscriptions, 60000); } main() ``` The code starts by defining an async function called `subscribeToNewBlocks` that subscribes to new blocks by registering a listener for the `block` event using the `provider.on` method. Each time a new block is mined, ethers.js emits the block number, and the `handleNewBlock` listener fetches the full block with `provider.getBlock` and logs it to the console. The `subscribeToNewBlocks` function is called from the `main` function, which is also defined in the code. The `main` function calls `subscribeToNewBlocks` to create a new subscription and then schedules the `clearSubscriptions` function to run once after 60 seconds using the `setTimeout` function. The `clearSubscriptions` function uses the `provider.removeAllListeners` method to cancel any existing subscriptions and log a message to the console if the cancellation was successful. ## Try the `clearSubscriptions` RPC method yourself # Client information | Polygon Source: https://docs.chainstack.com/reference/polygon-client-data-rpc-methods Polygon client information JSON-RPC methods — check client version, network ID, chain ID, listening status, and peer count for your Polygon PoS node. Retrieving blockchain client information from a Polygon node can be done by using the following methods: > [card] [web3\_clientVersion | Polygon](/reference/clientversion) > [card] [net\_peerCount | Polygon](/reference/peercount) > [card] [net\_listening | Polygon](/reference/netlistening) # Debug & Trace | Polygon Source: https://docs.chainstack.com/reference/polygon-debug-trace-rpc-methods Polygon debug and trace API methods for inspecting smart contract execution, analyzing gas usage, and tracing transactions on Chainstack nodes. Developers can access the debug and trace tools with the following methods: > [card] [debug\_traceBlockByHash](/reference/polygon-traceblockbyhash) > [card] [debug\_traceBlockByNumber](/reference/polygon-traceblockbynumber) > [card] [debug\_traceTransaction](/reference/polygon-tracetransaction) > [card] [debug\_traceCall](/reference/polygon-tracecall) ## Pre-built native tracers > [!NOTE] > > ### Custom JavaScript tracers need a Dedicated Node > > The tracers listed below are native (built-in) tracers—you select one by name. Custom JavaScript tracers, where you pass raw JavaScript as the `tracer`, are available as customized solutions on the [Enterprise plan](https://chainstack.com/pricing/) on [Dedicated Nodes](/docs/dedicated-node). Developers can customize the type of tracing using various debug and trace methods, and there are numerous pre-built tracers available to choose from. Further is a list of the pre-built tracers that can be utilized. ### `4byteTracer` This tracer looks for function signatures that are 4 bytes long during contract execution and gathers information about the method identifiers and the size of the provided data. This information can be helpful in matching a signature against the data size. ### `callTracer` This tracer extracts information about all internal calls made by a transaction, providing visibility into how contracts interact with each other. ### `flatCallTracer` This tracer returns the same call data as `callTracer` but as a flat array in the Parity-style trace format, where each entry's `traceAddress` marks its position in the call tree. It is the drop-in replacement for the Erigon `trace_*` methods, which Bor does not implement. ### `prestateTracer` This tracer returns sufficient information about an account to create a local execution of the transaction. This procedure runs the given transaction again and monitors every aspect of the state that is modified or accessed during execution. ### `noopTracer` This tracer does nothing but respond with a blank object. This a no operations tracer for testing setup. # Executing transactions | Polygon Source: https://docs.chainstack.com/reference/polygon-evm-excecution-rpc-methods Polygon transaction execution JSON-RPC methods — call smart contracts, send raw transactions, and estimate gas for building dapps on the Polygon network. Specifically, these methods can be used to execute function calls on smart contracts, sign transactions with a private key, and submit signed transactions to the network for execution. This enables developers to build a wide range of decentralized applications, including wallets, exchanges, and other tools for interacting with the Polygon network. These methods can be particularly useful for testing, offline transaction signing, and programmatic interaction with the network. Executing and simulating transactions on the Polygon blockchain can be done by using the following methods: > [card] [eth\_call](/reference/ethcall) > [card] [eth\_sendRawTransaction](/reference/sendrawtransaction) > [card] [eth\_sendRawTransactionSync](/reference/polygon-sendrawtransactionsync) # Filter handling | Polygon Source: https://docs.chainstack.com/reference/polygon-filters-rpc-methods Polygon filter JSON-RPC methods — create event and block filters, poll for changes, retrieve filter logs, and manage on-chain event subscriptions. Handling filters can be done by using the following methods: > [card] [eth\_getFilterChanges | Polygon](/reference/getfilterchanges) > [card] [eth\_uninstallFilter | Polygon](/reference/uninstallfilter) # Gas data | Polygon Source: https://docs.chainstack.com/reference/polygon-gas-data-rpc-methods Polygon gas data JSON-RPC methods — query current gas prices and estimate gas costs for transactions to build cost-effective dapps on Polygon PoS. Retrieving gas information from the Polygon blockchain can be done by using the following methods: > [card] [eth\_estimateGas](/reference/estimategas) > [card] [eth\_gasPrice](/reference/gasprice) # eth_getBlockByNumber | Polygon Source: https://docs.chainstack.com/reference/polygon-getblockbynumber openapi/polygon_node_api/blocks_info/eth_getBlockByNumber.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns information about the block matching the given block number. eth_getBlockByNumber on Polygon via Chainstack. Polygon API method that returns information about the block matching the given block number. This method can retrieve a wide range of information about the block, such as its hash, timestamp, difficulty, and gas limit. You can also retrieve transaction objects included in the block if needed. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). * `boolean` — if `true`, it returns the detail of each transaction. If `false`, only the hashes of the transactions. ## Response * `object` — a block object, or `null` when no block was found. * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ethereum nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ethereum blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain. Encoded as hexadecimal. * `totaldifficulty` — `null`. This field is obsolete as it was used during Ethereum's Proof of Work (PoW) era. Polygon PoS chain uses Proof of Stake consensus and this value is not relevant. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — array of uncle hashes. ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockByNumber = async () => { const blockByNumber = await provider.send("eth_getBlockByNumber", ["latest",false]); console.log(blockByNumber); }; eth_getBlockByNumber(); ``` ```python web3.py theme={"system"} # Polygon is a Proof of Authority (POA) chain. Web3.py requires the use of the POA compatibility middleware. from web3 import Web3 from web3.middleware import geth_poa_middleware node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Inject the POA compatibility middleware to the innermost layer. web3.middleware_onion.inject(geth_poa_middleware, layer=0) print(web3.eth.get_block("latest", False)) ``` ## Use case The `eth_getBlockByNumber` method can determine whether a block you are analyzing has been included in the main chain or was forked due to a 'reorg'. A block hash is generated through a cryptographic hash function, which takes the block's data, including transactions and other information, and the hash of the parent block (the previous block) and outputs a unique, fixed-length digest. This hash represents the current block within the blockchain. The script compares the parent hash of the following block with the hash under inspection. If the two values match, it can be concluded that the block was included in the main chain. Conversely, if the values do not match, it indicates that the block was not used to generate the next block's hash and has therefore been excluded from the main chain, resulting in a forked block. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT" const provider = new ethers.JsonRpcProvider(NODE_URL) // If the hash of the block to inspect is === to the parent hash of the next block, then this is not a forked block. async function isForked(block) { const nextBlock = blockToInspect + 1 console.log(`Block to inspect: ${block}`) console.log(`Next block: ${nextBlock} \n`) const blockToInspectHash = (await provider.getBlock(block)).hash console.log(`Block hash of the block you are inspecting: ${blockToInspectHash} \n`) const nextBlockParentHash = (await provider.getBlock(nextBlock)).parentHash console.log(`Parent hash of the next block: ${nextBlockParentHash} \n`) if (String(blockToInspectHash) == String(nextBlockParentHash)) { console.log(`You are inspecting the correct block`) return false } else { console.log(`You are inspecting a forked block!`) return true } } const blockToInspect = 16394103 isForked(blockToInspect) ``` In this case, the `eth_getBlockByNumber` is used to retrieve the hash of a block you want to analyze and the parent hash of the next block. # eth_blockNumber | Polygon Source: https://docs.chainstack.com/reference/polygon-getblocknumber openapi/polygon_node_api/blocks_info/eth_blockNumber.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the latest block number of the blockchain. Reference for eth_blockNumber on Polygon via Chainstack. Polygon API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0x69B5B` means that the block is the 432,987th block in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. > [!NOTE] > > Use the [Chainstack EVM Swiss Knife to convert hexadecimal to decimal](https://web3tools.chainstacklabs.com/hexadecimal-decimal). ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockNumber = async () => { const block_Number = await provider.getBlockNumber(); console.log(block_Number); }; eth_getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.block_number) ``` ## Use case A possible use case for the `eth_blockNumber` method in Ethereum is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. The following code uses the web3.js library and defines a function called `getCurrentBlockNumber`. When called, this function returns the latest block number from the network, and you can use it in your DApp. You can then use your application's `getCurrentBlockNumber` function to get the current block number whenever you need it. In this example, we use it to calculate on which block a special offer to a specific user will expire. We retrieve the block when we issue the offer to the user and add `50400`, which is approximately how many Ethereum blocks are produced weekly. Now, in the `offerExpires` constant, we have the last block when the user can exercise the special price. Use the `getCurrentBlockNumber` function to compare it to `offerExpires` to know if the offer is still valid. # eth_getBlockReceipts | Polygon Source: https://docs.chainstack.com/reference/polygon-getblockreceipts openapi/polygon_node_api/transaction_info/eth_getBlockReceipts.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that retrieves all transaction receipts for a given block. Reference for eth_getBlockReceipts on Polygon via Chainstack. Polygon API method that retrieves all transaction receipts for a given block. Transaction receipts contain information about the execution status of a transaction and can be useful for monitoring the status of transfers or contract execution on the blockchain. ## Parameters * `quantity or tag` — the block hash, block number encoded as hexadecimal, or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). ## Response * `result` — an array of objects with the following fields: * `Transaction receipt` — the object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the address of the contract created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects generated by this transaction, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getBlockReceipts` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockReceipts = async () => { const BlockReceipts = await provider.send("eth_getBlockReceipts", ["latest"]); console.log(BlockReceipts); }; eth_getBlockReceipts(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block_receipts = web3.provider.make_request('eth_getBlockReceipts', ['latest']) print(block_receipts) ``` ## Use case The `eth_getBlockReceipts` allows you to retrieve the receipts for all transactions in a specific block. A practical use case can be to retrieve all of the logs emitted from the transactions in a block. The method returns an array of transaction receipts, where each contains information about the transaction and any logs generated as a result of the transaction execution, so the logs can be isolated. Here is an example of how to use `eth_getBlockReceipts` in ethers.js to extract all of the logs in a specific block and return them in an array: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = 'CHAINSTACK_NODE_URL'; const provider = new ethers.JsonRpcProvider(NODE_URL); async function retrieveTransactionLogs(blockNumber) { // Call the eth_getBlockReceipts method to retrieve the receipts for all transactions in the block let receipts = await provider.send('eth_getBlockReceipts', [blockNumber]); // Flatten the receipts array into a single array of logs let logs = receipts.flatMap(receipt => receipt.logs); // Return the logs return logs; } // call retrieveTransactionLogs on the latest block of the chain async function main() { const blockNumber = 'latest'; const logs = await retrieveTransactionLogs(blockNumber); console.log(logs); } main() ``` In this example, the `retrieveTransactionLogs` function takes a block identifier as an input and returns an array of logs. The function first calls the `eth_getBlockReceipts` method using the `provider.send` method and passes in the block identifier as an argument. The result is an array of receipts, then flattened into a single array of logs using the `flatMap` function. # eth_getBlockTransactionCountByHash | Polygon Source: https://docs.chainstack.com/reference/polygon-getblocktransactioncountbyhash openapi/polygon_node_api/blocks_info/eth_getBlockTransactionCountByHash.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. Polygon API method that returns the number of transactions in a block specified by block hash. This information can be useful for analytics purposes. ## Parameters * `hash` — the block hash of the requested block. ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockHash) => { const count = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); console.log(count); }; getTransactionsCount('0x7709b7cacb9a3d647afe557d51891c00ad0c00e01d5bffdbcfe6c1730d36290c'); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_block_transaction_count("0x7709b7cacb9a3d647afe557d51891c00ad0c00e01d5bffdbcfe6c1730d36290c")) ``` ## Use case `eth_getBlockTransactionCountByHash` is a useful tool for analyzing transaction volume on the Polygon blockchain. On average, a [new block is generated on the Polygon mainnet every 2.1 seconds](https://polygonscan.com/chart/blocktime), resulting in approximately 1,700 blocks per hour. Using the ethers.js library, one can inspect the past 1,700 blocks starting from the latest block, retrieve the hash of each block, and use `eth_getBlockTransactionCountByHash` to find the number of transactions in each block. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function retrieveTransactionsCount() { // Get the current block number const currentBlockNumber = await provider.getBlockNumber(); // Calculate the block number from one hour ago const oneHourAgoBlockNumber = currentBlockNumber - 1700; // 1700 blocks/hr on average on the Polygon mainnet // Initialize a variable to store the total number of transactions let totalTransactionCount = 0; // Loop through all blocks from the current block number to the block number from one hour ago for (let block = currentBlockNumber; block > oneHourAgoBlockNumber; block--) { // Get the hash of each block const getBlockHash = await provider.getBlock(block, false); const blockHash = getBlockHash.hash // Get the number of transactions in this block using each hash const transactionCount = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); // Add the number of transactions in this block to the total totalTransactionCount += Number(transactionCount); console.log(`Block #${block} has ${Number(transactionCount)} transactions.`); } console.log(`The total number of transactions in the previous hour is: ${totalTransactionCount}`); } retrieveTransactionsCount(); ``` This example connects to a Polygon node using the ethers.js library. The `retrieveTransactionsCount` function gets the current block number, calculates the block number from one hour ago, and uses a for loop to iterate through all blocks in that range. The function then retrieves the hash of each block and the number of transactions in each block and adds it to a total transaction count. The final result, the total number of transactions in the previous hour, is logged to the console. This is a good example of how you can combine different methods. # eth_getBlockTransactionCountByNumber | Polygon Source: https://docs.chainstack.com/reference/polygon-getblocktransactioncountbynumber openapi/polygon_node_api/blocks_info/eth_getBlockTransactionCountByNumber.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. Polygon API method that returns the number of transactions in a block specified by block number or tag. This information can be useful for analytics purposes. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). ## Response * `quantity` — an integer value representing how many transactions are included in the block. ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionsCount = async (blockId) => { const count = await provider.send("eth_getBlockTransactionCountByNumber", [blockId]); console.log(count); }; getTransactionsCount('latest') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print (web3.eth.get_block_transaction_count(32732976)) # A hex value starting with "0x" is accepted as well. ``` ## Use case `eth_getBlockTransactionCountByNumber` can be used to analyze how many transactions are included on the Polygon blockchain in a certain period. For instance, on average, a [new block is generated on the Polygon mainnet every 2.1 seconds](https://polygonscan.com/chart/blocktime), resulting in approximately 1,700 blocks per hour. Using the ethers.js library, one can inspect the past 1,700 blocks starting from the latest block and use `eth_getBlockTransactionCountByNumber` to find the number of transactions in each block to sum them. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function retrieveTransactionCounts() { // Get the current block number const currentBlockNumber = await provider.getBlockNumber(); // Calculate the block number from one hour ago const oneHourAgoBlockNumber = currentBlockNumber - 1700; // 1700/blocks/hr on average on the Polygon mainnet // Initialize a variable to store the total number of transactions let totalTransactionCount = 0; // Loop through all blocks from the current block number to the block number from one hour ago for (let block = currentBlockNumber; block > oneHourAgoBlockNumber; block--) { // Get the number of transactions in this block const transactionCount = await provider.send("eth_getBlockTransactionCountByNumber", [block]); // Make the necessery conversions from hex to decimal const stringCount = BigInt(transactionCount).toString(); const decimalCount = Number(stringCount) // Add the number of transactions in this block to the total totalTransactionCount += decimalCount; const stringBlock = BigInt(block).toString(); const decimalBlock = Number(stringBlock) console.log(`Block ${decimalBlock} has ${decimalCount} transactions.`); } console.log(`The total number of transactions in the previous hour is: ${totalTransactionCount}`); } retrieveTransactionCounts(); ``` Using `eth_getBlockTransactionCountByNumber` is a bit more straightforward compared to the [example shown using `eth_getBlockTransactionCountByHash`](/reference/polygon-getblocktransactioncountbyhash) for this same use case, you don't need to extract the block hash; instead, you can query the transaction count directly. This example is made using the ethers.js library, and you can notice that it requires more parsing compared to web3.js, and the results are given as hexadecimal. # eth_getProof | Polygon Source: https://docs.chainstack.com/reference/polygon-getproof openapi/polygon_node_api/account_info/eth_getProof.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method eth_getProof returns the account and storage Merkle proof for an address, letting clients verify state without the full chain. On Polygon via Chainstack. Polygon API method `eth_getProof` returns the Merkle proof for an account and, optionally, for specific storage slots. Clients use it to verify account balances, nonces, contract code hashes, and storage values against a block's state root without holding the entire state. > [!WARNING] > > Polygon runs on the Bor client. On Bor with the path-based storage scheme, `eth_getProof` returns proofs only for blocks less than 127 behind the tip — historical proof queries return `-32000` `missing trie node`. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is billed as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address` — the address of the account to prove. * `storageKeys` — an array of 32-byte storage slot keys to prove. Pass an empty array to prove the account only. * `block` — the block number in hexadecimal, or one of `earliest`, `latest`, or `pending`. Defaults to `latest`. ## Response * `result` — an object with the account proof and state values: * `address` — the account address. * `accountProof` — an array of RLP-encoded Merkle-trie nodes proving the account against the block state root. * `balance` — the account balance, in hexadecimal wei. * `codeHash` — the hash of the account's contract code; for a non-contract (EOA) account this is the Keccak-256 hash of empty code. * `nonce` — the account nonce, in hexadecimal. * `storageHash` — the root hash of the account storage trie. * `storageProof` — an array of objects, one per requested `storageKeys` entry, each with the `key`, its `value`, and the `proof` (RLP-encoded trie nodes). ## Use case `eth_getProof` is used by light clients, cross-chain bridges, and auditing tools that need cryptographic proof of an account or storage value at a specific block without syncing the full Polygon state. # Polygon API reference: PoS sidechain JSON-RPC quickstart Source: https://docs.chainstack.com/reference/polygon-getting-started Use the Polygon JSON-RPC API on Chainstack to deploy nodes, send transactions, bridge assets, and build on the EVM-compatible PoS sidechain. ## What is the Polygon protocol? Polygon is an EVM-compatible sidechain and one of Ethereum's [scaling solutions](https://chainstack.com/solving-the-blockchain-trilemma-scaling-solutions-for-ethereum/) to help reduce costs and increase efficiency. Polygon runs independently from the Ethereum chain, has its own consensus mechanism, and can bridge assets between the two networks. Find useful Polygon tools in the [Polygon tooling](/docs/polygon-tooling) section. ## What is the Polygon API The Polygon API allows developers to communicate with the Polygon blockchain to build applications. To read data from and send transactions to the Polygon blockchain, an application must connect to a Polygon RPC node. When communicating with a Polygon RPC node, the Polygon client implements a JSON-RPC specification, a communication protocol allowing one to make remote calls and execute them as if they were made locally. ## How to start using the Polygon API To use the Polygon API, you need access to a Polygon RPC node. Follow these steps to sign up on Chainstack, deploy a Polygon RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the Polygon blockchain and use the Polygon API to build. > [!NOTE] > > [Build better with Polygon](https://chainstack.com/build-better-with-polygon/) # eth_getTransactionByBlockHashAndIndex | Polygon Source: https://docs.chainstack.com/reference/polygon-gettransactionbyblockhashandindex openapi/polygon_node_api/transaction_info/eth_getTransactionByBlockHashAndIndex.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. Polygon API method that retrieves information about a specific transaction based on the block hash and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Ethereum blockchain. ## Parameters * `hash` — the hash of the block * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal ## Response * `object` — a transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blockHash, index) => { const transaction = await provider.send("eth_getTransactionByBlockHashAndIndex", [blockHash, index]); console.log(transaction); } getTransaction("0xf9b6c767acc4b7b573e29b413e8cd7813e0b0c9c9828d0b987ab52d248f83841", "0x3D"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block("0x82e0da6a01019b1eaa77c699112af4d6fdb7d7a4af078c83d9a83e9532bcc2c5", 22)) ``` ## Use case The `eth_getTransactionByBlockHashAndIndex` can be used to retrieve transaction details from a block. In the following example the `getSendersInBlock` checks how many transactions exist in a block, then receives the sender of each (`from` field) and displays it. > [!NOTE] > > Note that there are simpler ways to get this information, and this is only a practical example of how to use `eth_getTransactionByBlockHashAndIndex`, if the number of transactions in the block is already known, the first step can be skipped. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getSendersInBlock(blockHash) { const block = await provider.getBlock(blockHash); const transactions = block.transactions; console.log(`Block ${blockHash} contains ${transactions.length} transactions`); for (let i = 0; i < transactions.length; i++) { const transaction = await provider.getTransaction(transactions[i]); console.log(`Sender of transaction at index ${i}:`, transaction.from); } } getSendersInBlock("0x23425eb885e7c32efa9ee6a7cf946487fe2e1f26ec0f39c0c7f9b5bd6b5345f3"); ``` The number of transactions in the block is determined using `transactions.length` and the total number of transactions logged to the console. A `for` loop is then used to retrieve information about each transaction in the block. For each iteration of the loop, the code calls the `provider.getTransaction` method with the transaction hash at the current index to retrieve information about that transaction. The `from` property of the returned transaction object is logged to the console to show the sender of the transaction. # eth_getTransactionByBlockNumberAndIndex | Polygon Source: https://docs.chainstack.com/reference/polygon-gettransactionbyblocknumberandindex openapi/polygon_node_api/transaction_info/eth_getTransactionByBlockNumberAndIndex.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. Polygon API method that retrieves information about a specific transaction based on the block number and the transaction index within the block. This information can be used to track transactions, debug issues, analyze data, and build decentralized applications on the Polygon blockchain. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter). * `quantity` — the integer identifying the transaction index position within the block, encoded as hexadecimal. ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet. It essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. It is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async (blocNumber, index) => { const transaction = await provider.send("eth_getTransactionByBlockNumberAndIndex", [blocNumber, index]); console.log(transaction); } getTransaction("latest", "0x1"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_by_block(32738491, 22)) # Hex encoded parameters starting with "0x" are accepted as well. ``` ## Use case The `eth_getTransactionByBlockNumberAndIndex` can be used to retrieve transaction details from a block. In the following example the `getTypeInBlock` function checks how many transactions exist in a block, then retrieves the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of each (`type` field), and displays it. > [!NOTE] > > Note that there are simpler ways to get this information, and this is only a practical example of how to use `eth_getTransactionByBlockNumberAndIndex`, if the number of transactions in the block is already known, the first step can be skipped. ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTypeInBlock() { const latestBlock = await provider.getBlockNumber(); const block = await provider.getBlock(latestBlock, false); const transactions = block.transactions; console.log(`The latest block is ${latestBlock} and contains ${transactions.length} transactions`); for (let i = 0; i < transactions.length; i++) { const transaction = await provider.send("eth_getTransactionByBlockNumberAndIndex", [latestBlock, i]); console.log(`The type of transaction at index ${i}:`, transaction.type); } } getTypeInBlock(); ``` The number of transactions in the block is determined using `transactions.length` and the total number of transactions logged to the console. A `for` loop is then used to retrieve information about each transaction in the block. For each iteration of the loop, the code calls `provider.send("eth_getTransactionByBlockNumberAndIndex", [latestBlock, i])` to retrieve information about the transaction at the current index. The `type` property of the returned transaction object is logged to the console to show the sender of the transaction. # eth_getTransactionByHash | Polygon Source: https://docs.chainstack.com/reference/polygon-gettransactionbyhash openapi/polygon_node_api/transaction_info/eth_getTransactionByHash.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns the information about a transaction from the transaction hash. Available on Polygon via Chainstack. Polygon API method that returns the information about a transaction from the transaction hash. It is used to retrieve information about a specific transaction by its hash. The method returns an object with details about the transaction, such as the sender, recipient, gas used, transaction value, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `object` — the transaction response object, or `null` if no transaction is found: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasPrice` — the price of gas in Wei included in the transaction by the sender. * `maxFeePerGas` — the maximum amount the sender of the transaction is willing to pay per unit of gas for the transaction to be executed. * `maxPriorityFeePerGas` — the maximum priority fee the sender of the transaction is willing to pay per unit of gas. * `hash` — the hash that uniquely identifies the transaction. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `nonce` — a counter identifying the transaction's number sent by the sender wallet; it essentially identifies how many transactions an account has made. Used to ensure each transaction is executed only once. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionIndex` — the index of the transaction within the block. This field is `null` for transactions that have not yet been included in a block. * `value` — the value of the native token transferred along with the transaction, in Wei. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. * `accessList` — a list of [authorized addresses and storage keys](https://eips.ethereum.org/EIPS/eip-2930#:~:text=The%20accessList%20specifies%20a%20list,of%20accessing%20outside%20the%20list.) the transaction plans to interact with. * `v` — the recovery parameter in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `r` — the first component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). * `s` — the second component of the signature in the [Ethereum Signature Algorithm](https://ethereum.org/en/glossary/#ecdsa) (ECDSA). ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const getTransaction = async (transactionHash) => { const transaction = await provider.send("eth_getTransactionByHash", [transactionHash]); console.log(transaction); }; getTransaction("0xcaed5e30e2ee09f7e9d8af1fad282924749e79ca1ed7598a4dce073b7443fcbe") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction("0x4b22e75f12d8d68a8b3d174782b75bdbfa51f489cfd7127ecdd70d10483984c5")) ``` ## Use case One use case for `eth_getTransactionByHash` is to inspect a specific transaction and display information about it, such as the address that was sent, the block it was included in, and the number of transactions the sender sent so far (nonce). This information can be useful for monitoring and analyzing transactions. Here's an example of how you can achieve this in ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getTransaction(transactionHash) { // Get the transaction using the provided transaction hash const transaction = await provider.getTransaction(transactionHash) // Store the desired parameters in constants const sender = transaction.from; const block = transaction.blockNumber const transactionsCount = transaction.nonce // Log the transaction information to the console console.log(`Transaction ${transactionHash} was sent from address ${sender}`); console.log(`It was included in block ${block}`); console.log(`The sender has sent ${transactionsCount} transactions so far\n`); } getTransaction("0xcaed5e30e2ee09f7e9d8af1fad282924749e79ca1ed7598a4dce073b7443fcbe") ``` In this example, the program starts by connecting to a node endpoint. Then, call `provider.getTransaction` and pass in the transaction hash as an argument. The method returns a Promise that resolves to a transaction object, which is used to retrieve the sender address, the block number the transaction was included in, and the address nonce. The last step is to log the information to the console. > [!NOTE] > > Note that the desired parameters are stored in constants, so you can use them for further processing. # eth_getTransactionReceipt | Polygon Source: https://docs.chainstack.com/reference/polygon-gettransactionreceipt openapi/polygon_node_api/transaction_info/eth_getTransactionReceipt.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that retrieves the receipt of a transaction from a transaction hash. eth_getTransactionReceipt on Polygon via Chainstack. > [!NOTE] > > **No response in the interactive API** > > The response to the call might be too big for the web app to handle, so if you are not getting it here, copy the CURL example and run in your terminal—this will work. Polygon API method that retrieves the receipt of a transaction from a transaction hash. It can be used to verify the success or failure of a transaction, check the execution status of a smart contract, retrieve logs, check if a transaction deployed a smart contract, and more. ## Parameters * `hash` — the hash identifying a transaction ## Response * `Transaction receipt` — the receipt object with: * `blockHash` — the block hash. Identifies the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `blockNumber` — the number of the block in which the transaction was included. This field is `null` for transactions that have not yet been included in a block. * `contractAddress` — the address of the contract created by the transaction if it was a contract creation transaction. Otherwise, the value is `null`. * `cumulativeGasUsed` — the total amount of gas used in the block until this transaction was executed. * `effectiveGasPrice` — the actual value deducted from the sender's account for this transaction. * `from` — the address of the sender who initiated the transaction. * `gasUsed` — the amount of gas used by this specific transaction alone. * `logs` — an array of log objects generated by this transaction, if any. Logs are generated by smart contracts. * `logsBloom` — the bloom filter used by light clients to quickly retrieve logs related to the transaction. * `status` — the success status of the transaction, represented as `1` for success or `0` for failure. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `transactionHash` — the hash that uniquely identifies the transaction. * `transactionIndex` — the index of the transaction within the block. * `type` — the [type](https://ethereum.org/en/developers/docs/transactions/#types-of-transactions) of the transaction. `0` indicates a regular transfer; `2` indicates a contract creation or smart contract function call. ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const getTransactionReceipt = async (transactionHash) => { const receipt = await provider.send("eth_getTransactionReceipt", [transactionHash]); console.log(receipt); }; getTransactionReceipt('0xcaed5e30e2ee09f7e9d8af1fad282924749e79ca1ed7598a4dce073b7443fcbe') ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.get_transaction_receipt("0xcaed5e30e2ee09f7e9d8af1fad282924749e79ca1ed7598a4dce073b7443fcbe")) ``` ## Use case A practical use case for `eth_getTransactionReceipt` is to verify a transaction's status and whether it was successful or not. This can be especially useful when deploying a contract, as you can use the receipt to confirm that the contract was deployed successfully and has a valid contract address. Here's an example in ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); async function verifyTransaction(transactionHash) { // Get the transaction receipt const receipt = await provider.getTransactionReceipt(transactionHash); // Check if the receipt is not yet available if (!receipt) { console.log(`Transaction ${transactionHash} is still pending...`); return; } // Check if the transaction was successful if (receipt.status) { console.log(`✅The transaction was successful! \nHash: ${transactionHash}`); // Check if the transaction deployed a smart contract if (receipt.contractAddress != null) { console.log(`📑The smart contract was deployed at address: ${receipt.contractAddress}`); } else { console.log(`❗This transaction did not deploy a smart contract`); } } else { console.log(`💥The transaction failed. \nHash: ${transactionHash}`); } } verifyTransaction('0xcaed5e30e2ee09f7e9d8af1fad282924749e79ca1ed7598a4dce073b7443fcbe') ``` This script holds an async function called `verifyTransaction` that takes a transaction hash as input. The function uses the ethers.js library to retrieve the transaction receipt of the specified transaction using the `eth_getTransactionReceipt` method. The function first checks if the transaction receipt is not yet available, and if so, it logs a message to the console indicating that the transaction is still pending. If the receipt is available, the function checks the status property of the receipt to determine if the transaction was successful. If the transaction was successful, the function checks if a smart contract was deployed as part of the transaction. The function logs the smart contract's address to the console if a smart contract is deployed. If not, the function logs a message indicating that no smart contract was deployed. If the transaction was not successful, the function logs a message indicating that the transaction failed, along with the transaction hash. # Logs and events | Polygon Source: https://docs.chainstack.com/reference/polygon-logs-rpc-methods Polygon PoS event log RPC methods for querying smart contract events, applying log filters, and tracking on-chain activity via Chainstack endpoints. Retrieving logs information from the Polygon blockchain can be done by using the following methods: > [card] [eth\_getLogs](https://dash.readme.com/project/chainstack/v1.0/refs/getlogs) > [card] [eth\_newFilter](https://dash.readme.com/project/chainstack/v1.0/refs/newfilter) > [!NOTE] > > Find logs limitations in [Understanding eth\_getLogs limitations](/docs/understanding-eth-getlogs-limitations) # eth_subscribe ("logs") | Polygon Source: https://docs.chainstack.com/reference/polygon-native-subscribe-logs Subscribe to real-time Polygon event logs via WebSocket. Receive notifications whenever new logs matching your specified filter criteria are emitted. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) the event filter options: * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. > [!NOTE] > > Read [Tracking some Bored Apes: The Ethereum event logs tutorial](/docs/tracking-some-bored-apes-the-ethereum-event-logs-tutorial) to learn more about topics and event logs. ## Response * `subscription` — the subscription ID. * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_subscribe("logs")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_subscribe", "params": ["logs", {"address":"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewLogs() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['logs', {"address":"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewLogs(); ``` This will generate a continuous stream of data displaying new event logs matching the filter. Use [eth\_unsubscribe](/reference/polygon-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("logs")` method in Polygon is used to subscribe to the logs generated by smart contracts on the blockchain. These logs contain information about events that have occurred on the blockchain, such as details of a smart contract function and outputs of transactions. A practical and real-world scenario for using the `eth_subscribe("logs")` method could be in the context of a decentralized exchange (DEX). In the context of a DEX, trades are executed using smart contracts that run on the Polygon blockchain. When a trade is executed on a DEX, a smart contract is called to process the transaction. This smart contract generates a log that contains information about the trade, such as the amount of cryptocurrency being traded, the parties involved, and so on. By using the `eth_subscribe("logs")` method, a user or application can subscribe to the logs generated by the DEX's smart contracts. This can be useful for tracking trades in real time, monitoring the performance of the DEX, and generating reports on trading activity. For example, a trader might use the `eth_subscribe("logs")` method to monitor the logs generated by a DEX in order to identify trends in trading activity. They might look for patterns in the volume and price of trades, which could help them make more informed trading decisions. Similarly, an analytics platform might use the `eth_subscribe("logs")` method to collect data on trading activity across multiple DEXs. By analyzing this data, they could generate insights into the performance of different DEXs, which could be useful for investors and traders looking to make informed decisions about where to trade. # eth_subscribe("newHeads") | Polygon Source: https://docs.chainstack.com/reference/polygon-native-subscribe-newheads Subscribe to new Polygon block headers via WebSocket. Receive real-time notifications whenever a new block is added to the Polygon PoS blockchain. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `newHeads` in this case. ## Response * `subscription` — the subscription ID. * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Polygon nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Polygon blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Polygon network since the inception of the network. It measures the overall security and integrity of the Polygon network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/polygon-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `eth_subscribe("newHeads")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewBlocks() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewBlocks(); ``` This will generate a continuous stream of data displaying new block headers as they are added to the chain. Use [eth\_unsubscribe](/reference/polygon-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newHeads")` method can be useful in a variety of real-world scenarios, such as: * Real-time monitoring of blockchain activity. Companies or individuals who rely on the Polygon network for their business can use `eth_subscribe("newHeads")` to receive real-time notifications about new blocks being added to the chain. This can help them keep track of important transactions, such as high-value trades, in real time and respond quickly to any issues that may arise. * Decentralized Finance (DeFi) applications. DeFi applications, such as lending platforms, can use `eth_subscribe("newHeads")` to monitor the blockchain for new blocks that might contain important data, such as changes in interest rates or new lending opportunities. * Smart contract automation. Developers can use `eth_subscribe("newHeads")` to trigger automated smart contract actions whenever a new block is added to the chain. For example, a smart contract could automatically execute an action order whenever a new block is added that meets certain criteria. # eth_subscribe ("newPendingTransactions") | Polygon Source: https://docs.chainstack.com/reference/polygon-native-subscribe-newpendingtransactions Subscribe to pending Polygon transactions via WebSocket. Receive real-time notifications when new unconfirmed transactions enter the Polygon mempool. ## Parameters * `newPendingTransactions` — the event to subscribe to. * `boolean` — optional. Pass `true` to receive full transaction bodies instead of hashes. Defaults to `false`. > [!WARNING] > > `newPendingTransactionsWithBody` is not available on Polygon. It was an Erigon extension, and Polygon nodes run the Bor client, which rejects it with `no "newPendingTransactionsWithBody" subscription in eth namespace`. Pass `true` as the second parameter instead — see [Polygon methods](/docs/polygon-methods) for the rest of the Erigon-to-Bor changes. ## Response * `subscription` — the subscription ID. * `result` — the hash of a pending transaction, or the full transaction object when you pass `true`. ## `eth_subscribe("newPendingTransactions")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) # Transaction hashes only > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newPendingTransactions"]} # Full transaction bodies > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newPendingTransactions",true]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewPendingTransactions() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newPendingTransactions', true], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewPendingTransactions(); ``` This will generate a continuous stream of data displaying new pending transactions as they are added to the mempool. Use [eth\_unsubscribe](/reference/polygon-native-unsubscribe) to remove the subscription. ## Use case The `eth_subscribe("newPendingTransactions")` method in Polygon is used to subscribe to the network and receive real-time updates on all new pending transactions on the network. This can be useful in a variety of real-world scenarios, including: * Trading applications. A trading application can use this method to keep track of any new transactions that are being made on the network. This can be particularly useful for high-frequency trading, where speed is crucial. By subscribing to this method, the trading application can receive real-time updates on new trades, allowing it to quickly respond and execute trades. * Monitoring tools. Monitoring tools can use this method to keep track of any suspicious activity on the network. By subscribing to this method, monitoring tools can receive real-time updates on any new pending transactions and quickly take action if necessary. * Payment processors. Payment processors can use the `eth_subscribe("newPendingTransactions")` method to monitor incoming transactions from their customers. This can be particularly useful for businesses that rely on blockchain technology to process payments, as it allows them to track incoming payments in real time. By subscribing to this method, payment processors can receive instant notifications when new payments are received, which can help streamline their payment processing workflows. # eth_unsubscribe | Polygon Source: https://docs.chainstack.com/reference/polygon-native-unsubscribe Cancel any active Polygon WebSocket subscription with eth_unsubscribe. Pass the subscription ID to stop receiving real-time blockchain event notifications. When a client subscribes to a particular event, the Polygon node will send updates as soon as they occur. However, in some cases, the client may no longer be interested in receiving updates for a particular event. The `eth_unsubscribe` method can be used to cancel the following subscriptions: > [card] [eth\_subscribe('newHeads')](/reference/polygon-native-subscribe-newheads) > [card] [eth\_subscribe('newPendingTransactions')](/reference/polygon-native-subscribe-newpendingtransactions) > [card] [eth\_subscribe('logs')](/reference/polygon-native-subscribe-logs) ## Parameters * `string` — the ID of the subscription that you want to cancel ## Response * `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `eth_unsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_unsubscribe", "params": ["0x7529626735859f32962b10b19ee2e3eb"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function removeSubscription() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_unsubscribe', params: ['0x9de3135dd3dd6c6b99bd104061a5af4f'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } removeSubscription(); ``` This will remove the subscription matching the ID passed as a parameter. # eth_newBlockFilter | Polygon Source: https://docs.chainstack.com/reference/polygon-newblockfilter openapi/polygon_node_api/blocks_info/eth_newBlockFilter.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that creates a filter that watches for new blocks on the blockchain. eth_newBlockFilter on Polygon via Chainstack. Polygon API method that creates a filter that watches for new blocks on the blockchain. It returns a filter ID, which can be used to retrieve the results using the [eth\_getFilterChanges](/reference/getfilterchanges) method. The `eth_newBlockFilter` method is useful for developers who must be notified of new blocks on the blockchain in real time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter. The filters created are stored on the blockchain client instance. The filter fill is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * [eth\_getFilterChanges](/reference/getfilterchanges) to retrieve updates * [eth\_uninstallFilter](/reference/uninstallfilter) to remove the filter ## `eth_newBlockFilter` code examples > [!NOTE] > > **web3.eth.filterdeprecation** > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/polygon-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newBlockFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') return blocks_filter except Exception as e: print(e) blocks = get_new_blocks() filter_id = blocks.filter_id print(filter_id) ``` ## Use case One use case for `eth_newBlockFilter` in a simple DApp is to listen for new blocks and update the user interface with the latest block information. When the DApp starts, it creates a new filter using `eth_newBlockFilter` to listen for new blocks. When a new block is added to the blockchain, the filter is triggered, and the DApp retrieves the latest block information using a Web3 library. Here is an implementation of this use case using web3.py: ```python main.py theme={"system"} import time from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Set up a new blocks filter def get_new_blocks(): try: blocks_filter = web3.eth.filter('latest') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) def watch_for_blocks(new_filter): # Wait for new blocks to arrive while True: # Retrieve the changes that match the filter changes = web3.eth.getFilterChanges(new_filter) # If there are no changes, wait for new blocks if len(changes) == 0: time.sleep(1) # Wait for 1 second continue # Process the new blocks for change in changes: block = web3.eth.getBlock(change) print('New block:', block) def main(): # Create a new filter to watch for new blocks new_filter = get_new_blocks() print(new_filter) # Watch for new blocks watch_for_blocks(new_filter) if __name__ == '__main__': main() ``` The code is divided into three main functions: `get_new_blocks`, `watch_for_blocks`, and `main`. Let's take a closer look at each of them: `get_new_blocks` is responsible for setting up a new filter to watch for new blocks. The try block sets up the filter by calling the `eth.filter` method of the web3.py library and passing it the string `latest`. The `split` method is then used to split the resulting string at each space character, and the second part of the resulting list is extracted as the filter ID. The filter ID is returned from the function. > [!NOTE] > > The `eth.filter` method of the web3.py library does not only return the filter ID but it returns a sentence saying `Filter for 0x3c3f5a5e637d29c033e4e7a3f7e870cd`; this is the reason for the string manipulation within the `get_new_blocks` function. `watch_for_blocks` is the function that watches for new blocks. It does this by first entering an infinite loop while `True`. Within this loop, the [eth\_getFilterChanges](/reference/getfilterchanges) method is used to retrieve any changes that match the filter ID passed to the function. If there are no changes, the code waits for 1 second using the `time.sleep` method and then continues with the loop. If there are changes, the new blocks are processed by looping through them and calling the `eth.getBlock` method of the web3 library to retrieve the details of each block. Finally, the details of the new blocks are printed on the console. `main` is the function that ties everything together. It first calls `get_new_blocks` to set up a new filter to watch for new blocks, and then passes the resulting filter ID to `watch_for_blocks` to start watching for new blocks. The filter ID is also printed to the console to confirm that the filter has been set up correctly. # eth_newPendingTransactionFilter | Polygon Source: https://docs.chainstack.com/reference/polygon-newpendingtransactionfilter openapi/polygon_node_api/transaction_info/eth_newPendingTransactionFilter.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that creates a filter that listens for new pending transactions on the blockchain. Chainstack Polygon reference. Polygon API method that creates a filter that listens for new pending transactions on the blockchain. It returns a filter ID, which can be used to retrieve the results using the `eth_getFilterChanges` method. The `eth_newPendingTransactionFilter` method is useful for developers who must be notified of new pending transactions in real time. ## Parameters * `none` ## Response * `result` — a hexadecimal string representing the ID of the newly created filter ## Code examples > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). Use the following methods with the filter ID: * `eth_getFilterChanges` to retrieve updates * `eth_uninstallFilter` to remove the filter ## `eth_newPendingTransactionFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/polygon-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createFilter = async () => { try { const filterId = await provider.send('eth_newPendingTransactionFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def get_new_pending_transactions(): try: blocks_filter = web3.eth.filter('pending') # Split the string at the space character parts = str(blocks_filter).split(' ') # Extract the filter value from the second part filter_id = parts[2] return filter_id except Exception as e: print(e) new_filter = get_new_pending_transactions() print(new_filter) ``` ## Use case One way to use the `eth_newPendingTransactionFilter` method is to listen for new pending transactions at predefined intervals and extract specific data from them. For instance, a decentralized application might check for pending transactions every second and identify those that transfer a value greater than a certain amount of the MATIC token. This could be useful for monitoring high-value transactions or detecting potential fraud or security threats in real time. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Create a filter using eth_newPendingTransactionFilter const createFilter = async () => { try { const filterId = await provider.send('eth_newPendingTransactionFilter', []); console.log(filterId); // the filter ID returned by eth_newFilter return filterId } catch (error) { console.log(error); } }; // Use the filter to extract the value from each transaction async function getValue(filter) { try { // Retrieve the list of new pending transactions const transactions = await provider.send('eth_getFilterChanges', [filter]); // Loop through the list of transactions and process each one for (const hash of transactions) { const receipt = await provider.send("eth_getTransactionByHash", [hash]); // Check that the receipt is not null and has a non-null value field if (receipt && receipt.value != null) { const value = receipt.value; const decimalValue = BigInt(value).toString(); const convertedValue = ethers.utils.formatEther(decimalValue); // Check if the transferred value is greater than or equal to 100 Matic if (convertedValue >= 100) { console.log(`This transaction is sending more than 100 Matic`); console.log(`Transaction Hash: ${hash}`); console.log(`Value transferred: ${convertedValue} \n`); } } } } catch (error) { console.error(error); // Handle errors that may occur } } // Main program setting an interval to call the `getValue` function at regular intervals async function main() { const filterId = await createFilter(); setInterval(getValue, 1000, filterId); } main() ``` This code sets up an Ethereum filter to listen for new pending transactions and extract specific data from them. The code consists of three functions: `createFilter`, `getValue`, and `main`. Here's an overview of how each function works: The `createFilter` function sets up a new filter using the `eth_newPendingTransactionFilter` method. It returns the ID of the filter, which can be used to retrieve data from the filter later. The `main` function sets up the program by first calling the `createFilter` function to create a new filter and retrieve its ID. It then sets an interval to call the `getValue` function every 1 second, passing the filter ID as an argument to retrieve the latest transaction data at regular intervals. # eth_sendRawTransactionSync | Polygon Source: https://docs.chainstack.com/reference/polygon-sendrawtransactionsync openapi/polygon_node_api/execute_transactions/eth_sendRawTransactionSync.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that submits a signed transaction and blocks until the transaction receipt is returned or the timeout expires. On Polygon. Polygon API method that submits a signed transaction and blocks until the transaction receipt is returned or the timeout expires. This is a synchronous version of [`eth_sendRawTransaction`](/reference/sendrawtransaction) — instead of returning just the transaction hash, it waits for the transaction to be included in a block and returns the full receipt. > [!WARNING] > > **Hex-encoded timeout** > > Polygon's Bor client predates [EIP-7966](https://eips.ethereum.org/EIPS/eip-7966), and its `timeout` parameter is typed as `hexutil.Uint64`. You must pass the timeout as a **hex-encoded** value (e.g., `"0x1388"` for 5000 ms), **not** as a decimal integer like on Base, Ethereum, or other EIP-7966-conformant implementations. Passing a decimal integer returns `invalid argument 1: json: cannot unmarshal non-string into Go value of type hexutil.Uint64`. ## Parameters * `signedTransactionData` — the signed transaction data in hexadecimal format. Includes nonce, gas price, gas limit, recipient address, value, data, and signature. * `timeout` (optional) — maximum wait time in milliseconds, passed as a **hex-encoded** uint64 string (e.g., `"0x1388"` for 5000 ms). If omitted, the node uses its configured default. ## Response * `result` — the full transaction receipt object once the transaction is included in a block, with the same fields as [`eth_getTransactionReceipt`](/reference/gettransactionreceipt). If the timeout expires before inclusion, the node returns an error. ## `eth_sendRawTransactionSync` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const sendTransactionSync = async () => { const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); const tx = { to: "0xRecipientAddress", value: ethers.parseEther("0"), chainId: 137, }; // Sign the transaction const signedTx = await wallet.signTransaction(tx); // Send synchronously — Bor expects a HEX-encoded timeout const receipt = await provider.send( "eth_sendRawTransactionSync", [signedTx, "0x1388"] // 5000 ms in hex ); console.log(`Confirmed in block: ${receipt.blockNumber}`); console.log(`Status: ${receipt.status}`); }; sendTransactionSync(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) account = web3.eth.account.from_key("YOUR_PRIVATE_KEY") tx = { 'nonce': web3.eth.get_transaction_count(account.address), 'to': "0xRecipientAddress", 'value': 0, 'gas': 21000, 'gasPrice': web3.eth.gas_price, 'chainId': 137, # Polygon mainnet } signed = account.sign_transaction(tx) # Bor requires the timeout as a hex string timeout_ms = hex(5000) # "0x1388" receipt = web3.provider.make_request( "eth_sendRawTransactionSync", [signed.raw_transaction.hex(), timeout_ms] ) print(f"Receipt: {receipt['result']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_sendRawTransactionSync", "params": ["SIGNED_TX_DATA", "0x1388"], "id": 1}' ``` ## When to use Use `eth_sendRawTransactionSync` instead of `eth_sendRawTransaction` when: * You need the receipt immediately and want to avoid polling with `eth_getTransactionReceipt`. * You are building synchronous request/response flows (payments, bots, simulations). * You want to simplify transaction submission logic by getting the result in a single call. For fire-and-forget scenarios or when submitting many transactions in parallel, use [`eth_sendRawTransaction`](/reference/sendrawtransaction). ## Cross-chain note Other Chainstack chains expose the same method but with different parameter conventions. Per EIP-7966, the timeout should be a decimal integer; Polygon's Bor is the exception because the method existed in Bor before the EIP standardized the encoding. See the [Ethereum equivalent](/reference/ethereum-sendrawtransactionsync) for the EIP-7966 form. # subscribe ("logs") | Polygon Source: https://docs.chainstack.com/reference/polygon-subscribelogs Reference for the subscribe ("logs") JSON-RPC method on the Polygon blockchain via Chainstack nodes. Parameters, response, and examples included. Subscription equivalent to [eth\_newFilter | Polygon](/reference/newfilter). `subscribe("logs")` allows developers to subscribe to real-time updates about new event logs on the Polygon blockchain; the application will receive notifications whenever new logs matching the filter are emitted. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) the subscription options: * `fromBlock` — an integer that specifies the starting block number from which the logs should be fetched. `null` by default. * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `subscribe("logs")` code example > [!NOTE] > > Note that subscriptions require a WebSocket connection. Use a `WebSocketProvider` and attach a listener to a log filter object: * The listener callback activates for each new log that matches the filter. * Wrap the subscription in a `try...catch` block to detect any error during setup. * `provider.removeAllListeners(filter)` removes the listener and stops the subscription. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); // Only log transfer events from the WMATIC smart contract. const logsFilter = { address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] } async function subscribeToLogs() { try { // Create a new subscription to the logs matching the filter provider.on(logsFilter, handleLogs); console.log("Subscribed to logs"); } catch (error) { console.error(`Error subscribing to new logs: ${error}`); } } /* Listener functions to react to the different events */ // Event listener that logs the filtered events function handleLogs(log) { console.log(log) } subscribeToLogs(); ``` ## Use case A practical use case for `subscribe("logs")` is a DApp that continuously listens for new specific event logs and isolates certain fields for analytics purposes. This is useful, for example, to only track events about a specific ERC-721 token. The following is an implementation of this concept using ethers.js subscriptions, this program will only track the `PunkTransfer` event from the [PolygonPunks collection](https://polygonscan.com/token/0x9498274B8C82B4a3127D67839F2127F2Ae9753f4#code) smart contract. The `PunkTransfer` event looks like this in the smart contract: ```sol PolygonPunksMarket.sol theme={"system"} event PunkTransfer(address indexed from, address indexed to, uint256 punkIndex); ``` The following code will log all of the new `PunkTransfer` events and extract the block number. This can be useful to analyze when tokens are moved. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); // Only log PunkTransfer events from the Polygon Punks smart contract. const logsFilter = { address: '0x9498274B8C82B4a3127D67839F2127F2Ae9753f4', topics: ['0x05af636b70da6819000c49f85b21fa82081c632069bb626f30932034099107d8'] } async function subscribeToLogs() { try { // Create a new subscription to the logs matching the filter provider.on(logsFilter, handleLogs); console.log("Subscribed to logs"); } catch (error) { console.error(`Error subscribing to new logs: ${error}`); } } // Use this function to unsubscribe and exit the program function unsubscribe() { provider.removeAllListeners(logsFilter); console.log("Successfully unsubscribed!"); return process.exit(1); } /* Listener functions to react to the different events */ // Event listener that logs the filtered events async function handleLogs(log) { const block = log.blockNumber console.log(`A Polygon Punk was transferred in this block: ${block}`) } subscribeToLogs(); ``` This code creates a new subscription to the logs matching the filter using the `provider.on` method with a `WebSocketProvider`. This method attaches a listener that is called for each new log matching the filter. The code defines the `handleLogs` listener function that is attached to the provider. The `handleLogs` function is called when a new log matching the filter is emitted, then extracts the block number field and logs it to the console. The subscription setup is wrapped in a `try...catch` block to detect any error during setup. The code includes the `unsubscribe` function that can be implemented in the logic to remove the listener with `provider.removeAllListeners` and exit the program when a condition is met. Finally, the code calls the `subscribeToLogs` function, which creates the subscription and attaches the event listeners. When a new event log is received, the `handleLogs` function is called to extract the data and log it to the console. ## Try the `subscribe("logs")` RPC method yourself # subscribe ("newBlockHeaders") | Polygon Source: https://docs.chainstack.com/reference/polygon-subscribenewblockheaders Reference docs for the subscribe ("newBlockHeaders") JSON-RPC method on the Polygon blockchain, available via Chainstack JSON-RPC nodes. The ethers.js subscription equivalent to [eth\_newBlockFilter](/reference/polygon-newblockfilter). `subscribe("newBlockHeaders")` allows developers to subscribe to real-time updates about new block headers on the Polygon blockchain; the application will receive notifications whenever a new block is added to the blockchain. The notification will include information about the new block, such as its block number, hash, and timestamp. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `newBlockHeaders` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Polygon nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Polygon blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Polygon network since the inception of the network. It measures the overall security and integrity of the Polygon network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. See [eth\_getTransactionByHash](/reference/ethereum-gettransactionbyhash) for the exact shape. * `uncles` — an array of uncle hashes. ## `subscribe("newBlockHeaders")` code example > [!NOTE] > > Note that subscriptions require a WebSocket connection. Use the `ethers.WebSocketProvider` class to set one up. Use the provider event listeners to react to new block events: * `provider.on("block", listener)` — registers a listener that activates for each new block; the listener receives the new block number. * `provider.off("block", listener)` — removes the listener and stops the subscription. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToNewBlocks() { try { // Register a listener for the 'block' event provider.on('block', handleNewBlock); console.log("Subscribed to new blocks"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } /* Listener to react to new block events */ // Event listener that fetches and logs the received block data async function handleNewBlock(blockNumber) { const block = await provider.getBlock(blockNumber); console.log(block); } subscribeToNewBlocks(); ``` ## Use case A practical use case for `subscribe("newBlockHeaders")` is a DApp that continuously listens for new block headers, then isolates the block number and `baseFeePerGas` for analytics purposes. The following is an implementation of this concept using ethers.js subscriptions: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); let blockCount = 0; async function subscribeToNewBlocks() { try { // Register a listener for the 'block' event provider.on('block', handleNewBlock); console.log("Subscribed to new blocks"); } catch (error) { console.error(`Error subscribing to new blocks: ${error}`); } } function unsubscribeFromNewBlocks() { provider.off('block', handleNewBlock); console.log("Successfully unsubscribed!"); return process.exit(1); } /* Fallback functions to react to the different events */ // Event listener that extracts block number and base fee per gas from the received block header data async function handleNewBlock(blockNumber) { const block = await provider.getBlock(blockNumber); const baseFeeGwei = ethers.formatUnits(block.baseFeePerGas, 'gwei'); blockCount++; console.log(`Block number: ${blockNumber} \n Base Fee per Gas: ${baseFeeGwei} Gwei \n`); if (blockCount === 100) { unsubscribeFromNewBlocks(); } } subscribeToNewBlocks(); ``` This code creates a new subscription to new block events using the `provider.on("block", listener)` method. This method registers a listener that activates for each new block and receives the new block number. The code defines the `handleNewBlock` listener function that is registered for the `block` event. The `handleNewBlock` function is called when a new block is received; it fetches the full block with `provider.getBlock`, then extracts the block number and base fee per gas from the block data using the `ethers.formatUnits` method to convert `baseFeePerGas` to gwei. This code includes an `unsubscribeFromNewBlocks` function which removes the listener with `provider.off("block", listener)` and quits the program using `return process.exit(1)` after it received 100 new blocks. This is to demonstrate its use. Finally, the code calls the `subscribeToNewBlocks` function, which creates the subscription and attaches the event listeners. When a new block header is received, the `handleNewBlock` function is called to extract the block data and log it to the console. ## Try the `subscribe("newBlockHeaders")` RPC method yourself # subscribe ("pendingTransactions") | Polygon Source: https://docs.chainstack.com/reference/polygon-subscribependingtransactions Reference docs for the subscribe ("pendingTransactions") JSON-RPC method on the Polygon blockchain, available via Chainstack JSON-RPC nodes. The ethers.js subscription equivalent to [eth\_newPendingTransactionFilter](/reference/polygon-newpendingtransactionfilter). `subscribe("pendingTransactions")` allows developers to subscribe to real-time updates about pending transactions on the Polygon blockchain; the application will receive notifications whenever a pending transaction appears on the blockchain. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `pendingTransactions` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `string` — the hash identifying the pending transaction. ## `subscribe("pendingTransactions")` code example > [!NOTE] > > Note that ethers.js subscriptions require a WebSocket connection. Use the [provider events](https://docs.ethers.org/v6/api/providers/#ProviderEvent) to attach listeners to the provider: * `"pending"` — activates for each new pending transaction. * `provider.on` — registers an event listener for the specified event. * `provider.off` — removes a previously registered event listener. * `provider.removeAllListeners` — removes all listeners for an event and stops the subscription. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToPendingTransactions() { try { // Subscribe to the 'pending' event to receive new pending transaction hashes provider.on("pending", handleNewPending); console.log("Subscribed to pending transactions"); } catch (error) { console.error(`Error subscribing to new pending transactions: ${error}`); } } /* Callback functions to react to the different events */ // Event listener that logs the received pending transaction hashes function handleNewPending(transactionHash) { console.log(transactionHash); } // Listen for any provider errors provider.on("error", handleError); // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new pending transactions: ${error}`); } subscribeToPendingTransactions(); ``` ## Use case A practical use case for `subscribe("pendingTransactions")` is a DApp that continuously listens for new pending transactions, then isolates the `from`, `to`, and `value` fields for analytics purposes. This is useful, for example, to only track transactions that move at least a certain amount of MATIC. The following is an implementation of this concept using ethers.js subscriptions: ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToPendingTransactions() { try { // Subscribe to the 'pending' event to receive new pending transaction hashes provider.on("pending", handleNewPending); console.log("Subscribed to pending transactions"); } catch (error) { console.error(`Error subscribing to new pending transactions: ${error}`); } } // Use this function to unsubscribe and exit the program function unsubscribe() { provider.removeAllListeners("pending"); console.log("Successfully unsubscribed!"); return process.exit(1); } /* Callback functions to react to the different events */ // Event listener that logs the received pending transactions and extracts from, to, and value fields async function handleNewPending(transactionHash) { try { const transaction = await provider.getTransaction(transactionHash); const from = transaction.from; const to = transaction.to; const value = transaction.value; if (value >= ethers.parseEther("100")) { // 100 MATIC in wei console.log(`----- New pending transaction ------`); console.log(`From: ${from}`) console.log(`To: ${to}`) console.log(`Value: ${Number(ethers.formatEther(value)).toFixed(2)} MATIC \n`) } } catch (error) { console.error(`Error getting transaction: ${error}`); } } // Listen for any provider errors provider.on("error", handleError); // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new pending transactions: ${error}`); } subscribeToPendingTransactions(); ``` This code creates a new subscription to the `pending` event by attaching a listener with the `provider.on` method. Each time a new pending transaction appears, the provider invokes the registered callback with the transaction hash. The code defines two event listener functions: `handleNewPending` and `handleError`. The `handleNewPending` function is called when a new pending transaction is received; it runs the [eth\_getTransactionByHash](/reference/polygon-gettransactionbyhash) method and extracts the `from`, `to`, and `value` fields. If the value transferred is above 100 MATIC, the data is logged. The code includes the `unsubscribe` function that can be implemented in the logic to unsubscribe and exit the program when a condition is met. It calls `provider.removeAllListeners` to remove the listeners and stop the subscription. The `handleError` function is called when an error occurs, and it logs an error message. Finally, the code calls the `subscribeToPendingTransactions` function, which registers the listener. When a new pending transaction is received, the `handleNewPending` function is called to extract the data and log it to the console. ## Try the `subscribe("pendingTransactions")` RPC method yourself # subscribe ("syncing") | Polygon Source: https://docs.chainstack.com/reference/polygon-subscribesyncing Monitor Polygon node sync status in real time with web3.js. Subscribe to syncing updates to track block download progress and chain synchronization. ## Parameters * `string` — a keyword identifying the type of event to subscribe to, `logs` in this case. * `function` — (optional) a callback function that will be called every time a new event of the specified type is received. This function takes two parameters: `error` and `result`. The error parameter contains any error that occurred while subscribing to the event, and the result parameter contains the data for the event that was received. ## Response * `object` — the following sync object when the node is currently syncing: * `startingBlock` — the block number from which the node began syncing. * `currentBlock` — the latest block number the node has synced to. * `highestBlock` — the estimated highest block number that needs to be synced. * `pulledStates` — the number of state entries that have already been downloaded. * `knownStates` — the estimated number of state entries to be downloaded during the sync. * `boolean` — returns `False` when the node is already in sync. ## `subscribe("syncing")` code example > [!NOTE] > > Note that subscriptions require a WebSocket connection. Use a `WebSocketProvider` with ethers.js. ethers.js does not expose a dedicated `syncing` event, so use the WebSocketProvider to send the raw `eth_subscribe` request and listen for `eth_subscription` notifications on the underlying socket: * `eth_subscription` notification — fires for each new syncing event. * `error` — fires if an error is detected during the subscription. * the subscription ID — returned by `eth_subscribe` after the subscription is successfully created. * `eth_unsubscribe` — removes the subscription and returns `true` if successful. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToSync() { try { // Create a new subscription to the 'syncing' event const subscriptionId = await provider.send("eth_subscribe", ["syncing"]); handleConnected(subscriptionId); // Listen for subscription notifications on the underlying WebSocket provider.websocket.onmessage = (message) => { const payload = JSON.parse(message.data); if (payload.method === "eth_subscription" && payload.params.subscription === subscriptionId) { handleSync(payload.params.result); } }; provider.websocket.onerror = handleError; } catch (error) { console.error(`Error subscribing to sync: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs a message when there is a new syncing event function handleConnected(subscriptionId) { console.log(`New subscription: ${subscriptionId}`); } // Event listener that logs the filtered events async function handleSync(sync) { console.log(sync) } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error: ${error}`); } subscribeToSync(); ``` ## Use case A practical use case for `subscribe("syncing")` is a DApp that continuously listens for the status of a node and notifies the developer if the node falls behind a certain number of blocks. The following is an implementation of this concept using ethers.js subscriptions, this program will leave a notification in the console if the node falls more than 100 blocks behind. ```javascript index.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_WSS_URL"; const provider = new ethers.WebSocketProvider(NODE_URL); async function subscribeToSync() { try { // Create a new subscription to the 'syncing' event const subscriptionId = await provider.send("eth_subscribe", ["syncing"]); handleConnected(subscriptionId); // Listen for subscription notifications on the underlying WebSocket provider.websocket.onmessage = (message) => { const payload = JSON.parse(message.data); if (payload.method === "eth_subscription" && payload.params.subscription === subscriptionId) { handleSync(payload.params.result); } }; provider.websocket.onerror = handleError; } catch (error) { console.error(`Error: ${error}`); } } /* Fallback functions to react to the different events */ // Event listener that logs a message when there is a new syncing event function handleConnected(subscriptionId) { console.log(`New subscription: ${subscriptionId}`); } // Event listener that logs the filtered events async function handleSync(sync) { let currentBlock; let highestBlock; const status = sync.status; for (const property in status) { if (property === 'CurrentBlock') { currentBlock = status[property]; } else if (property === 'HighestBlock') { highestBlock = status[property]; } } if (currentBlock !== undefined && highestBlock !== undefined) { const blocksBehind = highestBlock - currentBlock; console.log(`The node is ${blocksBehind} blocks behind the network.`); if (blocksBehind > 1000) { alert(`The node is ${blocksBehind} blocks behind the network. Please check your connection.`); } } } // Event listener that logs any errors that occur function handleError(error) { console.error(`Error receiving new blocks: ${error}`); } subscribeToSync(); ``` This code creates a new subscription to the `syncing` event using the `eth_subscribe` request sent through the `WebSocketProvider`. This returns a subscription ID that is used to match incoming notifications on the underlying WebSocket. The code defines three event listener functions: `handleConnected`, `handleSync`, and `handleError`. The `handleConnected` function is called when the subscription is created, and it logs a message with the subscription ID. The `handleSync` function is called when a new syncing notification arrives, extracts the `CurrentBlock` and the `HighestBlock` field, and then compares them. If the node is more than 1,000 blocks behind, the user will receive an alert. The `handleError` function is called when an error occurs, and it logs an error message. Finally, the code calls the `subscribeToSync` function, which creates the subscription and attaches the event listeners. When a new event is received, the `handleSync` function is called to extract the data and log it to the console. ## Try the `subscribe("syncing")` RPC method yourself # debug_traceBlockByHash | Polygon Source: https://docs.chainstack.com/reference/polygon-traceblockbyhash openapi/polygon_node_api/debug_and_trace/debug_traceBlockByHash.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that traces the execution of a block. debug_traceBlockByHash JSON-RPC method available on the Polygon blockchain via Chainstack. Polygon API method that traces the execution of a block. This method can be used to debug and analyze smart contracts and transactions on the Polygon blockchain. It provides a detailed trace of the execution of a block, including information on all the transactions and calls that interacted with the block, as well as the gas used, memory, storage operations, and other performance metrics for each operation. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#polygon) with the debug and trace API methods enabled. ## Parameters * `hash` — the hash of the block to be traced. * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. * `flatCallTracer` — tracer that returns the transaction's calls as a flat array in the Parity-style trace format, with each entry's position given by its `traceAddress`. This is the drop-in replacement for the Erigon `trace_*` methods, which Bor does not implement. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call. Encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `flatCallTracer` response * `array` — the block's calls as a flat list in the Parity trace format, one object per call: * `action` — the call details: `from`, `to`, `value`, `gas`, `input`, and `callType` (for example, `call`, `delegatecall`, or `staticcall`). Contract creations and self-destructs carry the corresponding Parity `create` and `suicide` fields instead. * `result` — the call outcome: `gasUsed` and `output`. Contract creations return the new contract `address` and its `code`. * `subtraces` — the number of child calls made directly by this call. * `traceAddress` — the position of this call in the call tree, as an array of child indices. * `type` — the call type: `call`, `create`, or `suicide`. * `error` — present only when the call failed, with the failure reason. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ## `debug_traceBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByHash = async (blockHash) => { // Specify the type of tracer: 4byteTracer, callTracer, flatCallTracer, or prestateTracer const tracer = { tracer: '4byteTracer' }; const traces = await provider.send("debug_traceBlockByHash", [blockHash, tracer]); console.log(traces); }; traceBlockByHash("0x22507441629262af0d44bfadebe086470df5936827bc482300b5183b4b8ac8bd") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block_hash = "0x22507441629262af0d44bfadebe086470df5936827bc482300b5183b4b8ac8bd" # Specify the type of tracer: 4byteTracer, callTracer, flatCallTracer, or prestateTracer tracer = { "tracer": '4byteTracer' } block_traces = web3.provider.make_request('debug_traceBlockByHash', [block_hash, tracer]) print(block_traces) ``` ## Use case One practical use case for `debug_traceBlockByHash` with the `4byteTracer` tracer would be for auditing or analysis purposes of a blockchain. For example, if there are token contracts on the blockchain and the transfer function of these contracts is used to send tokens between users, a developer could use the `debug_traceBlockByHash` method with the `4byteTracer` tracer to trace the transactions in a given block and count how many times the transfer function was called. The following is an implementation of this logic using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function traceTransferFunction(blockHash) { try { // Specify the type of tracer: 4byteTracer, callTracer, flatCallTracer, or prestateTracer const tracer = { tracer: '4byteTracer' }; const response = await provider.send("debug_traceBlockByHash", [blockHash, tracer]); let sum = 0; for (let obj of response) { for (let key in obj.result) { const signature = key.split('-')[0]; // Look for transfer function signatures if (signature === "0xa9059cbb") { const timesCalled = obj.result[key] sum += timesCalled } } } console.log(`In this block the transfer function was called ${sum} times!`) } catch (err) { console.error(`Error in traceBlockByHash: ${err.message}`); } } traceTransferFunction("0x18adf6d008688c0958e51d96597d265ae02aa46c764538ee4d73990537eed99d"); ``` This code uses `provider.send` to call the `debug_traceBlockByHash` method, which is not exposed as a dedicated ethers.js helper, to trace a block. This method uses the `4byteTracer` to identify the functions called in the block. Then, the `traceTransferFunction` function is defined, which takes a block hash as an argument. This function calls the `traceBlockByHash` method with the provided block hash and uses a loop to iterate over the results. Inside the loop, the function checks for the `transfer` function from the ERC-20 standard with signature `0xa9059cbb` and counts how many times it was called in that block by adding up the times it was called in each trace. Finally, the function logs the number of times the transfer function was called in that block. The code also has error handling in case anything goes wrong with the tracing process. # debug_traceBlockByNumber | Polygon Source: https://docs.chainstack.com/reference/polygon-traceblockbynumber openapi/polygon_node_api/debug_and_trace/debug_traceBlockByNumber.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that enables the tracing of the execution of a specific block using its number. Use it on Polygon via Chainstack. Polygon API method that enables the tracing of the execution of a specific block using its number. This method can be used to troubleshoot and analyze smart contracts and transactions on the Polygon blockchain. It provides an in-depth trace of the block execution, with details on all the interactions, such as transactions and calls, that took place. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#polygon) with the debug and trace API methods enabled. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. * `flatCallTracer` — tracer that returns the transaction's calls as a flat array in the Parity-style trace format, with each entry's position given by its `traceAddress`. This is the drop-in replacement for the Erigon `trace_*` methods, which Bor does not implement. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `flatCallTracer` response * `array` — the block's calls as a flat list in the Parity trace format, one object per call: * `action` — the call details: `from`, `to`, `value`, `gas`, `input`, and `callType` (for example, `call`, `delegatecall`, or `staticcall`). Contract creations and self-destructs carry the corresponding Parity `create` and `suicide` fields instead. * `result` — the call outcome: `gasUsed` and `output`. Contract creations return the new contract `address` and its `code`. * `subtraces` — the number of child calls made directly by this call. * `traceAddress` — the position of this call in the call tree, as an array of child indices. * `type` — the call type: `call`, `create`, or `suicide`. * `error` — present only when the call failed, with the failure reason. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ## `debug_traceBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByNumber = async (block) => { // Specify the type of tracer: 4byteTracer, callTracer, flatCallTracer, or prestateTracer const tracer = { tracer: 'callTracer' }; const traces = await provider.send("debug_traceBlockByNumber", [block, tracer]); console.log(traces); }; traceBlockByNumber("latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block = "latest" # Specify the type of tracer: 4byteTracer, callTracer, flatCallTracer, or prestateTracer tracer = { "tracer": 'callTracer' } block_traces = web3.provider.make_request('debug_traceBlockByNumber', [block, tracer]) print(block_traces) ``` ## Use case One use case for `traceBlockByNumber` would be to trace a block, calculate how many transactions are in it, and then display the number of transactions and the average gas used. To accomplish this, you can use `traceBlockByNumber` to retrieve the trace of each transaction in the block and then iterate over the transactions to count the total number of transactions and calculate the total gas used. Once you have this information, you can divide the total gas used by the total number of transactions to get the average gas used. Here is an implementation of this idea using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getAverageGasUsed(block) { const tracer = { tracer: 'callTracer' }; const traceResult = await provider.send("debug_traceBlockByNumber", [block, tracer]); let totalGasUsed = 0; let transactionCount = 0; for (let obj of traceResult) { const gasUsed = obj.result.gasUsed; const decimalGas = Number(gasUsed); totalGasUsed += decimalGas; transactionCount++; } const averageGasUsed = totalGasUsed / transactionCount; console.log(`Transaction in this block: ${transactionCount}`) console.log(`Average units of gas used per transaction: ${Math.round(averageGasUsed)}`); } getAverageGasUsed("latest"); ``` First, the code defines a custom method called `traceBlockByNumber` that allows tracing the execution of a block identified by its number. This method is then used in the `getAverageGasUsed` function to trace the transactions in the specified block. Inside the function, we define a tracer object with the value of `callTracer`, which will help us get more detailed information about the block. Then, we call the `traceBlockByNumber` RPC method, passing in the block number and the tracer object as parameters. This returns an array of objects containing information about each transaction in the block. We then declare two variables to keep track of the total gas used and the number of transactions in the block. We loop through each transaction in the block using a `for...of` loop and extract the `gasUsed` property of each transaction's result object. We convert this value from hexadecimal to decimal using the `Number` function and add it to the `totalGasUsed` variable. We also increment the `transactionCount` variable to keep track of how many transactions there are in the block. Finally, we calculate the `averageGasUsed` by dividing the `totalGasUsed` by the `transactionCount` and displaying the result. We use the `Math.round()` method to round the result to the nearest integer for readability. When we call the function, we pass in the string `"latest"` to get the average gas used for the latest block. # debug_traceCall | Polygon Source: https://docs.chainstack.com/reference/polygon-tracecall openapi/polygon_node_api/debug_and_trace/debug_traceCall.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that traces the execution of eth_call | Polygon within the context of a specific block's execution. On Polygon. Polygon API method that traces the execution of [eth\_call | Polygon](/reference/ethcall) within the context of a specific block's execution. This method uses the final state of the parent block as its base and allows developers to trace the execution of a particular call. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#polygon) with the debug and trace API methods enabled. ## Parameters * `object` — the transaction call object: * `from` — (optional) the string of the address used to send the transaction. * `to` — the string of the address to which the transaction is directed, a wallet, or a smart contract. * `gas` — (optional) the maximum amount of gas that can be used by the transaction. * `gasprice` — (optional) the amount of gas price the sender is willing to pay for each gas unit in [Wei](https://ethereum.org/en/developers/docs/intro-to-ether/#denominations). * `value` — (optional) the value sent with this transaction, encoded as hexadecimal. * `data` — (optional) additional data to be sent with the call, usually used to invoke functions from smart contracts as a string of the hash of the method signature and encoded parameters; see the [Ethereum Contract ABI](https://solidity.readthedocs.io/en/latest/abi-spec.html). * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. * `flatCallTracer` — tracer that returns the call's execution as a flat array in the Parity-style trace format, with each entry's position given by its `traceAddress`. This is the drop-in replacement for the Erigon `trace_*` methods, which Bor does not implement. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/polygon-debug-trace-rpc-methods#pre-built-native-tracers) in the debug and trace overview. You can also use additional configuration parameters. The following settings are available: * `disableStorage` — when enabled, it prevents tracing of storage changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. By default, `debug_traceTransaction` traces both memory and storage changes, but storage tracing can be particularly resource-intensive, especially for large transactions. * `disableStack` — when enabled, it skips tracing of [stack](https://en.wikipedia.org/wiki/Stack_\(abstract_data_type\)) changes made by the transaction being analyzed. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableMemory` — when `false`, it prevents tracing of memory changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableMemory`. * `enableReturnData` — when `false`, it prevents the method from tracing the return data produced by the transaction being analyzed. The return data tracing can be particularly resource-intensive, requiring a significant amount of time and processing power to complete. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableReturnData`. * `timeout` — setting that allows developers to customize the method's timeout period for JavaScript-based tracing calls. The default timeout is `5s`, and you can find the values formats in the [Go documentation](https://pkg.go.dev/time#ParseDuration). > [!NOTE] > > It's worth noting that when using one of the three built-in tracers in Polygon clients, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData`settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the [Struct/opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#structopcode-logger). ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction., usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract, if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `flatCallTracer` response * `array` — the call's execution as a flat list in the Parity trace format, one object per call: * `action` — the call details: `from`, `to`, `value`, `gas`, `input`, and `callType` (for example, `call`, `delegatecall`, or `staticcall`). Contract creations and self-destructs carry the corresponding Parity `create` and `suicide` fields instead. * `result` — the call outcome: `gasUsed` and `output`. Contract creations return the new contract `address` and its `code`. * `subtraces` — the number of child calls made directly by this call. * `traceAddress` — the position of this call in the call tree, as an array of child indices. * `type` — the call type: `call`, `create`, or `suicide`. * `error` — present only when the call failed, with the failure reason. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ### `Struct/opcode logger` response * `object` — the `Struct/opcode logger` traces object: * `pc` — the current program counter of the transaction, which represents the location of the current opcode being executed. A register that keeps track of the address of the next instruction to be executed in a program. * `op` — the current [opcode](https://ethereum.org/en/developers/docs/evm/opcodes/) being executed, such as PUSH1, ADD, or SWAP1. A code that represents a specific operation or instruction that a computer or processor can understand and execute. * `gas` — the amount of gas remaining in the transaction at the current step of execution. * `gasCost` — the amount of gas consumed by the current opcode being executed. * `depth` — the current depth of the call stack for the transaction, reflecting the number of active frames representing nested function calls. * `stack` — the contents of the EVM stack at the current step of the transaction's execution. The stack is a data structure used by the EVM to manage and manipulate data during the execution of opcodes * `memory` — the contents of the memory at the current step of execution. A temporary data storage location that is available to smart contracts during execution. ## `debug_traceCall` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceCall = async () => { const call = { from: '0xe7e8569267c4a3278be75a2d86fd1a9e0a6818d8', to: '0x0769fd68dFb93167989C6f7254cd0D766Fb2841F', gas: '0x1E9EF', gasPrice: '0x72AC5BA54E', data: '0x441a3e70000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000c6c3eca729cb9e' } const block = 'latest' // Specify the type of tracer: 4byteTracer, callTracer, flatCallTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. const tracer = {}; const traces = await provider.send("debug_traceCall", [call, block, tracer]); console.log(traces); }; traceCall() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) call = { 'from': '0xe7e8569267c4a3278be75a2d86fd1a9e0a6818d8', 'to': '0x0769fd68dFb93167989C6f7254cd0D766Fb2841F', 'gas': '0x1E9EF', 'gasPrice': '0x72AC5BA54E', 'data': '0x441a3e70000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000000000000000000000c6c3eca729cb9e' } block = 'latest' # Specify the type of tracer: 4byteTracer, callTracer, flatCallTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. tracer = { "tracer": 'prestateTracer' } tx_traces = web3.provider.make_request('debug_traceCall', [call, block, tracer]) print(tx_traces) ``` ## Use case A practical use case for the `debug_traceCall` method is to inspect the internal transactions that happened during a smart contract call. For example, a developer can examine a past transaction using the `callTracer` tracer where an account used a proxy smart contract to deploy a new instance using the `CREATE2` method. > [!NOTE] > > Read [Deploying a deterministic smart contract on Ethereum](https://chainstack.com/deploying-a-deterministic-contract-on-ethereum/) to learn more about the `CREATE2` method and how to deploy a smart contract to a predetermined address. The following code is an implementation of the logic to inspect internal transactions using ethers.js: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function traceCall(call, block) { try { // Specify the type of tracer: 4byteTracer, callTracer, flatCallTracer, or prestateTracer. // Leave empty {} for Struct/opcode logger. const tracer = { tracer: 'callTracer' }; // Trace the call const result = await provider.send("debug_traceCall", [call, block, tracer]); const calls = result.calls; console.log(`===== Internal Calls =====`); // Extract and display the desired properties from each result object using a loop for (const callObj of calls) { const from = callObj.from; const to = callObj.to; const internalCalls = callObj.calls; console.log(`From: ${from}`); console.log(`To: ${to}`); for (const internalCall of internalCalls) { const type = internalCall.type; console.log(`Type: ${type}\n`); } } } catch (error) { console.error(`Error while tracing call: ${error}`); } } async function main() { try { const call = { from: '0xa4c523eef643a033be9510c53358f71fc4aa2a91', to: '0x176c1cd760768e3aad920a42d3f68e91428a86ed', gas: '0x55730', gasPrice: '0x72AC5BA54E', data: '0x2ca9e7d00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ce0042b868300000d44a59004da54a005ffdcf9f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c44af63f020000000000000000000000000000000000000000000000000000000000000040f713fe246fee5f635decba83e09c6f733e14396c0555b5371324fdc6e709bb490000000000000000000000000000000000000000000000000000000000000048603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf300000000000000000000000095cb3b52d567d12406547e65c218aad42b1f40270000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bf1698c568071c9edf9a4e3c46e7cf0c6b2914cc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002483738f9a4361737361766100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' }; // Define the block number or block tag to trace const block = '0x26390F9'; await traceCall(call, block); } catch (error) { console.error(`Error while executing main: ${error}`); } } main(); ``` The `traceCall` function is an asynchronous function that takes two parameters: `call` and `block`. The `call` parameter is an object that contains the details of the call that needs to be traced. In this case, those are the details of a [transaction that deployed a smart contract instance using the CREATE2 method](https://polygonscan.com/tx/0x9c43baea5c33916b282707506db6d4e79e9e13d96fec1028493bd2ec2da66fd0#internal) on the Polygon blockchain. The block parameter is the block number or block tag to trace. The function first creates a tracer object that specifies the type of tracer to be used. In this case, it's set to `callTracer`, which means that the function will trace all the internal calls made by the smart contract during the execution of the specified call. The function then calls the `web3.debug.traceCall` function (which was previously created using `web3.extend`) with the call, block, and tracer parameters. This function returns an object that contains information about the internal calls made by the smart contract during the execution of the specified call. The function then loops through the array of calls in the result object and extracts the `from`, `to`, and `calls` properties of each call object. The `from` property is the address of the sender of the call, the `to` property is the address of the smart contract being called, and the `calls` property is an array of objects that represent the internal calls made by the smart contract during the execution of the call. The function then loops through the `internalCalls` array of each call object and extracts the `type property` of each `internalCall` object. The `type` property represents the type of the internal call, such as a contract creation or an external function call. Finally, the function logs the extracted information to the console. The main function is also an asynchronous function that simply calls the `traceCall` function with the appropriate parameters. It also wraps the function call in a try-catch block to handle any errors that might occur during execution. # debug_traceTransaction | Polygon Source: https://docs.chainstack.com/reference/polygon-tracetransaction openapi/polygon_node_api/debug_and_trace/debug_traceTransaction.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns a transaction's traces by replaying it. Reference for debug_traceTransaction on Polygon via Chainstack. Polygon API method that returns a transaction's traces by replaying it. This method provides a detailed breakdown of every step in the execution of a transaction on the Polygon blockchain, including gas usage and opcode output. What sets it apart is its ability to accurately simulate the transaction's execution path by replaying any prior transactions, making it a powerful tool for developers to identify and diagnose issues in their applications. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#polygon) with the debug and trace API methods enabled. ## Parameters * `hash` — the hash identifying a transaction. * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. This provides valuable information about the frequency and usage of each function within the transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the Ethereum Virtual Machine works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. The tracer operates by re-executing the transaction and tracking every state change made, resulting in an object with the account addresses as keys and the corresponding trie leaves as values. * `flatCallTracer` — tracer that returns the transaction's calls as a flat array in the Parity-style trace format, with each entry's position given by its `traceAddress`. This is the drop-in replacement for the Erigon `trace_*` methods, which Bor does not implement. > [!NOTE] > > Find a complete [list of available built-in tracers](/reference/polygon-debug-trace-rpc-methods#pre-built-native-tracers) in the debug and trace overview. You can also use additional configuration parameters. The following settings are available: * `disableStorage` — when enabled, it prevents tracing of storage changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. By default, `debug_traceTransaction` traces both memory and storage changes, but storage tracing can be particularly resource-intensive, especially for large transactions. * `disableStack` — when enabled, it skips tracing of [stack](https://en.wikipedia.org/wiki/Stack_\(abstract_data_type\)) changes made by the transaction being analyzed. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. * `enableMemory` — when `false`, it prevents tracing of memory changes made by the transaction being analyzed, which can reduce the resource requirements of the analysis. This can be helpful in optimizing performance and resource usage, especially when analyzing complex transactions, but may result in reduced analysis accuracy that should be considered. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableMemory`. * `enableReturnData` — when `false`, it prevents the method from tracing the return data produced by the transaction being analyzed. The return data tracing can be particularly resource-intensive, requiring a significant amount of time and processing power to complete. > [!WARNING] > > Note that on an Erigon client, this setting is the opposite and is named `disableReturnData`. * `timeout` — setting that allows developers to customize the method's timeout period for JavaScript-based tracing calls. The default timeout is `5s`, and you can find the values formats in the [Go documentation](https://pkg.go.dev/time#ParseDuration). > [!NOTE] > > It's worth noting that when using one of the three built-in tracers in Polygon clients, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData` settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the [Struct/opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#structopcode-logger). ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasused` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `flatCallTracer` response * `array` — the transaction's calls as a flat list in the Parity trace format, one object per call: * `action` — the call details: `from`, `to`, `value`, `gas`, `input`, and `callType` (for example, `call`, `delegatecall`, or `staticcall`). Contract creations and self-destructs carry the corresponding Parity `create` and `suicide` fields instead. * `result` — the call outcome: `gasUsed` and `output`. Contract creations return the new contract `address` and its `code`. * `subtraces` — the number of child calls made directly by this call. * `traceAddress` — the position of this call in the call tree, as an array of child indices. * `type` — the call type: `call`, `create`, or `suicide`. * `error` — present only when the call failed, with the failure reason. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. The keys and values are both encoded as hexadecimal strings. ### `Struct/opcode logger` response * `object` — the `Struct/opcode logger` traces object: * `pc` — the current program counter of the transaction, which represents the location of the current opcode being executed. A register that keeps track of the address of the next instruction to be executed in a program. * `op` — the current [opcode](https://ethereum.org/en/developers/docs/evm/opcodes/) being executed, such as PUSH1, ADD, or SWAP1. A code that represents a specific operation or instruction that a computer or processor can understand and execute. * `gas` — the amount of gas remaining in the transaction at the current step of execution. * `gasCost` — the amount of gas consumed by the current opcode being executed. * `depth` — the current depth of the call stack for the transaction, reflecting the number of active frames representing nested function calls. * `stack` — the contents of the EVM stack at the current step of the transaction's execution. The stack is a data structure used by the EVM to manage and manipulate data during the execution of opcodes * `memory` — the contents of the memory at the current step of execution. A temporary data storage location that is available to smart contracts during execution. ## `debug_traceTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txhash) => { // Specify the type of tracer: 4byteTracer, callTracer, flatCallTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. const tracer = {}; const traces = await provider.send("debug_traceTransaction", [txhash, tracer]); console.log(traces); }; traceTransaction("0x6be573c85c46d02cd2d47b997d2f153583fbc959541400bc41b9f140d1216772") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) tx_hash = "0x6be573c85c46d02cd2d47b997d2f153583fbc959541400bc41b9f140d1216772" # Specify the type of tracer: 4byteTracer, callTracer, flatCallTracer, or prestateTracer. Leave empty {} for Struct/opcode logger. tracer = { "tracer": '4byteTracer' } tx_traces = web3.provider.make_request('debug_traceTransaction', [tx_hash, tracer]) print(tx_traces) ``` ## Use case A potential use case for the `debug_traceTransaction` method is to inspect failed transactions on the Polygon blockchain by using the built-in `callTracer` tracer to extract the error and reason. By creating a script that analyzes the latest block's transactions and selectively extracts data only from the failed ones, you can effectively utilize this feature. Below is an example implementation of this logic using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Trace a failed transaction using the callTracer async function traceTransaction(txHash) { const tracer = { tracer: 'callTracer' }; const result = await provider.send("debug_traceTransaction", [txHash, tracer]); return result; } // Get all transactions from the specified block number async function getTransactionsFromBlock(blockNumber) { const block = await provider.getBlock(blockNumber, false); const txArray = block.transactions; return txArray; } // Main function that iterates through transactions and extracts data from failed ones async function main() { try { const transactions = await getTransactionsFromBlock('latest'); for (let i = 0; i < transactions.length; i++) { const txHash = transactions[i]; const tx = await provider.getTransactionReceipt(txHash); const txStatus = tx.status; // If transaction failed, extract data using traceTransaction method if (txStatus === 0) { console.log(`Transaction ${txHash} failed!`); const traces = await traceTransaction(txHash); const error = traces.error; const reason = traces.revertReason; console.log(`Transaction failed with error: ${error}`); console.log(`Revert reason: ${reason}\n`); } } } catch (error) { console.error(`Error occurred: ${error}`); } } // Call the main function main(); ``` The code is a JavaScript implementation that utilizes the ethers.js library to extract error and revert reason data from failed transactions on the Polygon blockchain. The code calls the `debug_traceTransaction` method directly with `provider.send`, which allows for tracing of transactions with built-in tracers such as `callTracer`. The `traceTransaction` function in the code is responsible for using the `debug_traceTransaction` method to extract error and revert reason data from a failed transaction. The function takes in a transaction hash as a parameter and specifies the `callTracer` tracer type, then sends the `debug_traceTransaction` request. The resulting data is then returned to the caller. The `getTransactionsFromBlock` function retrieves all the transactions from a specified block number and returns an array of transaction hashes. The `main` function in the code is the main entry point, which first calls the `getTransactionsFromBlock` function to retrieve all transactions from the latest block on the Polygon blockchain. It then iterates through the array of transactions and calls `traceTransaction` for each failed transaction, extracting the error and revert reason data. The extracted data is then printed to the console using `console.log`. The code also handles any errors that may occur during the execution of the code by wrapping the entire execution in a `try-catch` block. # Transactions info | Polygon Source: https://docs.chainstack.com/reference/polygon-transactions-rpc-methods Polygon transaction info JSON-RPC methods — retrieve transaction data, receipts, and status for monitoring and analyzing on-chain Polygon PoS activity. Retrieving transaction information from the Polygon blockchain can be done by using the following methods: > [card] [eth\_getTransactionByHash](/reference/polygon-gettransactionbyhash) > [card] [eth\_getTransactionReceipt](/reference/polygon-gettransactionreceipt) > [card] [eth\_getTransactionByBlockHashAndIndex](/reference/polygon-gettransactionbyblockhashandindex) > [card] [eth\_getTransactionByBlockNumberAndIndex](/reference/polygon-gettransactionbyblocknumberandindex) > [card] [eth\_getBlockReceipts](/reference/polygon-getblockreceipts) > [card] [eth\_newPendingTransactionFilter](/reference/polygon-newpendingtransactionfilter) # Subscriptions | Polygon Source: https://docs.chainstack.com/reference/polygon-web3js-subscriptions-methods Polygon web3.js subscription methods — subscribe to new block headers, pending transactions, contract events, and other real-time blockchain updates. > [!NOTE] > > Note that all subscriptions require a WebSocket connection. ## Native EVM subscriptions Developers can use the following native methods to subscribe to events: > [card] [eth\_subscribe('newHeads')](/reference/polygon-native-subscribe-newheads) > [card] [eth\_subscribe('newPendingTransactions')](/reference/polygon-native-subscribe-newpendingtransactions) > [card] [eth\_subscribe('logs')](/reference/polygon-native-subscribe-logs) The subscriptions created can be cleared using the following method: > [card] [eth\_unsubscribe](/reference/polygon-native-unsubscribe) ## web3.js subscriptions The [web3.js](https://web3js.readthedocs.io/en/v1.2.11/getting-started.html) library also offers subscription methods. web3.js subscriptions are the successors of the deprecated `web3.eth.filter` methods and provide a way for developers to receive real-time updates about events on the blockchain without constantly polling the blockchain for new data. Subscriptions can be created using one of the following methods: > [card] [subscribe('newBlockHeaders')](/reference/polygon-subscribenewblockheaders) > [card] [subscribe('pendingTransactions')](/reference/polygon-subscribependingtransactions) > [card] [subscribe('logs')](/reference/polygon-subscribelogs) > [card] [subscribe('syncing')](/reference/polygon-subscribesyncing) The subscriptions created can be cleared using the following method: > [card] [clearSubscriptions](/reference/polygon-clearsubscriptions) # Produce an unsigned blinded block Source: https://docs.chainstack.com/reference/produceblindedblock openapi/ethereum_beacon_chain_api/validatiors_info/produce_unsigned_blinded_block.json GET /validator/blinded_blocks/{slot} The /eth/v1/validator/blinded_blocks/{slot} method is a specialized API endpoint in the Ethereum Beacon Chain. Chainstack via Chainstack. > [!NOTE] > > The interactive example will return `Slot already finalized` unless you edit the parameters. The `/eth/v1/validator/blinded_blocks/{slot}` method is a specialized API endpoint in the Ethereum Beacon Chain. It is designed to provide information about blinded blocks for a specific slot. A blinded block is a form of block proposal that contains limited information, typically used in specific consensus mechanisms or when enhanced privacy and security are required during the block proposal process. This endpoint is particularly useful for validators and developers who need to interact with blinded blocks, whether for validation, analysis, or synchronization with the Beacon Chain. ### Parameters * `{slot}`: This parameter specifies the slot number for the blinded block information being requested. It is a crucial parameter and forms an integral part of the endpoint URL. ### Response * `object` — blinded block information. * `slot` — the slot number for which the blinded block information is provided. This confirms the specific time interval the response data is relevant to. * `proposer_index` — the index of the validator responsible for the blinded block proposal. This index is used to identify the specific validator within the Beacon Chain. * `parent_root` — the root of the parent block to which this blinded block is linked. Understanding the parent block is important for maintaining blockchain continuity. * `state_root` — the root of the beacon chain state after the inclusion of the blinded block. The state root is essential for verifying the integrity of the chain's state post-block inclusion. * `body` — the body of the blinded block, containing the necessary data in a blinded format. This section includes transactions and attestations but with limited visibility to protect certain aspects of the block data. The `/eth/v1/validator/blinded_blocks/{slot}` method plays a crucial role in the context of Ethereum, especially for validators and participants who engage with more advanced and privacy-focused aspects of blockchain operations. It ensures access to necessary but restricted information for effective blockchain management and security. # Produce unsigned block Source: https://docs.chainstack.com/reference/produceblock openapi/ethereum_beacon_chain_api/validatiors_info/produce_unsigned_block.json GET /validator/blocks/{slot} Reference docs for the Produce unsigned block JSON-RPC method on the Chainstack blockchain, available via Chainstack JSON-RPC nodes. > [!NOTE] > > The interactive example will return `Slot already finalized` unless you edit the parameters. The `/eth/v2/validator/blocks/{slot}` method is an essential API endpoint in the Ethereum Beacon Chain, designed for validators to retrieve proposed block details for a specific slot. A slot in the Beacon Chain is a discrete time interval during which a new block can be proposed. This method is crucial for validators who are assigned to propose a block, enabling them to access the necessary data to perform their duties effectively. ### Parameters * `{slot}`: This parameter specifies the slot number for which the block details are being requested. It is a required parameter and forms part of the endpoint URL. ### Response * `object` — proposed block information. * `slot` — the slot number for which the block information is provided. This confirms the specific time interval the response data is relevant to. * `proposer_index` — the index of the validator who is responsible for proposing the block at this slot. This index identifies the specific validator within the Beacon Chain. * `parent_root` — the root of the parent block to which this proposed block will be attached. This is vital for maintaining the continuity of the blockchain. * `state_root` — the root of the beacon chain state after the proposed block's effect. This state root ensures the integrity of the chain's state post-block proposal. * `body` — the body of the proposed block, containing transactions, attestations, and other relevant data. This detailed information is crucial for validators and network participants to understand the contents and impact of the proposed block. The `/eth/v2/validator/blocks/{slot}` method is a key tool for validators in the Ethereum network, allowing them to access detailed information about their block proposal responsibilities. This ensures they are well-prepared to contribute to the blockchain's growth and stability. # programSubscribe | Solana Source: https://docs.chainstack.com/reference/programsubscribe-solana Subscribe to real-time changes for all accounts owned by a Solana program using programSubscribe. Get WebSocket updates on program account modifications. ## Parameters * `publicKey` - The program ID (public key) to monitor, as a base-58 encoded string * `` - (optional) Configuration object: * `encoding` - Data encoding for the account data: * `"base58"` - Base-58 encoding (default) * `"base64"` - Base-64 encoding * `"base64+zstd"` - Compressed Base-64 encoding * `"jsonParsed"` - JSON format (if program supports it) * `filters` - (optional) Array of filter objects: * `memcmp` - Filter for memory comparison: * `offset` - Number of bytes into program account to start comparison * `bytes` - Data to match, as base-58 encoded string * `dataSize` - Filter for program account data size * `commitment` - (optional) The level of commitment desired when querying state. Default is "finalized". ## Response * `result` - An integer subscription id (needed to unsubscribe) * `subscription` - An object containing account information: * `pubkey` - The public key of the account that changed * `account` - Account information: * `lamports` - Number of lamports assigned to the account * `owner` - Base-58 encoded public key of the program this account is assigned to * `data` - Account data, encoded according to specified encoding parameter * `executable` - Boolean indicating if the account contains a program * `rentEpoch` - The epoch at which this account will next owe rent ## `programSubscribe` code examples This example subscribes to all accounts owned by the pump.fun program. > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"programSubscribe","params":["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",{"encoding":"jsonParsed","commitment":"finalized"}]} ``` ## Use case The `programSubscribe` method is particularly useful for applications that need to monitor all accounts associated with a specific program. Common use cases include: * DEX monitoring: Track changes to orderbooks, market states, and other trading-related accounts * Token tracking: Monitor token program accounts for transfers and balance changes * Gaming: Track game state changes across all player accounts * DeFi applications: Monitor lending pools, staking positions, and other DeFi-related accounts * Analytics platforms: Gather real-time data about program usage and account state changes By subscribing to program account changes, applications can provide real-time updates and analytics without constantly polling the blockchain for changes to individual accounts. # programUnsubscribe | Solana Source: https://docs.chainstack.com/reference/programunsubscribe-solana Cancel a Solana program subscription with programUnsubscribe. Stop receiving real-time WebSocket notifications for program-owned account changes. See also [programSubscribe | Solana](/reference/programsubscribe-solana). ## Parameters * `integer` - The ID of the subscription that you want to cancel. This is the subscription ID that was returned when calling `programSubscribe`. ## Response * `boolean` - A boolean value indicating if the subscription was removed successfully. `true` if removed successfully, `false` if not. ## `programUnsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection. You can use a WebSocket client library for your programming language of choice to interact with Solana's WebSocket API. ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"programUnsubscribe","params":[0]} ``` # eth_protocolVersion | Base Source: https://docs.chainstack.com/reference/protocolversion openapi/base_node_api/eth_protocolVersion.json POST /2fc1de7f08c0465f6a28e3c355e0cb14 Base API method that returns the protocol version of the node. eth_protocolVersion JSON-RPC method available on the Base blockchain via Chainstack. Base API method that returns the protocol version of the node. The protocol version is a string that identifies the version of the Base protocol that the node is running. ## Parameters * `none` ## Response * `result` — a string specifying the Base protocol version that the node is using. ## Use case The `eth_protocolVersion` method is useful for applications that need to verify the protocol version of the node they're connecting to. This can be important for compatibility checks and ensuring that the node supports the features required by the application. # Accounts info | Robinhood Source: https://docs.chainstack.com/reference/robinhood-accounts-info-rpc-methods Robinhood Chain account information JSON-RPC methods — retrieve account balances, nonces, storage values, and contract bytecode for addresses on the Robinhood Chain network. Retrieving account information from the Robinhood Chain blockchain can be done using the following methods: > [card] [eth\_getBalance](/reference/robinhood-eth-getbalance) > [card] [eth\_getCode](/reference/robinhood-eth-getcode) > [card] [eth\_getStorageAt](/reference/robinhood-eth-getstorageat) > [card] [eth\_getTransactionCount](/reference/robinhood-eth-gettransactioncount) > [card] [eth\_getProof](/reference/robinhood-eth-getproof) This information can be used for: * **Wallet applications** — display user balances and transaction counts * **Smart contract interaction** — read contract storage and bytecode * **Account monitoring** — track account state changes over time * **DApp development** — integrate account data into decentralized applications # arb_getRawBlockMetadata | Robinhood Source: https://docs.chainstack.com/reference/robinhood-arb-getrawblockmetadata openapi/robinhood_node_api/chain_info/arb_getRawBlockMetadata.json POST / Robinhood Chain API method (Arbitrum Nitro `arb` namespace) that returns raw block metadata for a range of blocks, including the batch and per-block timing information Nitro records for each L2 block. Robinhood Chain API method (Arbitrum Nitro `arb` namespace) that returns raw block metadata for a range of blocks, including the batch and per-block timing information Nitro records for each L2 block. ## Parameters * `fromBlock` — the first block number in hex * `toBlock` — the last block number in hex ## Response * `array` — a list of raw block metadata entries, each with the block number and its associated metadata bytes ## `arb_getRawBlockMetadata` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("arb_getRawBlockMetadata", ["0xc3007d", "0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("arb_getRawBlockMetadata", ["0xc3007d", "0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "arb_getRawBlockMetadata", "params": ["0xc3007d", "0xc3007d"], "id": 1 }' ``` # Blocks info | Robinhood Source: https://docs.chainstack.com/reference/robinhood-blocks-info-rpc-methods Robinhood Chain block information JSON-RPC methods — query block details including transactions, timestamps, block height, and header data on the Robinhood Chain blockchain. Retrieving block information from the Robinhood Chain blockchain can be done using the following methods: > [card] [eth\_blockNumber](/reference/robinhood-eth-blocknumber) > [card] [eth\_getBlockByNumber](/reference/robinhood-eth-getblockbynumber) > [card] [eth\_getBlockByHash](/reference/robinhood-eth-getblockbyhash) > [card] [eth\_getBlockTransactionCountByNumber](/reference/robinhood-eth-getblocktransactioncountbynumber) > [card] [eth\_getBlockTransactionCountByHash](/reference/robinhood-eth-getblocktransactioncountbyhash) > [card] [eth\_getHeaderByNumber](/reference/robinhood-eth-getheaderbynumber) > [card] [eth\_getHeaderByHash](/reference/robinhood-eth-getheaderbyhash) > [card] [eth\_getBlockReceipts](/reference/robinhood-eth-getblockreceipts) > [card] [eth\_getUncleCountByBlockNumber](/reference/robinhood-eth-getunclecountbyblocknumber) > [card] [eth\_getUncleCountByBlockHash](/reference/robinhood-eth-getunclecountbyblockhash) > [card] [eth\_getUncleByBlockNumberAndIndex](/reference/robinhood-eth-getunclebyblocknumberandindex) > [card] [eth\_getUncleByBlockHashAndIndex](/reference/robinhood-eth-getunclebyblockhashandindex) This information can be used for: * **Blockchain analysis** — insights into the overall network activity * **Block explorers** — web-based tools to view and analyze blockchain data * **Smart contract analysis** — track contract activity at specific blocks * **DApp development** — utilize block information in decentralized applications # Chain info | Robinhood Source: https://docs.chainstack.com/reference/robinhood-chain-info-rpc-methods The chain info JSON-RPC methods allow developers to access blockchain-level information such as chain ID, sync status, and protocol version. Retrieving chain information from the Robinhood Chain blockchain can be done using the following methods: > [card] [eth\_chainId](/reference/robinhood-eth-chainid) > [card] [eth\_syncing](/reference/robinhood-eth-syncing) > [card] [eth\_accounts](/reference/robinhood-eth-accounts) > [card] [arb\_getRawBlockMetadata](/reference/robinhood-arb-getrawblockmetadata) This information can be used for: * **Network identification** — verify the correct blockchain network * **Sync monitoring** — track node synchronization status * **Protocol compatibility** — check client version and protocol support * **Multi-chain applications** — identify and switch between networks # Client info | Robinhood Source: https://docs.chainstack.com/reference/robinhood-client-info-rpc-methods The client info JSON-RPC methods allow developers to access information about the Robinhood Chain node client, including version, network ID, and peer connections. Retrieving client information from the Robinhood Chain node can be done using the following methods: > [card] [net\_version](/reference/robinhood-net-version) > [card] [web3\_clientVersion](/reference/robinhood-web3-clientversion) > [card] [web3\_sha3](/reference/robinhood-web3-sha3) This information can be used for: * **Node monitoring** — check client version and peer count * **Network diagnostics** — verify network connectivity * **Compatibility checks** — ensure client version compatibility * **Infrastructure management** — monitor node health and status # debug_accountRange | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-accountrange openapi/robinhood_node_api/debug_and_trace/debug_accountRange.json POST / Robinhood Chain API method that enumerates all accounts at a given block with paging capability. debug_accountRange on Robinhood Chain via Chainstack. Robinhood Chain API method that enumerates all accounts at a given block with paging capability. This method returns a range of accounts stored in the state trie, providing account details such as balance, nonce, code hash, and storage. It is useful for state inspection and debugging purposes. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#robinhood-chain) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `blockNrOrHash` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`), or block hash. * `start` — the start key hash for pagination, encoded as hex bytes. Use `0x0000000000000000000000000000000000000000000000000000000000000000` to start from the beginning. * `maxResults` — the maximum number of accounts to return. Capped at 256. * `nocode` — if `true`, excludes contract bytecode from the result. * `nostorage` — if `true`, excludes storage data from the result. * `incompletes` — if `true`, includes accounts without known addresses. ## Response * `result` — an object containing the state dump: * `root` — the state root hash of the block. * `accounts` — a map of account addresses to account data: * `balance` — the account balance in Wei. * `nonce` — the account nonce. * `root` — the storage root hash. * `codeHash` — the hash of the account's contract code. * `code` — the contract bytecode (omitted if `nocode` is `true`). * `storage` — the account's storage slots (omitted if `nostorage` is `true`). * `address` — the account address. * `next` — the next key hash for pagination. Use this value as the `start` parameter in the next call to continue iterating. ## `debug_accountRange` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_accountRange", ["0xc3007d", "0x0", "0x0", 5, true, true, true]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_accountRange", ["0xc3007d", "0x0", "0x0", 5, true, true, true]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_accountRange", "params": ["0xc3007d", "0x0", "0x0", 5, true, true, true], "id": 1 }' ``` # Debug and trace | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-and-trace-rpc-methods The debug and trace JSON-RPC methods provide advanced debugging capabilities for developers, including transaction tracing, call tracing, and raw data access. Debugging and tracing on the Robinhood Chain blockchain can be done using the following methods: > [card] [debug\_traceTransaction](/reference/robinhood-debug-tracetransaction) > [card] [debug\_traceBlockByNumber](/reference/robinhood-debug-traceblockbynumber) > [card] [debug\_traceBlockByHash](/reference/robinhood-debug-traceblockbyhash) > [card] [debug\_traceCall](/reference/robinhood-debug-tracecall) > [card] [debug\_getRawBlock](/reference/robinhood-debug-getrawblock) > [card] [debug\_getRawHeader](/reference/robinhood-debug-getrawheader) > [card] [debug\_getRawReceipts](/reference/robinhood-debug-getrawreceipts) > [card] [debug\_getRawTransaction](/reference/robinhood-debug-getrawtransaction) > [card] [debug\_getBadBlocks](/reference/robinhood-debug-getbadblocks) > [card] [debug\_accountRange](/reference/robinhood-debug-accountrange) > [card] [debug\_storageRangeAt](/reference/robinhood-debug-storagerangeat) > [card] [debug\_getModifiedAccountsByNumber](/reference/robinhood-debug-getmodifiedaccountsbynumber) > [card] [debug\_getModifiedAccountsByHash](/reference/robinhood-debug-getmodifiedaccountsbyhash) > [card] [debug\_dumpBlock](/reference/robinhood-debug-dumpblock) > [card] [debug\_intermediateRoots](/reference/robinhood-debug-intermediateroots) > [card] [debug\_printBlock](/reference/robinhood-debug-printblock) > [card] [debug\_preimage](/reference/robinhood-debug-preimage) > [card] [debug\_getAccessibleState](/reference/robinhood-debug-getaccessiblestate) > [card] [debug\_chaindbProperty](/reference/robinhood-debug-chaindbproperty) > [card] [debug\_traceBadBlock](/reference/robinhood-debug-tracebadblock) This information can be used for: * **Transaction debugging** — analyze transaction execution in detail * **Smart contract analysis** — trace contract calls and state changes * **Error investigation** — identify and fix transaction failures * **Performance optimization** — analyze gas usage and execution paths # debug_chaindbProperty | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-chaindbproperty openapi/robinhood_node_api/debug_and_trace/debug_chaindbProperty.json POST / Robinhood Chain API method that returns a low-level property of the node's underlying key-value database. It is an operational debug method used for node diagnostics. Robinhood Chain API method that returns a low-level property of the node's underlying key-value database. It is an operational debug method used for node diagnostics. ## Parameters * `property` — the database property name to read ## Response * `string` — the value of the requested database property ## `debug_chaindbProperty` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_chaindbProperty", ["leveldb.stats"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_chaindbProperty", ["leveldb.stats"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_chaindbProperty", "params": ["leveldb.stats"], "id": 1 }' ``` # debug_dumpBlock | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-dumpblock openapi/robinhood_node_api/debug_and_trace/debug_dumpBlock.json POST / Robinhood Chain API method that retrieves the state of all accounts at a given block number. Not available on Chainstack Global Nodes. Robinhood Chain API method that retrieves the state of all accounts at a given block number. This method returns a dump of the state trie, including account balances, nonces, code hashes, and storage. It is primarily used for debugging and state inspection. > [!WARNING] > > `debug_dumpBlock` is not available on Robinhood Chain Global Nodes — the call returns `-32601`. See [Robinhood Chain methods](/docs/robinhood-methods) for the `debug_*` methods that are available. ## Parameters * `quantity` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`). ## Response * `result` — an object containing the state dump: * `root` — the state root hash of the block. * `accounts` — a map of account addresses to account data: * `balance` — the account balance in Wei. * `nonce` — the account nonce. * `root` — the storage root hash. * `codeHash` — the hash of the account's contract code. * `code` — the contract bytecode. * `storage` — the account's storage slots. * `address` — the account address. > [!WARNING] > > This method returns up to 256 accounts per call. For iterating over all accounts, use `debug_accountRange` which supports pagination. ## `debug_dumpBlock` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_dumpBlock", ["0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_dumpBlock", ["0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_dumpBlock", "params": ["0xc3007d"], "id": 1 }' ``` # debug_getAccessibleState | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-getaccessiblestate openapi/robinhood_node_api/debug_and_trace/debug_getAccessibleState.json POST / Robinhood Chain API method that finds the first block number in a given range where the state is accessible on disk. Robinhood Chain via Chainstack. Robinhood Chain API method that finds the first block number in a given range where the state is accessible on disk. This is useful for determining which historical states are available for querying on the node, as nodes may prune older state data. > [!WARNING] > > `debug_getAccessibleState` is not available on Robinhood Chain Global Nodes — the call returns `-32601`. See [Robinhood Chain methods](/docs/robinhood-methods) for the `debug_*` methods that are available. ## Parameters * `from` — the start of the block range to search, as a hex-encoded block number or block tag. * `to` — the end of the block range to search, as a hex-encoded block number or block tag. ## Response * `result` — the block number (as a decimal integer) of the first block in the range where the state is accessible. Returns an error if no accessible state is found in the range. ## `debug_getAccessibleState` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getAccessibleState", ["0xc3007d", "0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_getAccessibleState", ["0xc3007d", "0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getAccessibleState", "params": ["0xc3007d", "0xc3007d"], "id": 1 }' ``` # debug_getBadBlocks | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-getbadblocks openapi/robinhood_node_api/debug_and_trace/debug_getBadBlocks.json POST / Robinhood Chain API method that returns a list of invalid blocks that the node has encountered. Useful for debugging consensus or block validation issues. Robinhood Chain API method that returns a list of invalid blocks that the node has encountered. Useful for debugging consensus or block validation issues. ## Parameters * `none` ## Response * `result` — an array of bad block objects that the node has encountered. Each object contains the full block data and the reason it was rejected. ## `debug_getBadBlocks` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getBadBlocks", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_getBadBlocks", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getBadBlocks", "params": [], "id": 1 }' ``` # debug_getModifiedAccountsByHash | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-getmodifiedaccountsbyhash openapi/robinhood_node_api/debug_and_trace/debug_getModifiedAccountsByHash.json POST / Robinhood Chain API method that retrieves a list of accounts that were modified in a specific block, identified by the block's hash. On Robinhood Chain. Robinhood Chain API method that retrieves a list of accounts that were modified in a specific block, identified by the block's hash. This method compares the state tries between the specified block and its parent to find all accounts with changed state. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#robinhood-chain) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `startHash` — the hash of the block for which to retrieve modified accounts. When only this parameter is provided, the method returns accounts modified in this single block (compared to its parent). * `endHash` — (optional) the hash of the end block. When provided, the method returns accounts modified between the `startHash` block and the `endHash` block. ## Response * `result` — an array of account addresses that were modified in the specified block or range. ## `debug_getModifiedAccountsByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getModifiedAccountsByHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_getModifiedAccountsByHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getModifiedAccountsByHash", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"], "id": 1 }' ``` # debug_getModifiedAccountsByNumber | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-getmodifiedaccountsbynumber openapi/robinhood_node_api/debug_and_trace/debug_getModifiedAccountsByNumber.json POST / Robinhood Chain API method that retrieves a list of accounts that were modified in a specific block or between two blocks, identified by block number. Robinhood Chain API method that retrieves a list of accounts that were modified in a specific block or between two blocks, identified by block number. This method compares the state tries between the specified blocks to find all accounts with changed state. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#robinhood-chain) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `startNum` — the block number (as a decimal integer) for which to retrieve modified accounts. When only this parameter is provided, the method returns accounts modified in this single block (compared to its parent). * `endNum` — (optional) the end block number (as a decimal integer). When provided, the method returns accounts modified between `startNum` and `endNum`. ## Response * `result` — an array of account addresses that were modified in the specified block or range. ## `debug_getModifiedAccountsByNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getModifiedAccountsByNumber", ["0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_getModifiedAccountsByNumber", ["0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getModifiedAccountsByNumber", "params": ["0xc3007d"], "id": 1 }' ``` # debug_getRawBlock | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-getrawblock openapi/robinhood_node_api/debug_and_trace/debug_getRawBlock.json POST / Robinhood Chain API method that returns the RLP-encoded block. quantity or tag — the block number in hex format or block tag (latest, earliest, pending, safe. Robinhood Chain API method that returns the RLP-encoded block. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `data` — the RLP-encoded block ## `debug_getRawBlock` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getRawBlock", ["0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_getRawBlock", ["0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getRawBlock", "params": ["0xc3007d"], "id": 1 }' ``` # debug_getRawHeader | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-getrawheader openapi/robinhood_node_api/debug_and_trace/debug_getRawHeader.json POST / Robinhood Chain API method that returns the RLP-encoded header. quantity or tag — the block number in hex format or block tag (latest, earliest, pending, safe. Robinhood Chain API method that returns the RLP-encoded header. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `data` — the RLP-encoded header ## `debug_getRawHeader` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getRawHeader", ["0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_getRawHeader", ["0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getRawHeader", "params": ["0xc3007d"], "id": 1 }' ``` # debug_getRawReceipts | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-getrawreceipts openapi/robinhood_node_api/debug_and_trace/debug_getRawReceipts.json POST / Robinhood Chain API method that returns the RLP-encoded receipts for a block. quantity or tag — the block number in hex format or block tag (latest, earliest, pending. Robinhood Chain API method that returns the RLP-encoded receipts for a block. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `data` — the RLP-encoded receipts ## `debug_getRawReceipts` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getRawReceipts", ["0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_getRawReceipts", ["0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getRawReceipts", "params": ["0xc3007d"], "id": 1 }' ``` # debug_getRawTransaction | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-getrawtransaction openapi/robinhood_node_api/debug_and_trace/debug_getRawTransaction.json POST / Robinhood Chain API method that returns the RLP-encoded transaction. data — the hash of the transaction data — the RLP-encoded transaction. On Robinhood Chain. Robinhood Chain API method that returns the RLP-encoded transaction. ## Parameters * `data` — the hash of the transaction ## Response * `data` — the RLP-encoded transaction ## `debug_getRawTransaction` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_getRawTransaction", ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_getRawTransaction", ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_getRawTransaction", "params": ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"], "id": 1 }' ``` # debug_intermediateRoots | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-intermediateroots openapi/robinhood_node_api/debug_and_trace/debug_intermediateRoots.json POST / Robinhood Chain API method that computes the intermediate state roots for a given block by re-executing all transactions. Robinhood Chain via Chainstack. Robinhood Chain API method that computes the intermediate state roots for a given block by re-executing all transactions. For each transaction in the block, it returns the state root after that transaction was applied. This provides a detailed view of how the state trie evolves within a single block. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#robinhood-chain) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. > [!WARNING] > > This method re-executes all transactions in the block, which can be resource-intensive for blocks with many transactions. ## Parameters * `hash` — the hash of the block to compute intermediate roots for. * `object` — (optional) tracing configuration: * `timeout` — the timeout for the computation. Defaults to `5s`. See [Go time format](https://pkg.go.dev/time#ParseDuration) for accepted values. * `reexec` — the number of blocks to re-execute to reconstruct historical state. Defaults to 128. ## Response * `result` — an array of state root hashes (as hex strings), one per transaction in the block. Each root represents the state after the corresponding transaction was executed. ## `debug_intermediateRoots` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_intermediateRoots", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_intermediateRoots", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_intermediateRoots", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"], "id": 1 }' ``` # debug_preimage | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-preimage openapi/robinhood_node_api/debug_and_trace/debug_preimage.json POST / Robinhood Chain API method that returns the preimage for a given Keccak-256 hash. In Ethereum's state trie, account addresses are stored as their Keccak-256 hashes. Robinhood Chain API method that returns the preimage for a given Keccak-256 hash. In Ethereum's state trie, account addresses are stored as their Keccak-256 hashes. This method allows retrieving the original data (preimage) that was hashed. The node must have preimage recording enabled for this method to return results. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#robinhood-chain) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. > [!WARNING] > > Preimage recording must be enabled on the node (`--cache.preimages`). If preimages are not stored, this method returns an error. ## Parameters * `hash` — the Keccak-256 hash to look up the preimage for, encoded as a hex string. ## Response * `data` — the raw preimage bytes as a hex string. Returns an error if the preimage is not found. ## `debug_preimage` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_preimage", ["0x0000000000000000000000000000000000000000000000000000000000000000"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_preimage", ["0x0000000000000000000000000000000000000000000000000000000000000000"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_preimage", "params": ["0x0000000000000000000000000000000000000000000000000000000000000000"], "id": 1 }' ``` # debug_printBlock | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-printblock openapi/robinhood_node_api/debug_and_trace/debug_printBlock.json POST / Robinhood Chain API method that returns a human-readable representation of a block. Unlike eth_getBlockByNumber which returns structured JSON. Robinhood Chain API method that returns a human-readable representation of a block. Unlike `eth_getBlockByNumber` which returns structured JSON, this method returns a verbose text dump of the entire block structure including all header fields, transactions, and uncles. The output format uses Go's `spew.Sdump` formatting. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#robinhood-chain) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `number` — the block number as a decimal integer. ## Response * `result` — a human-readable string representation of the block, including all internal fields and nested structures. ## `debug_printBlock` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_printBlock", ["0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_printBlock", ["0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_printBlock", "params": ["0xc3007d"], "id": 1 }' ``` # debug_storageRangeAt | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-storagerangeat openapi/robinhood_node_api/debug_and_trace/debug_storageRangeAt.json POST / Robinhood Chain API method that returns a range of storage slots for a contract at a given block and transaction index. It requires an archive node because it reads historical contract storage. Robinhood Chain API method that returns a range of storage slots for a contract at a given block and transaction index. It requires an archive node because it reads historical contract storage. ## Parameters * `blockHash` — the block hash * `txIndex` — the transaction index within the block * `address` — the contract address * `startKey` — the storage key to start from * `maxResult` — the maximum number of storage entries to return ## Response * `storage` — the map of storage entries in the requested range * `nextKey` — the next storage key, or `null` if the range is complete ## `debug_storageRangeAt` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_storageRangeAt", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", 0, "0x00000000000000000000000000000000000a4b05", "0x0", "0x10"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_storageRangeAt", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", 0, "0x00000000000000000000000000000000000a4b05", "0x0", "0x10"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_storageRangeAt", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", 0, "0x00000000000000000000000000000000000a4b05", "0x0", "0x10"], "id": 1 }' ``` # debug_traceBadBlock | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-tracebadblock openapi/robinhood_node_api/debug_and_trace/debug_traceBadBlock.json POST / Robinhood Chain API method that traces all transactions in a block that has been marked as 'bad' by the node. Chainstack Robinhood Chain reference. Robinhood Chain API method that traces all transactions in a block that has been marked as "bad" by the node. Bad blocks are blocks that failed validation during import. This method works similarly to `debug_traceBlockByHash` but looks up the block from the bad blocks cache instead of the canonical chain. It is primarily used for diagnosing why a block was rejected. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#robinhood-chain) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use the `arbtrace_*` methods instead. ## Parameters * `hash` — the hash of the bad block to trace. * `object` — (optional) an object identifying the type of tracer and its configuration: * `tracer` — the name of the tracer to use (`callTracer`, `prestateTracer`, `4byteTracer`, or a custom JS tracer). * `timeout` — the timeout for the tracing operation. Defaults to `5s`. * `reexec` — the number of blocks to re-execute to reconstruct historical state. Defaults to 128. ## Response * `result` — an array of trace results, one per transaction in the block: * `txHash` — the hash of the transaction. * `result` — the trace output for the transaction (format depends on the tracer used). * `error` — an error message if the transaction trace failed. ## `debug_traceBadBlock` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_traceBadBlock", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceBadBlock", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_traceBadBlock", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"], "id": 1 }' ``` # debug_traceBlockByHash | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-traceblockbyhash openapi/robinhood_node_api/debug_and_trace/debug_traceBlockByHash.json POST / Robinhood Chain API method that traces the execution of all transactions in a block identified by its hash. Chainstack Robinhood Chain reference. Robinhood Chain API method that traces the execution of all transactions in a block identified by its hash. This method provides detailed traces for every transaction and call that took place in the block, including gas usage and performance metrics for each operation. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#robinhood-chain) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use `arbtrace_block` instead. ## Parameters * `hash` — the hash of the block to be traced. * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the EVM works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. ## `debug_traceBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByHash = async (blockHash) => { const tracer = { tracer: "callTracer" }; const traces = await provider.send("debug_traceBlockByHash", [blockHash, tracer]); console.log(traces); }; traceBlockByHash("0xb3a662bef958318d25b80ba2ce6932d7e2f10262086247fa848f2d96de75fa18"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0xb3a662bef958318d25b80ba2ce6932d7e2f10262086247fa848f2d96de75fa18" tracer = {"tracer": "callTracer"} block_traces = web3.provider.make_request('debug_traceBlockByHash', [block_hash, tracer]) print(block_traces) ``` ## Use case The `debug_traceBlockByHash` method is useful for auditing all transactions in a specific block. Developers can use it to analyze gas consumption patterns, identify function call frequencies using the `4byteTracer`, or inspect the full call tree of every transaction in a block using the `callTracer`. ## `debug_traceBlockByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_traceBlockByHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", {"tracer": "callTracer"}]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceBlockByHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", {"tracer": "callTracer"}]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_traceBlockByHash", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", {"tracer": "callTracer"}], "id": 1 }' ``` # debug_traceBlockByNumber | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-traceblockbynumber openapi/robinhood_node_api/debug_and_trace/debug_traceBlockByNumber.json POST / Robinhood Chain API method that traces the execution of all transactions in a block identified by its number. Chainstack Robinhood Chain reference. Robinhood Chain API method that traces the execution of all transactions in a block identified by its number. This method provides detailed traces for every transaction and call in the block, including gas usage and internal call information. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#robinhood-chain) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro blocks, use `arbtrace_block` instead. ## Parameters * `quantity or tag` — the block number in hex format or block tag: * `latest` — the most recent block. * `safe` — the block that received justification from the beacon chain. * `finalized` — the block accepted as canonical by more than 2/3 of validators. * `earliest` — the earliest available or genesis block. * `pending` — the pending state and transactions block. * `tracer` — an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction. * `callTracer` — tracer that captures information on all call frames executed during a transaction. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, returning account states needed to execute transactions. ## Response types ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. ## `debug_traceBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByNumber = async (block) => { const tracer = { tracer: "callTracer" }; const traces = await provider.send("debug_traceBlockByNumber", [block, tracer]); console.log(traces); }; traceBlockByNumber("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) block = "latest" tracer = {"tracer": "callTracer"} block_traces = web3.provider.make_request('debug_traceBlockByNumber', [block, tracer]) print(block_traces) ``` ## Use case The `debug_traceBlockByNumber` method is useful for analyzing all transactions in a block by number. Developers can use it to calculate average gas usage across transactions, monitor smart contract activity patterns, or build block-level analytics dashboards. ## `debug_traceBlockByNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_traceBlockByNumber", ["0xc3007d", {"tracer": "callTracer"}]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceBlockByNumber", ["0xc3007d", {"tracer": "callTracer"}]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_traceBlockByNumber", "params": ["0xc3007d", {"tracer": "callTracer"}], "id": 1 }' ``` # debug_traceCall | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-tracecall openapi/robinhood_node_api/debug_and_trace/debug_traceCall.json POST / Robinhood Chain API method that runs an eth_call within the context of the given block execution and returns trace results. Robinhood Chain via Chainstack. Robinhood Chain API method that runs an eth\_call within the context of the given block execution and returns trace results. ## Parameters * `object` — the transaction call object * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `object` — (optional) tracer configuration options ## Response * `object` — the trace result ## `debug_traceCall` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_traceCall", [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}, "latest", {"tracer": "callTracer"}]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceCall", [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}, "latest", {"tracer": "callTracer"}]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_traceCall", "params": [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}, "latest", {"tracer": "callTracer"}], "id": 1 }' ``` # debug_traceTransaction | Robinhood Source: https://docs.chainstack.com/reference/robinhood-debug-tracetransaction openapi/robinhood_node_api/debug_and_trace/debug_traceTransaction.json POST / Robinhood Chain API method that returns a transaction's traces by replaying it. Reference for debug_traceTransaction on Robinhood Chain via Chainstack. Robinhood Chain API method that returns a transaction's traces by replaying it. This method provides a detailed breakdown of every step in the execution of a transaction, including gas usage and opcode output. It accurately simulates the transaction's execution path by replaying any prior transactions, making it a powerful tool for developers to identify and diagnose issues. > [!NOTE] > > Learn how to [deploy a node](/docs/debug-and-trace-apis#robinhood-chain) with the debug and trace API methods enabled. > [!WARNING] > > This method is available for post-Nitro blocks only (block 22,207,815 and later). For pre-Nitro transactions, use `arbtrace_transaction` instead. > > Robinhood Chain does not support the `trace_*` namespace (e.g., `trace_transaction`). Use `debug_traceTransaction` for post-Nitro blocks or `arbtrace_transaction` for pre-Nitro blocks. ## Parameters * `hash` — the hash identifying a transaction. * `object` — (optional) an object identifying the type of tracer and its configuration: * `4byteTracer` — tracer that captures the function signatures and call data sizes for all functions executed during a transaction, creating a map that links each selector and size combination to the number of times it occurred. * `callTracer` — tracer that captures information on all call frames executed during a transaction. The resulting nested list of call frames is organized into a tree structure that reflects the way the EVM works and can be used for debugging and analysis purposes. * `prestateTracer` — tracer with two modes: `prestate` and `diff`, where the former returns the accounts needed to execute a transaction, and the latter returns the differences between the pre and post-states of the transaction. > [!NOTE] > > Find a complete list of available built-in tracers in the debug and trace overview. You can also use additional configuration parameters: * `disableStorage` — when enabled, prevents tracing of storage changes made by the transaction. * `disableStack` — when enabled, skips tracing of stack changes. * `enableMemory` — when `false`, prevents tracing of memory changes. * `enableReturnData` — when `false`, prevents tracing of return data. * `timeout` — timeout period for JavaScript-based tracing calls. Default is `5s`. See [Go time format](https://pkg.go.dev/time#ParseDuration) for accepted values. > [!NOTE] > > When using one of the built-in tracers, the `enableMemory`, `disableStorage`, `disableStack`, and `enableReturnData` settings will not have any effect. > > When no built-in tracer is selected, the response defaults to the Struct/opcode logger. ## Response types ### `callTracer` response * `object` — the `callTracer` traces object: * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas included in the transaction by the sender. * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is `null`. * `input` — the optional input data sent with the transaction. * `output` — the return value of the call, encoded as a hexadecimal string. * `error` — an error message in case the execution failed. * `revertReason` — the reason why the transaction was reverted, returned by the smart contract if any. * `calls` — a list of sub-calls made by the contract during the call, each represented as a nested call frame object. ### `4byteTracer` response * `object` — the `4byteTracer` traces object: * `result` — a map of the function signature, the call data size, and how many times the function was called. ### `prestateTracer` response * `object` — the `prestateTracer` traces object: * `smart contract address` — the address of the smart contract associated with the result. * `balance` — the balance of the contract, expressed in Wei and encoded as a hexadecimal string. * `code` — the bytecode of the contract, encoded as a hexadecimal string. * `nonce` — the nonce of the account associated with the contract, represented as an unsigned integer. * `storage` — a map of key-value pairs representing the storage slots of the contract. ## `debug_traceTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "YOUR_CHAINSTACK_ENDPOINT"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txHash) => { // Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer const tracer = { tracer: "callTracer" }; const traces = await provider.send("debug_traceTransaction", [txHash, tracer]); console.log(traces); }; traceTransaction("0x2cb57e963111cf4d231ec8f66f0eb7a964f10625c2749bf8571d35332fd326c0"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "YOUR_CHAINSTACK_ENDPOINT" web3 = Web3(Web3.HTTPProvider(node_url)) tx_hash = "0x2cb57e963111cf4d231ec8f66f0eb7a964f10625c2749bf8571d35332fd326c0" # Specify the type of tracer: 4byteTracer, callTracer, or prestateTracer tracer = {"tracer": "callTracer"} tx_traces = web3.provider.make_request('debug_traceTransaction', [tx_hash, tracer]) print(tx_traces) ``` ## Use case A practical use case for the `debug_traceTransaction` method is to inspect failed transactions on Robinhood Chain by using the built-in `callTracer` to extract the error and revert reason. By tracing a failed transaction, developers can identify exactly where and why the execution reverted, which is essential for debugging smart contract interactions. ## `debug_traceTransaction` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("debug_traceTransaction", ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8", {"tracer": "callTracer"}]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceTransaction", ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8", {"tracer": "callTracer"}]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "debug_traceTransaction", "params": ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8", {"tracer": "callTracer"}], "id": 1 }' ``` # eth_accounts | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-accounts openapi/robinhood_node_api/chain_info/eth_accounts.json POST / Robinhood Chain API method that returns a list of addresses owned by the client. Chainstack supports eth_accounts on Robinhood Chain JSON-RPC nodes. Robinhood Chain API method that returns a list of addresses owned by the client. ## Parameters None ## Response * `array` — an array of addresses owned by the client ## `eth_accounts` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_accounts", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_accounts", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1 }' ``` # eth_blobBaseFee | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-blobbasefee openapi/robinhood_node_api/gas_data/eth_blobBaseFee.json POST / Robinhood Chain API method that returns the base fee for blob gas in Wei. eth_blobBaseFee JSON-RPC method available on the Robinhood Chain blockchain via Chainstack. Robinhood Chain API method that returns the base fee for blob gas in Wei. ## Parameters None ## Response * `quantity` — the current blob base fee in Wei ## `eth_blobBaseFee` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_blobBaseFee", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_blobBaseFee", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_blobBaseFee", "params": [], "id": 1 }' ``` # eth_blockNumber | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-blocknumber openapi/robinhood_node_api/blocks_info/eth_blockNumber.json POST / Robinhood Chain API method that returns the number of the most recent block. eth_blockNumber JSON-RPC method available on the Robinhood Chain blockchain via Chainstack. Robinhood Chain API method that returns the number of the most recent block. ## Parameters None ## Response * `quantity` — the current block number the client is on ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_blockNumber", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_blockNumber", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1 }' ``` # eth_call | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-call openapi/robinhood_node_api/execute_transactions/eth_call.json POST / Robinhood Chain API method that executes a new message call immediately without creating a transaction on the blockchain. Robinhood Chain via Chainstack. Robinhood Chain API method that executes a new message call immediately without creating a transaction on the blockchain. ## Parameters * `object` — the transaction call object with fields like `from`, `to`, `gas`, `gasPrice`, `value`, `data` * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `data` — the return value of the executed contract ## `eth_call` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_call", [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}, "latest"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_call", [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}, "latest"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_call", "params": [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}, "latest"], "id": 1 }' ``` # eth_chainId | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-chainid openapi/robinhood_node_api/chain_info/eth_chainId.json POST / Robinhood Chain API method that returns the chain ID of the current network. eth_chainId JSON-RPC method available on the Robinhood Chain blockchain via Chainstack. Robinhood Chain API method that returns the chain ID of the current network. ## Parameters None ## Response * `quantity` — the chain ID ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_chainId", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_chainId", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_chainId", "params": [], "id": 1 }' ``` # eth_createAccessList | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-createaccesslist openapi/robinhood_node_api/execute_transactions/eth_createAccessList.json POST / Robinhood Chain API method that creates an EIP-2930 access list for a transaction. Reference for eth_createAccessList on Robinhood Chain via Chainstack. Robinhood Chain API method that creates an EIP-2930 access list for a transaction. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `object` — the transaction call object * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — an object with `accessList` and `gasUsed` ## `eth_createAccessList` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_createAccessList", [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}, "latest"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_createAccessList", [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}, "latest"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_createAccessList", "params": [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}, "latest"], "id": 1 }' ``` # eth_estimateGas | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-estimategas openapi/robinhood_node_api/execute_transactions/eth_estimateGas.json POST / Robinhood Chain API method that generates and returns an estimate of how much gas is necessary for the transaction to complete. Robinhood Chain via Chainstack. Robinhood Chain API method that generates and returns an estimate of how much gas is necessary for the transaction to complete. ## Parameters * `object` — the transaction call object with fields like `from`, `to`, `gas`, `gasPrice`, `value`, `data` ## Response * `quantity` — the amount of gas used ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_estimateGas", [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_estimateGas", [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_estimateGas", "params": [{"to": "0x00000000000000000000000000000000000a4b05", "data": "0x"}], "id": 1 }' ``` # eth_feeHistory | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-feehistory openapi/robinhood_node_api/gas_data/eth_feeHistory.json POST / Robinhood Chain API method that returns historical gas information for a range of blocks. Available on Robinhood Chain via Chainstack JSON-RPC nodes. Robinhood Chain API method that returns historical gas information for a range of blocks. ## Parameters * `quantity` — number of blocks in the requested range (between 1 and 1024) * `quantity or tag` — the highest block of the requested range * `array` — array of percentile values (between 0 and 100) ## Response * `object` — an object with fee history including `baseFeePerGas`, `gasUsedRatio`, and `reward` ## `eth_feeHistory` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_feeHistory", ["0x5", "latest", [25, 75]]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_feeHistory", ["0x5", "latest", [25, 75]]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_feeHistory", "params": ["0x5", "latest", [25, 75]], "id": 1 }' ``` # eth_gasPrice | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-gasprice openapi/robinhood_node_api/gas_data/eth_gasPrice.json POST / Robinhood Chain API method that returns the current price per gas in Wei. eth_gasPrice JSON-RPC method available on the Robinhood Chain blockchain via Chainstack. Robinhood Chain API method that returns the current price per gas in Wei. ## Parameters None ## Response * `quantity` — the current gas price in Wei ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_gasPrice", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_gasPrice", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_gasPrice", "params": [], "id": 1 }' ``` # eth_getBalance | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getbalance openapi/robinhood_node_api/accounts_info/eth_getBalance.json POST / Robinhood Chain API method that returns the balance of a specific account in Wei, the smallest unit of ether. Use it on Robinhood Chain via Chainstack. Robinhood Chain API method that returns the balance of a specific account in Wei, the smallest unit of ether. ## Parameters * `address` — the address to check the balance of * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `quantity` — the integer value of the current balance in Wei ## `eth_getBalance` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBalance", ["0x00000000000000000000000000000000000a4b05", "latest"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getBalance", ["0x00000000000000000000000000000000000a4b05", "latest"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBalance", "params": ["0x00000000000000000000000000000000000a4b05", "latest"], "id": 1 }' ``` # eth_getBlockByHash | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getblockbyhash openapi/robinhood_node_api/blocks_info/eth_getBlockByHash.json POST / Robinhood Chain API method that returns information about a block by block hash. data — the hash of the block boolean — if true, returns full transaction objects. Robinhood Chain API method that returns information about a block by block hash. ## Parameters * `data` — the hash of the block * `boolean` — if `true`, returns full transaction objects; if `false`, returns only transaction hashes ## Response * `object` — a block object, or `null` when no block was found ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBlockByHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", false]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getBlockByHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", false]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBlockByHash", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", false], "id": 1 }' ``` # eth_getBlockByNumber | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getblockbynumber openapi/robinhood_node_api/blocks_info/eth_getBlockByNumber.json POST / Robinhood Chain API method that returns information about a block by block number. quantity or tag — the block number in hex format or block tag (latest, earliest. Robinhood Chain API method that returns information about a block by block number. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `boolean` — if `true`, returns full transaction objects; if `false`, returns only transaction hashes ## Response * `object` — a block object, or `null` when no block was found ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBlockByNumber", ["0xc3007d", false]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getBlockByNumber", ["0xc3007d", false]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBlockByNumber", "params": ["0xc3007d", false], "id": 1 }' ``` # eth_getBlockReceipts | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getblockreceipts openapi/robinhood_node_api/blocks_info/eth_getBlockReceipts.json POST / Robinhood Chain API method that returns all transaction receipts for a given block. quantity or tag — the block number in hex format or block tag (latest, earliest. Robinhood Chain API method that returns all transaction receipts for a given block. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `array` — an array of receipt objects for all transactions in the block ## `eth_getBlockReceipts` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBlockReceipts", ["0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getBlockReceipts", ["0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBlockReceipts", "params": ["0xc3007d"], "id": 1 }' ``` # eth_getBlockTransactionCountByHash | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getblocktransactioncountbyhash openapi/robinhood_node_api/blocks_info/eth_getBlockTransactionCountByHash.json POST / Robinhood Chain API method that returns the number of transactions in a block by block hash. data — the hash of the block. Robinhood Chain via Chainstack. Robinhood Chain API method that returns the number of transactions in a block by block hash. ## Parameters * `data` — the hash of the block ## Response * `quantity` — the number of transactions in the block ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBlockTransactionCountByHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getBlockTransactionCountByHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByHash", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"], "id": 1 }' ``` # eth_getBlockTransactionCountByNumber | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getblocktransactioncountbynumber openapi/robinhood_node_api/blocks_info/eth_getBlockTransactionCountByNumber.json POST / Robinhood Chain API method that returns the number of transactions in a block by block number. eth_getBlockTransactionCountByNumber on Robinhood Chain via Chainstack. Robinhood Chain API method that returns the number of transactions in a block by block number. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `quantity` — the number of transactions in the block ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getBlockTransactionCountByNumber", ["0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getBlockTransactionCountByNumber", ["0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByNumber", "params": ["0xc3007d"], "id": 1 }' ``` # eth_getCode | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getcode openapi/robinhood_node_api/accounts_info/eth_getCode.json POST / Robinhood Chain API method that returns the bytecode of a smart contract at a given address. Available on Robinhood Chain via Chainstack JSON-RPC nodes. Robinhood Chain API method that returns the bytecode of a smart contract at a given address. ## Parameters * `address` — the contract address * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `data` — the bytecode at the given address ## `eth_getCode` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getCode", ["0x00000000000000000000000000000000000a4b05", "latest"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getCode", ["0x00000000000000000000000000000000000a4b05", "latest"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getCode", "params": ["0x00000000000000000000000000000000000a4b05", "latest"], "id": 1 }' ``` # eth_getFilterChanges | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getfilterchanges openapi/robinhood_node_api/filter_handling/eth_getFilterChanges.json POST / Robinhood Chain API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Robinhood Chain API polling method used to retrieve updates from a filter. A filter is an object used to track changes to the state of the blockchain. Filters can be created using one of the following methods: * eth\_newFilter * eth\_newBlockFilter After creating a filter using one of the available methods, the resulting filter ID can be used to fetch changes by calling the `eth_getFilterChanges` method. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * eth\_newFilter > * eth\_newBlockFilter > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Parameters * `string` — the filter ID of the filter for which you want to retrieve the changes ## Response * `array` — an array that represents the changes that have occurred on the blockchain since the last time the filter was polled: * For filters created with `eth_newBlockFilter`: * `blockHash` — the hashes of the new blocks since the last time the filter was polled. * For filters created with `eth_newFilter`, the following event logs: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_getFilterChanges` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getFilterChanges", ["0x1"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getFilterChanges", ["0x1"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x1"], "id": 1 }' ``` # eth_getFilterLogs | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getfilterlogs openapi/robinhood_node_api/filter_handling/eth_getFilterLogs.json POST / Robinhood Chain API method that returns an array of all logs matching the filter with the given ID. eth_getFilterLogs on Robinhood Chain via Chainstack. Robinhood Chain API method that returns an array of all logs matching the filter with the given ID. Unlike `eth_getFilterChanges`, this returns all matching logs, not just new ones since the last poll. ## Parameters * `filterId` — the filter ID returned from `eth_newFilter` ## Response * `result` — array of log objects matching the filter: * `address` — contract address that emitted the log * `topics` — array of indexed log parameters * `data` — non-indexed log parameters * `blockNumber` — block number containing the log * `blockHash` — hash of the block * `transactionHash` — hash of the transaction that emitted the log * `transactionIndex` — index of the transaction in the block * `logIndex` — position of the log in the block * `removed` — `true` if the log was removed due to a chain reorganization ## `eth_getFilterLogs` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getFilterLogs", ["0x1"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getFilterLogs", ["0x1"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getFilterLogs", "params": ["0x1"], "id": 1 }' ``` # eth_getHeaderByHash | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getheaderbyhash openapi/robinhood_node_api/blocks_info/eth_getHeaderByHash.json POST / Robinhood Chain API method that returns the block header by block hash. data — the hash of the block object — the block header object. On Robinhood Chain. Robinhood Chain API method that returns the block header by block hash. ## Parameters * `data` — the hash of the block ## Response * `object` — the block header object ## `eth_getHeaderByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getHeaderByHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getHeaderByHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getHeaderByHash", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"], "id": 1 }' ``` # eth_getHeaderByNumber | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getheaderbynumber openapi/robinhood_node_api/blocks_info/eth_getHeaderByNumber.json POST / Robinhood Chain API method that returns the block header by block number. quantity or tag — the block number in hex format or block tag (latest, earliest, pending. Robinhood Chain API method that returns the block header by block number. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — the block header object ## `eth_getHeaderByNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getHeaderByNumber", ["0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getHeaderByNumber", ["0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getHeaderByNumber", "params": ["0xc3007d"], "id": 1 }' ``` # eth_getLogs | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getlogs openapi/robinhood_node_api/logs_and_events/eth_getLogs.json POST / Robinhood Chain API method that returns an array of all logs matching a given filter object. Available on Robinhood Chain via Chainstack JSON-RPC nodes. Robinhood Chain API method that returns an array of all logs matching a given filter object. ## Parameters * `object` — the filter options with fields like `fromBlock`, `toBlock`, `address`, `topics`, `blockHash` ## Response * `array` — an array of log objects ## `eth_getLogs` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getLogs", [{"fromBlock": "0xc3007d", "toBlock": "0xc3007d"}]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getLogs", [{"fromBlock": "0xc3007d", "toBlock": "0xc3007d"}]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"fromBlock": "0xc3007d", "toBlock": "0xc3007d"}], "id": 1 }' ``` # eth_getProof | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getproof openapi/robinhood_node_api/accounts_info/eth_getProof.json POST / Robinhood Chain API method that returns the account and storage values including the Merkle-proof. Available on Robinhood Chain via Chainstack JSON-RPC nodes. Robinhood Chain API method that returns the account and storage values including the Merkle-proof. > [!NOTE] > > On Global Nodes, Robinhood Chain Mainnet returns proofs for about the latest 100 blocks, and an older block returns `-32000` `missing trie node`. Robinhood Chain Testnet returns proofs for every block. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). ## Parameters * `address` — the address of the account * `array` — an array of storage keys to generate proofs for * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — account object with balance, codeHash, nonce, storageHash, accountProof, and storageProof ## `eth_getProof` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getProof", ["0x00000000000000000000000000000000000a4b05", ["0x0"], "latest"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getProof", ["0x00000000000000000000000000000000000a4b05", ["0x0"], "latest"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getProof", "params": ["0x00000000000000000000000000000000000a4b05", ["0x0"], "latest"], "id": 1 }' ``` # eth_getRawTransactionByBlockHashAndIndex | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getrawtransactionbyblockhashandindex openapi/robinhood_node_api/transaction_info/eth_getRawTransactionByBlockHashAndIndex.json POST / Robinhood Chain API method that returns the raw transaction data by block hash and transaction index. Available on Robinhood Chain via Chainstack JSON-RPC nodes. Robinhood Chain API method that returns the raw transaction data by block hash and transaction index. ## Parameters * `data` — the hash of the block * `quantity` — the transaction index position ## Response * `data` — the raw transaction data, or `null` when no transaction was found ## `eth_getRawTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getRawTransactionByBlockHashAndIndex", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", "0x0"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getRawTransactionByBlockHashAndIndex", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", "0x0"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getRawTransactionByBlockHashAndIndex", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", "0x0"], "id": 1 }' ``` # eth_getRawTransactionByBlockNumberAndIndex | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getrawtransactionbyblocknumberandindex openapi/robinhood_node_api/transaction_info/eth_getRawTransactionByBlockNumberAndIndex.json POST / Robinhood Chain API method that returns the raw transaction data by block number and transaction index. Available on Robinhood Chain via Chainstack. Robinhood Chain API method that returns the raw transaction data by block number and transaction index. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `quantity` — the transaction index position ## Response * `data` — the raw transaction data, or `null` when no transaction was found ## `eth_getRawTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getRawTransactionByBlockNumberAndIndex", ["0xc3007d", "0x0"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getRawTransactionByBlockNumberAndIndex", ["0xc3007d", "0x0"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getRawTransactionByBlockNumberAndIndex", "params": ["0xc3007d", "0x0"], "id": 1 }' ``` # eth_getRawTransactionByHash | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getrawtransactionbyhash openapi/robinhood_node_api/transaction_info/eth_getRawTransactionByHash.json POST / Robinhood Chain API method that returns the raw transaction data by transaction hash. data — the hash of the transaction. Robinhood Chain via Chainstack. Robinhood Chain API method that returns the raw transaction data by transaction hash. ## Parameters * `data` — the hash of the transaction ## Response * `data` — the raw transaction data, or `null` when no transaction was found ## `eth_getRawTransactionByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getRawTransactionByHash", ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getRawTransactionByHash", ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getRawTransactionByHash", "params": ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"], "id": 1 }' ``` # eth_getStorageAt | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getstorageat openapi/robinhood_node_api/accounts_info/eth_getStorageAt.json POST / Robinhood Chain API method that returns the value from a storage position at a given address. Available on Robinhood Chain via Chainstack JSON-RPC nodes. Robinhood Chain API method that returns the value from a storage position at a given address. ## Parameters * `address` — the address to get storage from * `quantity` — the position in the storage * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `data` — the value at this storage position ## `eth_getStorageAt` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getStorageAt", ["0x00000000000000000000000000000000000a4b05", "0x0", "latest"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getStorageAt", ["0x00000000000000000000000000000000000a4b05", "0x0", "latest"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getStorageAt", "params": ["0x00000000000000000000000000000000000a4b05", "0x0", "latest"], "id": 1 }' ``` # eth_getTransactionByBlockHashAndIndex | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-gettransactionbyblockhashandindex openapi/robinhood_node_api/transaction_info/eth_getTransactionByBlockHashAndIndex.json POST / Robinhood Chain API method that returns information about a transaction by block hash and transaction index position. Robinhood Chain via Chainstack. Robinhood Chain API method that returns information about a transaction by block hash and transaction index position. ## Parameters * `data` — the hash of the block * `quantity` — the transaction index position ## Response * `object` — a transaction object, or `null` when no transaction was found ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionByBlockHashAndIndex", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", "0x0"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getTransactionByBlockHashAndIndex", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", "0x0"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionByBlockHashAndIndex", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", "0x0"], "id": 1 }' ``` # eth_getTransactionByBlockNumberAndIndex | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-gettransactionbyblocknumberandindex openapi/robinhood_node_api/transaction_info/eth_getTransactionByBlockNumberAndIndex.json POST / Robinhood Chain API method that returns information about a transaction by block number and transaction index position. Robinhood Chain via Chainstack. Robinhood Chain API method that returns information about a transaction by block number and transaction index position. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `quantity` — the transaction index position ## Response * `object` — a transaction object, or `null` when no transaction was found ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionByBlockNumberAndIndex", ["0xc3007d", "0x0"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getTransactionByBlockNumberAndIndex", ["0xc3007d", "0x0"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionByBlockNumberAndIndex", "params": ["0xc3007d", "0x0"], "id": 1 }' ``` # eth_getTransactionByHash | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-gettransactionbyhash openapi/robinhood_node_api/transaction_info/eth_getTransactionByHash.json POST / Robinhood Chain API method that returns information about a transaction by transaction hash. data — the hash of the transaction. On Robinhood Chain. Robinhood Chain API method that returns information about a transaction by transaction hash. ## Parameters * `data` — the hash of the transaction ## Response * `object` — a transaction object, or `null` when no transaction was found ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionByHash", ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getTransactionByHash", ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", "params": ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"], "id": 1 }' ``` # eth_getTransactionCount | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-gettransactioncount openapi/robinhood_node_api/accounts_info/eth_getTransactionCount.json POST / Robinhood Chain API method that returns the number of transactions sent from an address (nonce). eth_getTransactionCount on Robinhood Chain via Chainstack. Robinhood Chain API method that returns the number of transactions sent from an address (nonce). ## Parameters * `address` — the address to get the transaction count from * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `quantity` — the number of transactions sent from this address ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionCount", ["0x00000000000000000000000000000000000a4b05", "latest"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getTransactionCount", ["0x00000000000000000000000000000000000a4b05", "latest"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionCount", "params": ["0x00000000000000000000000000000000000a4b05", "latest"], "id": 1 }' ``` # eth_getTransactionReceipt | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-gettransactionreceipt openapi/robinhood_node_api/transaction_info/eth_getTransactionReceipt.json POST / Robinhood Chain API method that returns the receipt of a transaction by transaction hash. data — the hash of the transaction. Robinhood Chain via Chainstack. Robinhood Chain API method that returns the receipt of a transaction by transaction hash. ## Parameters * `data` — the hash of the transaction ## Response * `object` — a transaction receipt object, or `null` when no receipt was found ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getTransactionReceipt", ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getTransactionReceipt", ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getTransactionReceipt", "params": ["0x73b5269af660acf6f1074c5f9f312d415c359f2e5f5f67e95b369d0fbd3bd6b8"], "id": 1 }' ``` # eth_getUncleByBlockHashAndIndex | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getunclebyblockhashandindex openapi/robinhood_node_api/blocks_info/eth_getUncleByBlockHashAndIndex.json POST / Robinhood Chain API method that returns information about an uncle by block hash and uncle index position. Use it on Robinhood Chain via Chainstack. Robinhood Chain API method that returns information about an uncle by block hash and uncle index position. ## Parameters * `data` — the hash of the block * `quantity` — the uncle index position ## Response * `object` — a block object, or `null` when no uncle was found ## `eth_getUncleByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleByBlockHashAndIndex", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", "0x0"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getUncleByBlockHashAndIndex", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", "0x0"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleByBlockHashAndIndex", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a", "0x0"], "id": 1 }' ``` # eth_getUncleByBlockNumberAndIndex | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getunclebyblocknumberandindex openapi/robinhood_node_api/blocks_info/eth_getUncleByBlockNumberAndIndex.json POST / Robinhood Chain API method that returns information about an uncle by block number and uncle index position. Use it on Robinhood Chain via Chainstack. Robinhood Chain API method that returns information about an uncle by block number and uncle index position. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) * `quantity` — the uncle index position ## Response * `object` — a block object, or `null` when no uncle was found ## `eth_getUncleByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleByBlockNumberAndIndex", ["0xc3007d", "0x0"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getUncleByBlockNumberAndIndex", ["0xc3007d", "0x0"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleByBlockNumberAndIndex", "params": ["0xc3007d", "0x0"], "id": 1 }' ``` # eth_getUncleCountByBlockHash | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getunclecountbyblockhash openapi/robinhood_node_api/blocks_info/eth_getUncleCountByBlockHash.json POST / Robinhood Chain API method that returns the number of uncles in a block by block hash. data — the hash of the block quantity — the number of uncles in the block. Robinhood Chain API method that returns the number of uncles in a block by block hash. ## Parameters * `data` — the hash of the block ## Response * `quantity` — the number of uncles in the block ## `eth_getUncleCountByBlockHash` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleCountByBlockHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getUncleCountByBlockHash", ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleCountByBlockHash", "params": ["0xf4aab7a559489b816aca197b9a9315b5363a9932c52c3e8bfcf4152588ee096a"], "id": 1 }' ``` # eth_getUncleCountByBlockNumber | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-getunclecountbyblocknumber openapi/robinhood_node_api/blocks_info/eth_getUncleCountByBlockNumber.json POST / Robinhood Chain API method that returns the number of uncles in a block by block number. eth_getUncleCountByBlockNumber on Robinhood Chain via Chainstack. Robinhood Chain API method that returns the number of uncles in a block by block number. ## Parameters * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `quantity` — the number of uncles in the block ## `eth_getUncleCountByBlockNumber` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_getUncleCountByBlockNumber", ["0xc3007d"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getUncleCountByBlockNumber", ["0xc3007d"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getUncleCountByBlockNumber", "params": ["0xc3007d"], "id": 1 }' ``` # eth_maxPriorityFeePerGas | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-maxpriorityfeepergas openapi/robinhood_node_api/gas_data/eth_maxPriorityFeePerGas.json POST / Robinhood Chain API method that returns the current max priority fee per gas in Wei. Reference for eth_maxPriorityFeePerGas on Robinhood Chain via Chainstack. Robinhood Chain API method that returns the current max priority fee per gas in Wei. ## Parameters None ## Response * `quantity` — the current max priority fee per gas in Wei ## `eth_maxPriorityFeePerGas` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_maxPriorityFeePerGas", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_maxPriorityFeePerGas", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_maxPriorityFeePerGas", "params": [], "id": 1 }' ``` # eth_newBlockFilter | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-newblockfilter openapi/robinhood_node_api/filter_handling/eth_newBlockFilter.json POST / Robinhood Chain API method that creates a filter in the node to notify when a new block arrives. eth_newBlockFilter on Robinhood Chain via Chainstack. Robinhood Chain API method that creates a filter in the node to notify when a new block arrives. ## Parameters None ## Response * `quantity` — a filter ID ## `eth_newBlockFilter` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_newBlockFilter", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_newBlockFilter", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_newBlockFilter", "params": [], "id": 1 }' ``` # eth_newFilter | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-newfilter openapi/robinhood_node_api/filter_handling/eth_newFilter.json POST / Robinhood Chain API method that creates a filter object based on filter options to notify when the state changes (logs). Robinhood Chain via Chainstack. Robinhood Chain API method that creates a filter object based on filter options to notify when the state changes (logs). ## Parameters * `object` — the filter options with fields like `fromBlock`, `toBlock`, `address`, `topics` ## Response * `quantity` — a filter ID ## `eth_newFilter` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_newFilter", [{"fromBlock": "0xc3007d", "toBlock": "0xc3007d"}]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_newFilter", [{"fromBlock": "0xc3007d", "toBlock": "0xc3007d"}]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_newFilter", "params": [{"fromBlock": "0xc3007d", "toBlock": "0xc3007d"}], "id": 1 }' ``` # eth_newPendingTransactionFilter | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-newpendingtransactionfilter openapi/robinhood_node_api/filter_handling/eth_newPendingTransactionFilter.json POST / Robinhood Chain API method that creates a filter in the node to notify when new pending transactions arrive. Use it on Robinhood Chain via Chainstack. Robinhood Chain API method that creates a filter in the node to notify when new pending transactions arrive. ## Parameters None ## Response * `quantity` — a filter ID ## `eth_newPendingTransactionFilter` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_newPendingTransactionFilter", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_newPendingTransactionFilter", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_newPendingTransactionFilter", "params": [], "id": 1 }' ``` # eth_sendRawTransaction | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-sendrawtransaction openapi/robinhood_node_api/execute_transactions/eth_sendRawTransaction.json POST / Robinhood Chain API method that allows submitting a signed transaction to the network. eth_sendRawTransaction on Robinhood Chain via Chainstack. Robinhood Chain API method that allows submitting a signed transaction to the network. Once a transaction is signed, you can use the `eth_sendRawTransaction` method to submit the signed transaction to Robinhood Chain for processing. > [!WARNING] > > Note that the interactive example in this page will not work, due to the fact that `eth_sendRawTransaction` is used to modify the state of the blockchain, it is not possible to duplicate the same request. ## Parameters * `data` — the signed transaction. The serialized transaction data, which includes the transaction metadata such as the nonce, gas price, gas limit, recipient address, and data payload, as well as the digital signature generated using the private key associated with the address that is sending the transaction. Typically, signed with a library such as web3.py, web3.js, or ethers.js using the sender's private key. ## Response * `result` — the unique hash identifying the transaction or the zero hash if the transaction is not available yet. The zero hash is a special value that represents an invalid or null hash. ## `eth_sendRawTransaction` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_sendRawTransaction", ["0x02"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_sendRawTransaction", ["0x02"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": ["0x02"], "id": 1 }' ``` # eth_sendRawTransactionSync | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-sendrawtransactionsync openapi/robinhood_node_api/execute_transactions/eth_sendRawTransactionSync.json POST / Robinhood Chain API method that submits a signed transaction and blocks until the transaction receipt is returned or the timeout expires. Robinhood Chain API method `eth_sendRawTransactionSync` allows for the submission of a pre-signed transaction to Robinhood Chain and waits synchronously for the transaction receipt or a configurable timeout. This method reduces transaction submission latency by combining transaction submission and receipt retrieval into a single RPC call, based on [EIP-7966](https://eips.ethereum.org/EIPS/eip-7966). ## Parameters * `signedTransactionData` — the signed transaction data in hexadecimal format. This data includes the transaction details such as nonce, gas price, gas limit, to address, value, data, and the signature. * `timeout` (optional) — maximum time to wait for the transaction receipt, in milliseconds, passed as a `0x`-prefixed hex-encoded quantity, for example `0x1388` for 5000 ms. If omitted, the node's default applies (20 seconds in the standard Nitro configuration). Requests above the node's maximum (60 seconds by default) are silently capped to the maximum rather than rejected. > [!NOTE] > > Although [EIP-7966](https://eips.ethereum.org/EIPS/eip-7966) defines `timeout` as a plain integer, Robinhood Chain Nitro—a [go-ethereum](https://github.com/OffchainLabs/go-ethereum) fork—types it as a hex-encoded quantity (`hexutil.Uint64`), so you must pass it as a `0x`-prefixed hex string. Plain integers (`5000`) and non-hex strings (`"5000"`) are rejected with an `invalid argument 1` error. ## Response * `result` — the complete transaction receipt object, the same structure returned by eth\_getTransactionReceipt: transaction hash, block information, gas usage, contract address (if applicable), logs, and status. If the timeout elapses before the transaction is mined, the call returns an error instead of a receipt. Alongside the standard receipt fields, Robinhood Chain includes: * `gasUsedForL1` — the amount of gas spent on parent chain (L1) calldata, in units of child chain gas. * `l1BlockNumber` — the block number of the first non-Robinhood Chain ancestor chain, usable for `block.number` calls. * `timeboosted` — `true` if the transaction was sequenced through the Robinhood Chain [Timeboost](https://docs.robinhood.io/how-robinhood-works/timeboost/gentle-introduction) express lane, `false` otherwise. ## Use case The `eth_sendRawTransactionSync` method is particularly useful for: * High-frequency applications requiring immediate transaction confirmation feedback. * Layer 2 solutions with fast block times where rapid transaction throughput is critical. * DApps and wallets seeking to improve user experience with faster transaction confirmations. * Trading applications where transaction latency directly impacts performance. * Applications requiring stronger delivery guarantees than standard asynchronous transaction submission. ## `eth_sendRawTransactionSync` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_sendRawTransactionSync", ["0x02"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_sendRawTransactionSync", ["0x02"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_sendRawTransactionSync", "params": ["0x02"], "id": 1 }' ``` # eth_simulateV1 | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-simulatev1 openapi/robinhood_node_api/execute_transactions/eth_simulateV1.json POST / Robinhood Chain API method that simulates transaction execution. eth_simulateV1 JSON-RPC method available on the Robinhood Chain blockchain via Chainstack. Robinhood Chain API method that simulates transaction execution. ## Parameters * `object` — the simulation configuration object * `quantity or tag` — the block number in hex format or block tag (`latest`, `earliest`, `pending`, `safe`, `finalized`) ## Response * `object` — the simulation result ## `eth_simulateV1` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_simulateV1", [{"blockStateCalls": [{"calls": []}]}, "latest"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_simulateV1", [{"blockStateCalls": [{"calls": []}]}, "latest"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_simulateV1", "params": [{"blockStateCalls": [{"calls": []}]}, "latest"], "id": 1 }' ``` # eth_syncing | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-syncing openapi/robinhood_node_api/chain_info/eth_syncing.json POST / Robinhood Chain API method that returns an object with data about the sync status or false. Available on Robinhood Chain via Chainstack JSON-RPC nodes. Robinhood Chain API method that returns an object with data about the sync status or `false`. ## Parameters None ## Response * `object|boolean` — an object with sync status data or `false` when not syncing ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_syncing", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_syncing", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1 }' ``` # eth_uninstallFilter | Robinhood Source: https://docs.chainstack.com/reference/robinhood-eth-uninstallfilter openapi/robinhood_node_api/filter_handling/eth_uninstallFilter.json POST / Robinhood Chain API method that uninstalls a filter with the given ID. quantity — the filter ID boolean — true if the filter was successfully uninstalled. Robinhood Chain API method that uninstalls a filter with the given ID. ## Parameters * `quantity` — the filter ID ## Response * `boolean` — `true` if the filter was successfully uninstalled, `false` otherwise ## `eth_uninstallFilter` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("eth_uninstallFilter", ["0x1"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_uninstallFilter", ["0x1"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_uninstallFilter", "params": ["0x1"], "id": 1 }' ``` # Execute transactions | Robinhood Source: https://docs.chainstack.com/reference/robinhood-execute-transactions-rpc-methods Robinhood Chain transaction execution JSON-RPC methods — simulate eth_call executions and estimate gas costs without broadcasting transactions to the Robinhood Chain blockchain. Executing and simulating transactions on the Robinhood Chain blockchain can be done using the following methods: > [card] [eth\_call](/reference/robinhood-eth-call) > [card] [eth\_estimateGas](/reference/robinhood-eth-estimategas) > [card] [eth\_createAccessList](/reference/robinhood-eth-createaccesslist) > [card] [eth\_simulateV1](/reference/robinhood-eth-simulatev1) > [card] [eth\_sendRawTransaction](/reference/robinhood-eth-sendrawtransaction) > [card] [eth\_sendRawTransactionSync](/reference/robinhood-eth-sendrawtransactionsync) This information can be used for: * **Gas estimation** — calculate required gas before sending transactions * **Transaction simulation** — test transaction execution without state changes * **Smart contract testing** — verify contract behavior before deployment * **DApp optimization** — optimize transaction parameters for cost efficiency # Filter handling | Robinhood Source: https://docs.chainstack.com/reference/robinhood-filter-handling-rpc-methods The filter handling JSON-RPC methods allow developers to create and manage filters for monitoring blockchain events such as new blocks and logs. Managing filters on the Robinhood Chain blockchain can be done using the following methods: > [card] [eth\_newFilter](/reference/robinhood-eth-newfilter) > [card] [eth\_newBlockFilter](/reference/robinhood-eth-newblockfilter) > [card] [eth\_newPendingTransactionFilter](/reference/robinhood-eth-newpendingtransactionfilter) > [card] [eth\_getFilterChanges](/reference/robinhood-eth-getfilterchanges) > [card] [eth\_getFilterLogs](/reference/robinhood-eth-getfilterlogs) > [card] [eth\_uninstallFilter](/reference/robinhood-eth-uninstallfilter) This information can be used for: * **Event monitoring** — track specific smart contract events * **Block notifications** — receive updates when new blocks are mined * **Real-time applications** — build responsive DApps with live data * **Log filtering** — efficiently query blockchain logs # Gas data | Robinhood Source: https://docs.chainstack.com/reference/robinhood-gas-data-rpc-methods The gas data JSON-RPC methods allow developers to access current gas prices and historical fee information on the Robinhood Chain blockchain. Retrieving gas and fee information from the Robinhood Chain blockchain can be done using the following methods: > [card] [eth\_gasPrice](/reference/robinhood-eth-gasprice) > [card] [eth\_maxPriorityFeePerGas](/reference/robinhood-eth-maxpriorityfeepergas) > [card] [eth\_feeHistory](/reference/robinhood-eth-feehistory) > [card] [eth\_blobBaseFee](/reference/robinhood-eth-blobbasefee) This information can be used for: * **Fee estimation** — calculate optimal gas prices for transactions * **Cost analysis** — analyze historical gas price trends * **Wallet applications** — display current network fee information * **Transaction optimization** — choose appropriate gas parameters # Robinhood Chain API reference: Arbitrum Orbit JSON-RPC quickstart Source: https://docs.chainstack.com/reference/robinhood-getting-started Use the Robinhood Chain JSON-RPC API on Chainstack to deploy nodes, send transactions, and build on the Arbitrum Orbit L2 for tokenized stocks and real-world assets. Chain ID 4663, ETH gas, full and archive with debug and trace on mainnet and testnet. The Robinhood Chain API gives developers programmatic access to Robinhood Chain — an Arbitrum Orbit layer-2 built on Ethereum for tokenized stocks and real-world assets (RWAs). It speaks the standard Ethereum JSON-RPC interface, so the accounts, contracts, logs, and transactions you already know from Ethereum work unchanged. Chainstack serves it through a single node endpoint. ## Robinhood Chain API at a glance | Property | Value | | - | - | | Chain | Robinhood Chain — Arbitrum Orbit layer-2 on Ethereum | | Mainnet chain ID | `4663` | | Testnet chain ID | `46630` | | Native gas token | ETH | | Confirmation latency | \~100 ms target via preconfirmations (Arbitrum Nitro) | | Execution | JSON-RPC — `eth`, `net`, `web3`, and `debug` namespaces | | Node types | Global Node and Dedicated Node, in full and archive modes | | Debug & trace | `debug_*` namespace on a paid plan and a Global Node | | Real-time data | WebSocket `eth_subscribe` — newHeads, logs, newPendingTransactions | | Networks | Mainnet and Testnet | | Request units | 1 RU per full request, 2 RUs per archive request | | Block explorer | [robinhoodchain.blockscout.com](https://robinhoodchain.blockscout.com) | ## What is Robinhood Chain Robinhood Chain is a layer-2 network built with the Arbitrum platform (Arbitrum Orbit, Nitro client) and launched on mainnet on July 1, 2026. It targets tokenized real-world assets — tokenized U.S. stocks, ETPs, and other regulated financial instruments — with customized infrastructure tuned for the performance, security, and regulatory requirements of financial services. As an Arbitrum Orbit chain, Robinhood Chain inherits the Ethereum Virtual Machine (EVM) and the Ethereum JSON-RPC specification, and it settles to Ethereum. ETH is the native gas token, so you pay transaction fees in ETH and need no separate chain token to start building. Contract bytecode, tooling, and JSON-RPC calls that target Ethereum or Arbitrum carry over with minimal changes. ## What is the Robinhood Chain API The Robinhood Chain API lets developers build decentralized applications, analytics tools, wallets, and indexers. To read data from and send transactions to Robinhood Chain, an application connects to a Robinhood Chain node endpoint and calls the JSON-RPC method it needs. The JSON-RPC specification is a communication protocol that lets you make remote calls and execute them as if they were local — querying balances, blocks, logs, and receipts, calling and deploying smart contracts, and submitting signed transactions. A Chainstack Robinhood Chain node serves execution-layer JSON-RPC over HTTPS, WebSocket subscriptions for real-time data, and the `debug_*` tracing namespace. ## Methods Robinhood Chain serves the standard Ethereum JSON-RPC namespaces plus Geth-style debug and trace: | Namespace | Purpose | | - | - | | `eth_*` | Accounts, blocks, logs, gas, contract calls, transactions, and subscriptions | | `net_*` | Network listening status and peer count | | `web3_*` | Client version and Keccak-256 hashing | | `debug_*` | Transaction and block tracing — `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceCall` | > [!NOTE] > > Debug and trace methods require a [paid plan](https://chainstack.com/pricing/) and a node deployed as a [Global Node](/docs/global-elastic-node). See [Debug and trace APIs](/docs/debug-and-trace-apis#robinhood-chain) for details. ### What you can build With a Chainstack Robinhood Chain endpoint you can: * Query chain state — balances, blocks, logs, receipts, storage, and gas — through the standard `eth_*` JSON-RPC interface, compatible with ethers, viem, and web3.py. * Deploy and call Solidity contracts with Hardhat, Foundry, and Remix, using `eth_call`, `eth_estimateGas`, and `eth_sendRawTransaction`. * Debug and trace transactions with `debug_traceTransaction`, `debug_traceBlockByNumber`, and `debug_traceCall` on a paid Global Node. * Stream real-time data over WebSocket with `eth_subscribe` for `newHeads`, `logs`, and `newPendingTransactions`. * Index historical state at any block height — balances, contract storage, and traces — against an archive node. ## How to start using the Robinhood Chain API You need access to a Robinhood Chain node endpoint to use the API. Follow these steps to sign up, deploy a node, and find your credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to Robinhood Chain and use the API to build. ## SDKs and tooling You can call the API directly over HTTP and WebSocket, or use a maintained EVM library: * JavaScript and TypeScript — [ethers.js](https://github.com/ethers-io/ethers.js) and [viem](https://github.com/wevm/viem). * Python — [web3.py](https://github.com/ethereum/web3.py). For smart contract development, point [Foundry](https://github.com/foundry-rs/foundry), [Hardhat](https://github.com/NomicFoundation/hardhat), or Remix at your Chainstack endpoint and set the chain ID. See [Robinhood Chain tooling](/docs/robinhood-tooling) for wallet, IDE, library, and framework setup. ## Networks Chainstack supports Robinhood Chain mainnet and testnet, with full and archive node modes, WebSocket connections, and debug and trace APIs. | Feature | Mainnet | Testnet | | - | - | - | | Chain ID | `4663` | `46630` | | Full nodes | Yes | Yes | | Archive nodes | Yes | Yes | | WebSocket | Yes | Yes | | Debug & trace APIs | Yes | Yes | > [!NOTE] > > A full node keeps recent state — the latest 128 blocks — while an archive node holds all historical state from genesis. Use an archive node for historical balances, contract storage at past blocks, and `debug_*` traces of older transactions. See [Networks](/docs/protocols-networks) for the full deployment matrix. ## Frequently asked questions ### What is the Robinhood Chain chain ID? Robinhood Chain mainnet is chain ID `4663` and the testnet is chain ID `46630`. Set the chain ID in your wallet, SDK, or framework config alongside your Chainstack RPC endpoint. ### What is the native gas token on Robinhood Chain? ETH. Robinhood Chain is an Arbitrum Orbit layer-2 that uses ETH as its native gas token, so you pay transaction fees in ETH and do not need a separate chain token to get started. ### What is the difference between a full node and an archive node? A full node keeps recent state — the latest 128 blocks — which covers most live application reads and transaction submission. An archive node retains all historical state from genesis, so you can query balances and contract storage at any past block and trace older transactions. Chainstack offers both modes on Robinhood Chain mainnet and testnet. ### Does Robinhood Chain support debug and trace methods? Yes. Chainstack Robinhood Chain nodes serve the Geth-style `debug_*` namespace — including `debug_traceTransaction`, `debug_traceBlockByNumber`, and `debug_traceCall` — for transaction tracing and smart contract debugging. Debug and trace require a paid plan and a node deployed as a Global Node. See [Debug and trace APIs](/docs/debug-and-trace-apis#robinhood-chain). ### Which SDKs work with the Robinhood Chain API? Any standard Ethereum library works against a Chainstack endpoint: ethers.js and viem for JavaScript and TypeScript, and web3.py for Python. For contracts, use Foundry, Hardhat, or Remix. See [Robinhood Chain tooling](/docs/robinhood-tooling). ### How are Robinhood Chain requests priced? Pricing is 1 request unit (RU) per full request and 2 RUs per archive request. For which EVM methods bill as full versus archive, see [Request units — EVM methods affected by block age](/docs/request-units#evm-methods-affected-by-block-age). ## Related Robinhood Chain resources * [Robinhood Chain tooling](/docs/robinhood-tooling) — wallets, IDEs, libraries, and frameworks * [Debug and trace APIs](/docs/debug-and-trace-apis#robinhood-chain) — enabling the `debug_*` namespace * [Networks](/docs/protocols-networks) — the full protocol and node deployment matrix * [Request units](/docs/request-units) — how full and archive requests are priced # Logs and events | Robinhood Source: https://docs.chainstack.com/reference/robinhood-logs-and-events-rpc-methods Robinhood Chain logs and events JSON-RPC methods — query smart contract event logs, filter by topics and address, and track on-chain activity on the Robinhood Chain network. Retrieving logs and events from the Robinhood Chain blockchain can be done using the following methods: > [card] [eth\_getLogs](/reference/robinhood-eth-getlogs) This information can be used for: * **Event tracking** — monitor specific smart contract events * **Historical analysis** — analyze past contract interactions * **DApp integration** — display contract events in applications * **Audit and compliance** — track on-chain activity for verification # net_version | Robinhood Source: https://docs.chainstack.com/reference/robinhood-net-version openapi/robinhood_node_api/client_info/net_version.json POST / Robinhood Chain API method that returns the current network ID. net_version JSON-RPC method available on the Robinhood Chain blockchain via Chainstack. Robinhood Chain API method that returns the current network ID. ## Parameters None ## Response * `string` — the current network ID ## `net_version` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("net_version", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("net_version", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "net_version", "params": [], "id": 1 }' ``` # Transaction info | Robinhood Source: https://docs.chainstack.com/reference/robinhood-transaction-info-rpc-methods Robinhood Chain transaction info JSON-RPC methods — retrieve transaction data, receipts, and confirmation status for monitoring on-chain activity on the Robinhood Chain network. Retrieving transaction information from the Robinhood Chain blockchain can be done using the following methods: > [card] [eth\_getTransactionByHash](/reference/robinhood-eth-gettransactionbyhash) > [card] [eth\_getTransactionReceipt](/reference/robinhood-eth-gettransactionreceipt) > [card] [eth\_getTransactionByBlockNumberAndIndex](/reference/robinhood-eth-gettransactionbyblocknumberandindex) > [card] [eth\_getTransactionByBlockHashAndIndex](/reference/robinhood-eth-gettransactionbyblockhashandindex) > [card] [eth\_getRawTransactionByHash](/reference/robinhood-eth-getrawtransactionbyhash) > [card] [eth\_getRawTransactionByBlockNumberAndIndex](/reference/robinhood-eth-getrawtransactionbyblocknumberandindex) > [card] [eth\_getRawTransactionByBlockHashAndIndex](/reference/robinhood-eth-getrawtransactionbyblockhashandindex) This information can be used for: * **Transaction tracking** — monitor transaction status and confirmations * **Receipt verification** — verify transaction execution and event logs * **Wallet applications** — display transaction history and details * **DApp development** — integrate transaction data into applications # web3_clientVersion | Robinhood Source: https://docs.chainstack.com/reference/robinhood-web3-clientversion openapi/robinhood_node_api/client_info/web3_clientVersion.json POST / Robinhood Chain API method that returns the current client version. web3_clientVersion JSON-RPC method available on the Robinhood Chain blockchain via Chainstack. Robinhood Chain API method that returns the current client version. ## Parameters None ## Response * `string` — the current client version ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("web3_clientVersion", []); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("web3_clientVersion", []) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "web3_clientVersion", "params": [], "id": 1 }' ``` # web3_sha3 | Robinhood Source: https://docs.chainstack.com/reference/robinhood-web3-sha3 openapi/robinhood_node_api/client_info/web3_sha3.json POST / Robinhood Chain API method that returns Keccak-256 (not the standardized SHA3-256) of the given data. data — the data to convert into a SHA3 hash. Robinhood Chain API method that returns Keccak-256 (not the standardized SHA3-256) of the given data. ## Parameters * `data` — the data to convert into a SHA3 hash ## Response * `data` — the SHA3 result of the given string ## `web3_sha3` code examples ```javascript ethers.js theme={"system"} const { JsonRpcProvider } = require("ethers"); const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL"); async function call() { const result = await provider.send("web3_sha3", ["0x68656c6c6f"]); console.log(result); } call(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("web3_sha3", ["0x68656c6c6f"]) print(result) ``` ```shell cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x68656c6c6f"], "id": 1 }' ``` # eth_blockNumber | Ronin Source: https://docs.chainstack.com/reference/ronin-blocknumber openapi/ronin_node_api/eth_blockNumber.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns the latest block number of the blockchain. Ronin developers can call eth_blockNumber via Chainstack. Ronin API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. For example, the block number `0x69B5B` means that the block is the 432,987th block in the blockchain. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number is synced to, encoded as hexadecimal. The block number is used to identify the block's position in the blockchain and is updated every time a new block is added to the chain. ## Use case A possible use case for the `eth_blockNumber` method in Ronin is for applications that need to be aware of the current block number—some applications may need to be mindful of the current block number to function correctly. For example, a DApp may use the current block number to determine the expiration date of a time-limited offer. # eth_call | Ronin Source: https://docs.chainstack.com/reference/ronin-call openapi/ronin_node_api/eth_call.json POST /3997273fc956a67dc6982384500e669e The interactive example here is a call to the Ronin Staking Vesting contract. Specifically, to the lastBlockSendingBonus function. The interactive example here is a call to the Ronin Staking Vesting contract. Specifically, to the `lastBlockSendingBonus` function. The data `0x0d9160e7` in the call is the signature for the function `lastBlockSendingBonus`. You can generate the function signature using Chainstack's [Web3Tools](https://web3tools.chainstacklabs.com/generate-solidity-functions-signature) by typing `lastBlockSendingBonus()` into the function name field. Ronin API method `eth_call` allows for executing a message call transaction, which directly interacts with a smart contract method without creating a transaction on the blockchain. This is particularly useful for read-only operations that don't require a transaction to be committed to a block. ## Parameters * `object` - The transaction call object: * `to`: The address of the contract to call. * `data`: The data to send to the contract. This is usually the method signature and encoded parameters. * `string` - The block number or string (`latest`, `earliest`, `pending`) specifying the state to use for executing the call. ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `result` - The return value of the executed contract method, encoded in data. This could be a direct value or a set of values depending on the contract's method called. ## Use case The `eth_call` method is essential for interacting with smart contracts when you need to query their state or execute their read-only methods. For example, getting the balance of a token for a specific address, checking the status of a decentralized application, or any other operation that doesn't change the state of the blockchain. # web3_clientVersion | Ronin Source: https://docs.chainstack.com/reference/ronin-clientversion openapi/ronin_node_api/web3_clientVersion.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns the client version of the Ronin node. The client version includes details about the specific build of the node software. Ronin API method that returns the client version of the Ronin node. The client version includes details about the specific build of the node software, including the client name, version, operating system, and commit hashes. ## Parameters * `none` ## Response * `result` — a string containing the client version information. This information is useful for debugging purposes, to ensure compatibility between different versions of the client software, or simply to identify the software running on a node. ## Use case The `web3_clientVersion` method is particularly useful for developers and system administrators who need to verify the version of the Ronin node they are interacting with. This can be crucial for debugging issues, ensuring compatibility between different software versions, or simply for record-keeping purposes. # eth_estimateGas | Ronin Source: https://docs.chainstack.com/reference/ronin-estimategas openapi/ronin_node_api/eth_estimateGas.json POST /3997273fc956a67dc6982384500e669e The eth_estimateGas method calculates the amount of gas required to execute a transaction. eth_estimateGas on Ronin via Chainstack. The `eth_estimateGas` method calculates the amount of gas required to execute a transaction. This estimation is done without sending the transaction to the network. It's important to note that the actual gas used by the transaction may differ from the estimate due to the dynamic nature of Ronin transactions and block inclusion. ## Parameters * `transaction`: An object containing transaction fields, including: * `from` (optional): The address the transaction is sent from. * `to`: The address the transaction is directed to. * Additional fields such as `value`, `data`, and `gasPrice` can also be included for a more accurate estimate. * `blockParameter` (optional): A string representing the block number (in hexadecimal) or one of the strings "latest", "earliest", or "pending". This parameter determines the state against which the transaction is estimated. ## Response * `result`: The estimated amount of gas required to execute the transaction, returned as a hexadecimal number. ## Use case Estimating gas is crucial for interacting with the Ronin network as it helps users and applications determine the appropriate gas limit to specify for a transaction. This prevents transactions from running out of gas (and thus failing) or from overestimating and locking unnecessary amounts of ether for gas. # eth_getBalance | Ronin Source: https://docs.chainstack.com/reference/ronin-getbalance openapi/ronin_node_api/eth_getBalance.json POST /3997273fc956a67dc6982384500e669e The eth_getBalance method returns the balance of the account of a given address, expressed in wei. Use it on Ronin via Chainstack. The `eth_getBalance` method returns the balance of the account of a given address, expressed in wei. This method provides a simple way to query the amount of Ether held by an address at a specific block height. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`: The address to get the balance from. * `blockParameter` (optional): This can be a hexadecimal block number, or the strings "latest", "earliest", or "pending", indicating the state from which to get the balance. ## Response * `result`: The balance of the account at the given address, returned as a hexadecimal number. ## Use case This method is commonly used to check the balance of an address. It's useful for wallets, explorers, and other applications that need to display the current or historical balance of a Ronin account. # eth_getBlockByHash | Ronin Source: https://docs.chainstack.com/reference/ronin-getblockbyhash openapi/ronin_node_api/eth_getBlockByHash.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns information about a specific block in the blockchain by its hash. Available on Ronin via Chainstack. Ronin API method that returns information about a specific block in the blockchain by its hash. The block hash is a unique identifier for each block, represented as a hexadecimal string. ## Parameters * `blockHash` — the hash of the block. * `fullTransactionObjects` — if set to `true`, returns the full transaction objects; if `false`, only the hashes of the transactions. ## Response * `result` — an object containing information about the block, such as the block number (`number`), the hash of the block (`hash`), the hash of the parent block (`parentHash`), and, depending on the `fullTransactionObjects` parameter, either a list of transaction hashes or a list of transaction objects. ## Use case The `eth_getBlockByHash` method is useful for applications that need detailed information about specific blocks in the blockchain. For example, a blockchain explorer might use this method to display detailed information about a block, including its transactions, when a user searches for a block by its hash. # eth_getBlockByNumber | Ronin Source: https://docs.chainstack.com/reference/ronin-getblockbynumber openapi/ronin_node_api/eth_getBlockByNumber.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns information about a specific block in the blockchain by its number. Available on Ronin via Chainstack. Ronin API method that returns information about a specific block in the blockchain by its number. The block number is a sequential identifier for each block in the blockchain. ## Parameters * `blockNumber` — the number of the block. It can be a hexadecimal value or a predefined tag such as `latest`, `earliest`, or `pending`. * `fullTransactionObjects` — if set to `true`, returns the full transaction objects; if `false`, only the hashes of the transactions. ## Response * `result` — an object containing information about the block, such as the block number (`number`), the hash of the block (`hash`), the hash of the parent block (`parentHash`), and, depending on the `fullTransactionObjects` parameter, either a list of transaction hashes or a list of transaction objects. ## Use case The `eth_getBlockByNumber` method is useful for applications that need to retrieve information about blocks at specific points in the blockchain. For instance, a financial application might use this method to analyze transactions within a particular block to audit or reconcile financial records. # eth_getBlockTransactionCountByHash | Ronin Source: https://docs.chainstack.com/reference/ronin-getblocktransactioncountbyhash openapi/ronin_node_api/eth_getBlockTransactionCountByHash.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns the number of transactions in a block from a block matching the given block hash. Ronin via Chainstack. Ronin API method that returns the number of transactions in a block from a block matching the given block hash. This is useful for determining the volume of transactions within a specific block. ## Parameters * `blockHash` — the hash of the block for which to retrieve the transaction count. ## Response * `result` — the number of transactions in the specified block, encoded as a hexadecimal. ## Use case The `eth_getBlockTransactionCountByHash` method can be particularly useful for analytics and monitoring services that need to track the transaction throughput or density of blocks within the Ronin blockchain. It allows these services to identify blocks that contain a high or low number of transactions, which can be indicative of network congestion or usage patterns. # eth_getBlockTransactionCountByNumber | Ronin Source: https://docs.chainstack.com/reference/ronin-getblocktransactioncountbynumber openapi/ronin_node_api/eth_getBlockTransactionCountByNumber.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns the number of transactions in a block matching the given block number. Use it on Ronin via Chainstack. Ronin API method that returns the number of transactions in a block matching the given block number. This method allows users to query the transaction count for blocks identified by their height in the blockchain. ## Parameters * `blockNumber` — the number of the block for which to retrieve the transaction count. It can be specified as a hexadecimal value or as one of the special strings `"earliest"`, `"latest"`, or `"pending"`. ## Response * `result` — the number of transactions in the specified block, encoded as a hexadecimal. ## Use case The `eth_getBlockTransactionCountByNumber` method is useful for applications and services that analyze the Ronin blockchain. It can help in understanding the transaction throughput and identifying blocks with unusually high or low numbers of transactions, which could indicate significant events or changes in network activity. # eth_chainId | Ronin Source: https://docs.chainstack.com/reference/ronin-getchainid openapi/ronin_node_api/eth_chainId.json POST /3997273fc956a67dc6982384500e669e The eth_chainId method is a simple RPC call that returns the chain ID of the currently connected Ronin network. Ronin via Chainstack. The `eth_chainId` method is a simple RPC call that returns the chain ID of the currently connected Ronin network. The chain ID is a critical value used in the EIP-155 standard to prevent replay attacks across different Ethereum networks. ## Parameters * None ## Response * `result` - The chain ID of the current network, returned as a hexadecimal number. ## Use case Knowing the chain ID is essential for developers when signing transactions, as it ensures that signed transactions are only valid on the intended network. This method provides a straightforward way to programmatically determine the network's chain ID, which is especially useful in applications that operate across multiple Ronin networks. # eth_getCode | Ronin Source: https://docs.chainstack.com/reference/ronin-getcode openapi/ronin_node_api/eth_getCode.json POST /3997273fc956a67dc6982384500e669e The eth_getCode method returns the bytecode stored at a given address. eth_getCode JSON-RPC method available on the Ronin blockchain via Chainstack. The `eth_getCode` method returns the bytecode stored at a given address. This method is typically used to retrieve the bytecode of smart contracts deployed on the Ronin network. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`: The address to get the code from. * `blockParameter` (optional): This can be a hexadecimal block number, or the strings "latest", "earliest", or "pending", indicating the state from which to get the code. ## Response * `result`: The code at the given address, returned as a hexadecimal string. ## Use case This method is essential for developers and tools that need to interact with or verify the bytecode of deployed contracts. It can be used for auditing, verifying contract deployments, or simply exploring the code of existing contracts on the blockchain. # eth_getFilterChanges | Ronin Source: https://docs.chainstack.com/reference/ronin-getfilterchanges openapi/ronin_node_api/eth_getFilterChanges.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns an array of log objects for all logs that have occurred since the last poll on the Ronin node. On Ronin. Ronin API method that returns an array of log objects for all logs that have occurred since the last poll on the Ronin node. This method is used to retrieve event logs for a specific filter, which can be particularly useful for dApps that need to react to specific contract events. ## Parameters * `filter_id` — a single filter identifier as a hexadecimal string. This filter ID is returned by `eth_newFilter` or `eth_newBlockFilter` and is used to poll for changes to the filter. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/ronin-newfilter) > * [eth\_newPendingTransactionFilter](/reference/ronin-getfilterchanges) > * [eth\_newBlockFilter](/reference/ronin-newfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Response * `result` — an array of log objects. Each log object contains details about the event, including whether the log was removed (due to a chain reorganization), log index, transaction index, transaction hash, block hash, block number, address that generated the log, data associated with the log, and topics (event signatures and indexed event parameters). ## Use case The `eth_getFilterChanges` method is essential for decentralized applications that need to monitor specific events on the blockchain. For example, a dApp might use this method to listen for token transfer events or contract interactions that trigger specific functionalities within the application. # eth_gasPrice | Ronin Source: https://docs.chainstack.com/reference/ronin-getgasprice openapi/ronin_node_api/eth_gasPrice.json POST /3997273fc956a67dc6982384500e669e The eth_gasPrice method returns the current gas price on the Ronin network, expressed in wei. eth_gasPrice on Ronin via Chainstack. The `eth_gasPrice` method returns the current gas price on the Ronin network, expressed in wei. This value is determined by the recent costs of transactions and is used to calculate the transaction fee (gas used \* gas price). ## Parameters * None ## Response * `result`: The current gas price in wei, returned as a hexadecimal number. ## Use case Understanding the current gas price is essential for users and applications to set appropriate gas limits for transactions, ensuring they are executed efficiently while optimizing for cost. This method provides a quick way to fetch the current market rate for transaction processing on the Ronin network. # eth_getLogs | Ronin Source: https://docs.chainstack.com/reference/ronin-getlogs openapi/ronin_node_api/eth_getLogs.json POST /3997273fc956a67dc6982384500e669e The interactive example is tracking transfers for the USDC contract on the Ronin mainnet. Available on Ronin via Chainstack JSON-RPC nodes. The interactive example is tracking transfers for the [USDC contract](https://app.roninchain.com/address/0x0b7007c13325c48911f73a2dad5fa5dcbf808adc) on the Ronin mainnet. The `eth_getLogs` method returns an array of logs matching a specified filter object. This is a powerful feature for fetching historical data, such as transactions or events emitted by smart contracts. > [!WARNING] > > ### Saigon testnet block range sharding > > After the Saigon testnet L1 to L2 migration on February 5, 2026 (block 45,528,550), Chainstack nodes serve two separate backends: > > * Blocks 0–45,528,549 — old L1 backend (chain ID `2021`) > * Blocks 45,528,550 onward — new L2 backend (chain ID `202601`) > > There is no block overlap. Your `eth_getLogs` calls with `fromBlock` and `toBlock` parameters **must not span block 45,528,550**. Query either the L1 range or the L2 range in a single request—requests that cross the boundary will return incomplete results. > > For example, to query the L1 range, set `toBlock` to `0x2B6B5E5` (45,528,549) or earlier. To query the L2 range, set `fromBlock` to `0x2B6B5E6` (45,528,550) or later. ## Parameters * `filter object`: An object with the following fields: * `fromBlock` (optional): The block number from which to start fetching logs. In the example, `latest`. * `address`: The contract address to fetch the logs from. Example: `0x0b7007c13325c48911f73a2dad5fa5dcbf808adc`. * `topics` (optional): An array of topics used to filter the logs. Example: `["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]`. ## Response * `result`: An array of log objects that match the filter criteria. ## Use case This method is essential for applications that need to retrieve and process historical events emitted by smart contracts, such as token transfers, contract interactions, and other event-driven data. # eth_maxPriorityFeePerGas | Ronin Source: https://docs.chainstack.com/reference/ronin-getmaxpriorityfeepergas openapi/ronin_node_api/eth_maxPriorityFeePerGas.json POST /3997273fc956a67dc6982384500e669e The eth_maxPriorityFeePerGas method returns the current maximum priority fee per gas in wei. eth_maxPriorityFeePerGas on Ronin via Chainstack. The `eth_maxPriorityFeePerGas` method returns the current maximum priority fee per gas in wei. This fee, also known as a "tip," is an additional amount paid directly to miners to incentivize them to include a transaction in a block. This is part of the EIP-1559 transaction pricing mechanism. ## Parameters * None ## Response * `result`: The current maximum priority fee per gas, returned as a hexadecimal number. ## Use case This method is crucial for users and applications to dynamically adjust transaction fees to ensure timely processing, especially during periods of high network congestion. By specifying an appropriate max priority fee, transactions can be prioritized by miners without overpaying. # eth_getStorageAt | Ronin Source: https://docs.chainstack.com/reference/ronin-getstorageat openapi/ronin_node_api/eth_getStorageAt.json POST /3997273fc956a67dc6982384500e669e This example makes a call to slot 0 of the USDC contract on the Ronin Mainnet. Reference for eth_getStorageAt on Ronin via Chainstack. This example makes a call to slot 0 of the [USDC contract](https://app.roninchain.com/address/0x0b7007c13325c48911f73a2dad5fa5dcbf808adc) on the Ronin Mainnet. The `eth_getStorageAt` method returns the value stored at a specific position of the storage of a given address, expressed in hexadecimal. This method is useful for inspecting the state of smart contracts at specific storage slots. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`: The address to get the storage from. For example, `0x0b7007c13325c48911f73a2dad5fa5dcbf808adc`. * `position`: The storage position to inspect, given as a hexadecimal value. For example, `0x0` for the first position. * `blockParameter` (optional): This can be a hexadecimal block number, or the strings "latest", "earliest", or "pending", indicating the state from which to get the storage value. The default is `latest`. ## Response * `result`: The value at the specified storage position, returned as a hexadecimal string. ## Use case This method is particularly useful for developers and auditors who need to verify the state of a smart contract's storage directly on the blockchain. It allows for detailed inspection of contract state and can be used to debug or verify contract behavior. # eth_getTransactionByBlockHashAndIndex | Ronin Source: https://docs.chainstack.com/reference/ronin-gettransactionbyblockhashandindex openapi/ronin_node_api/eth_getTransactionByBlockHashAndIndex.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns information about a transaction by specifying the block hash and the transaction's index position within the block. Ronin API method that returns information about a transaction by specifying the block hash and the transaction's index position within the block. This method allows for precise queries about specific transactions contained in a block. ## Parameters * `blockHash` — the hash of the block. * `transactionIndex` — the index position of the transaction in the block, encoded as a hexadecimal. ## Response * `result` — an object containing detailed information about the transaction, including fields like `from`, `to`, `value`, `gas`, `gasPrice`, `nonce`, `hash`, and more. ## Use case The `eth_getTransactionByBlockHashAndIndex` method is useful for applications that need to retrieve specific transactions from a block, such as blockchain explorers, wallets, or auditing tools. It provides a direct way to access transaction details without needing to retrieve all transactions in a block. # eth_getTransactionByBlockNumberAndIndex | Ronin Source: https://docs.chainstack.com/reference/ronin-gettransactionbyblocknumberandindex openapi/ronin_node_api/eth_getTransactionByBlockNumberAndIndex.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns information about a transaction by specifying the block number (or tag) and the transaction's index position within the block. Ronin API method that returns information about a transaction by specifying the block number (or tag) and the transaction's index position within the block. This method facilitates accessing details of specific transactions within a given block. ## Parameters * `blockNumber` — the number of the block (or a tag such as "latest", "earliest", "pending") encoded as a hexadecimal. * `transactionIndex` — the index position of the transaction in the block, encoded as a hexadecimal. ## Response * `result` — an object containing detailed information about the transaction, including fields like `from`, `to`, `value`, `gas`, `gasPrice`, `nonce`, `hash`, and more. ## Use case The `eth_getTransactionByBlockNumberAndIndex` method is particularly useful for applications requiring information on specific transactions within a block, such as for transaction tracking, auditing, or displaying detailed transaction information in blockchain explorers. # eth_getTransactionByHash | Ronin Source: https://docs.chainstack.com/reference/ronin-gettransactionbyhash openapi/ronin_node_api/eth_getTransactionByHash.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns information about a transaction by its hash. This includes details such as the transaction's sender and receiver. Ronin API method that returns information about a transaction by its hash. This includes details such as the transaction's sender and receiver, the amount of Ether transferred, and the transaction's current state (e.g., pending, included in a block). ## Parameters * `transactionHash` — the hash of the transaction to retrieve. ## Response * `result` — an object containing detailed information about the transaction, including fields like `from`, `to`, `value`, `gas`, `gasPrice`, `nonce`, `blockHash`, `blockNumber`, and `transactionIndex`. ## Use case The `eth_getTransactionByHash` method is essential for tracking specific transactions on the Ronin blockchain. It can be used by wallets to show transaction details, by exchanges to track deposits and withdrawals, or by any application that needs to verify the status and details of a transaction. # eth_getTransactionCount | Ronin Source: https://docs.chainstack.com/reference/ronin-gettransactioncount openapi/ronin_node_api/eth_getTransactionCount.json POST /3997273fc956a67dc6982384500e669e The eth_getTransactionCount method returns the number of transactions sent from a specified address. Use it on Ronin via Chainstack. The `eth_getTransactionCount` method returns the number of transactions sent from a specified address. This count includes the transaction index position in the block. > [!NOTE] > > When called against a block older than the latest \~128 blocks, this method is treated as an archive request (2 RUs instead of 1 RU). See [request units](/docs/request-units#evm-methods-affected-by-block-age). ## Parameters * `address`: The address to get the number of transactions from. * `blockParameter` (optional): This can be a hexadecimal block number, or the strings "latest", "earliest", or "pending", indicating the state from which to count the transactions. ## Response * `result`: The number of transactions sent from the address, returned as a hexadecimal number. ## Use case This method is useful for determining the nonce for the next transaction to be sent from the specified address. The nonce ensures transactions are processed in order and prevents double-spending and replay attacks. # eth_getTransactionReceipt | Ronin Source: https://docs.chainstack.com/reference/ronin-gettransactionreceipt openapi/ronin_node_api/eth_getTransactionReceipt.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns the receipt of a transaction by its hash. Reference for eth_getTransactionReceipt on Ronin via Chainstack. Ronin API method that returns the receipt of a transaction by its hash. The transaction receipt includes details such as the status of the transaction (success or failure), the gas used, the contract address created (if the transaction was a contract creation), and any logs generated by the transaction. ## Parameters * `transactionHash` — the hash of the transaction for which to retrieve the receipt. ## Response * `result` — an object containing the transaction receipt information, including fields like `transactionHash`, `transactionIndex`, `blockHash`, `blockNumber`, `from`, `to`, `cumulativeGasUsed`, `gasUsed`, `contractAddress`, `logs`, and `status`. ## Use case The `eth_getTransactionReceipt` method is crucial for verifying the outcome of a transaction, especially for applications that execute transactions and need to confirm their success or failure. It is also used to obtain detailed execution results, such as event logs, which are essential for DApps to interpret the effects of transactions. # net_listening | Ronin Source: https://docs.chainstack.com/reference/ronin-netlistening openapi/ronin_node_api/net_listening.json POST /3997273fc956a67dc6982384500e669e Ronin API method that checks if the Ronin node is actively listening for network connections. net_listening on Ronin via Chainstack. Ronin API method that checks if the Ronin node is actively listening for network connections. This method is crucial for network diagnostics and ensuring that the node is correctly configured to participate in the network. ## Parameters * `none` ## Response * `result` — a boolean value indicating whether the node is listening (`true`) or not (`true`). This can help in diagnosing network issues or confirming that the node is correctly set up to receive incoming connections. ## Use case The `net_listening` method is essential for developers and network administrators to ensure that their Ronin node is properly connected to the network and capable of receiving incoming connections. This can be particularly useful after setting up a new node, changing network configurations, or troubleshooting network connectivity issues. # net_peerCount | Ronin Source: https://docs.chainstack.com/reference/ronin-netpeercount openapi/ronin_node_api/net_peerCount.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns the number of peers currently connected to the Ronin node. Available on Ronin via Chainstack JSON-RPC nodes. Ronin API method that returns the number of peers currently connected to the Ronin node. This metric is essential for understanding the node's connectivity and participation in the network. ## Parameters * `none` ## Response * `result` — the number of connected peers, encoded as a hexadecimal value. This number reflects how well the node is connected to the rest of the network, which can impact its ability to receive and broadcast transactions and blocks. ## Use case The `net_peerCount` method is crucial for network administrators and developers who need to monitor their node's health and connectivity. A higher number of peers indicates good network health and connectivity, while a low number of peers might suggest network issues or isolation from the network. # eth_newBlockFilter | Ronin Source: https://docs.chainstack.com/reference/ronin-newblockfilter openapi/ronin_node_api/eth_newBlockFilter.json POST /3997273fc956a67dc6982384500e669e Ronin API method that creates a new filter in the node. This filter allows applications to receive notifications when new blocks are added to the blockchain. Ronin API method that creates a new filter in the node. This filter allows applications to receive notifications when new blocks are added to the blockchain. Once the filter is created, the application can periodically use `eth_getFilterChanges` to get an array of block hashes of the latest blocks. ## Parameters * `none` — This method does not require any parameters. ## Response * `result` — the filter identifier (`filterId`) as a hexadecimal string. This ID is used to query the filter changes or to uninstall the filter. ## Use case The `eth_newBlockFilter` method is particularly useful for applications that need to stay synchronized with the latest state of the blockchain, such as wallets, block explorers, or decentralized applications (DApps) that react to new blocks. It provides a more efficient way to monitor the blockchain for new blocks compared to polling the latest block number. # eth_newFilter | Ronin Source: https://docs.chainstack.com/reference/ronin-newfilter openapi/ronin_node_api/eth_newFilter.json POST /3997273fc956a67dc6982384500e669e Ronin API method eth_newFilter creates a new filter object for filtering logs that match specific criteria. Chainstack Ronin reference. Ronin API method `eth_newFilter` creates a new filter object for filtering logs that match specific criteria. This is particularly useful for applications that need to listen for specific events on the Ronin blockchain. ## Parameters * `fromBlock` (optional): The block number from which to start filtering. This can be a hexadecimal number or one of the strings "earliest", "latest" or "pending". In the provided example, "latest" is used. * `address` (optional): The address of the contract to filter logs from. This can be a single address as a string or an array of addresses. The example uses "0x0b7007c13325c48911f73a2dad5fa5dcbf808adc". * `topics` (optional): An array of topics used to filter logs. Each event signature generated by the contract will have a unique topic. The example filters logs with the topic "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef". ## Response * `result` — the ID of the created filter. This ID can then be used with methods like `eth_getFilterChanges` or `eth_getFilterLogs` to retrieve the logs that match the filter. > [!WARNING] > > The filters created are stored on the blockchain client instance. The filter is automatically deleted if not polled within a certain time (5 minutes by default). ## Use case Creating a filter is essential for dApps and services that need to monitor blockchain events efficiently. Instead of retrieving all events and filtering them client-side, `eth_newFilter` allows for specifying precise criteria, reducing the amount of data to process and improving responsiveness. # eth_getFilterChanges | Ronin Source: https://docs.chainstack.com/reference/ronin-newpendingtransactionfilter openapi/ronin_node_api/eth_getFilterChanges.json POST /3997273fc956a67dc6982384500e669e Ronin API method that returns an array of log objects for all logs that have occurred since the last poll on. eth_getFilterChanges on Ronin via Chainstack. Ronin API method that returns an array of log objects for all logs that have occurred since the last poll on the Ronin node. This method is used to retrieve event logs for a specific filter, which can be particularly useful for dApps that need to react to specific contract events. ## Parameters * `filter_id` — a single filter identifier as a hexadecimal string. This filter ID is returned by `eth_newFilter` or `eth_newBlockFilter` and is used to poll for changes to the filter. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_getFilterChanges` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/ronin-newfilter) > * [eth\_newPendingTransactionFilter](/reference/ronin-getfilterchanges) > * [eth\_newBlockFilter](/reference/ronin-newfilter) > > Then use the fresh filter ID as the parameter for `eth_getFilterChanges`. ## Response * `result` — an array of log objects. Each log object contains details about the event, including whether the log was removed (due to a chain reorganization), log index, transaction index, transaction hash, block hash, block number, address that generated the log, data associated with the log, and topics (event signatures and indexed event parameters). ## Use case The `eth_getFilterChanges` method is essential for decentralized applications that need to monitor specific events on the blockchain. For example, a dApp might use this method to listen for token transfer events or contract interactions that trigger specific functionalities within the application. # eth_sendRawTransaction | Ronin Source: https://docs.chainstack.com/reference/ronin-sendrawtransaction openapi/ronin_node_api/eth_sendRawTransaction.json POST /3997273fc956a67dc6982384500e669e Ronin API method eth_sendRawTransaction is used to submit a pre-signed transaction to the network. Use it on Ronin via Chainstack. Ronin API method `eth_sendRawTransaction` is used to submit a pre-signed transaction to the network. This is typically used for transactions that have been signed offline or in a secure environment, allowing for the transaction to be broadcast without exposing private keys online. > [!WARNING] > > Note that the interactive example in this page will not work, due to the fact that `eth_sendRawTransaction` is used to modify the state of the blockchain, it is not possible to duplicate the same request. ## Parameters * `string` - The full, hex-encoded data of the signed transaction. ## Response * `result` - The transaction hash of the successfully submitted transaction. ## Use case The `eth_sendRawTransaction` method is crucial for securely interacting with the Ronin network, especially in scenarios where an extra layer of security is required. It's commonly used in wallet applications, automated trading systems, and other applications where transactions need to be prepared and signed before being broadcast to the network. # eth_subscribe ("logs") | Ronin Source: https://docs.chainstack.com/reference/ronin-subscribelogs Subscribe to real-time Ronin event logs via WebSocket. Receive notifications whenever new logs matching your specified filter criteria are emitted on-chain. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `logs` in this case. * `object` — (optional) the event filter options: * `address` — the contract address from which the logs should be fetched. It can be a single address or an array of addresses. * `topics` — an array of `DATA` topics. The event topics for which the logs should be fetched. It can be a single topic or an array of topics. ## Response * `subscription` — the subscription ID. * `array` — an array of log objects that match the specified filter: * `address` — the contract address from which the event originated. * `topics` — an array of 32-byte data fields containing indexed event parameters. * `data` — the non-indexed data that was emitted along with the event. * `blocknumber` — the block number in which the event was included. `null` if it is pending. * `transactionhash` — the hash of the transaction that triggered the event. `null` if pending. * `transactionindex` — the integer index of the transaction within the block's list of transactions. `null` if it is pending. * `blockhash` — the hash of the block in which the event was included. `null` if it is pending. * `logindex` — the integer identifying the index of the event within the block's list of events. `null` if pending. * `removed` — the boolean value indicating if the event was removed from the blockchain due to a chain reorganization. `True` if the log was removed. `False` if it is a valid log. ## `eth_subscribe("logs")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_subscribe", "params": ["logs", {"address":"0x0b7007c13325c48911f73a2dad5fa5dcbf808adc","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewLogs() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['logs', {"address":"0x0b7007c13325c48911f73a2dad5fa5dcbf808adc","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewLogs(); ``` This will generate a continuous stream of data displaying new event logs matching the filter. ## Use case The `eth_subscribe("logs")` method in Ronin is used to subscribe to the logs generated by smart contracts on the blockchain. These logs contain information about events that have occurred on the blockchain, such as details of a smart contract function and outputs of transactions. A practical and real-world scenario for using the `eth_subscribe("logs")` method could be in the context of a decentralized exchange (DEX). In the context of a DEX, trades are executed using smart contracts that run on the Ronin blockchain. When a trade is executed on a DEX, a smart contract is called to process the transaction. This smart contract generates a log that contains information about the trade, such as the amount of cryptocurrency being traded, the parties involved, and so on. By using the `eth_subscribe("logs")` method, a user or application can subscribe to the logs generated by the DEX's smart contracts. This can be useful for tracking trades in real time, monitoring the performance of the DEX, and generating reports on trading activity. For example, a trader might use the `eth_subscribe("logs")` method to monitor the logs generated by a DEX in order to identify trends in trading activity. They might look for patterns in the volume and price of trades, which could help them make more informed trading decisions. Similarly, an analytics platform might use the `eth_subscribe("logs")` method to collect data on trading activity across multiple DEXs. By analyzing this data, they could generate insights into the performance of different DEXs, which could be useful for investors and traders looking to make informed decisions about where to trade. # eth_subscribe ("newHeads") | Ronin Source: https://docs.chainstack.com/reference/ronin-subscribenewheads Subscribe to new Ronin block headers via WebSocket. Receive real-time notifications whenever a new block is added to the Ronin blockchain network. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `newHeads` in this case. ## Response * `subscription` — the subscription ID. * `object`— a block object with the following fields: * `number` — the block number of the requested block, encoded as hexadecimal. `null` if the block is pending. * `hash` — the block hash of the requested block. `null` if the block pending. * `parenthash` — hash of the previous block used to generate the current block. Also known as the 'parent block'. * `nonce` — the hash used to demonstrate proof-of-work. `null` if the block pending. It returns `0x0000000000000000` when the consensus is proof-of-stake. * `sha3uncles` — the hash of the list of uncles included in the block. It is used to identify the block uniquely and to verify the integrity of the block's data. * `logsbloom` — the bloom filter for the logs of the block, a data structure that allows for efficient membership testing of elements in a set, in this case, the logs included in the block. `null` if pending. * `transactionsroot` — the root of the transaction trie of the block. The `transactionsRoot` field allows Ronin nodes to verify the integrity of the transactions in a block. * `stateroot` — the root of the final state trie of the block. The `stateroot` field is included in the block header and is used to verify the integrity of the state at the time the block was processed * `receiptsroot` — the root of the receipts trie of the block. A 32-byte hash of the root node of the receipts trie of all transactions in the block. It is used to verify the integrity of the receipts data for all transactions in the block. * `miner` — the address of the miner receiving the reward. * `difficulty` — a measure of how hard it is to find a valid block for the Ronin blockchain. It is a number that increases as more miners join the network and more blocks are added to the chain, encoded as hexadecimal. * `totaldifficulty` — the cumulative sum of the difficulty of all blocks that have been mined in the Ronin network since the inception of the network. It measures the overall security and integrity of the Ronin network. * `extradata` — extra data included in a block by the miner who mined it. It often includes messages or other information related to the block. * `size` — the size of this block in bytes as an integer value, encoded as hexadecimal. * `gaslimit` — the maximum gas allowed in this block, encoded as hexadecimal. * `gasused` — the total used gas by all transactions in this block, encoded as hexadecimal. * `timestamp` — the Unix timestamp for when the block was collated. * `transactions` — an array of transaction objects. * `uncles` — an array of uncle hashes. ## `eth_subscribe("newHeads")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewBlocks() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewBlocks(); ``` This will generate a continuous stream of data displaying new block headers as they are added to the chain. ## Use case The `eth_subscribe("newHeads")` method can be useful in a variety of real-world scenarios, such as: * Real-time monitoring of blockchain activity. Companies or individuals who rely on the Ronin network for their business can use `eth_subscribe("newHeads")` to receive real-time notifications about new blocks being added to the chain. This can help them keep track of important transactions, such as high-value trades, in real time and respond quickly to any issues that may arise. * Decentralized Finance (DeFi) applications. DeFi applications, such as lending platforms, can use `eth_subscribe("newHeads")` to monitor the blockchain for new blocks that might contain important data, such as changes in interest rates or new lending opportunities. * Smart contract automation. Developers can use `eth_subscribe("newHeads")` to trigger automated smart contract actions whenever a new block is added to the chain. For example, a smart contract could automatically execute an action order whenever a new block is added that meets certain criteria. # eth_subscribe("newPendingTransactions") | Ronin Source: https://docs.chainstack.com/reference/ronin-subscribenewpendingtransactions Subscribe to pending Ronin transactions via WebSocket. Receive real-time notifications when new unconfirmed transactions enter the Ronin network mempool. ## Parameters * `string` — keyword identifying the type of event to subscribe to, `newPendingTransactions` in this case. ## Response * `subscription` — the subscription ID. * `object` — a transaction hash ## `eth_subscribe("newPendingTransactions")` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newPendingTransactions"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function subscribeToNewPendingTransactions() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newPendingTransactions', true], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } subscribeToNewPendingTransactions(); ``` This will generate a continuous stream of data displaying new pending transactions as they are added to the mempool. ## Use case The `eth_subscribe("newPendingTransactions")` method in Ronin is used to subscribe to the network and receive real-time updates on all new pending transactions on the network. This can be useful in a variety of real-world scenarios, including: * Trading applications. A trading application can use this method to keep track of any new transactions that are being made on the network. This can be particularly useful for high-frequency trading, where speed is crucial. By subscribing to this method, the trading application can receive real-time updates on new trades, allowing it to quickly respond and execute trades. * Monitoring tools. Monitoring tools can use this method to keep track of any suspicious activity on the network. By subscribing to this method, monitoring tools can receive real-time updates on any new pending transactions and quickly take action if necessary. * Payment processors. Payment processors can use the `eth_subscribe("newPendingTransactions")` method to monitor incoming transactions from their customers. This can be particularly useful for businesses that rely on blockchain technology to process payments, as it allows them to track incoming payments in real time. By subscribing to this method, payment processors can receive instant notifications when new payments are received, which can help streamline their payment processing workflows. # eth_syncing | Ronin Source: https://docs.chainstack.com/reference/ronin-syncing openapi/ronin_node_api/eth_syncing.json POST /3997273fc956a67dc6982384500e669e The eth_syncing method provides information about the node's current syncing status with the Ethereum network. Ronin via Chainstack. The `eth_syncing` method provides information about the node's current syncing status with the Ethereum network. If the node is not syncing, the method returns `false`. If the node is syncing, it returns an object containing syncing details. ## Parameters * None ## Response * If syncing: * `startingBlock`: The block number where syncing started. * `currentBlock`: The current block number where the node is synced to. * `highestBlock`: The estimated highest block number to sync to. * If not syncing, returns `false`. ## Use case This method is useful for monitoring the progress of a node's synchronization with the network, especially after the node has been offline or is catching up with the blockchain. It helps in determining whether the node is fully synced and ready to interact with the network without any data inconsistencies. # debug_traceBlockByHash | Ronin Source: https://docs.chainstack.com/reference/ronin-traceblockbyhash openapi/ronin_node_api/debug_traceBlockByHash.json POST /3997273fc956a67dc6982384500e669e The debug_traceBlockByHash method provides a way to trace the execution of all transactions in a specific block identified by its hash. The `debug_traceBlockByHash` method provides a way to trace the execution of all transactions in a specific block identified by its hash. This is particularly useful for developers and auditors who need to understand the exact behavior of smart contracts under specific conditions as recorded on the blockchain. ## Parameters * `blockHash` - The hash of the block to trace. ## Response * `result` - An object containing detailed execution traces for each transaction in the block, including executed operations, gas used, and state changes. ## Use case This method is invaluable for debugging smart contracts, analyzing transaction execution, and auditing contract interactions for security purposes. It provides deep insights into the EVM (Ethereum Virtual Machine) execution at a granular level. # debug_traceBlockByNumber | Ronin Source: https://docs.chainstack.com/reference/ronin-traceblockbynumber openapi/ronin_node_api/debug_traceBlockByNumber.json POST /3997273fc956a67dc6982384500e669e The debug_traceBlockByNumber method allows for tracing the execution of all transactions in a specified block. Ronin via Chainstack. The `debug_traceBlockByNumber` method allows for tracing the execution of all transactions in a specified block. This method is essential for understanding the behavior of transactions and smart contracts within a block, providing insights into executed operations, gas used, and state changes. ## Parameters * `blockNumber` - The number of the block to trace. This can be a hexadecimal value or one of the special strings "latest", "earliest", or "pending". ## Response * `result` - An object containing detailed execution traces for each transaction in the block. ## Use case This method is invaluable for debugging smart contracts, analyzing transaction execution, and conducting security audits of contract interactions. It offers a granular view of the EVM (Ethereum Virtual Machine) execution process. # debug_traceCall | Ronin Source: https://docs.chainstack.com/reference/ronin-tracecall openapi/ronin_node_api/debug_traceCall.json POST /3997273fc956a67dc6982384500e669e Ronin debug_traceCall executes a call on the node like eth_call and returns detailed execution traces for debugging contract interactions on Chainstack. The interactive example here is a call to the Ronin Staking Vesting contract. Specifically, to the `lastBlockSendingBonus` function. The data `0x0d9160e7` in the call is the signature for the function `lastBlockSendingBonus`. You can generate the function signature using Chainstack's [Web3Tools](https://web3tools.chainstacklabs.com/generate-solidity-functions-signature) by typing `lastBlockSendingBonus()` into the function name field. The `debug_traceCall` method allows for the execution of a call directly on the node, similar to `eth_call`, but with the added benefit of returning detailed execution traces. This method is invaluable for debugging contract interactions by providing insights into the execution flow, state changes, and gas usage without requiring a transaction to be committed to a block. ## Parameters * `object` - The transaction call object, which includes: * `to`: The address of the contract to call. * `data`: The data to send to the contract, typically including the method signature and encoded parameters. * `string` - The block number or string (`latest`, `earliest`, `pending`) specifying the state to use for executing the call. ## Response * `result` - An object containing the detailed execution trace of the call. This includes information on gas usage, the operations performed, and any state changes that occurred as a result of the call's execution. ## Use case The `debug_traceCall` method is particularly useful for developers during the contract development and debugging phase. It provides a powerful tool for analyzing how contract methods behave when invoked, identifying potential issues, and optimizing gas usage. # debug_traceTransaction | Ronin Source: https://docs.chainstack.com/reference/ronin-tracetransaction openapi/ronin_node_api/debug_traceTransaction.json POST /3997273fc956a67dc6982384500e669e The debug_traceTransaction method provides a detailed execution trace of a single transaction, identified by its hash. Ronin via Chainstack. The `debug_traceTransaction` method provides a detailed execution trace of a single transaction, identified by its hash. This trace includes step-by-step execution details, gas usage, and changes to the Ethereum Virtual Machine (EVM) state, offering deep insights into the transaction's execution process. ## Parameters * `transactionHash` - The hash of the transaction to trace. ## Response * `result` - An object containing the detailed execution trace of the transaction. This includes information such as the gas used at each step, the operations performed, and any state changes that occurred as a result of the transaction's execution. ## Use case This method is invaluable for developers and auditors who need to analyze the behavior of transactions, debug smart contracts, or verify the correctness and efficiency of transaction execution. It provides a granular view of how a transaction interacts with the Ronin network and smart contracts, making it an essential tool for advanced Ronin development and security analysis. # eth_uninstallFilter | Ronin Source: https://docs.chainstack.com/reference/ronin-uninstallfilter openapi/ronin_node_api/eth_uninstallFilter.json POST /3997273fc956a67dc6982384500e669e Ronin API method that allows for the removal of a filter previously installed on the Ronin node. Available on Ronin via Chainstack. Ronin API method that allows for the removal of a filter previously installed on the Ronin node. This method is crucial for resource management, ensuring that filters are only kept as long as they are needed and are cleaned up afterwards. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter](/reference/ronin-newfilter) > * [eth\_newPendingTransactionFilter](/reference/ronin-getfilterchanges) > * [eth\_newBlockFilter](/reference/ronin-newfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `filter_id` — a single filter identifier as a hexadecimal string. This is the ID of the filter that was returned by `eth_newFilter` or `eth_newBlockFilter` and is now being uninstalled. ## Response * `result` — a boolean value indicating the success of the uninstallation. `true` if the filter was successfully uninstalled, otherwise `false`. This allows clients to verify that resources have been freed up appropriately. ## Use case The `eth_uninstallFilter` method is used to manage the lifecycle of filters in decentralized applications. It is particularly useful in scenarios where an application dynamically creates filters to track specific events or logs and needs to clean them up after they are no longer necessary, helping to prevent resource leaks and maintain optimal performance. # eth_unsubscribe | Ronin Source: https://docs.chainstack.com/reference/ronin-unsubscribe Cancel any active Ronin WebSocket subscription with eth_unsubscribe. Pass the subscription ID to stop receiving real-time blockchain event notifications. When a client subscribes to a particular event, the Ronin node will send updates as soon as they occur. However, in some cases, the client may no longer be interested in receiving updates for a particular event. The `eth_unsubscribe` method can be used to cancel the following subscriptions: ## Parameters * `string` — the ID of the subscription that you want to cancel ## Response * `boolean` — the boolean value indicating if the subscriptions were removed successfully. `true` if removed successfully, `false` if not. ## `eth_unsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id": 1, "method": "eth_unsubscribe", "params": ["0x7529626735859f32962b10b19ee2e3eb"]} ``` ```javascript javascript theme={"system"} const WebSocket = require('ws'); const webSocket = new WebSocket('CHAINSTACK_WSS_URL'); async function removeSubscription() { const request = { id: 1, jsonrpc: '2.0', method: 'eth_unsubscribe', params: ['0x7529626735859f32962b10b19ee2e3eb'], }; const onOpen = (event) => { webSocket.send(JSON.stringify(request)); }; const onMessage = (event) => { const response = JSON.parse(event.data); console.log(response); }; try { webSocket.addEventListener('open', onOpen); webSocket.addEventListener('message', onMessage); } catch (error) { console.error(error); } } removeSubscription(); ``` This will remove the subscription matching the ID passed as a parameter. # rootSubscribe | Solana Source: https://docs.chainstack.com/reference/rootsubscribe-solana Subscribe to Solana ledger root changes with rootSubscribe. Receive real-time WebSocket notifications whenever the validator confirms a new root slot. ## Parameters * `` - (optional) The level of commitment desired when querying state. Default is "finalized". ## Response * `result` - An integer subscription id (needed to unsubscribe) * `subscription` - An object containing root information: * `root` - The slot number that has been rooted * `timestamp` - The estimated production time of the rooted slot, as Unix timestamp (seconds since the Unix epoch) ## `rootSubscribe` code examples This example subscribes to root updates. > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > { "jsonrpc": "2.0", "id": 1, "method": "rootSubscribe" } ``` ## Use case The `rootSubscribe` method is particularly useful for applications that need to monitor the finality of the Solana blockchain. Common use cases include: * Finality tracking: Monitor when slots become rooted (finalized) in the blockchain * Chain reorganization detection: Track changes in the root to detect potential chain reorganizations * Historical data processing: Know when slots are safe to process for historical data analysis * Consensus monitoring: Track the progress and health of consensus by monitoring root advancement By subscribing to root updates, applications can maintain accurate information about the finalized state of the blockchain and make decisions based on confirmed data. # rootUnsubscribe | Solana Source: https://docs.chainstack.com/reference/rootunsubscribe-solana Cancel a Solana root subscription with rootUnsubscribe. Stop receiving real-time WebSocket notifications for confirmed root slot updates on the network. See also [rootSubscribe | Solana](/reference/rootsubscribe-solana). ## Parameters * `integer` - The ID of the subscription that you want to cancel. This is the subscription ID that was returned when calling `rootSubscribe`. ## Response * `boolean` - A boolean value indicating if the subscription was removed successfully. `true` if removed successfully, `false` if not. ## `rootUnsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection. You can use a WebSocket client library for your programming language of choice to interact with Solana's WebSocket API. ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"rootUnsubscribe","params":[0]} ``` # eth_sendRawTransaction | Polygon Source: https://docs.chainstack.com/reference/sendrawtransaction openapi/polygon_node_api/execute_transactions/eth_sendRawTransaction.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that allows submitting a signed transaction to the network. Available on Polygon via Chainstack JSON-RPC nodes. Polygon API method that allows submitting a signed transaction to the network. Once a transaction is signed, you can use the `eth_sendRawTransaction` method to submit the signed transaction to the Polygon network for processing. > [!WARNING] > > Note that the interactive example in this page will not work, due to the fact that `eth_sendRawTransaction` is used to modify the state of the blockchain, it is not possible to duplicate the same request. ## Parameters * `data` — the signed transaction. The serialized transaction data, which includes the transaction metadata such as the nonce, gas price, gas limit, recipient address, and data payload, as well as the digital signature generated using the private key associated with the address that is sending the transaction. Typically, signed with a library such as web3.py, web3.js, or ethers.js using the sender's private key. ## Response * `result` — the unique hash identifying the transaction or the zero hash if the transaction is not available yet. The zero hash is a special value that represents an invalid or null hash. ## `eth_sendRawTransaction` code examples The following examples demonstrate how to use Web3 libraries to make a MATIC transfer on the Mumbai testnet. ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function sendEth(value) { // Define the sender and receiver addresses, and the private key const sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a'; const receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114'; const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Define the gas limit const gasLimit = await provider.estimateGas({ from: sender, to: receiver, value: ethers.parseEther(value), }); // Get the transaction count for the sender address const nonce = await provider.getTransactionCount(sender); // Define the transaction object const transactionObject = { to: receiver, gasPrice: ethers.parseUnits('50', 'gwei'), gasLimit: gasLimit.toString(), nonce: nonce, value: ethers.parseEther(value), }; // Sign the transaction using the private key const wallet = new ethers.Wallet(privateKey, provider); console.log(`Sending transaction...`) const transaction = await wallet.sendTransaction(transactionObject); console.log('Transaction hash:', transaction.hash); } sendEth("2"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" # Define the sender and receiver addresses, and the private key sender = '0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a' receiver = '0xe9ba4b4d84d7a3c80245514213b88d50ed937114' private_key = '1111111111111111111111111111111111111111111111111111111111111111' # Estimate gas limit gas_limit = web3.eth.estimate_gas({ 'from': sender, 'to': receiver }) # Build the transaction object transaction = { 'nonce': web3.eth.get_transaction_count(sender), 'to': receiver, 'value': web3.to_wei(1, 'ether'), # value to send 'gas': gas_limit, 'gasPrice': web3.eth.gas_price, 'chainId': web3.eth.chain_id } # Sign the transaction signed_tx = web3.eth.account.sign_transaction(transaction, private_key) print(f'Signed transaction: {signed_tx}') # Send the transaction print('Sending transaction...') tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) print(f'Transaction hash: {web3.to_hex(tx_hash)}') ``` > [!NOTE] > > Note that you will need to edit the `sender`, `receiver`, and `privateKey` fields to be able to use this code. ## Use case One potential use case for `eth_sendRawTransaction` is to automatically swap MATIC for a token when its price reaches a certain level. `eth_sendRawTransaction` will allow specifying custom transaction parameters, such as gas price or gas limit, to optimize the speed and cost of the transaction. For instance, you could build a program monitoring the price of a specific token and execute a `swapExactETHForTokens` transaction when the token reaches a certain price level, it could use `eth_sendRawTransaction` to submit a raw transaction with a custom gas price and limit that are optimized for the current network conditions. The following code shows how to call the `swapExactETHForTokens` function from the SushiSwap exchange on Polygon using ethers.js: > [!NOTE] > > Note that this code only shows how to execute the `swapExactETHForTokens` function on [SushiSwap on Polygon mainnet](https://polygonscan.com/address/0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506#code), and not how to track the prices. You need to build a separate part for this. This particular example shows how to swap MATIC for AAVE tokens. ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); // ABI for the swapExactETHForTokens function const sushiRouterABI = [ { "inputs":[ { "internalType":"uint256", "name":"amountOutMin", "type":"uint256" }, { "internalType":"address[]", "name":"path", "type":"address[]" }, { "internalType":"address", "name":"to", "type":"address" }, { "internalType":"uint256", "name":"deadline", "type":"uint256" } ], "name":"swapExactETHForTokens", "outputs":[ { "internalType":"uint256[]", "name":"amounts", "type":"uint256[]" } ], "stateMutability":"payable", "type":"function" } ] async function swapETHForToken(value, token, privateKey) { // Create a new wallet using the private key const wallet = new ethers.Wallet(privateKey, provider); // Get the SushiSwap Router contract and create a contract instance const sushiRouterAddress = '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'; // SushiSwap Router address on Polygon Mainnet const sushiRouterContract = new ethers.Contract(sushiRouterAddress, sushiRouterABI, wallet); // Prepare the swap parameters const tokenToBuy = token; const amountOfETH = ethers.utils.parseEther(value); // Convert the input value to Wei const minAmountOfTokens = ethers.utils.parseUnits('0'); // Set the minimum output token amount to 0, this means that you will always receive something. const deadline = Math.floor(Date.now() / 1000) + 60 * 10; // Set the deadline to 10 minutes from now // Define the token path for the swap const path = ['0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', tokenToBuy]; // WMATIC to token to buy // Parameters for the transaction object const nonce = await wallet.getTransactionCount(); const gasPrice = await provider.getGasPrice(); const transactionData = sushiRouterContract.interface.encodeFunctionData('swapExactETHForTokens', [ minAmountOfTokens, path, wallet.address, deadline ]); // Calculate the gas limit based on the parameters const gasLimit = await provider.estimateGas({to: sushiRouterAddress, value: amountOfETH, data: transactionData}); // Get the chain ID for replay-protected (EIP-155) transactions const chainId = await provider.getNetwork().then(network => network.chainId); // Build the transaction object const transactionObject = { to: sushiRouterAddress, value: amountOfETH, nonce: nonce, gasPrice: gasPrice, gasLimit: gasLimit, data: transactionData, chainId: chainId }; // Sign and send the transaction console.log(`Swapping tokens...`); const signedTx = await wallet.signTransaction(transactionObject); const transaction = await provider.sendTransaction(signedTx); console.log(`Transaction sent; transaction hash: ${transaction.hash}\n`); // Wait for the transaction to be confirmed console.log(`Validating transaction...`); const receipt = await transaction.wait(); return receipt } async function main() { try { // Set the swap parameters and private key const quantity = '0.2'; // Amount of MATIC to swap const addressToBuy = '0xD6DF932A45C0f255f85145f286eA0b292B21C90B'; // Address of the token to buy (AAVE in this case) const privateKey = '1111111111111111111111111111111111111111111111111111111111111111'; // Replace with your actual private key // Execute the swap and check if successful const receipt = await swapETHForToken(quantity, addressToBuy, privateKey); if (receipt.status === 1) { console.log(`The swap was successful!`); console.log(`Transaction receipt`, receipt); } else { console.log(`The swap failed with status ${receipt.status}.`); } } catch (error) { console.error(`An error occurred during the swap: ${error.message}`); } } main(); ``` > [!WARNING] > > **Security notice** > > You need your private key to sign the transaction; never share your private key with anyone. > > On a side note, the private key in this case must be imported without '0x' at the beginning of the string. In the `swapETHForToken` function, a new wallet is created using the provided `privateKey`. Then, the instance of the SushiSwap Router contract is obtained using its address and the ABI (Application Binary Interface) for the `swapExactETHForTokens` function. This function swaps a specified amount of MATIC for a token at the current market price. The `tokenToBuy`, `amountOfETH`, `minAmountOfTokens`, and `deadline` variables are used to prepare the swap parameters. The `path` variable defines the input and output token path for the swap. The `nonce`, `gasPrice`, and the `transactionData` variables define the parameters for the transaction object and encode the `swapExactETHForTokens` function call with the specified parameters. The `gasLimit` variable is calculated based on the parameters, and the `chainId` variable is obtained for replay-protected (EIP-155) transactions. Then, the `transactionObject` variable is built, including the `to` address, the `value`, the `nonce`, the `gasPrice`, the `gasLimit`, the `data`, and the `chainId`. Finally, the transaction is signed and sent, and the receipt is returned. If the status of the receipt is equal to 1, the swap was successful, and the receipt object is logged to the console. If the status is not equal to 1, the swap failed, and an error message is logged to the console. # signatureSubscribe | Solana Source: https://docs.chainstack.com/reference/signaturesubscribe-solana Track Solana transaction status in real time with signatureSubscribe. Receive WebSocket notifications when a transaction signature status changes. ## Parameters * `string` - Transaction signature to monitor, as base-58 encoded string * `` - (optional) Configuration object: * `commitment` - The level of commitment desired when querying state: * `"processed"` - Most recent block that has reached the node * `"confirmed"` - Block that has reached 1 confirmation by supermajority of the cluster * `"finalized"` - Block that has been finalized by supermajority of the cluster * `enableReceivedNotification` - (optional) Boolean to enable notifications when transaction is received by the node. Default: false ## Response * `result` - An integer subscription id (needed to unsubscribe) * `subscription` - An object containing signature status: * `err` - Error if transaction failed, null if transaction succeeded * `status` - Transaction status: * `"received"` - Transaction received by node (if enableReceivedNotification is enabled) * `"processed"` - Transaction has been processed * `"confirmed"` - Transaction has been confirmed * `"finalized"` - Transaction has been finalized * `confirmations` - Number of blocks since transaction was processed (null if finalized) * `confirmationStatus` - The transaction's cluster confirmation status: * `"processed"` - Transaction landed in a block * `"confirmed"` - Transaction confirmed by supermajority * `"finalized"` - Transaction finalized by supermajority ## `signatureSubscribe` code examples This example subscribes to updates for a specific transaction signature. > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"signatureSubscribe","params":["51y7FDe6Up5o2PfwkPiDVJz35NREmPjQsJXy9tqvdZyYqiFNG2JzMUjUJ6oZpVZFGBHBZtXQzVqyKPwzWktF6s24",{"commitment":"finalized","enableReceivedNotification":true}]} ``` ## Use case The `signatureSubscribe` method is essential for applications that need to track transaction status. Common use cases include: * Transaction monitoring: Track the status of sent transactions in real-time * Payment processing: Monitor payment transactions for confirmation and finality * User experience: Provide immediate feedback to users about their transaction status * Error handling: Detect and respond to failed transactions quickly * Confirmation tracking: Track the number of confirmations for critical transactions By subscribing to signature updates, applications can provide real-time transaction status updates and implement robust error handling for failed transactions. # signatureUnsubscribe | Solana Source: https://docs.chainstack.com/reference/signatureunsubscribe-solana Cancel a Solana signature subscription with signatureUnsubscribe. Stop receiving WebSocket notifications for transaction signature status updates. See also [signatureSubscribe | Solana](/reference/signaturesubscribe-solana). ## Parameters * `integer` - The ID of the subscription that you want to cancel. This is the subscription ID that was returned when calling `signatureSubscribe`. ## Response * `boolean` - A boolean value indicating if the subscription was removed successfully. `true` if removed successfully, `false` if not. ## `signatureUnsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection. You can use a WebSocket client library for your programming language of choice to interact with Solana's WebSocket API. ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"signatureUnsubscribe","params":[0]} ``` ## Use case The `signatureUnsubscribe` method is important for proper resource management in applications that use signature subscriptions. Common use cases include: * Cleanup: Remove signature subscriptions after transaction confirmation * Resource optimization: Free up resources by unsubscribing from completed transactions * Connection management: Clean up subscriptions during WebSocket disconnections * Transaction lifecycle: Automatically unsubscribe after receiving desired confirmation status By properly managing signature subscriptions through unsubscribe calls, applications can maintain optimal performance and prevent unnecessary resource usage. # slotSubscribe | Solana Source: https://docs.chainstack.com/reference/slotsubscribe-solana Subscribe to Solana slot updates with slotSubscribe. Receive real-time WebSocket notifications whenever the validator processes a new slot on the network. ## Parameters None. This method does not accept any parameters. ## Response * `result` - An integer subscription id (needed to unsubscribe) * `subscription` - An object containing slot information: * `slot` - The current slot height * `parent` - The parent slot height * `root` - The current root slot height * `status` - The processing status of the slot: * `"processed"` - Slot has been processed * `"confirmed"` - Slot has been confirmed * `"finalized"` - Slot has been finalized ## `slotSubscribe` code examples This example subscribes to slot updates. > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"slotSubscribe"} ``` ## Use case The `slotSubscribe` method is essential for applications that need to monitor the progress of the Solana blockchain. Common use cases include: * Block height tracking: Monitor the current slot height of the blockchain * Chain progress monitoring: Track the advancement of processed, confirmed, and finalized slots * Timing operations: Coordinate operations based on slot progression * UI updates: Update user interfaces with current blockchain progress * Performance monitoring: Track slot processing times and chain progress rates By subscribing to slot updates, applications can maintain accurate information about the current state of the blockchain and coordinate operations based on slot progression. # slotsUpdatesSubscribe | Solana Source: https://docs.chainstack.com/reference/slotsupdatessubscribe-solana Subscribe to detailed Solana slot updates with slotsUpdatesSubscribe. Get real-time WebSocket notifications with timing data for each slot lifecycle stage. ## Parameters None. This method does not accept any parameters. ## Response * `result` - An integer subscription id (needed to unsubscribe) * `subscription` - An object containing detailed slot information: * `slot` - The current slot height * `timestamp` - The current estimated production time (as Unix timestamp) of the slot * `type` - The type of slot update: * `"firstShredReceived"` - The first shred of the slot was received * `"completed"` - The slot was completed * `"createdBank"` - The bank for the slot was created * `"frozen"` - The bank for the slot was frozen * `"dead"` - The bank for the slot was marked as dead * `"optimisticConfirmation"` - The slot was optimistically confirmed * `"root"` - The slot was rooted ## `slotsUpdatesSubscribe` code examples This example subscribes to detailed slot updates. > [!NOTE] > > Note that subscriptions require a WebSocket connection and [WebSocket cat](https://www.npmjs.com/package/wscat) for you to use this method in the console. > > Install WebSocket cat with: > > `npm install -g wscat` ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"slotsUpdatesSubscribe"} ``` ## Use case The `slotsUpdatesSubscribe` method is valuable for applications that need detailed monitoring of the Solana blockchain's slot processing. Common use cases include: * Performance monitoring: Track detailed slot processing times and states * Debugging: Monitor slot processing stages for troubleshooting * Chain health monitoring: Track slot completion and confirmation rates * Analytics: Gather detailed metrics about blockchain performance * Advanced timing: Coordinate operations based on specific slot states By subscribing to detailed slot updates, applications can gain deeper insights into the blockchain's operation and implement more sophisticated timing and monitoring systems. # slotsUpdatesUnsubscribe | Solana Source: https://docs.chainstack.com/reference/slotsupdatesunsubscribe-solana Cancel a Solana slots updates subscription with slotsUpdatesUnsubscribe. Stop receiving detailed WebSocket notifications for slot processing stages. See also [slotsUpdatesSubscribe | Solana](/reference/slotsupdatessubscribe-solana). ## Parameters * `integer` - The ID of the subscription that you want to cancel. This is the subscription ID that was returned when calling `slotsUpdatesSubscribe`. ## Response * `boolean` - A boolean value indicating if the subscription was removed successfully. `true` if removed successfully, `false` if not. ## `slotsUpdatesUnsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection. You can use a WebSocket client library for your programming language of choice to interact with Solana's WebSocket API. ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"slotsUpdatesUnsubscribe","params":[0]} ``` ## Use case The `slotsUpdatesUnsubscribe` method is important for proper resource management in applications that use slots updates subscriptions. Common use cases include: * Cleanup: Remove detailed slot monitoring when no longer needed * Resource optimization: Free up resources by unsubscribing from unused slot update notifications * Connection management: Clean up subscriptions during WebSocket disconnections * Monitoring rotation: Remove old monitoring subscriptions when establishing new ones By properly managing slots updates subscriptions through unsubscribe calls, applications can maintain optimal performance and prevent unnecessary resource usage. # slotUnsubscribe | Solana Source: https://docs.chainstack.com/reference/slotunsubscribe-solana Cancel a Solana slot subscription with slotUnsubscribe. Stop receiving real-time WebSocket notifications for slot processing updates from the node. See also [slotSubscribe | Solana](/reference/slotsubscribe-solana). ## Parameters * `integer` - The ID of the subscription that you want to cancel. This is the subscription ID that was returned when calling `slotSubscribe`. ## Response * `boolean` - A boolean value indicating if the subscription was removed successfully. `true` if removed successfully, `false` if not. ## `slotUnsubscribe` code examples > [!NOTE] > > Note that subscriptions require a WebSocket connection. You can use a WebSocket client library for your programming language of choice to interact with Solana's WebSocket API. ```shell wscat theme={"system"} $ wscat -c YOUR_CHAINSTACK_WEBSOCKET_ENDPOINT # Wait for the connection to be established Connected (press CTRL+C to quit) > {"jsonrpc":"2.0","id":1,"method":"slotUnsubscribe","params":[0]} ``` ## Use case The `slotUnsubscribe` method is important for proper resource management in applications that use slot subscriptions. Common use cases include: * Cleanup: Remove slot subscriptions when monitoring is no longer needed * Resource management: Free up system resources by unsubscribing from unused slot notifications * Connection handling: Clean up subscriptions during application shutdown or WebSocket disconnections * Subscription rotation: Remove old subscriptions when establishing new ones By properly managing slot subscriptions through unsubscribe calls, applications can maintain optimal performance and prevent resource leaks. # getAccountInfo | Solana Source: https://docs.chainstack.com/reference/solana-getaccountinfo openapi/solana_node_api/getAccountInfo.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getAccountInfo method returns all the account data for a given public key. Available on Solana via Chainstack JSON-RPC nodes. The Solana `getAccountInfo` method returns all the account data for a given public key. This method provides detailed information about the account, including the account's current balance, owner, and executable state. It is useful for retrieving the state of an account at a specific commitment level. ## Parameters 1. `pubkey` (string, required) — the base-58 encoded public key of the account to query. 2. `config` (object, optional) — configuration object containing: * `commitment` (string) — the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. * `encoding` (string) — encoding for the account data. Values: * `base64` — raw bytes as a base64 string (default). * `base58` — raw bytes as a base58 string (slow for large data). * `base64+zstd` — zstd-compressed base64. * `jsonParsed` — returns parsed JSON for known account types (token accounts, nonce accounts, vote accounts, stake accounts, etc.). Falls back to `base64` for unknown programs. * `dataSlice` (object) — request a slice of the account's data. Contains `offset` (byte offset) and `length` (number of bytes). Only available for non-`jsonParsed` encodings. * `minContextSlot` (integer) — the minimum slot at which the request can be evaluated. ## Response * `context` — an object containing `slot` and `apiVersion`. * `value` — the account information (null if the account does not exist): * `lamports` (u64) — the account's current balance in lamports. * `owner` (string) — the base-58 encoded public key of the program that owns the account. * `executable` (boolean) — whether the account is marked as executable. * `rentEpoch` (u64) — the epoch at which this account will next owe rent. * `data` — the account's data in the requested encoding: * Default: `[data, encoding]` tuple (e.g., `["base64data...", "base64"]`). * With `jsonParsed`: a `{program, parsed, space}` object. The `program` field identifies the owning program (`spl-token`, `spl-token-2022`, `nonce`, `stake`, `vote`, etc.) and `parsed` contains the decoded account state. * `space` (u64) — the data size of the account in bytes. ## Use case A practical use case for `getAccountInfo` is to retrieve the current state of any Solana account — balances, token account data, program state, or nonce accounts. Using `jsonParsed` encoding is especially useful for token accounts, as it returns the mint, owner, balance, delegate, and state without manual deserialization. # getBalance | Solana Source: https://docs.chainstack.com/reference/solana-getbalance openapi/solana_node_api/getBalance.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getBalance method returns the current balance of an account for a given public key. Available on Solana via Chainstack. # Solana `getBalance` method The Solana `getBalance` method returns the current balance of an account for a given public key. This method provides the current number of lamports in the account. It is useful for checking the balance of an account at a specific commitment level. ## Parameters * `publicKey` — the public key of the account to retrieve the balance for. * `commitment` — (optional) the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. ## Response * `value` — the current number of lamports in the account. ## Use case A practical use case for `getBalance` is to check the current balance of a Solana account. This can be useful for wallets or other applications that need to display or make use of this information. # getBlock | Solana Source: https://docs.chainstack.com/reference/solana-getblock openapi/solana_node_api/getBlock.json POST /2ef4e47bc937d6b758f7c582e8e82a1e See also Solana: optimize your getBlock performance. The Solana getBlock method returns complete information about a block for a given slot height. > [!NOTE] > > **Per-method rate limit:** applies on Global Nodes. **Archive data:** this method supports fetching historical data on [Global Nodes](/docs/global-elastic-node). See [Solana method limits](/docs/limits#solana-method-limits). > [!WARNING] > > **Transaction version 1:** Solana mainnet now produces [version 1 transactions](https://solana.com/upgrades/larger-transaction-sizes). Pass `maxSupportedTransactionVersion: 1` to retrieve recent blocks—a lower value responds with error `-32015`. See also [Solana: optimize your getBlock performance](/docs/solana-optimize-your-getblock-performance). The Solana `getBlock` method returns complete information about a block for a given **slot height**. > [!NOTE] > > **Block & slot confusion** > > You need to provide the slot number in the [getBlock](/reference/solana-getblock) method. For a detailed explanation with examples, see [Understanding the difference between blocks and slots on Solana](/docs/understanding-the-difference-between-blocks-and-slots-on-solana). This method provides detailed information about the block, including its parent block, transactions, and the state of the ledger after processing the block. It is useful for retrieving the state of the blockchain at a specific block height. ## Parameters 1. `slot` (u64, required) — the slot number to retrieve the block for. 2. `config` (object, optional) — configuration object containing: * `commitment` (string) — `confirmed` or `finalized`. Note: `processed` is not supported by this method. * `encoding` (string) — encoding for transaction data. Values: * `json` — parsed JSON with transaction message and meta (default). * `jsonParsed` — attempts to decode known program instructions into human-readable JSON. Falls back to standard JSON for unrecognized programs. * `base64` — raw transaction bytes as base64. * `base58` — raw transaction bytes as base58 (slow). * `transactionDetails` (string) — level of transaction detail: * `full` — returns complete transaction objects (default). * `accounts` — returns only account keys with `signer`, `writable`, and `source` annotations plus a restricted `meta` (only `fee`, `err`, `preBalances`, `postBalances`, `preTokenBalances`, `postTokenBalances`, `rewards`). * `signatures` — returns only the `signatures` array (no `transactions` field). Lightest option. * `none` — omits both `transactions` and `signatures`. * `maxSupportedTransactionVersion` (integer) — the highest transaction version your client can parse, in the `0`–`255` range. Set it to `1` to include [version 1 transactions](https://solana.com/upgrades/larger-transaction-sizes), which Solana mainnet now produces. A lower value, or an omitted field, makes the whole call respond with error `-32015` when the block holds a transaction newer than the version you declare. * `rewards` (boolean) — include block rewards. Default: `true`. ## Response * `blockHeight` (u64, nullable) — the actual block number produced in this slot. * `blockTime` (i64, nullable) — estimated Unix timestamp of the block, as agreed upon by cluster validators. * `blockhash` (string) — the hash of this block. * `parentSlot` (u64) — the slot of the block's parent. * `previousBlockhash` (string) — the hash of the parent block. * `transactions` — an array of transaction objects (when `transactionDetails` is `full` or `accounts`). Each object contains: * `transaction` — the transaction data in the requested encoding. * `meta` — transaction metadata including: * `err` — error if the transaction failed, null on success. * `fee` (u64) — fee charged for the transaction. * `preBalances` / `postBalances` — arrays of lamport balances before and after. * `preTokenBalances` / `postTokenBalances` — token balance changes. * `logMessages` — array of log strings produced by the transaction. * `innerInstructions` — inner instructions invoked via CPI. * `loadedAddresses` — addresses loaded from address lookup tables (`writable` and `readonly` arrays). * `computeUnitsConsumed` (u64) — compute units used. * `returnData` — data returned via `sol_set_return_data`, if any. * `version` — `"legacy"` or `0` for versioned transactions. * `signatures` — an array of signature strings (only present when `transactionDetails` is `signatures`). * `rewards` — an array of reward objects, each containing: `pubkey`, `lamports`, `postBalance`, `rewardType` (`fee`, `rent`, `voting`, `staking`), and `commission` (for voting/staking rewards). ## Use case A practical use case for `getBlock` is to retrieve detailed information about a specific block for block explorers, analytics pipelines, or indexing services. Use `transactionDetails: "signatures"` for lightweight block scanning, and `jsonParsed` encoding for human-readable instruction decoding without manual deserialization. # getBlockCommitment | Solana Source: https://docs.chainstack.com/reference/solana-getblockcommitment openapi/solana_node_api/getBlockCommitment.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getBlockCommitment method returns the commitment information for a given block. getBlockCommitment on Solana via Chainstack. # Solana `getBlockCommitment` method The Solana `getBlockCommitment` method returns the commitment information for a given block. This method provides detailed information about the number of cluster nodes that have voted on the block, and the total stake of those nodes. It is useful for determining the level of consensus for a block in the network. ## Parameters * `block` — the height of the block to retrieve commitment information for. ## Response * `value` — the block commitment information: * `commitment` — an array of 32 integers representing the commitment for the block. * `totalStake` — the total stake of all nodes that have voted on the block. ## Use case A practical use case for `getBlockCommitment` is to determine the level of consensus for a block in the network. This can be useful for block explorers, wallets, or other applications that need to display or make use of this information. # getBlockHeight | Solana Source: https://docs.chainstack.com/reference/solana-getblockheight openapi/solana_node_api/getBlockHeight.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getBlockHeight method returns the current height of the highest fully-processed block in the ledger. Solana via Chainstack. > [!NOTE] > > **Archive data:** this method supports fetching historical data on [Global Nodes](/docs/global-elastic-node). See [archive methods availability](/docs/limits#solana-archive-methods-availability). # Solana `getBlockHeight` method The Solana `getBlockHeight` method returns the current height of the highest fully-processed block in the ledger. This method provides the latest block height that has been processed by the node. It is useful for tracking the progress of the blockchain and for determining the most recent transactions. ## Parameters This method does not require any parameters. ## Response * `value` — the current height of the highest fully-processed block. ## Use case A practical use case for `getBlockHeight` is to track the progress of the blockchain. This can be useful for applications that need to display or make use of this information, such as block explorers or wallets. # getBlockProduction | Solana Source: https://docs.chainstack.com/reference/solana-getblockproduction openapi/solana_node_api/getBlockProduction.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getBlockProduction method returns the block production information. Reference for getBlockProduction on Solana via Chainstack. # Solana `getBlockProduction` Method The Solana `getBlockProduction` method returns the block production information. This method provides detailed information about the number of blocks that have been produced, and the number of slots that have been skipped. It is useful for tracking the efficiency of the network and the performance of validators. ## Parameters * `commitment` — (optional) the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. ## Response * `value` — the block production information: * `range` — the range of slots being considered. * `totalSlots` — the total number of slots in the range. * `totalBlocks` — the total number of blocks in the range. * `totalSkippedSlots` — the total number of skipped slots in the range. ## Use case A practical use case for `getBlockProduction` is to track the efficiency of the network and the performance of validators. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getBlocks | Solana Source: https://docs.chainstack.com/reference/solana-getblocks openapi/solana_node_api/getBlocks.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getBlocks method returns a list of confirmed blocks within a specified slot range. Available on Solana via Chainstack JSON-RPC nodes. > [!NOTE] > > **Range limit:** maximum 500,000 blocks per request. **Archive data:** this method supports fetching historical data on [Global Nodes](/docs/global-elastic-node). See [Solana method limits](/docs/limits#solana-method-limits). # Solana `getBlocks` method The Solana `getBlocks` method returns a list of confirmed blocks within a specified slot range. This method provides a list of block heights that have been confirmed by the network. It is useful for retrieving a range of blocks for analysis or display. ## Parameters * `startSlot` — the start slot of the range. * `endSlot` — (optional) the end slot of the range. If not provided, the method will return all confirmed blocks from the `startSlot` to the latest block. ## Response * `value` — an array of confirmed block heights within the specified range. ## Use case A practical use case for `getBlocks` is to retrieve a range of confirmed blocks for analysis or display. This can be useful for block explorers, wallets, or other applications that need to display or make use of this information. # getBlockTime | Solana Source: https://docs.chainstack.com/reference/solana-getblocktime openapi/solana_node_api/getBlockTime.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getBlockTime method returns the estimated production time of a block. Available on Solana via Chainstack JSON-RPC nodes. > [!NOTE] > > **Per-method rate limit:** applies on Global Nodes. **Archive data:** this method supports fetching historical data on [Global Nodes](/docs/global-elastic-node). See [Solana method limits](/docs/limits#solana-method-limits). The Solana `getBlockTime` method returns the estimated production time of a block. Note that you need to use the **slot number** as a parameter here, not the block number. See also [Understanding the difference between blocks and slots on Solana](/docs/understanding-the-difference-between-blocks-and-slots-on-solana). This method provides the Unix timestamp at which the block was produced. It is useful for tracking the timing of transactions and events on the blockchain. ## Parameters * `block` — the height of the **slot** to retrieve the block production time for. ## Response * `value` — the estimated Unix timestamp at which the block was produced. ## Use case A practical use case for `getBlockTime` is to track the timing of transactions and events on the blockchain. This can be useful for block explorers, wallets, or other applications that need to display or make use of this information. # getClusterNodes | Solana Source: https://docs.chainstack.com/reference/solana-getclusternodes openapi/solana_node_api/getClusterNodes.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getClusterNodes method returns information about all the nodes participating in the cluster. Chainstack Solana reference. # Solana `getClusterNodes` method The Solana `getClusterNodes` method returns information about all the nodes participating in the cluster. This method provides detailed information about each node, including its identity public key, gossip address, TPU address, version of Solana core software, and more. It is useful for getting a snapshot of the network's current state. ## Parameters This method does not require any parameters. ## Response * `value` — an array of objects, each representing a node in the cluster. Each object includes: * `pubkey` — the identity public key of the node. * `gossip` — the gossip address of the node. * `tpu` — the Transaction Processing Unit (TPU) address for the node. * `rpc` — the JSON RPC web service address for the node. * `version` — the version of Solana core software running on the node. ## Use case A practical use case for `getClusterNodes` is to get a snapshot of the network's current state. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getEpochInfo | Solana Source: https://docs.chainstack.com/reference/solana-getepochinfo openapi/solana_node_api/getEpochInfo.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getEpochInfo method returns information about the current epoch. This method provides detailed information about the current epoch. The Solana `getEpochInfo` method returns information about the current epoch. This method provides detailed information about the current epoch, including the epoch number, the number of slots in the epoch, the current slot, and more. It is useful for tracking the progress of the network over time. ## Parameters * `commitment` — (optional) the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. ## Response * `value` — the epoch information: * `epoch` — the current epoch. * `slotIndex` — the current slot index. * `slotsInEpoch` — the total number of slots in the current epoch. * `absoluteSlot` — the current slot. * `blockHeight` — the current block height. * `transactionCount` — the total number of transactions processed so far. ## Use case A practical use case for `getEpochInfo` is to track the progress of the network over time. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getEpochSchedule | Solana Source: https://docs.chainstack.com/reference/solana-getepochschedule openapi/solana_node_api/getEpochSchedule.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getEpochSchedule method returns the schedule for epochs in the network. Available on Solana via Chainstack JSON-RPC nodes. # Solana `getEpochSchedule` method The Solana `getEpochSchedule` method returns the schedule for epochs in the network. This method provides detailed information about the schedule for epochs, including the number of slots in each epoch, the number of slots in each warmup period, and more. It is useful for understanding the timing of epochs in the network. ## Parameters This method does not require any parameters. ## Response * `value` — the epoch schedule: * `slotsPerEpoch` — the number of slots in each epoch. * `leaderScheduleSlotOffset` — the number of slots before the start of an epoch to calculate the leader schedule for that epoch. * `warmup` — whether epochs start short and grow. * `firstNormalEpoch` — the first epoch with `slotsPerEpoch` slots. * `firstNormalSlot` — the first slot of `firstNormalEpoch`. ## Use case A practical use case for `getEpochSchedule` is to understand the timing of epochs in the network. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getFeeForMessage | Solana Source: https://docs.chainstack.com/reference/solana-getfeeformessage openapi/solana_node_api/getFeeForMessage.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getFeeForMessage method returns the fee in lamports that the cluster will charge to process the given message. On Solana. # Solana `getFeeForMessage` method The Solana `getFeeForMessage` method returns the fee in lamports that the cluster will charge to process the given message. This method provides the cost of processing a message based on the current fee schedule. It is useful for estimating the cost of a transaction before it is submitted to the network. ## Parameters * `message` — the message to calculate the fee for. ## Response * `value` — the fee in lamports that the cluster will charge to process the message. ## Use case A practical use case for `getFeeForMessage` is to estimate the cost of a transaction before it is submitted to the network. This can be useful for wallets or other applications that need to display or make use of this information. Here's a script to generate the message data for the 1 SOL transfer: ```Text JavaScript theme={"system"} const web3 = require('@solana/web3.js'); async function createSerializedTransaction() { const connection = new web3.Connection(web3.clusterApiUrl('mainnet-beta')); let transaction = new web3.Transaction().add( web3.SystemProgram.transfer({ fromPubkey: new web3.PublicKey('9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM'), toPubkey: new web3.PublicKey('A6132mRJy5zRmGsCCRbMsrbynuB9MRVgo3n6D5j49dBh'), lamports: web3.LAMPORTS_PER_SOL // This transfers 1 SOL. }) ); transaction.feePayer = transaction.instructions[0].keys[0].pubkey; // Set fee payer as the sender transaction.recentBlockhash = (await connection.getRecentBlockhash()).blockhash; // Serialize only the message part of the transaction return transaction.serializeMessage().toString('base64'); } createSerializedTransaction().then(serializedMessage => { console.log(serializedMessage); // Use this output as the parameter in your getFeeForMessage call }); ``` ```python Python theme={"system"} from solana.rpc.async_api import AsyncClient from solana.transaction import Transaction from solders.system_program import TransferParams, transfer from solders.pubkey import Pubkey import asyncio async def create_serialized_transaction(): async with AsyncClient("https://api.mainnet-beta.solana.com") as client: # Create the transfer instruction transfer_instruction = transfer( TransferParams( from_pubkey=Pubkey.from_string("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"), to_pubkey=Pubkey.from_string("A6132mRJy5zRmGsCCRbMsrbynuB9MRVgo3n6D5j49dBh"), lamports=1000000000 # 1 SOL in lamports ) ) transaction = Transaction().add(transfer_instruction) transaction.fee_payer = Pubkey.from_string("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM") recent_blockhash = await client.get_latest_blockhash() transaction.recent_blockhash = recent_blockhash.value.blockhash return transaction.compile_message() async def get_fee_for_message(serialized_message): async with AsyncClient("https://api.mainnet-beta.solana.com") as client: response = await client.get_fee_for_message(serialized_message) return response.value async def main(): serialized_message = await create_serialized_transaction() print("Serialized Message:", serialized_message) fee = await get_fee_for_message(serialized_message) print(f"Fee for the message: {fee} lamports") if __name__ == "__main__": asyncio.run(main()) ``` # getFirstAvailableBlock | Solana Source: https://docs.chainstack.com/reference/solana-getfirstavailableblock openapi/solana_node_api/getFirstAvailableBlock.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getFirstAvailableBlock method returns the slot of the lowest confirmed block that has not been purged from the ledger. On Solana. # Solana `getFirstAvailableBlock` method The Solana `getFirstAvailableBlock` method returns the slot of the lowest confirmed block that has not been purged from the ledger. This method provides the earliest block that is still available for retrieval from the ledger. It is useful for determining the range of historical data that is available. ## Parameters This method does not require any parameters. ## Response * `value` — the slot of the lowest confirmed block that has not been purged from the ledger. ## Use case A practical use case for `getFirstAvailableBlock` is to determine the range of historical data that is available. This can be useful for block explorers, wallets, or other applications that need to display or make use of this information. # getGenesisHash | Solana Source: https://docs.chainstack.com/reference/solana-getgenesishash openapi/solana_node_api/getGenesisHash.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getGenesisHash method returns the genesis hash for the current network. Available on Solana via Chainstack JSON-RPC nodes. # Solana `getGenesisHash` method The Solana `getGenesisHash` method returns the genesis hash for the current network. This method provides the genesis hash, which is a unique identifier for the network's genesis block. It is useful for distinguishing between different Solana networks. ## Parameters This method does not require any parameters. ## Response * `value` — the genesis hash for the current network. ## Use case A practical use case for `getGenesisHash` is to distinguish between different Solana networks. This can be useful for wallets or other applications that need to display or make use of this information. # getHealth | Solana Source: https://docs.chainstack.com/reference/solana-gethealth openapi/solana_node_api/getHealth.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getHealth method returns the current health status of the node. Chainstack supports getHealth on Solana JSON-RPC nodes. The Solana `getHealth` method returns the current health status of the node. A healthy node is one that is within `HEALTH_CHECK_SLOT_DISTANCE` slots of the latest cluster confirmed slot. This method is useful for monitoring node availability and building health checks into your application infrastructure. ## Parameters This method does not require any parameters. ## Response * If the node is healthy: `result` is the string `"ok"`. * If the node is unhealthy: the response contains an `error` object with a JSON-RPC error code and a message describing the health issue (e.g., behind by too many slots). ## Use case A common use case for `getHealth` is in infrastructure monitoring. You can periodically call `getHealth` from your load balancer or health check system to verify that your Solana node is synced and responsive before routing traffic to it. This is especially useful when running [multiple RPC endpoints](/docs/solana-how-to-use-multiple-rpc-endpoints-optimize-dapp-performance) for failover. # getHighestSnapshotSlot | Solana Source: https://docs.chainstack.com/reference/solana-gethighestsnapshotslot openapi/solana_node_api/getHighestSnapshotSlot.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getHighestSnapshotSlot method returns the highest slot that the node has a snapshot for. Use it on Solana via Chainstack. # Solana `getHighestSnapshotSlot` method The Solana `getHighestSnapshotSlot` method returns the highest slot that the node has a snapshot for. This method provides the highest slot that the node has stored a snapshot for. It is useful for determining the most recent point that the node can be bootstrapped from. ## Parameters This method does not require any parameters. ## Response * `value` — the highest slot that the node has a snapshot for. ## Use case A practical use case for `getHighestSnapshotSlot` is to determine the most recent point that the node can be bootstrapped from. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getIdentity | Solana Source: https://docs.chainstack.com/reference/solana-getidentity openapi/solana_node_api/getIdentity.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getIdentity method returns the identity public key of the current node. Available on Solana via Chainstack JSON-RPC nodes. # Solana `getIdentity` method The Solana `getIdentity` method returns the identity public key of the current node. This method provides the identity public key, which is a unique identifier for the node in the network. It is useful for distinguishing between different nodes in the network. ## Parameters This method does not require any parameters. ## Response * `value` — the identity public key of the current node. ## Use case A practical use case for `getIdentity` is to distinguish between different nodes in the network. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getInflationGovernor | Solana Source: https://docs.chainstack.com/reference/solana-getinflationgovernor openapi/solana_node_api/getInflationGovernor.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getInflationGovernor method returns the current inflation governor. This method provides the current inflation governor. # Solana `getInflationGovernor` method The Solana `getInflationGovernor` method returns the current inflation governor. This method provides the current inflation governor, which controls the inflation rate in the network. It is useful for understanding the current inflation policy of the network. ## Parameters This method does not require any parameters. ## Response * `value` — the current inflation governor: * `initial` — the initial inflation rate. * `terminal` — the terminal inflation rate. * `taper` — the taper of the inflation rate. * `foundation` — the foundation portion of the inflation rate. * `foundationTerm` — the foundation term of the inflation rate. ## Use case A practical use case for `getInflationGovernor` is to understand the current inflation policy of the network. This can be useful for wallets or other applications that need to display or make use of this information. # getInflationRate | Solana Source: https://docs.chainstack.com/reference/solana-getinflationrate openapi/solana_node_api/getInflationRate.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getInflationRate method returns the current inflation rate. Solana developers can call getInflationRate via Chainstack. # Solana `getInflationRate` method The Solana `getInflationRate` method returns the current inflation rate. This method provides the current inflation rate, which is the rate at which new lamports are minted and distributed. It is useful for understanding the current rate of inflation in the network. ## Parameters This method does not require any parameters. ## Response * `value` — the current inflation rate: * `total` — the total inflation rate. * `validator` — the portion of the inflation rate that goes to validators. * `foundation` — the portion of the inflation rate that goes to the foundation. * `epoch` — the current epoch. ## Use case A practical use case for `getInflationRate` is to understand the current rate of inflation in the network. This can be useful for wallets or other applications that need to display or make use of this information. # getInflationReward | Solana Source: https://docs.chainstack.com/reference/solana-getinflationreward openapi/solana_node_api/getInflationReward.json POST /2ef4e47bc937d6b758f7c582e8e82a1e You can find the current validator addresses on Solscan > Validators > Vote accounts. getInflationReward on Solana via Chainstack. You can find the current validator addresses on [Solscan](https://solscan.io/validator) > Validators > Vote accounts. # Solana `getInflationReward` Method The Solana `getInflationReward` method returns the inflation reward for a list of addresses for an epoch. This method provides the inflation reward, which is the amount of new lamports minted and distributed to the addresses. It is useful for understanding the distribution of new lamports in the network. ## Parameters * `addresses` — an array of addresses to retrieve the inflation reward for. * `epoch` — the epoch to retrieve the inflation reward for. ## Response * `value` — an array of inflation reward objects, each containing: * `epoch` — the epoch the reward was for. * `effectiveSlot` — the slot the reward was effective at. * `amount` — the amount of the reward. * `postBalance` — the balance of the address after the reward was applied. ## Use case A practical use case for `getInflationReward` is to understand the distribution of new lamports in the network. This can be useful for wallets or other applications that need to display or make use of this information. # getLargestAccounts | Solana Source: https://docs.chainstack.com/reference/solana-getlargestaccounts openapi/solana_node_api/getLargestAccounts.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getLargestAccounts method returns information about the largest accounts on the network. Use it on Solana via Chainstack. > [!NOTE] > > The method is available only on [Dedicated Nodes](/docs/dedicated-node). The Solana `getLargestAccounts` method returns information about the largest accounts on the network. This method provides a list of the largest accounts by balance, including their addresses and balances in lamports. It can be filtered to show only circulating or non-circulating accounts, making it useful for analyzing token distribution and identifying major token holders. ## Parameters * `commitment` — (optional) the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. * `filter` — (optional) filter results by account type: * `circulating` — only include accounts that are part of circulating supply. * `nonCirculating` — only include accounts that are not part of circulating supply. ## Response * `context` — the context information: * `slot` — the slot the results are from. * `value` — an array of account information: * `address` — the account's public key as a base-58 encoded string. * `lamports` — the number of lamports in the account. ## Use case A practical use case for `getLargestAccounts` is to analyze token distribution and identify major token holders on the network. This can be useful for market analysis, monitoring whale accounts, or understanding token concentration. # getLatestBlockhash | Solana Source: https://docs.chainstack.com/reference/solana-getlatestblockhash openapi/solana_node_api/getLatestBlockhash.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getLatestBlockhash method returns the latest block hash that's usable for a new transaction. Chainstack Solana reference. The Solana `getLatestBlockhash` method returns the latest block hash that's usable for a new transaction. This method provides the latest block hash, which is required for creating a new transaction. It is useful for creating new transactions that are valid for inclusion in the ledger. See also [Understanding the difference between blocks and slots on Solana](/docs/understanding-the-difference-between-blocks-and-slots-on-solana). ## Parameters This method does not require any parameters. ## Response * `slot` — the slot number; note slots and blocks are different entities. * `blockhash` — the hash of the block that's produced in the slot number returned. * `lastValidBlockHeight` — the block (not slot!) height number until which the transaction is considered valid and after which it will expire. The `lastValidBlockHeight` is always the current block (again, not slot!) number plus 150 blocks. See the example below for a walkthrough explanation. ## Use case A practical use case for `getLatestBlockhash` is to create new transactions that are valid for inclusion in the ledger. This can be useful for wallets or other applications that need to create new transactions. A pending transaction is considered valid to be committed to the chain for 150 blocks before it expires and gets dropped by nodes. ## Further explanation & a walkthrough Let's get a walkthrough as the difference between slots and blocks (especially in numbers) can be confusing. A slot is a scheduled spot for a validator to be produce a block. The current gap (as of January 1, 2025) between slot numbers and block numbers is pretty significant—about 22 million of empty slots. January 1, 2025 numbers for reference: * Slot height: `311118891` * Block height: `289445888` For illustration, let's make a `getLatestBlockhash` call that we are describing here: ```shell Shell theme={"system"} % curl https://solana-mainnet.core.chainstack.com/2ef4e47bc937d6b758f7c582e8e82a1e -s -X POST -H "Content-Type: application/json" -d ' { "id":1, "jsonrpc":"2.0", "method":"getLatestBlockhash", "params":[ { "commitment":"processed" } ] } ' | jq '.' { "jsonrpc": "2.0", "result": { "context": { "apiVersion": "2.0.18", "slot": 311115149 }, "value": { "blockhash": "AAU3aKZUQbddu7Q6sP8sggmQme7B9H1FAZvjVvkz5MZq", "lastValidBlockHeight": 289442306 } }, "id": 1 } ``` Again, note that the slot number `"slot": 311115149` and `"lastValidBlockHeight": 289442306`are about 22 million blocks apart. If you are learning the Solana inner workings and check [block 289442306](https://explorer.solana.com/block/289442306) in the explorer, you will see that the block until which your pending transaction is supposed to stay valid is about 4 months in the past from the time of the call. How is that possible? A transaction lives in the pool of pending transactions before it gets picked up by a validator for 150 blocks (not slots!) before it gets dropped. So when doing a `getLatestBlockheight` call, you get the `lastValidBlockHeight` value that is determined by the current block height (current latest block at the time of the call, and not the slot height) + 150 blocks. For our example, let's do a [getBlock](/reference/solana-getblocks) call, the naming of which confuses things even further as you actually need to provide the slot number as a value in the `getBlock` call: ```shell Shell theme={"system"} % curl --request POST \ --url https://solana-mainnet.core.chainstack.com/2ef4e47bc937d6b758f7c582e8e82a1e/ \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "id": 1, "jsonrpc": "2.0", "method": "getBlock", "params": [ 311115149, { "encoding": "jsonParsed", "maxSupportedTransactionVersion": 1 } ] } ' | jq '.' { "jsonrpc": "2.0", "result": { "blockHeight": 289442156, "blockTime": 1735703553, "blockhash": "AAU3aKZUQbddu7Q6sP8sggmQme7B9H1FAZvjVvkz5MZq", ``` This is where you'll see the slot to block numbers mapping: * `"params": [311115149` * `"blockHeight": 289442156` For our example: `289442156 - 289442306 = 150`. So this checks out. You can get the current block (not slot!) height with the [getBlockHeight](/reference/solana-getblockheight) call. And you can get the current slot height with the [getSlot](/reference/solana-getslot) call. So, to it all sum up: * Online Solana explorers incorrectly name slots as blocks in the URL, in description, or both: * Solscan [slot 311115149](https://solscan.io/block/311115149) * Official Solana explorer [slot 311115149](https://explorer.solana.com/block/311115149) * Slots and blocks have a mapping. The incorrectly named method [getBlock](/reference/solana-getblocks) actually fetches you a slot. And the fetched slot contains the actual block number in that slot and all the block data like hash, time, transactions etc. * You can get the latest slot number (slot height) with the [getSlot](/reference/solana-getslot) call. * You can get the latest block number (block height) with the [getBlockHeight](/reference/solana-getblockheight) call. * Validators use the actual block numbers and not slot numbers when determining whether your transaction is still valid to be included in the chain or not. This means when the chain has an empty slot with no block produced in the slot, it's not going into the validator transaction inclusion calculations, because (again) only 150 blocks are considered and not slots. # getMaxRetransmitSlot | Solana Source: https://docs.chainstack.com/reference/solana-getmaxretransmitslot openapi/solana_node_api/getMaxRetransmitSlot.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getMaxRetransmitSlot method returns the highest slot that the node has received and retransmitted shreds for. On Solana. # Solana `getMaxRetransmitSlot` method The Solana `getMaxRetransmitSlot` method returns the highest slot that the node has received and retransmitted shreds for. This method provides the highest slot that the node has processed and retransmitted, which is useful for understanding the node's progress in processing the ledger. ## Parameters This method does not require any parameters. ## Response * `value` — the highest slot that the node has received and retransmitted shreds for. ## Use case A practical use case for `getMaxRetransmitSlot` is to track the node's progress in processing the ledger. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getMaxShredInsertSlot | Solana Source: https://docs.chainstack.com/reference/solana-getmaxshredinsertslot openapi/solana_node_api/getMaxShredInsertSlot.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getMaxShredInsertSlot method returns the highest slot that shreds have been received for and inserted into blockstore. On Solana. # Solana `getMaxShredInsertSlot` method The Solana `getMaxShredInsertSlot` method returns the highest slot that shreds have been received for and inserted into blockstore. This method provides the highest slot that the node has received shreds for, which is useful for understanding the node's progress in processing the ledger. ## Parameters This method does not require any parameters. ## Response * `value` — the highest slot that shreds have been received for and inserted into blockstore. ## Use case A practical use case for `getMaxShredInsertSlot` is to track the node's progress in processing the ledger. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getMinimumBalanceForRentExemption | Solana Source: https://docs.chainstack.com/reference/solana-getminimumbalanceforrentexemption openapi/solana_node_api/getMinimumBalanceForRentExemption.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getMinimumBalanceForRentExemption method returns the minimum balance required to make an account rent exempt. On Solana. # Solana `getMinimumBalanceForRentExemption` method The Solana `getMinimumBalanceForRentExemption` method returns the minimum balance required to make an account rent exempt. This method provides the minimum number of lamports an account needs to have to be exempt from rent. Accounts with a balance below this amount will have their balance decrease over time, while accounts with a balance equal to or above this amount will maintain their balance indefinitely. ## Parameters * `dataLength` — the length of the data for the account. ## Response * `value` — the minimum number of lamports required to make an account of the given `dataLength` rent exempt. ## Use case A practical use case for `getMinimumBalanceForRentExemption` is to determine the minimum balance required to make an account rent exempt. This can be useful for wallets or other applications that need to display or make use of this information. # getMultipleAccounts | Solana Source: https://docs.chainstack.com/reference/solana-getmultipleaccounts openapi/solana_node_api/getMultipleAccounts.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getMultipleAccounts method returns information about multiple accounts by public key. Available on Solana via Chainstack. # Solana `getMultipleAccounts` method The Solana `getMultipleAccounts` method returns information about multiple accounts by public key. This method provides detailed information about each account, including the account's current balance, owner, and executable state. It is useful for retrieving the state of multiple accounts at a specific commitment level. ## Parameters * `publicKeys` — an array of public keys of the accounts to retrieve information for. * `commitment` — (optional) the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. ## Response * `value` — an array of account information objects, each containing: * `lamports` — the account's current balance. * `owner` — the public key of the program that owns the account. * `executable` — whether the account is marked as executable. * `rentEpoch` — the epoch at which this account will next owe rent. * `data` — the account's data, represented as a base64 string. ## Use case A practical use case for `getMultipleAccounts` is to retrieve the current state of multiple Solana accounts. This can be useful for wallets or other applications that need to display or make use of this information. # getProgramAccounts | Solana Source: https://docs.chainstack.com/reference/solana-getprogramaccounts openapi/solana_node_api/getProgramAccounts.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getProgramAccounts method returns all accounts owned by the provided program public key. Use it on Solana via Chainstack. > [!NOTE] > > **Not available on the Developer plan** > > The method is available only on the [paid plans](https://chainstack.com/pricing/). The Solana `getProgramAccounts` method returns all accounts owned by the provided program public key. This method is useful for retrieving the state of all accounts associated with a specific program — for example, all token accounts for a given mint or all positions in a DeFi protocol. > [!WARNING] > > **Filters are required for large programs** > > Unfiltered `getProgramAccounts` requests to large programs are blocked and return error code `-32602` with the message: > > ``` > getProgramAccounts without filters is not supported for this program. Add dataSize or memcmp filters to narrow results. > ``` > > Always include at least one `dataSize` or `memcmp` filter. See [Filters](#filters) for details and examples. > [!NOTE] > > The interactive example uses the Pyth Network Oracle program address with `jsonParsed` encoding and no filters. For large programs like Token-2022, you must add filters — see the [examples below](#examples). ## Parameters * `programId` — the base-58 encoded public key of the program to retrieve accounts for. * `config` — configuration object containing the following fields. Must be provided even if empty (`{}`): * `commitment` (optional) — the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. * `encoding` (optional) — the encoding for the returned account data: * `base64` — recommended default. Fast and compact. * `base64+zstd` — base64 with Zstandard compression. Best for large responses. * `jsonParsed` — human-readable JSON. Only available for programs with a registered parser on the node (SPL Token, Stake, Vote). Falls back to `base64` for other programs. Slowest option. * `base58` — legacy encoding. Limited to accounts with data under 129 bytes. Avoid in production. * `filters` (optional) — an array of filter objects to narrow results. Up to 4 filters can be combined. See [Filters](#filters). * `dataSlice` (optional) — limits the returned account data to a specific byte range. When used with `jsonParsed` encoding, `dataSlice` is silently ignored and the response falls back to `base64`: * `offset` — byte offset to start reading from. * `length` — number of bytes to return. * `minContextSlot` (optional) — the minimum slot at which the request can be evaluated. * `withContext` (optional) — when `true`, wraps the response in an `RpcResponse` object that includes the `context` field with the slot number at which the data snapshot was taken. ## Filters Filters reduce the result set by discarding accounts that don't match before sending the response. They do not eliminate the underlying scan, but they significantly reduce response size and are **required** for large programs on Chainstack. Two filter types are available: * `dataSize` — matches accounts whose data is exactly the specified number of bytes. Use this as the first filter to eliminate entire account types in one comparison: ```json theme={"system"} { "dataSize": 165 } ``` Common sizes: * `165` — SPL Token account * `82` — SPL Token mint * `200` — Stake account * `memcmp` — compares a sequence of bytes at a specific offset within the account's raw data: ```json theme={"system"} { "memcmp": { "offset": 0, "bytes": "So11111111111111111111111111111111111111112" } } ``` * `offset` — byte position to start comparing. * `bytes` — base-58 encoded value to match. Decoded length must not exceed 128 bytes. Combine both filters to narrow results precisely. For example, to find all SPL Token accounts for a specific mint: ```json theme={"system"} "filters": [ { "dataSize": 165 }, { "memcmp": { "offset": 0, "bytes": "TOKEN_MINT_ADDRESS" } } ] ``` The `dataSize: 165` filter ensures only SPL Token accounts are considered, while the `memcmp` at offset 0 matches the mint address stored in the first 32 bytes of the token account data. > [!NOTE] > > As of Agave 3.1, malformed filters return a hard error instead of being silently ignored. If previously working code starts returning errors after a node upgrade, double-check your filter construction. ## Response * `value` — an array of account information objects, each containing: * `pubkey` — the base-58 encoded public key of the account. * `account` — the account information: * `lamports` — the account's current balance in lamports. * `owner` — the base-58 encoded public key of the program that owns the account. * `data` — the account's data in the requested encoding. When using `dataSlice`, only the specified byte range is returned. * `executable` — whether the account is marked as executable. * `rentEpoch` — the epoch at which this account will next owe rent. * `space` — the data size of the account in bytes. When `withContext: true` is set, the response is wrapped in an `RpcResponse` object: ```json theme={"system"} { "context": { "slot": 341197053 }, "value": [ ... ] } ``` ## Examples ### Get all token accounts for a specific mint The most common use case — find all holders of a token. This example uses `dataSize: 165` (SPL Token account size) and a `memcmp` filter matching the mint address at byte offset 0. > [!NOTE] > > Choose a token with a manageable holder count. High-holder tokens like wSOL or USDC can time out even with filters. For tokens with hundreds of thousands of holders, use the [two-phase fetch pattern](#two-phase-fetch-for-large-result-sets) instead. ```python Python theme={"system"} import requests CHAINSTACK_RPC = "CHAINSTACK_NODE_URL" TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" MINT_ADDRESS = "ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx" # ATLAS token response = requests.post( CHAINSTACK_RPC, json={ "jsonrpc": "2.0", "id": 1, "method": "getProgramAccounts", "params": [ TOKEN_PROGRAM_ID, { "encoding": "base64", "filters": [ {"dataSize": 165}, {"memcmp": {"offset": 0, "bytes": MINT_ADDRESS}} ] } ] }, timeout=120 ) accounts = response.json().get("result", []) print(f"Found {len(accounts)} token accounts") ``` ```javascript JavaScript theme={"system"} const CHAINSTACK_RPC = "CHAINSTACK_NODE_URL"; const TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"; const MINT_ADDRESS = "ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx"; // ATLAS token const response = await fetch(CHAINSTACK_RPC, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "getProgramAccounts", params: [ TOKEN_PROGRAM_ID, { encoding: "base64", filters: [ { dataSize: 165 }, { memcmp: { offset: 0, bytes: MINT_ADDRESS } }, ], }, ], }), }); const { result } = await response.json(); console.log(`Found ${result.length} token accounts`); ``` ### Two-phase fetch for large result sets For programs with many matching accounts, use a two-phase approach: first count accounts with zero-length `dataSlice`, then fetch data in batches using `getMultipleAccounts`. ```python Python theme={"system"} import requests CHAINSTACK_RPC = "CHAINSTACK_NODE_URL" TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" MINT_ADDRESS = "ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx" # Phase 1: get pubkeys only (zero-length dataSlice) response = requests.post( CHAINSTACK_RPC, json={ "jsonrpc": "2.0", "id": 1, "method": "getProgramAccounts", "params": [ TOKEN_PROGRAM_ID, { "filters": [ {"dataSize": 165}, {"memcmp": {"offset": 0, "bytes": MINT_ADDRESS}} ], "dataSlice": {"offset": 0, "length": 0} } ] }, timeout=60 ) pubkeys = [item["pubkey"] for item in response.json().get("result", [])] print(f"Found {len(pubkeys)} accounts") # Phase 2: fetch full data in batches of 100 via getMultipleAccounts BATCH_SIZE = 100 for i in range(0, len(pubkeys), BATCH_SIZE): batch = pubkeys[i:i + BATCH_SIZE] response = requests.post( CHAINSTACK_RPC, json={ "jsonrpc": "2.0", "id": 1, "method": "getMultipleAccounts", "params": [ batch, {"encoding": "base64"} ] }, timeout=60 ) accounts = response.json().get("result", {}).get("value", []) print(f"Batch {i // BATCH_SIZE + 1}: fetched {len(accounts)} accounts") ``` ### Filter by Anchor program discriminator Anchor programs use an 8-byte discriminator at the start of each account's data to identify the account type. Filter on it with `memcmp` at offset 0. ```python Python theme={"system"} import requests import hashlib CHAINSTACK_RPC = "CHAINSTACK_NODE_URL" PROGRAM_ID = "YOUR_PROGRAM_ID" # Anchor discriminator: first 8 bytes of SHA-256("account:") discriminator = hashlib.sha256(b"account:UserPosition").digest()[:8] # Convert to base-58 for the memcmp filter import base58 discriminator_b58 = base58.b58encode(discriminator).decode() response = requests.post( CHAINSTACK_RPC, json={ "jsonrpc": "2.0", "id": 1, "method": "getProgramAccounts", "params": [ PROGRAM_ID, { "encoding": "base64", "filters": [ {"memcmp": {"offset": 0, "bytes": discriminator_b58}} ] } ] }, timeout=60 ) accounts = response.json().get("result", []) print(f"Found {len(accounts)} UserPosition accounts") ``` ## Use case A practical use case for `getProgramAccounts` is to retrieve the current state of all accounts associated with a specific Solana program. Common scenarios include: * Listing all holders of an SPL token by filtering on the Token Program with a `memcmp` on the mint address * Retrieving all user positions in a DeFi protocol by filtering on the program's account discriminator * Building a local index of program state for analytics or monitoring * Tracking account changes over time by combining `getProgramAccounts` with `withContext` for slot-consistent snapshots For real-time account monitoring, consider [Solana Geyser with Yellowstone gRPC](/docs/yellowstone-grpc-geyser-plugin) as a streaming alternative. ## Performance tips * Always use filters — `dataSize` as the first filter, then `memcmp` for further narrowing. * Prefer `base64` encoding over `jsonParsed` for performance. Only use `jsonParsed` when you need human-readable output and the program has a registered parser. * Use `dataSlice` to reduce response size when you only need specific fields from account data. * For large result sets, use the [two-phase fetch pattern](#two-phase-fetch-for-large-result-sets) — get pubkeys first, then fetch data in batches with `getMultipleAccounts`. * Be mindful of [rate limits](/docs/limits) — `getProgramAccounts` has lower per-method RPS limits than standard calls. * Do not call `getProgramAccounts` on the Token Program (`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) or Kin (`kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6`) directly — these are [excluded from indexing](/docs/limits). Use [`getTokenAccountsByOwner`](/reference/solana-gettokenaccountsbyowner) instead. ## Error reference Three common errors specific to `getProgramAccounts`: * **`-32602` — unfiltered request blocked** — returned when calling `getProgramAccounts` without filters on a large program (e.g., Token-2022). Add `dataSize` and/or `memcmp` filters. * **`-32010` — program excluded from secondary index** — returned when the program is too large to index (e.g., the Token Program `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`). Use [`getTokenAccountsByOwner`](/reference/solana-gettokenaccountsbyowner) instead. * **`500` — missing config object** — returned when the `params` array contains only the program ID without a configuration object. Always pass a config object as the second parameter, even if empty (`{}`). ## Related methods * [`getTokenAccountsByOwner`](/reference/solana-gettokenaccountsbyowner) — faster alternative when querying SPL Token accounts by wallet address. Uses a dedicated secondary index. * [`getMultipleAccounts`](/reference/solana-getmultipleaccounts) — fetch up to 100 accounts by pubkey in a single call. Best combined with `getProgramAccounts` in the two-phase pattern. * [`getAccountInfo`](/reference/solana-getaccountinfo) — fetch a single account by pubkey. # getRecentBlockhash | Solana Source: https://docs.chainstack.com/reference/solana-getrecentblockhash openapi/solana_node_api/getRecentBlockhash.json POST /2ef4e47bc937d6b758f7c582e8e82a1e Reference for the getRecentBlockhash JSON-RPC method on the Solana blockchain via Chainstack nodes. Parameters, response, and examples included. > [!WARNING] > > **Removed** > > The `getRecentBlockhash` method was removed in Agave 2.0 and returns `Method not found` on current validators. Use [getLatestBlockhash](/reference/solana-getlatestblockhash) instead. The Solana `getRecentBlockhash` method returns a recent block hash from the ledger, and a fee schedule that can be used to compute the cost of submitting a transaction using it. This method is crucial for transaction construction and submission in Solana. The returned blockhash is used to indicate when a transaction was constructed, which is important for security reasons. The fee calculator provides information about the current network fees. ## Parameters This method does not require any parameters. ## Response * `blockhash` — a string representing the blockhash of a recent block. * `feeCalculator` — an object containing: * `lamportsPerSignature` — the current fee in lamports per signature. ## Use case A common use case for `getRecentBlockhash` is in transaction preparation. When constructing a transaction, you need to include a recent blockhash to ensure the transaction is processed in a timely manner and to prevent replay attacks. The fee calculator information can be used to estimate the cost of the transaction. # getRecentPerformanceSamples | Solana Source: https://docs.chainstack.com/reference/solana-getrecentperformancesamples openapi/solana_node_api/getRecentPerformanceSamples.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getRecentPerformanceSamples method returns recent performance samples. getRecentPerformanceSamples on Solana via Chainstack. # Solana `getRecentPerformanceSamples` method The Solana `getRecentPerformanceSamples` method returns recent performance samples. This method provides detailed information about the node's recent performance, including the number of slots and blocks processed, and the number of slots skipped. It is useful for monitoring the node's performance. ## Parameters * `limit` — (optional) the maximum number of performance samples to return. If not provided, the method will return all available performance samples. ## Response * `value` — an array of performance sample objects, each containing: * `numSlots` — the number of slots processed. * `numBlocks` — the number of blocks processed. * `numSkipped` — the number of slots skipped. ## Use case A practical use case for `getRecentPerformanceSamples` is to monitor the node's performance. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getRecentPrioritizationFees | Solana Source: https://docs.chainstack.com/reference/solana-getrecentprioritizationfees openapi/solana_node_api/getRecentPrioritizationFees.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The getRecentPrioritizationFees method returns a list of recent prioritization fees observed in the network. Solana via Chainstack. # Solana `getRecentPrioritizationFees` method The `getRecentPrioritizationFees` method returns a list of recent prioritization fees observed in the network. These prioritization fees represent the per-[compute-unit](/docs/solana-glossary#compute-units) fees paid by transactions to gain priority over other transactions in the same block. The prioritization fee is optional and can be attached to a transaction to increase its likelihood of being included in the next block. A transaction can be prioritized over other transactions with lower or no prioritization fees by paying a higher prioritization fee. > [!NOTE] > > Learn how to add priority fees to your transactions reading [Solana: How to use Priority Fees to unlock faster transactions](/docs/solana-how-to-priority-fees-faster-transactions). ## Parameters * `array` — an array of base-58 encoded public key strings representing Solana account addresses, with a maximum of 128 addresses. ## Response The response is an array of objects, where each object represents a prioritization fee observed in a recent slot (block). Each object has the following properties: * `slot` (u64) — the slot where the transactions associated with the prioritization fees were processed. * `prioritizationFee` (u64) — the per-compute-unit fee in micro-lamports. With no `params`, this is the minimum prioritization fee observed across all transactions in the slot. With `params`, this is the maximum across the supplied accounts of each account's minimum writable-account fee for that slot — that is, the floor fee for the most expensive of your accounts in that slot. The node's prioritization-fee cache stores data from the most recent 150 blocks. ### How the account filter works For each slot in the cache, the node: 1. Looks up the minimum fee paid by any transaction that wrote to each supplied account that slot. 2. Returns the maximum of those per-account minimums. The returned value is a floor — the cheapest fee that landed while writing to the most expensive of your accounts that slot — not a typical or competitive fee. If you omit the `params` array entirely, the response returns the minimum prioritization fee per slot across all transactions. Because every slot contains 0-priority-fee vote transactions, this floor is effectively 0 in every slot and is not useful for fee estimation. > [!WARNING] > > Pass writable state accounts (pool state accounts, reserves, user token accounts) — not program IDs. Programs are typically executable and read-only, so they are not present in the per-account writable fee map. Passing a program ID like a Jupiter or Raydium program returns near-empty data and gives a misleading picture of recent fees. ## Use case A practical use case for `getRecentPrioritizationFees` is to set a floor for your priority fee. By querying recent fees for the specific writable accounts your transaction locks, you see the cheapest competing transactions that landed touching those accounts. To produce a competitive bid, take a high percentile (p95) or the maximum of non-zero values over the 150-slot window and multiply by an urgency factor. See also [Estimate priority fees with getRecentPrioritizationFees](/docs/solana-estimate-priority-fees-getrecentprioritizationfees) for a practical implementation guide. # getSignaturesForAddress | Solana Source: https://docs.chainstack.com/reference/solana-getsignaturesforaddress openapi/solana_node_api/getSignaturesForAddress.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getSignaturesForAddress method returns the confirmed signatures for transactions involving an address. Solana via Chainstack. > [!NOTE] > > **Archive data:** this method supports fetching historical data on [Global Nodes](/docs/global-elastic-node). See [archive methods availability](/docs/limits#solana-archive-methods-availability). # Solana `getSignaturesForAddress` method The Solana `getSignaturesForAddress` method returns the confirmed signatures for transactions involving an address. This method provides a list of signatures for transactions that involve a specific address. It is useful for tracking the transaction history of an address. ## Parameters * `address` — the public key of the address to retrieve signatures for. * `options` — (optional) additional options: * `limit` — the maximum number of signatures to return. * `before` — only return signatures before this signature. * `until` — only return signatures until this signature. ## Response * `value` — an array of signature information objects, each containing: * `signature` — the signature of the transaction. * `slot` — the slot in which the transaction was processed. * `err` — whether the transaction resulted in an error. ## Use case A practical use case for `getSignaturesForAddress` is to track the transaction history of an address. This can be useful for wallets or other applications that need to display or make use of this information. # getSignatureStatuses | Solana Source: https://docs.chainstack.com/reference/solana-getsignaturestatuses openapi/solana_node_api/getSignatureStatuses.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getSignatureStatuses method returns the status of a list of signatures. Available on Solana via Chainstack JSON-RPC nodes. # Solana `getSignatureStatuses` method The Solana `getSignatureStatuses` method returns the status of a list of signatures. This method provides the status of each signature, which indicates whether the transaction was successfully processed, is still being processed, or resulted in an error. It is useful for tracking the status of transactions. ## Parameters * `signatures` — an array of signatures to retrieve the status for. * `options` — (optional) additional options: * `searchTransactionHistory` — whether to search the transaction history for the status of each signature. ## Response * `value` — an array of signature status objects, each containing: * `slot` — the slot in which the transaction was processed. * `confirmations` — the number of confirmations for the transaction. * `err` — whether the transaction resulted in an error. * `status` — the status of the transaction. ## Use case A practical use case for `getSignatureStatuses` is to track the status of transactions. This can be useful for wallets or other applications that need to display or make use of this information. # getSlot | Solana Source: https://docs.chainstack.com/reference/solana-getslot openapi/solana_node_api/getSlot.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getSlot method returns the current slot. This method provides the current slot, which is a unit of time in the Solana network. # Solana `getSlot` method The Solana `getSlot` method returns the current slot. This method provides the current slot, which is a unit of time in the Solana network. It is useful for tracking the progress of the network. See also [Understanding the difference between blocks and slots on Solana](/docs/understanding-the-difference-between-blocks-and-slots-on-solana). ## Parameters This method does not require any parameters. ## Response * `value` — the current slot. ## Use case A practical use case for `getSlot` is to track the progress of the network. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getSlotLeader | Solana Source: https://docs.chainstack.com/reference/solana-getslotleader openapi/solana_node_api/getSlotLeader.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getSlotLeader method returns the current slot leader. getSlotLeader JSON-RPC method available on the Solana blockchain via Chainstack. # Solana `getSlotLeader` method The Solana `getSlotLeader` method returns the current slot leader. This method provides the public key of the current slot leader, which is the validator responsible for producing blocks in the current slot. It is useful for tracking which validator is currently producing blocks. ## Parameters This method does not require any parameters. ## Response * `value` — the public key of the current slot leader. ## Use case A practical use case for `getSlotLeader` is to track which validator is currently producing blocks. This can be useful for network monitoring tools or other applications that need to display or make use of this information. # getSlotLeaders | Solana Source: https://docs.chainstack.com/reference/solana-getslotleaders openapi/solana_node_api/getSlotLeaders.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getSlotLeaders method returns the slot leaders for a given slot range. Available on Solana via Chainstack JSON-RPC nodes. The Solana `getSlotLeaders` method returns the slot leaders for a given slot range. Each slot on Solana has a designated leader validator responsible for producing the block. This method returns the ordered list of validator identities assigned to produce blocks in the requested range. ## Parameters 1. `startSlot` (integer, required) — the start slot. 2. `limit` (integer, required) — the number of slot leaders to return (1–5000). ## Response * `result` (array of strings) — an array of node identity public keys as base58 encoded strings, in order of the slot leaders for each slot in the requested range. ## Use case A common use case for `getSlotLeaders` is in transaction landing optimization. By knowing which validator is the upcoming leader, you can route transactions directly to that validator's TPU port for faster inclusion. This is especially useful in latency-sensitive applications like trading bots and arbitrage. # getStakeMinimumDelegation | Solana Source: https://docs.chainstack.com/reference/solana-getstakeminimumdelegation openapi/solana_node_api/getStakeMinimumDelegation.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getStakeMinimumDelegation method returns the minimum amount of lamports needed to delegate a stake account. Solana via Chainstack. # Solana `getStakeMinimumDelegation` method The Solana `getStakeMinimumDelegation` method returns the minimum amount of lamports needed to delegate a stake account. This method provides the minimum number of lamports required to delegate a stake account. It is useful for understanding the minimum stake required to participate in the network as a delegator. ## Parameters This method does not require any parameters. ## Response * `value` — the minimum number of lamports needed to delegate a stake account. ## Use case A practical use case for `getStakeMinimumDelegation` is to understand the minimum stake required to participate in the network as a delegator. This can be useful for wallets or other applications that need to display or make use of this information. # getSupply | Solana Source: https://docs.chainstack.com/reference/solana-getsupply openapi/solana_node_api/getSupply.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getSupply method returns information about the current supply of lamports in the network. Use it on Solana via Chainstack. > [!NOTE] > > **Not available on the Developer plan** > > The method is available only on the [paid plans](https://chainstack.com/pricing/). A per-method rate limit applies on Global Nodes. See [Solana method limits](/docs/limits#solana-method-limits). The Solana `getSupply` method returns information about the current supply of lamports in the network. This method provides detailed information about the current supply, including the total supply, the circulating supply, and the non-circulating supply. It is useful for understanding the current state of the network's economy. ## Parameters * `commitment` — (optional) the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. ## Response * `value` — the current supply information: * `total` — the total supply of lamports. * `circulating` — the circulating supply of lamports. * `nonCirculating` — the non-circulating supply of lamports. * `nonCirculatingAccounts` — the accounts that hold the non-circulating supply. ## Use case A practical use case for `getSupply` is to understand the current state of the network's economy. This can be useful for wallets or other applications that need to display or make use of this information. # Solana API reference: JSON-RPC and WebSocket quickstart Source: https://docs.chainstack.com/reference/solana-getting-started Use the Solana JSON-RPC and WebSocket APIs on Chainstack to query accounts, send transactions, and build apps on the high-throughput PoH chain. 67 methods, mainnet and devnet, archive, WebSocket subscriptions, and Yellowstone gRPC Geyser. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). The Solana API gives developers programmatic access to the Solana blockchain over JSON-RPC and WebSocket. Chainstack serves it across mainnet and devnet, with archive data, real-time subscriptions, and the Yellowstone gRPC Geyser plugin for low-latency streaming. Solana is not EVM — it has no chain ID. Instead, you point your application at a cluster (mainnet or devnet) through a node endpoint. ## Solana API at a glance | Property | Value | | - | - | | Chain | Solana — Layer 1, proof-of-history plus proof-of-stake | | Network model | Clusters, not chain IDs — mainnet and devnet | | Slot interval | \~400 ms scheduled slots; blocks are produced only when a slot succeeds | | API protocols | JSON-RPC over HTTPS and WebSocket subscriptions over WSS | | Methods served by Chainstack | 67 — 51 HTTP JSON-RPC methods and 16 WebSocket subscription methods | | Real-time streaming | WebSocket `*Subscribe` methods, plus the Yellowstone gRPC Geyser add-on | | Geyser gRPC | Yellowstone gRPC Geyser, mainnet only, paid add-on from the Growth plan | | Networks | Mainnet and devnet, full and archive nodes | | Transaction sending | `sendTransaction` forwarded over QUIC to the current and next leaders | | Request units | 1 RU per full request, 2 RUs per archive request | ## What is the Solana protocol Solana is a permissionless network protocol with parallel smart contract processing. For consensus, Solana uses a combination of proof-of-stake to secure the network and the proof-of-history mechanism to synchronize the network state. Validators on the network stake SOL to get the right to confirm blocks in assigned time slots. To get an assigned time slot, validator nodes use the sequence of hashed events that each of them maintains, which is called proof of history. Solana measures time in slots — scheduled intervals of roughly 400 ms each, designated for validators to attempt block production. Not every slot produces a block, so slot numbers and block heights diverge over time. See [Solana: Understanding block time](/docs/solana-understanding-block-time) for how this affects confirmation latency. ## What is the Solana API The Solana API allows developers to communicate with the Solana blockchain to build applications. To read data from and send transactions to the Solana blockchain, an application must connect to a Solana RPC node. When communicating with a Solana RPC node, the Solana client implements a JSON-RPC specification — a communication protocol that lets one make remote calls and execute them as if they were made locally. For real-time data, the same node exposes WebSocket subscriptions, and Chainstack offers the Yellowstone gRPC Geyser plugin for the lowest-latency streaming. ## Solana API surface Chainstack serves 67 Solana methods across two protocols on the same node — JSON-RPC over HTTPS for queries and transactions, and WebSocket subscriptions over WSS for real-time updates. | API surface | Protocol | Methods | What it covers | | - | - | - | - | | Solana JSON-RPC | HTTPS | 51 | Account and token reads, block and slot queries, transaction lookups, network and validator info, sending and simulating transactions | | WebSocket subscriptions | WSS | 16 | 8 subscribe and 8 unsubscribe methods — `accountSubscribe`, `blockSubscribe`, `logsSubscribe`, `programSubscribe`, `signatureSubscribe`, `slotSubscribe`, `rootSubscribe`, `slotsUpdatesSubscribe` | For the complete, per-method availability matrix, including per-method rate limits and archive support, see [Solana methods](/docs/solana-methods). ### What you can build With a Chainstack Solana endpoint you can: * Read account and program state — fetch one account with `getAccountInfo`, batch with `getMultipleAccounts`, and scan a program's accounts with `getProgramAccounts` using `dataSize` or `memcmp` filters. * Query SPL token data — balances, supply, and largest holders through `getTokenAccountBalance`, `getTokenAccountsByOwner`, `getTokenSupply`, and `getTokenLargestAccounts`. * Read blocks, slots, and history — `getBlock`, `getSlot`, `getBlockTime`, and `getLatestBlockhash`, with archive nodes and `getSignaturesForAddress` for transaction history. * Send and simulate transactions — submit with `sendTransaction` (forwarded over QUIC to the current and next leaders), preflight with `simulateTransaction`, and estimate cost with `getFeeForMessage`. * Set priority fees — sample recent network fees with `getRecentPrioritizationFees` to land transactions under contention. * Stream in real time — subscribe to accounts, programs, logs, slots, and signatures over WebSocket, or use the Yellowstone gRPC Geyser plugin for protobuf streaming straight from validator memory. ## How to start using the Solana API To use the Solana API, you need access to a Solana RPC node. Follow these steps to sign up on Chainstack, deploy a Solana RPC node, and find your endpoint credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the Solana blockchain and use the Solana API to build. ## SDKs and tooling You can call the API directly over HTTP and WebSocket, or use a maintained SDK: * JavaScript and TypeScript — [`@solana/kit`](https://github.com/anza-xyz/kit), the modern tree-shakeable successor to `@solana/web3.js` v1 (`npm install @solana/kit`). * Python — [Solana.py](https://github.com/michaelhly/solana-py), built on the [solders](https://github.com/kevinheavey/solders) core types (`pip install solana`). * Programs — [Anchor 1.0](https://www.anchor-lang.com), the Rust framework for Solana programs, installed through the Anchor Version Manager. * Local testing — [LiteSVM](/docs/solana-litesvm-testing) for fast in-process unit tests and [Surfpool](/docs/solana-surfpool) for a full RPC server with a mainnet fork. * CLI — the Agave [Solana CLI](https://docs.anza.xyz/cli), pointed at your endpoint with `solana config set --url`. Point any SDK at your Chainstack endpoint. See [Solana tooling](/docs/solana-tooling) for setup, the modern stack, and end-to-end examples. > [!NOTE] > > This applies to **Trader Nodes** only. Their HTTPS and WebSocket endpoints are on different hosts — `nd-….p2pify.com` for HTTPS and `ws-nd-….p2pify.com` for WSS — so `solana config set` cannot derive the WebSocket URL from the HTTPS one by swapping the protocol alone. Set it explicitly with `solana config set --ws YOUR_CHAINSTACK_WSS_ENDPOINT`. On **Global Nodes**, HTTPS and WebSocket share the same host (for example, `solana-mainnet.core.chainstack.com`), so the derived WebSocket URL is correct and no `--ws` flag is needed. ## Networks Chainstack supports Solana mainnet and devnet, with full and archive node modes and WebSocket connections. Solana has no debug and trace namespace, and the Yellowstone gRPC Geyser plugin is available on mainnet only. | Feature | Mainnet | Devnet | | - | - | - | | Full nodes | Yes | Yes | | Archive nodes | Yes | Yes | | WebSocket | Yes | Yes | | Yellowstone gRPC Geyser | Yes (paid add-on) | No | | Warp transactions (Trader Nodes) | Yes | Yes | > [!NOTE] > > A full Solana node keeps recent state — roughly the first block available on the node, about 1.5 days' worth. An archive node adds all historical state. For historical reads such as old transactions or balances at a past block, use an archive node. See [Solana archive nodes](/docs/solana-archive-nodes-the-backbone-of-solanas-data-availability-and-developer-tooling). ## Frequently asked questions ### Which Solana clusters does Chainstack support? Chainstack supports Solana mainnet and devnet. Solana is not EVM, so there is no chain ID — you select a cluster when you deploy a node and connect your application to that node's endpoint. Both clusters offer full and archive nodes with WebSocket connections. ### Does Solana have a chain ID? No. Chain IDs are an EVM concept. Solana uses clusters — mainnet and devnet — and a genesis hash to distinguish networks. Point your application at the endpoint for the cluster you want; you do not set a numeric chain ID. ### What is the difference between a full and an archive Solana node? A full node keeps recent state — about the first block available on the node, roughly 1.5 days' worth. An archive node retains all historical state. Use an archive node for historical queries such as `getSignaturesForAddress` over old ranges or `getBlock` for old slots. On Chainstack, archive requests bill at 2 RUs versus 1 RU for a full request. ### How does getProgramAccounts work on Chainstack? `getProgramAccounts` is available on paid plans and requires a `dataSize` or `memcmp` filter, which keeps the scan bounded. For individual accounts, prefer `getAccountInfo` or batch with `getMultipleAccounts`. See [Solana: getAccountInfo vs getMultipleAccounts](/docs/solana-getaccountinfo-getmultipleaccounts). ### When should I use WebSocket subscriptions versus Yellowstone gRPC? WebSocket subscriptions (`accountSubscribe`, `programSubscribe`, `logsSubscribe`, and others) are built into every node and are ideal for reacting to specific accounts, programs, or signatures without polling. Yellowstone gRPC Geyser streams live data directly from validator memory with lower latency and protobuf payloads — better for high-throughput indexing, analytics, and trading. It is a paid add-on available on mainnet from the Growth plan. See [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin). ### How do I land transactions reliably and set priority fees? `sendTransaction` on a Chainstack Solana node forwards over QUIC to the current and next leaders, with no client configuration required. To prioritize under contention, sample recent fees with `getRecentPrioritizationFees` and add a compute-budget priority fee. For the highest landing rate, [Solana Trader Nodes](/docs/solana-trader-nodes) route `sendTransaction` through bloXroute's staked Trader API. See [Solana: Priority fees for faster transactions](/docs/solana-how-to-priority-fees-faster-transactions). ### Which SDK should I use? For JavaScript and TypeScript, use `@solana/kit` — the modern, tree-shakeable successor to `@solana/web3.js` v1, which is now on a maintenance branch. For Python, use Solana.py, which is built on the solders core types. For on-chain programs, use Anchor 1.0. See [Solana tooling](/docs/solana-tooling) for the full modern stack. ## Related Solana resources * [Solana methods](/docs/solana-methods) — complete per-method availability and rate-limit reference * [Mastering Solana](/docs/solana-development) — development overview and quickstart * [Solana tooling](/docs/solana-tooling) — the modern SDK and testing stack * [Yellowstone gRPC Geyser plugin](/docs/yellowstone-grpc-geyser-plugin) — low-latency streaming from validator memory * [Solana Trader Nodes with Warp transactions](/docs/solana-trader-nodes) — staked transaction routing for high landing rates * [Solana: Priority fees for faster transactions](/docs/solana-how-to-priority-fees-faster-transactions) — land transactions under contention * [Solana: Estimate priority fees with RPC methods](/docs/solana-estimate-priority-fees-getrecentprioritizationfees) — size your priority fee from network data * [Solana: getAccountInfo vs getMultipleAccounts](/docs/solana-getaccountinfo-getmultipleaccounts) — choose the right account-read method * [Solana: PDAs and cross-program invocations](/docs/solana-program-derived-addresses-and-cross-program-invocations) — program-derived addresses and CPIs * [Solana: Anchor development](/docs/solana-anchor-development) — build and test programs with Anchor * [Solana: Fast unit testing with LiteSVM](/docs/solana-litesvm-testing) — in-process program tests * [Solana: Understanding block time](/docs/solana-understanding-block-time) — slots, blocks, and confirmation latency * [Solana archive nodes](/docs/solana-archive-nodes-the-backbone-of-solanas-data-availability-and-developer-tooling) — historical data and tooling * [Solana MCP server](/docs/solana-mcp-server) — drive a Solana node from an AI agent # getTokenAccountBalance | Solana Source: https://docs.chainstack.com/reference/solana-gettokenaccountbalance openapi/solana_node_api/getTokenAccountBalance.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getTokenAccountBalance method returns the token balance of an account. getTokenAccountBalance on Solana via Chainstack. # Solana `getTokenAccountBalance` method The Solana `getTokenAccountBalance` method returns the token balance of an account. This method provides the token balance of a specific account, which is the amount of tokens the account holds. It is useful for tracking the token balance of an account. > [!NOTE] > > The interactive example is querying on the Solana mainnet the top USDC token account holder, which is Circle. ## Parameters * `accountPubkey` — the public key of the account to retrieve the token balance for. * `commitment` — (optional) the level of commitment desired: * `processed` — the node has processed the block and the block may be on a fork. * `confirmed` — the block is confirmed by the cluster as not being on a fork. * `finalized` — the block is finalized by the cluster. ## Response * `value` — the token balance of the account: * `amount` — the amount of tokens the account holds. * `decimals` — the number of decimal places the token has. * `uiAmount` — the amount of tokens the account holds, adjusted for decimals. ## Use case A practical use case for `getTokenAccountBalance` is to track the token balance of an account. This can be useful for wallets or other applications that need to display or make use of this information. # getTokenAccountsByDelegate | Solana Source: https://docs.chainstack.com/reference/solana-gettokenaccountsbydelegate openapi/solana_node_api/getTokenAccountsByDelegate.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getTokenAccountsByDelegate method returns all SPL token accounts that have been delegated to a given account. On Solana. The Solana `getTokenAccountsByDelegate` method returns all SPL token accounts that have been delegated to a given account. This method is useful for querying which token accounts a specific address has been granted spending authority over via the SPL token `approve` instruction. ## Parameters 1. `pubkey` (string, required) — the pubkey of the delegate account to query, as a base58 encoded string. 2. `filter` (object, required) — filter results by token mint or program ID. One of: * `mint` (string) — the pubkey of the specific token mint to limit accounts to. * `programId` (string) — the pubkey of the Token program that owns the accounts (`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` for SPL Token, `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` for Token-2022). 3. `config` (object, optional) — configuration object containing: * `encoding` (string) — encoding for account data. Values: `base58`, `base64`, `base64+zstd`, `jsonParsed`. Default: `base64`. `jsonParsed` returns parsed JSON data for known token account structures. * `commitment` (string) — the commitment level. ## Response The response includes a `result` object with: * `context` — an object containing `slot`. * `value` — an array of objects, each containing: * `pubkey` (string) — the token account address. * `account` — an object with: * `lamports` (integer) — the account balance in lamports. * `owner` (string) — the program that owns the account. * `data` — the account data, encoded as requested. With `jsonParsed`, returns the parsed token account structure including `mint`, `owner`, `tokenAmount`, `delegate`, and `delegatedAmount`. * `executable` (boolean) — whether the account is executable. * `rentEpoch` (integer) — the epoch at which rent is next due. ## Use case A common use case for `getTokenAccountsByDelegate` is in DeFi protocols that use token delegation. For example, a lending protocol can query all token accounts delegated to its program to display a user's approved spending limits, or an analytics tool can track delegation patterns across the network. # getTokenAccountsByOwner | Solana Source: https://docs.chainstack.com/reference/solana-gettokenaccountsbyowner openapi/solana_node_api/getTokenAccountsByOwner.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getTokenAccountsByOwner method retrieves a list of accounts owned by a specified wallet, filtered by their associated token. > [!NOTE] > > **Not available on the Developer plan** > > The method is available only on the [paid plans](https://chainstack.com/pricing/). A per-method rate limit applies on Global Nodes. See [Solana method limits](/docs/limits#solana-method-limits). The Solana `getTokenAccountsByOwner` method retrieves a list of accounts owned by a specified wallet, filtered by their associated token. This method is useful for identifying all accounts associated with a particular owner and a specific token, allowing for a comprehensive view of token distribution and ownership. > [!NOTE] > > The interactive example fetches the [GME tokens](https://solscan.io/token/8wXtPeU6557ETkp9WHFY1n1EcU6NxDvbAggHGsMYiHsB#holders) owned by an [account on Solana mainnet](https://solscan.io/account/CEXq1uy9y15PL2Wb4vDQwQfcJakBGjaAjeuR2nKLj8dk). ## Parameters 1. `ownerPubkey` (string, required) — the base-58 encoded public key of the account owner to query. 2. `filter` (object, required) — filter results by token mint or program ID. Must include exactly one of: * `mint` (string) — the base-58 encoded public key of a specific token mint to limit accounts to. * `programId` (string) — the base-58 encoded public key of the Token program that owns the accounts (`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` for SPL Token, `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` for Token-2022). 3. `config` (object, optional) — configuration object containing: * `commitment` (string) — the level of commitment desired for the query. * `minContextSlot` (integer) — the minimum slot at which the request can be evaluated. * `dataSlice` (object) — request a slice of the account's data with `length` and `offset` fields. Only available for non-`jsonParsed` encodings. * `encoding` (string) — the format of the returned account data. Values: `base58`, `base64`, `base64+zstd`, `jsonParsed`. With `jsonParsed`, returns parsed token account data including mint, owner, token amount, delegate, and state. ## Response * `context` — an object containing `slot` (the slot at which the data was fetched) and `apiVersion`. * `value` — an array of objects, each containing: * `pubkey` (string) — the token account address as a base-58 encoded string. * `account` — an object with: * `lamports` (u64) — number of lamports assigned to this account. * `owner` (string) — base-58 encoded public key of the program that owns this account. * `data` — account data. With `jsonParsed` encoding, returns a `{program, parsed, space}` object where `parsed.info` contains: `mint`, `owner`, `tokenAmount` (with `amount`, `decimals`, `uiAmount`, `uiAmountString`), `delegate`, `delegatedAmount`, `state` (`initialized` or `frozen`), `isNative`, `rentExemptReserve`, `closeAuthority`, and `extensions` (for Token-2022 accounts). * `executable` (boolean) — whether the account contains a program. * `rentEpoch` (u64) — the epoch at which this account will next owe rent. * `space` (u64) — the data size of the account in bytes. ## Use Case A practical use case for `getTokenAccountsByOwner` is for wallet interfaces or financial tracking applications to list all accounts associated with a particular owner and token type. This can be especially useful for assessing the distribution of tokens across different accounts and for transaction history analysis. # getTokenLargestAccounts | Solana Source: https://docs.chainstack.com/reference/solana-gettokenlargestaccounts openapi/solana_node_api/getTokenLargestAccounts.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The getTokenLargestAccounts method is a useful RPC method in Solana that returns the 20 largest accounts for a specific SPL Token type. ## Solana `getTokenLargestAccounts` Method > [!WARNING] > > `getTokenLargestAccounts` is available on [Dedicated Nodes](/docs/dedicated-node) only. The `getTokenLargestAccounts` method is a useful RPC method in Solana that returns the 20 largest accounts for a specific SPL Token type. This method offers a snapshot of the largest token holders, providing insights into the token's distribution and potential market dynamics. This can be particularly useful for developers, analysts, and investors looking to gauge the distribution of tokens in the Solana ecosystem. > [!NOTE] > > The interactive example fetches the top 20 holders of the [Jupiter](https://solscan.io/token/JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN#holders) token. ## Parameters * `Pubkey` — a base-58 encoded string that represents the public key of the token Mint to query. * `Config` (optional) — an object to customize the query: * `commitment` (optional) — specifies the level of commitment desired for the query, allowing users to choose the state the returned information represents. ## Response * `address` — the base-58 encoded string of the token account address. * `amount` — the token account balance represented as a string of u64, without considering the decimals. * `decimals` — the number of base ten digits to the right of the decimal place, indicating the token's divisibility. * `uiAmountString` — the token account balance as a string, adjusted for mint-prescribed decimals, providing a more understandable representation of the balance. ## Use case This method is particularly useful for portfolio management applications, market analysis tools, and blockchain explorers that aim to provide detailed insights into token distribution in Solana. By identifying the largest accounts, users can understand the potential influence of key holders on token price and liquidity. # getTokenSupply | Solana Source: https://docs.chainstack.com/reference/solana-gettokensupply openapi/solana_node_api/getTokenSupply.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getTokenSupply method returns the total supply of an SPL token. This method is useful for getting the circulating supply of a token. The Solana `getTokenSupply` method returns the total supply of an SPL token. This method is useful for getting the circulating supply of a token, building token analytics dashboards, and verifying token mint configurations. > [!NOTE] > > **Per-method rate limit** > > Global Nodes apply a per-method RPS limit to `getTokenSupply`. See [Solana method limits](/docs/limits#solana-method-limits). ## Parameters 1. `pubkey` (string, required) — the pubkey of the token mint to query, as a base58 encoded string. 2. `commitment` (string, optional) — the commitment level. Values: `processed`, `confirmed`, `finalized`. ## Response The response includes a `result` object with: * `context` — an object containing `slot`, the slot at which the data was fetched. * `value` — an object containing: * `amount` (string) — the raw total supply without decimals, as a string representation of a u64. * `decimals` (integer) — the number of base-10 digits to the right of the decimal place. * `uiAmount` (number, nullable) — the total supply using mint-prescribed decimals. Deprecated. * `uiAmountString` (string) — the total supply as a string, using mint-prescribed decimals. ## Use case A common use case for `getTokenSupply` is in token analytics. For example, you can query the USDC mint to check the total USDC supply on Solana, or verify that a newly deployed token has the expected supply before interacting with it in a DeFi protocol. # getTransactionCount | Solana Source: https://docs.chainstack.com/reference/solana-gettransactioncount openapi/solana_node_api/getTransactionCount.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getTransactionCount method returns the total number of transactions that the node has processed since genesis. On Solana. The Solana `getTransactionCount` method returns the total number of transactions that the node has processed since genesis. This method is useful for monitoring network activity and throughput over time. ## Parameters 1. `commitment` (string, optional) — the commitment level. Values: `processed`, `confirmed`, `finalized`. ## Response * `result` (integer) — the total number of transactions processed by the node. ## Use case A common use case for `getTransactionCount` is in network monitoring dashboards that track Solana's throughput over time. By polling this method at regular intervals, you can calculate the transactions-per-second rate and detect periods of high or low network activity. # getVersion | Solana Source: https://docs.chainstack.com/reference/solana-getversion openapi/solana_node_api/getVersion.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getVersion method returns the current Solana version running on the node. Available on Solana via Chainstack JSON-RPC nodes. The Solana `getVersion` method returns the current Solana version running on the node. This method is useful for verifying which validator client and version your RPC node is running, which can affect feature availability and behavior. ## Parameters This method does not require any parameters. ## Response The response includes a `result` object with: * `solana-core` (string) — the software version of the Solana node (e.g., `"2.1.10"`). * `feature-set` (integer) — the unique identifier of the current software's feature set. ## Use case A common use case for `getVersion` is in infrastructure monitoring to verify that your node is running the expected validator version. This is especially relevant during network upgrades (e.g., Agave 2.0) to confirm your node has updated and supports the latest RPC methods. # getVoteAccounts | Solana Source: https://docs.chainstack.com/reference/solana-getvoteaccounts openapi/solana_node_api/getVoteAccounts.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana getVoteAccounts method returns the account info and associated stake for all voting validators in the current epoch. On Solana. The Solana `getVoteAccounts` method returns the account info and associated stake for all voting validators in the current epoch. This method is useful for querying validator information, monitoring network decentralization, and building staking analytics. ## Parameters 1. `config` (object, optional) — configuration object containing: * `votePubkey` (string) — filter results to a single validator vote address. * `commitment` (string) — the commitment level. ## Response The response includes a `result` object with: * `current` — an array of vote accounts that have voted in the current epoch. Each object contains: * `votePubkey` (string) — the vote account address. * `nodePubkey` (string) — the validator identity. * `activatedStake` (integer) — the stake in lamports delegated to this vote account and active in this epoch. * `epochVoteAccount` (boolean) — whether the vote account is staked for this epoch. * `commission` (integer) — the percentage (0–100) of rewards payout owed to the vote account. * `lastVote` (integer) — the most recent slot voted on by this vote account. * `epochCredits` — an array of `[epoch, credits, previousCredits]` tuples. * `rootSlot` (integer) — the current root slot for this vote account. * `delinquent` — an array of vote accounts that have not voted recently, with the same structure as `current`. ## Use case A common use case for `getVoteAccounts` is in staking dashboards and validator monitoring tools. You can use it to list all active validators, check their commission rates, and identify delinquent validators that may be underperforming. # minimumLedgerSlot | Solana Source: https://docs.chainstack.com/reference/solana-minimumledgerslot openapi/solana_node_api/minimumLedgerSlot.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana minimumLedgerSlot method returns the lowest slot that the node has information about in its ledger. Solana via Chainstack. The Solana `minimumLedgerSlot` method returns the lowest slot that the node has information about in its ledger. As validators prune old ledger data, this value increases over time. This method is useful for determining how far back in history a node can serve data. ## Parameters This method does not require any parameters. ## Response * `result` (integer) — the minimum ledger slot number available on the node. ## Use case A common use case for `minimumLedgerSlot` is in determining whether a node can serve historical data for a given slot. Before requesting block or transaction data for a specific slot, you can check `minimumLedgerSlot` to verify the data is still available. This is especially relevant when deciding between a full node and an [archive node](/docs/solana-archive-nodes-the-backbone-of-solanas-data-availability-and-developer-tooling). # sendTransaction | Solana Source: https://docs.chainstack.com/reference/solana-sendtransaction openapi/solana_node_api/sendTransaction.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana sendTransaction method submits a fully signed transaction to the cluster for processing. Use it on Solana via Chainstack. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). The Solana `sendTransaction` method submits a fully signed transaction to the cluster for processing. The transaction must be signed and serialized before calling this method. On success, the method returns the first transaction signature embedded in the transaction (base58 encoded), which serves as the transaction identifier. > [!NOTE] > > **Warp transactions** > > Chainstack Solana nodes support [Warp transactions](/docs/warp-transactions) for faster transaction landing. Warp transactions route your `sendTransaction` calls through the bloXroute relay network for optimized propagation to the current leader. See [Solana Trader Nodes](/docs/solana-trader-nodes) for details. ## Parameters 1. `transaction` (string, required) — fully signed transaction, as a base64 or base58 encoded string. 2. `config` (object, optional) — configuration object containing the following fields: * `encoding` (string) — encoding used for the transaction data. Values: `base58` (slow, deprecated), `base64` (recommended). Default: `base58`. The encoding also caps the transaction size: `base64` accepts up to 4096 raw bytes, `base58` only 1232. A [version 1 transaction](/docs/solana-transaction-v1) above 1232 bytes must be submitted as `base64`, otherwise the call responds with error `-32602` and a `too large` message. * `skipPreflight` (bool) — when `true`, skip the preflight transaction checks (signature verification and simulation). Default: `false`. When `true`, the `preflightCommitment` field is ignored and the node internally uses `processed`. * `preflightCommitment` (string) — commitment level to use for the preflight simulation. Default: `finalized`. Ignored when `skipPreflight` is `true`. * `maxRetries` (integer) — maximum number of times the RPC node retries sending the transaction to the leader. If not provided, the node retries the transaction until it is finalized or the blockhash expires. * `minContextSlot` (integer) — the minimum slot at which the request can be evaluated. The preflight checks performed when `skipPreflight` is `false`: 1. **Signature verification** — confirms that all required signatures are present and valid. 2. **Transaction simulation** — runs the transaction against the bank state at the `preflightCommitment` level. If simulation fails, the response returns the same error structure as [simulateTransaction](/reference/solana-simulatetransaction), including `err`, `logs`, and `unitsConsumed`. ## Response * `result` (string) — the first transaction signature embedded in the transaction, as a base58 encoded string. Use this signature with [getSignatureStatuses](/reference/solana-getsignaturestatuses) to confirm the transaction. If the transaction fails preflight checks, the response contains an `error` object with simulation failure details including `err`, `logs`, and `unitsConsumed`. ## Use case The `sendTransaction` method is the primary way to submit transactions to the Solana network. Common patterns include: 1. **Build, sign, send** — construct a transaction with instructions, sign it with the appropriate keypairs, serialize it, and submit via `sendTransaction`. 2. **Preflight simulation** — by default, the node runs [simulateTransaction](/reference/solana-simulatetransaction) before forwarding to the leader. Set `skipPreflight: true` to bypass this check when you need maximum speed (e.g., arbitrage, sniping). 3. **Confirmation polling** — after sending, poll [getSignatureStatuses](/reference/solana-getsignaturestatuses) until the transaction reaches the desired commitment level. 4. **Retry with fresh blockhash** — if the transaction expires (`TransactionExpiredBlockheightExceededError`), rebuild with a fresh [getLatestBlockhash](/reference/solana-getlatestblockhash) and resend. See [How to handle the transaction expiry error](/docs/solana-how-to-handle-the-transaction-expiry-error). > [!WARNING] > > The **Try it** example uses a pre-built transaction with an expired blockhash. It will return a blockhash-related error. To test with a live transaction, serialize and sign your own transaction, then paste the base64-encoded string into the first parameter. # simulateTransaction | Solana Source: https://docs.chainstack.com/reference/solana-simulatetransaction openapi/solana_node_api/simulateTransaction.json POST /2ef4e47bc937d6b758f7c582e8e82a1e The Solana simulateTransaction method simulates sending a transaction. Reference for simulateTransaction on Solana via Chainstack. The Solana `simulateTransaction` method simulates sending a transaction. This method does not send a transaction, but rather runs the transaction and returns the result of the execution. This method is useful for predicting how a transaction will affect the state of the blockchain without actually committing any changes. It can be used to estimate transaction fees, check for potential errors, and understand the effects of a transaction before sending it. ## Parameters 1. `transaction` (string, required) — a base64 or base58 encoded string representing the transaction to simulate. 2. `config` (object, optional) — configuration object containing: * `encoding` (string) — encoding used for the transaction data. Values: `base58` (deprecated), `base64` (recommended). Default: `base58`. * `commitment` (string) — the bank state to simulate against. Default: `finalized`. * `sigVerify` (boolean) — when `true`, verify transaction signatures. Default: `false`. Conflicts with `replaceRecentBlockhash`. * `replaceRecentBlockhash` (boolean) — when `true`, replace the transaction's recent blockhash with the most recent blockhash from the bank. Default: `false`. Useful for simulating transactions with expired blockhashes. Conflicts with `sigVerify`. * `minContextSlot` (integer) — the minimum slot at which the request can be evaluated. * `innerInstructions` (boolean) — when `true`, include inner instructions in the response. Default: `false`. * `accounts` (object) — configuration for returning post-simulation account state: * `addresses` (array of strings) — array of account addresses to return. * `encoding` (string) — encoding for the returned account data. Values: `base64`, `base64+zstd`, `jsonParsed`. ## Response The response includes a `result` object with: * `err` — null if the simulation was successful, otherwise a transaction error object. * `logs` (array of strings) — log messages the transaction produced during simulation. * `unitsConsumed` (u64) — number of compute units consumed by the transaction. * `accounts` — an array of account objects (or null) corresponding to the `accounts.addresses` request field. * `returnData` — data returned by the last program that invoked `sol_set_return_data`. Contains `programId` (string) and `data` (`[base64_string, "base64"]` tuple). * `innerInstructions` — array of inner instruction sets (when `innerInstructions: true` in the request). Each set contains `index` (the instruction index) and `instructions` (array of inner instruction objects). * `fee` (u64, nullable) — the transaction fee in lamports. * `preBalances` (array of u64) — account balances before the simulation. * `postBalances` (array of u64) — account balances after the simulation. * `preTokenBalances` — token balances before the simulation. * `postTokenBalances` — token balances after the simulation. * `loadedAddresses` — addresses loaded from address lookup tables. Contains `writable` (array of strings) and `readonly` (array of strings). * `replacementBlockhash` — present when `replaceRecentBlockhash: true`. Contains `blockhash` (string) and `lastValidBlockHeight` (u64). ## Use case A common use case for `simulateTransaction` is to estimate compute units, predict fee costs, and verify that a transaction will succeed before submitting it via [sendTransaction](/reference/solana-sendtransaction). This is especially useful for: * Estimating the exact compute units needed to set via `ComputeBudgetProgram.setComputeUnitLimit` * Debugging failed transactions by inspecting `logs` and `innerInstructions` * Previewing token balance changes with `preTokenBalances` and `postTokenBalances` * Checking post-execution account state via the `accounts` config # Pathfinder RPC version removal 0.23.0 | Starknet Source: https://docs.chainstack.com/reference/starknet-pathfinder-0-23-0-rpc-version-removal Pathfinder 0.23.0 removes Starknet JSON-RPC API versions 0.6, 0.7, and 0.8, and serves 0.9 by default on the root endpoint. Learn what breaks and how to migrate to v0_9 or v0_10. > [!WARNING] > > **Breaking change notice** > > With the roll-out of Pathfinder node version [0.23.0](https://github.com/equilibriumco/pathfinder/releases/tag/v0.23.0), Starknet JSON-RPC API versions **0.6, 0.7, and 0.8 are removed**, and the default version served on the `/` path changes to **0.9**. Requests to `/rpc/v0_6`, `/rpc/v0_7`, or `/rpc/v0_8` now fail. Migrate to `/rpc/v0_9` or `/rpc/v0_10` before the upgrade reaches your node. This change affects you if your application pins one of the removed API versions in its endpoint URL, or if it relies on the default `/` endpoint serving version 0.8. ## What changed Pathfinder 0.23.0 makes two changes to JSON-RPC API version support: * **Removed** — JSON-RPC API versions 0.6, 0.7, and 0.8. The `/rpc/v0_6`, `/rpc/v0_7`, and `/rpc/v0_8` paths no longer serve requests. * **Default version** — the root (`/`) endpoint now serves JSON-RPC API version 0.9. Previously it served 0.8. The supported versions after the upgrade are: | JSON-RPC API version | Endpoint path | Notes | | - | - | - | | 0.9 | `/rpc/v0_9` | Also served on the default `/` path | | 0.10 | `/rpc/v0_10` | Latest available version | ## What breaks A request to a removed version path returns an HTTP `404` with a JSON-RPC error. For example, calling `/rpc/v0_8` after the upgrade: ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "error": { "code": -32603, "message": "Internal error" } } ``` Applications that call the default `/` endpoint keep working, but they now receive JSON-RPC 0.9 responses instead of 0.8. Review the [Starknet spec differences](https://github.com/starkware-libs/starknet-specs/releases) between 0.8 and 0.9 for any response-shape changes that affect your code. ## Checking the current API version Verify which JSON-RPC API version an endpoint serves with the `starknet_specVersion` method: ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_PATHFINDER_ENDPOINT \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "starknet_specVersion", "params": [], "id": 1 }' ``` Against the default endpoint this returns `0.9.0` after the upgrade. Append `/rpc/v0_10` to the endpoint to confirm the 0.10 version is served. ## Migration guide ### If you pin a removed version (v0\_6, v0\_7, v0\_8) Move your requests to a supported version. Version 0.9 is the closest to the removed 0.8: **Before (removed after 0.23.0):** ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_PATHFINDER_ENDPOINT/rpc/v0_8 \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "starknet_specVersion", "params": [], "id": 1 }' ``` **After (pin v0.9):** ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_PATHFINDER_ENDPOINT/rpc/v0_9 \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "starknet_specVersion", "params": [], "id": 1 }' ``` ### If you use the default endpoint No URL change is required — the default `/` endpoint keeps working and now serves 0.9. Test your application against 0.9 before the upgrade reaches your node, because response shapes can differ between spec versions. To pin the version explicitly and avoid future default changes, append `/rpc/v0_9`. ## Best practices 1. **Always pin the API version explicitly** in production applications (for example, `/rpc/v0_9`) so a future default change does not affect you. 2. **Test against 0.9** before the upgrade, and review the spec differences from 0.8. 3. **Check the spec changes** to understand new features and breaking changes between versions. ## Additional resources * [Chainstack changelog: Starknet Pathfinder v0.23.0](/changelog/chainstack-updates-july-21-2026) — the dated release note for this change * [Pathfinder v0.23.0 release notes](https://github.com/equilibriumco/pathfinder/releases/tag/v0.23.0) — details of this release * [Pathfinder GitHub repository](https://github.com/equilibriumco/pathfinder) — latest version information * [Starknet spec releases](https://github.com/starkware-libs/starknet-specs/releases) — complete changelog and differences between spec versions # Pathfinder default API version change 0.19.0 | Starknet Source: https://docs.chainstack.com/reference/starknet-pathfinder-default-api-version-change Pathfinder 0.19.0 changes the default Starknet JSON-RPC API at the root endpoint from v0.7.1 to v0.8.0. Learn how to verify and migrate safely. > [!WARNING] > > **Breaking change notice** > > With the roll-out of Pathfinder node version [0.19.0](https://github.com/equilibriumco/pathfinder/releases/tag/v0.19.0), the default version of the JSON-RPC API served on the `/` path has changed to 0.8.0. Previously, the Starknet spec version served on the default endpoint was 0.7.1. > [!NOTE] > > This page documents the 0.19.0 default change. A later upgrade, [Pathfinder 0.23.0](/reference/starknet-pathfinder-0-23-0-rpc-version-removal), removed versions 0.6, 0.7, and 0.8 and moved the default to 0.9. If your node runs 0.23.0 or later, the current default is 0.9 and versions 0.7 and 0.8 in the examples below are no longer available. This change affects how you interact with Starknet through the Pathfinder node if you're using the default endpoint without specifying a specific API version. ## What changed The default JSON-RPC API version served on the root (`/`) endpoint has been updated: * **Before upgrade**: Default endpoint served JSON-RPC API version 0.7.1 * **After upgrade**: Default endpoint now serves JSON-RPC API version 0.8.0 ## Checking the current API version You can verify which JSON-RPC API version is being served on your endpoint using the `starknet_specVersion` method: ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_PATHFINDER_ENDPOINT \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "starknet_specVersion", "params": [], "id": 1 }' ``` ## Accessing different API versions Different JSON-RPC API spec versions are served over specific endpoints. You can access them by appending the version path to your Chainstack Starknet Pathfinder endpoint: * **Version 0.7**: `CHAINSTACK_STARKNET_PATHFINDER_ENDPOINT/rpc/v0_7` * **Version 0.8**: `CHAINSTACK_STARKNET_PATHFINDER_ENDPOINT/rpc/v0_8` * **Version 0.9**: `CHAINSTACK_STARKNET_PATHFINDER_ENDPOINT/rpc/v0_9` ## Migration guide ### Continuing to use version 0.7.1 If your application requires the previous JSON-RPC API version 0.7.1, you can still access it by explicitly specifying the version in your endpoint URL: **Before (using default endpoint):** ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_PATHFINDER_ENDPOINT \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "starknet_getBlockWithTxHashes", "params": {"block_id": "latest"}, "id": 1 }' ``` **After (explicitly using v0.7):** ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_PATHFINDER_ENDPOINT/rpc/v0_7 \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "starknet_getBlockWithTxHashes", "params": {"block_id": "latest"}, "id": 1 }' ``` ### Using the new default (v0.8) If you want to use the new default version 0.8.0, you can either: 1. Continue using the default endpoint (`/`) which now serves v0.8 2. Explicitly specify `/rpc/v0_8` for clarity ## Version differences To understand the differences between the JSON-RPC API versions and what changes you might need to make to your application, refer to the official Starknet specifications: * [Starknet Specs Releases](https://github.com/starkware-libs/starknet-specs/releases) - Complete changelog and differences between spec versions ## Best practices 1. **Always specify the API version explicitly** in production applications to avoid unexpected changes when defaults are updated 2. **Test your application** with the new API version before migrating 3. **Review the specification changes** to understand new features and breaking changes between versions ## Additional resources * [Chainstack changelog: Starknet Pathfinder v0.19.0](/changelog/chainstack-updates-august-19-2025) - the dated release note for this change * [Pathfinder GitHub repository](https://github.com/equilibriumco/pathfinder) - Latest version information and updates * [Starknet OpenRPC spec](https://github.com/starkware-libs/starknet-specs) - Complete API specification for all versions * [Pathfinder v0.19.0 Release Notes](https://github.com/equilibriumco/pathfinder/releases/tag/v0.19.0) - Detailed information about this release # Pathfinder methods deprecation notice 0.17.0 | Starknet Source: https://docs.chainstack.com/reference/starknet-pathfinder-methods-deprecation Three Pathfinder-specific methods have been deprecated and replaced with standardized Starknet methods. Chainstack Starknet reference. > [!WARNING] > > **Deprecated methods notice** > > The following Pathfinder-specific methods have been deprecated starting from Pathfinder version \[0.17.0] - 2025-06-29. Please use the recommended replacements listed below. Three Pathfinder-specific methods have been deprecated and replaced with standardized Starknet methods. This deprecation aligns with the broader Starknet ecosystem standardization efforts. ## Deprecated methods The following methods are no longer supported in Pathfinder version [0.17.0](https://github.com/equilibriumco/pathfinder/blob/v0.17.0/CHANGELOG.md#removed) and later: * `pathfinder_getProof` * `pathfinder_getClassProof` * `pathfinder_getTransactionStatus` ## Migration guide ### pathfinder\_getProof → starknet\_getStorageProof **Deprecated call:** ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_ENDPOINT \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "pathfinder_getProof", "params": { "block_id": {"block_number": 1543097}, "contract_address": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "keys": ["0x5496768776e3db30053404f18067d81a6e06f5a2b0de326e21298fd9d569a9a"] }, "id": 1 }' ``` **Replacement call:** ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_ENDPOINT/rpc/v0_9 \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "starknet_getStorageProof", "params": { "block_id": {"block_number": 1543097}, "contract_addresses": ["0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"], "contracts_storage_keys": [{ "contract_address": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "storage_keys": ["0x5496768776e3db30053404f18067d81a6e06f5a2b0de326e21298fd9d569a9a"] }] }, "id": 1 }' ``` ### pathfinder\_getClassProof → starknet\_getStorageProof **Deprecated call:** ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_ENDPOINT/rpc/pathfinder/v0_1 \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "pathfinder_getClassProof", "params": { "block_id": {"block_number": 1543097}, "class_hash": "0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918" }, "id": 1 }' ``` **Replacement call:** ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_ENDPOINT/rpc/v0_9 \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "starknet_getStorageProof", "params": { "block_id": {"block_number": 1543097}, "class_hashes": ["0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918"] }, "id": 1 }' ``` ### pathfinder\_getTransactionStatus → starknet\_getTransactionStatus **Deprecated call:** ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_ENDPOINT/rpc/pathfinder/v0_1 \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "pathfinder_getTransactionStatus", "params": { "transaction_hash": "0x01a5da27c511d58ef4220217a383f4c2ebf040ecf0d712dc33183223e9768d68" }, "id": 1 }' ``` **Replacement call:** ```bash theme={"system"} curl -X POST CHAINSTACK_STARKNET_ENDPOINT/rpc/v0_9 \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "starknet_getTransactionStatus", "params": { "transaction_hash": "0x01a5da27c511d58ef4220217a383f4c2ebf040ecf0d712dc33183223e9768d68" }, "id": 1 }' ``` ## Key changes * **Endpoint updates** — Note the endpoint path changes from `/rpc/pathfinder/v0_1` to `/rpc/v0_9` for the replacement methods * **Parameter structure** — The replacement methods use standardized Starknet parameter structures * **Method names** — All replacement methods use the `starknet_` prefix instead of `pathfinder_` ## Additional resources * [Chainstack changelog: Starknet Pathfinder v0.17.0](/changelog/chainstack-updates-june-29-2025) - the dated release note for this change * [Pathfinder GitHub repository](https://github.com/equilibriumco/pathfinder) - for the latest version information * [Starknet OpenRPC spec](https://github.com/starkware-libs/starknet-specs) - for complete API specification * [Pathfinder changelog](https://github.com/equilibriumco/pathfinder/blob/v0.17.0/CHANGELOG.md#removed) - for detailed deprecation information # starknet_call | Starknet Source: https://docs.chainstack.com/reference/starknet-starknetcall openapi/starknet_node_api/starknet_call.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that executes a function call to a contract without creating a Starknet transaction. Chainstack Starknet reference. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that executes a function call to a contract without creating a Starknet transaction. It allows you to call a function in a contract and returns the value of the function that you run on the contract. Using this call will not create a transaction; hence, it will not change the state. ## Parameters * `request` — the details of the function call: * `contract_address` — the address of the contract to be called. * `entry_point_selector` — the selector of the function to be called. * `calldata` — the parameters passed to the function. * `block_id` — the hash of the requested block, or number (height) of the requested block, or a block tag, for the block referencing the state or call the transaction on. ## Response * `result` — the function's return value, as defined in the Cairo output. ## Use case A practical use case for `starknet_call` is to interact with a contract's function without creating a transaction or changing the state. This can be useful for querying contract state or testing function calls. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl --request POST \ --url 'https://alpha-mainnet.starknet.io/feeder_gateway/call_contract?blockNumber=pending' \ --header 'Content-Type: application/json' \ --data-raw '{ "contract_address": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "entry_point_selector": "0x2e4263afad30923c891518314c3c95dbe830a16874e8abc5777a9a20b54c76e", "calldata": ["0x03a20d4f7b4229e7c4863dab158b4d076d7f454b893d90a62011882dc4caca2a"] }' ``` # starknet_estimateFee | Starknet Source: https://docs.chainstack.com/reference/starknet-starknetestimatefee openapi/starknet_node_api/starknet_estimateFee.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that returns an estimate of the fee for Starknet transactions. starknet_estimateFee on Starknet via Chainstack. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that returns an estimate of the fee for Starknet transactions. It estimates the resources required by transactions when applied on a given state. A client or wallet can use this estimate to determine the appropriate amount of fee to include in a transaction to ensure that the network processes it. ## Parameters * `request` — the transaction to estimate. * `block_id` — the hash of the requested block, or number (height) of the requested block, or a block tag, for the block referencing the state or call the transaction on. ## Response * `result` — the fee estimation. ## Use case A practical use case for `starknet_estimateFee` is to calculate the fee required for a transaction that interacts with a contract on Starknet. The fee is an important factor in transactions, as it determines the amount of fees that need to be paid by the sender to the network for processing the transaction. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl --request POST \ --url 'https://alpha-mainnet.starknet.io/feeder_gateway/estimate_fee?blockNumber=385940' \ --header 'Content-Type: application/json' \ --data-raw '{ "type": "INVOKE_FUNCTION", "sender_address": "0x0569b13e8164bc8000c0bbcf4887856516643af123c5bc3b01e229e92f9cfd10", "nonce": "0x51", "max_fee": "0xb3a2f1ab6d632", "version": "0x1", "calldata": [ "0x3", "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c", "0x3", "0xfff107e2403123c7df78d91728a7ee5cfd557aec0fa2d2bdc5891c286bbfff", "0x984e103a8acd6", "0x0", "0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8", "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c", "0x3", "0xfff107e2403123c7df78d91728a7ee5cfd557aec0fa2d2bdc5891c286bbfff", "0x4c4b40", "0x0", "0xfff107e2403123c7df78d91728a7ee5cfd557aec0fa2d2bdc5891c286bbfff", "0x2f0b3c5710379609eb5495f1ecd348cb28167711b73609fe565a72734550354", "0xe", "0x569b13e8164bc8000c0bbcf4887856516643af123c5bc3b01e229e92f9cfd10", "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8", "0x1f4", "0xa76cc", "0xa7b5e", "0x984e103a8acd6", "0x0", "0x4c4b40", "0x0", "0x9542431aa6bf1", "0x0", "0x4ac4a0", "0x0" ], "signature": ["0x54c2201c7b9021777389e208e28eafc67e4ba1f1aa7016a1123b61c6ff79c29", "0x58c2d97614e5abfd9668b314f344c945d16ea154cb32a7b08b4445204f1f1d3"] }' ``` # starknet_estimateMessageFee | Starknet Source: https://docs.chainstack.com/reference/starknet-starknetestimatemessagefee openapi/starknet_node_api/starknet_estimateMessageFee.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that returns the fee estimate for an L1 to L2 message sent from Ethereum. starknet_estimateMessageFee on Starknet via Chainstack. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that returns the fee estimate for an L1 to L2 message, which is a message that an Ethereum contract sends to a Starknet contract. The estimate covers the `l1_handler` transaction that the message triggers on Starknet. You pay this fee on Ethereum when you send the message through the Starknet core contract. The returned `overall_fee` is at least 50,000,000,000,000 wei (0.00005 ETH). ## Parameters * `message` — the L1 to L2 message to estimate, with the following fields: * `from_address` — the address of the Ethereum contract that sends the message. * `to_address` — the address of the Starknet contract that receives the message. * `entry_point_selector` — the selector of the `l1_handler` function in the receiving contract. * `payload` — the message payload as an array of field elements. * `block_id` — the hash of the requested block, or number (height) of the requested block, or a block tag, for the block referencing the state or call the transaction on. ## Response * `result` — the fee estimate, with the following fields. Numeric values are hexadecimal strings. * `l1_gas_consumed` — the Ethereum gas the message consumes. * `l1_gas_price` — the Ethereum gas price used in the estimate, in wei. * `l2_gas_consumed` — the Starknet gas the message consumes. * `l2_gas_price` — the Starknet gas price used in the estimate, in wei. * `l1_data_gas_consumed` — the Ethereum data gas the message consumes. * `l1_data_gas_price` — the Ethereum data gas price used in the estimate, in wei. * `overall_fee` — the fee for the message, in wei. The value is at least 50,000,000,000,000 wei (0.00005 ETH). When the gas amounts multiplied by their prices add up to less than that, `overall_fee` is exactly 50,000,000,000,000 wei. * `unit` — the fee unit, always `WEI`. ## Use case A practical use case for `starknet_estimateMessageFee` is to set the fee for a message that you send from Ethereum to Starknet. The message fee is the ETH value sent with the `sendMessageToL2` call to the Starknet core contract, so pass the returned `overall_fee` as that value. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl --request POST \ --url 'https://alpha-mainnet.starknet.io/feeder_gateway/estimate_fee?blockNumber=385940' \ --header 'Content-Type: application/json' \ --data-raw '{ "type": "INVOKE_FUNCTION", "sender_address": "0x0569b13e8164bc8000c0bbcf4887856516643af123c5bc3b01e229e92f9cfd10", "nonce": "0x51", "max_fee": "0xb3a2f1ab6d632", "version": "0x1", "calldata": [ "0x3", "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c", "0x3", "0xfff107e2403123c7df78d91728a7ee5cfd557aec0fa2d2bdc5891c286bbfff", "0x984e103a8acd6", "0x0", "0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8", "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c", "0x3", "0xfff107e2403123c7df78d91728a7ee5cfd557aec0fa2d2bdc5891c286bbfff", "0x4c4b40", "0x0", "0xfff107e2403123c7df78d91728a7ee5cfd557aec0fa2d2bdc5891c286bbfff", "0x2f0b3c5710379609eb5495f1ecd348cb28167711b73609fe565a72734550354", "0xe", "0x569b13e8164bc8000c0bbcf4887856516643af123c5bc3b01e229e92f9cfd10", "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8", "0x1f4", "0xa76cc", "0xa7b5e", "0x984e103a8acd6", "0x0", "0x4c4b40", "0x0", "0x9542431aa6bf1", "0x0", "0x4ac4a0", "0x0" ], "signature": ["0x54c2201c7b9021777389e208e28eafc67e4ba1f1aa7016a1123b61c6ff79c29", "0x58c2d97614e5abfd9668b314f344c945d16ea154cb32a7b08b4445204f1f1d3"] }' ``` # starknet_getClassAt | Starknet Source: https://docs.chainstack.com/reference/starknet-starknetgetclassat openapi/starknet_node_api/starknet_getClassAt.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that returns the class of a contract at a given address. It provides the class of a contract at a specific address. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that returns the class of a contract at a given address. It provides the class of a contract at a specific address. This is useful for clients or wallets to inspect the class of a contract, which can provide information about the contract's functionality and behavior. ## Parameters * `address` — the string of the address of the contract. ## Response * `class` — the class of the contract at the given address. ## Use case A practical use case for `starknet_getClassAt` is to retrieve the class of a specific contract. For example, a wallet may use this method to display the class of a contract to a user, or a client may use it to verify the class of a contract before interacting with it. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl 'https://alpha-mainnet.starknet.io/feeder_gateway/get_full_contract?contractAddress=0x073314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82' ``` # starknet_getClassHashAt | Starknet Source: https://docs.chainstack.com/reference/starknet-starknetgetclasshashat openapi/starknet_node_api/starknet_getClassHashAt.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that returns the hash of the class at a given address. It provides the hash of the class at the specified address. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that returns the hash of the class at a given address. It provides the hash of the class at the specified address. This can be useful for verifying the type of contract deployed at a given address. ## Parameters * `address` — the address of the contract. * `block_id` — the hash of the requested block, or number (height) of the requested block, or a block tag: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. ## Response * `result` — the hash of the class at the given address, represented as a hexadecimal string. ## Use case A practical use case for `starknet_getClassHashAt` is to verify the type of contract deployed at a given address. This can be useful for developers to ensure that the correct contract has been deployed at the expected address. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl 'https://alpha-mainnet.starknet.io/feeder_gateway/get_class_hash_at?contractAddress=0x073314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82' ``` # starknet_getNonce | Starknet Source: https://docs.chainstack.com/reference/starknet-starknetgetnonce openapi/starknet_node_api/starknet_getNonce.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that returns the nonce for a given account. starknet_getNonce JSON-RPC method available on the Starknet blockchain via Chainstack. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that returns the nonce for a given account. It provides the nonce of the account, which is the number of transactions sent from the account's address. This is useful for clients or wallets to determine the nonce to include in a transaction to ensure that the network processes it in the correct order. ## Parameters * `address` — the string of the address of the account. ## Response * `result` — the nonce of the account, represented as a hexadecimal string. ## Use case A practical use case for `starknet_getNonce` is to retrieve the nonce for an account before sending a transaction. The nonce is an important factor in transactions, as it determines the order in which the network processes transactions from a given account. For example, if an account has a nonce of 10, the next transaction sent from that account should include a nonce of 11 to ensure that it is processed after all previous transactions. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl 'https://alpha-mainnet.starknet.io/feeder_gateway/get_nonce?contractAddress=0x0569b13e8164bc8000c0bbcf4887856516643af123c5bc3b01e229e92f9cfd10' ``` # starknet_getStorageAt | Starknet Source: https://docs.chainstack.com/reference/starknet-starknetgetstorageat openapi/starknet_node_api/starknet_getStorageAt.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that returns the storage value at a given position for a given account. starknet_getStorageAt on Starknet via Chainstack. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that returns the storage value at a given position for a given account. It provides the value stored at a specific position in the storage of an account. This is useful for clients or wallets to inspect the state of a smart contract at a specific block. ## Parameters * `address` — the string of the address of the account. * `position` — the string of the position in the storage. ## Response * `result` — the value stored at the given position in the account's storage, represented as a hexadecimal string. ## Use case A practical use case for `starknet_getStorageAt` is to inspect the state of a smart contract. For example, if a smart contract stores the balance of an account at a specific position in its storage, you can use `starknet_getStorageAt` to retrieve the balance of the account at a specific block. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl 'https://alpha-mainnet.starknet.io/feeder_gateway/get_storage_at?contractAddress=0x00dA114221cb83fa859DBdb4C44bEeaa0BB37C7537ad5ae66Fe5e0efD20E6eB3&key=1297713107789520337982513184691958288380927033924334369025906284470037001886&blockNumber=latest' ``` # starknet_getTransactionByBlockIdAndIndex | Starknet Source: https://docs.chainstack.com/reference/starknet-starknetgettransactionbyblockidandindex openapi/starknet_node_api/starknet_getTransactionByBlockIdAndIndex.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that returns a transaction by its block ID and index within the block. Available on Starknet via Chainstack JSON-RPC nodes. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that returns a transaction by its block ID and index within the block. It provides the details of a specific transaction that is included in a block. This is useful for clients or wallets to inspect the details of a transaction, such as its sender, receiver, amount, and status. ## Parameters * `block_id` — the string of the block ID in which the transaction is included. * `index` — the integer of the index of the transaction within the block. ## Response * `transaction` — the details of the transaction, including its sender, receiver, amount, and status. ## Use case A practical use case for `starknet_getTransactionByBlockIdAndIndex` is to retrieve the details of a specific transaction. For example, a wallet may use this method to display the details of a user's past transactions, or a client may use it to verify the status of a transaction. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl 'https://alpha-mainnet.starknet.io/feeder_gateway/get_transaction_hash_by_id?transactionId=888888' ``` # starknet_getTransactionByHash | Starknet Source: https://docs.chainstack.com/reference/starknet-starknetgettransactionbyhash openapi/starknet_node_api/starknet_getTransactionByHash.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that returns a transaction by its hash. It provides the details of a specific transaction that is identified by its unique hash. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that returns a transaction by its hash. It provides the details of a specific transaction that is identified by its unique hash. This is useful for clients or wallets to inspect the details of a transaction, such as its sender, receiver, amount, and status. ## Parameters * `hash` — the string of the transaction hash. ## Response * `transaction` — the details of the transaction, including its sender, receiver, amount, and status. ## Use case A practical use case for `starknet_getTransactionByHash` is to retrieve the details of a specific transaction. For example, a wallet may use this method to display the details of a user's past transactions, or a client may use it to verify the status of a transaction. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl 'https://alpha-mainnet.starknet.io/feeder_gateway/get_transaction_id_by_hash?transactionHash=0x295ee040949bd23a4bba5d5fa0d9d776c3b1fc7cafd511d025e689e665d05b2' ``` # starknet_simulateTransactions | Starknet Source: https://docs.chainstack.com/reference/starknet-starknetsimulatetransactions openapi/starknet_node_api/starknet_simulateTransactions.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that simulates the execution of a list of transactions on a given state. Available on Starknet via Chainstack. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that simulates the execution of a list of transactions on a given state. It provides a simulation of the execution of a list of transactions based on the network's current state and the transactions' parameters. A client or wallet can use this simulation to understand the potential outcome of these transactions if they were to be included in a block. ## Parameters * `request` — the list of transactions to simulate. * `block_id` — the hash of the requested block, or number (height) of the requested block, or a block tag, for the block referencing the state or call the transaction on. ## Response * `result` — the result of the simulation, represented as a list of transaction receipts. ## Use case A practical use case for `starknet_simulateTransactions` is to simulate the execution of a list of transactions that interact with contracts on StarkNet. This can be useful for testing and debugging purposes, as it allows developers to understand the potential outcome of these transactions without actually executing them on the network. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl --request POST \ --url 'https://alpha-mainnet.starknet.io/feeder_gateway/simulate_transaction?blockNumber=385940' \ --header 'Content-Type: application/json' \ --data-raw '{ "type": "INVOKE_FUNCTION", "sender_address": "0x0569b13e8164bc8000c0bbcf4887856516643af123c5bc3b01e229e92f9cfd10", "nonce": "0x51", "max_fee": "0xb3a2f1ab6d632", "version": "0x1", "calldata": [ "0x3", "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c", "0x3", "0xfff107e2403123c7df78d91728a7ee5cfd557aec0fa2d2bdc5891c286bbfff", "0x984e103a8acd6", "0x0", "0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8", "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c", "0x3", "0xfff107e2403123c7df78d91728a7ee5cfd557aec0fa2d2bdc5891c286bbfff", "0x4c4b40", "0x0", "0xfff107e2403123c7df78d91728a7ee5cfd557aec0fa2d2bdc5891c286bbfff", "0x2f0b3c5710379609eb5495f1ecd348cb28167711b73609fe565a72734550354", "0xe", "0x569b13e8164bc8000c0bbcf4887856516643af123c5bc3b01e229e92f9cfd10", "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8", "0x1f4", "0xa76cc", "0xa7b5e", "0x984e103a8acd6", "0x0", "0x4c4b40", "0x0", "0x9542431aa6bf1", "0x0", "0x4ac4a0", "0x0" ], "signature": ["0x54c2201c7b9021777389e208e28eafc67e4ba1f1aa7016a1123b61c6ff79c29", "0x58c2d97614e5abfd9668b314f344c945d16ea154cb32a7b08b4445204f1f1d3"] }' ``` # starknet_traceBlockTransactions | Starknet Source: https://docs.chainstack.com/reference/starknet-starknettraceblocktransactions openapi/starknet_node_api/starknet_traceBlockTransactions.json POST /365cf697a3ad6d950b4c4a911e2e4f4d Starknet API method that returns the execution trace of all transactions in a given block. starknet_traceBlockTransactions on Starknet via Chainstack. > [!NOTE] > > **Starknet OpenRPC spec** > > For the latest version of the publicly maintained OpenRPC spec, see the [Starknet specs GitHub repo](https://github.com/starkware-libs/starknet-specs). Starknet API method that returns the execution trace of all transactions in a given block. It provides a detailed execution trace for each transaction in the specified block. This can be useful for debugging and analysis purposes, as it allows developers to see the exact sequence of operations that were performed during the execution of each transaction. ## Parameters * `block_id` — the hash of the requested block, or number (height) of the requested block, or a block tag: * `block_hash` — hash of the block. * `block_number` — block number, accept integer. * `block_tag` — `latest` for the latest one added to the chain or `pending` for the pending one. ## Response * `result` — the execution trace of all transactions in the block, represented as a list of transaction traces. ## Use case A practical use case for `starknet_traceBlockTransactions` is to analyze the execution of transactions in a specific block. This can be useful for debugging purposes, as it allows developers to understand the sequence of operations that were performed during the execution of each transaction. ## Deprecated feeder gateway method The feeder gateway was deprecated on [December 11, 2023](https://community.starknet.io/t/feeder-gateway-deprecation/100233). Example of the call you had to make to the feeder gateway before the deprecation: ```Text Shell theme={"system"} curl 'https://alpha-mainnet.starknet.io/feeder_gateway/get_block_traces?blockNumber=456600' ``` # Subscribe to events Source: https://docs.chainstack.com/reference/subscribetobeaconevents openapi/ethereum_beacon_chain_api/events/subscribe_to_events.json GET /beacon/43bcb3d5ceb917bab4353db33b087b37/eth/v1/events Subscribe to the Beacon Chain events and receive a stream of the following data: Event type (event: head): This specifies the type of event being streamed. > [!NOTE] > > **This method will not work in the interactive section as it receives a continuous stream of data.** > > You can test it using Postman: > > ## Instructions: > > 1. Click **Run in Postman**.\ > [![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/20722034-74bcf624-cd2a-47ff-b790-ec3e25c138fa?action=collection%2Ffork\&collection-url=entityId%3D20722034-74bcf624-cd2a-47ff-b790-ec3e25c138fa%26entityType%3Dcollection%26workspaceId%3Df1ba12a1-51de-4de6-8a9e-97b26ce685e0) > 2. Click **Fork collection** and follow the steps to set it up in your environment. > 3. Select **Get Beacon Chain events**. > 4. After the collection is set up, click **Send**. Subscribe to the Beacon Chain events and receive a stream of the following data: * **Event type** (event: `head`): This specifies the type of event being streamed. In this case, it's a head event, which relates to the latest confirmed block on the Beacon Chain. * **Data fields**: * `slot`: The slot number at which this block was proposed. Slots are time periods during which a block can be proposed. * `block`: The hash of the block that has been accepted as the head of the chain. * `state`: The state root of the Ethereum network after processing this block. * `epoch_transition`: A boolean indicating whether or not this block involved a transition between epochs. * `previous_duty_dependent_root` and `current_duty_dependent_root`: These roots help in understanding the duties thatvalidators need to perform, which may vary from one epoch to another. * `execution_optimistic`: A boolean indicating the execution mode. If false, it means the block is fully validated; if true, it implies the block is processed in an optimistic mode, where certain validations might be pending. # eth_syncing | Polygon Source: https://docs.chainstack.com/reference/syncing openapi/polygon_node_api/chain_info/eth_syncing.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method that returns an object with information about the current synchronization status of the node, or false if the node is fully synced. Polygon API method that returns an object with information about the current synchronization status of the node, or `false` if the node is fully synced. This method can track the progress of a node's synchronization with the Polygon blockchain. The returned object contains data such as the starting block, current block, and highest block of the node, allowing developers to monitor and estimate the time remaining for the node to sync fully. ## Parameters * `none` ## Response * `result` — the boolean value `false` when not syncing or a JSON object when syncing: * `startingBlock` — the block number where the synchronization process started, encoded as hexadecimal. * `currentBlock` — the block number that the node has currently processed, same as `eth_blockNumber`, encoded as hexadecimal. * `highestBlock` — the block number of the latest block in the blockchain known to the node. ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const syncStatus = async () => { const status = await provider.send("eth_syncing", []); console.log(status); }; syncStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" print (web3.eth.syncing) ``` ## Use case The `eth_syncing` method on Polygon can be useful for developers building applications that interact with the Polygon blockchain. For example, you may want to provide feedback to your application's user about the status of the Polygon node the application is connecting to. Here is a code example using the ethers.js library: ```javascript index.js theme={"system"} const ethers = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function syncstatus() { try { // Query the sync status const syncing = await provider.send("eth_syncing", []); if (syncing) { console.log(`Node is syncing. Current block: ${syncing.currentBlock}. Highest block: ${syncing.highestBlock}`); } else { console.log('Node is fully synced.'); } } catch (error) { console.error(error); } }; syncstatus() ``` In this example, the program creates a new `provider` instance and connects to the node. Then, it calls `provider.send("eth_syncing", [])` to get the current synchronization status of the node. If the node is fully synced, the function logs a message to the console indicating that the node is fully synced. If the node is still syncing, the function logs the current block number and the highest block number known to the node. # debug_getBadBlocks | Tempo Source: https://docs.chainstack.com/reference/tempo-debug-getbadblocks openapi/tempo_node_api/debug_and_trace/debug_getBadBlocks.json POST / Tempo API method that returns a list of invalid blocks that the node has encountered. Useful for debugging consensus or block validation issues. Tempo API method that returns a list of invalid blocks that the node has encountered. Useful for debugging consensus or block validation issues. ## Parameters * `none` ## Response * `result` — an array of bad block objects that the node has encountered. Each object contains the full block data and the reason it was rejected. ## `debug_getBadBlocks` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_getBadBlocks", "params": [], "id": 1}' ``` # debug_getRawBlock | Tempo Source: https://docs.chainstack.com/reference/tempo-debug-getrawblock openapi/tempo_node_api/debug_and_trace/debug_getRawBlock.json POST / Tempo API method that returns the RLP-encoded block data for a given block number or tag. debug_getRawBlock on Tempo via Chainstack. Tempo API method that returns the RLP-encoded block data for a given block number or tag. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the RLP-encoded block data as a hex string. ## `debug_getRawBlock` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_getRawBlock", "params": ["latest"], "id": 1}' ``` # debug_getRawHeader | Tempo Source: https://docs.chainstack.com/reference/tempo-debug-getrawheader openapi/tempo_node_api/debug_and_trace/debug_getRawHeader.json POST / Tempo API method that returns the RLP-encoded block header for a given block number or tag. debug_getRawHeader on Tempo via Chainstack. Tempo API method that returns the RLP-encoded block header for a given block number or tag. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the RLP-encoded block header as a hex string. ## `debug_getRawHeader` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_getRawHeader", "params": ["latest"], "id": 1}' ``` # debug_getRawReceipts | Tempo Source: https://docs.chainstack.com/reference/tempo-debug-getrawreceipts openapi/tempo_node_api/debug_and_trace/debug_getRawReceipts.json POST / Tempo API method that returns the RLP-encoded transaction receipts for a given block. debug_getRawReceipts on Tempo via Chainstack. Tempo API method that returns the RLP-encoded transaction receipts for a given block. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — an array of RLP-encoded transaction receipts as hex strings. ## `debug_getRawReceipts` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_getRawReceipts", "params": ["latest"], "id": 1}' ``` # debug_getRawTransaction | Tempo Source: https://docs.chainstack.com/reference/tempo-debug-getrawtransaction openapi/tempo_node_api/debug_and_trace/debug_getRawTransaction.json POST / Tempo API method that returns the RLP-encoded transaction data for a given transaction hash. transactionHash — the hash of the transaction. Tempo API method that returns the RLP-encoded transaction data for a given transaction hash. ## Parameters * `transactionHash` — the hash of the transaction ## Response * `result` — the RLP-encoded transaction data as a hex string, or `null` if the transaction is not found. ## `debug_getRawTransaction` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_getRawTransaction", "params": ["0x0000000000000000000000000000000000000000000000000000000000000000"], "id": 1}' ``` # debug_traceBlockByHash | Tempo Source: https://docs.chainstack.com/reference/tempo-debug-traceblockbyhash openapi/tempo_node_api/debug_and_trace/debug_traceBlockByHash.json POST / Tempo API method that returns detailed traces for all transactions in a block specified by block hash. Chainstack Tempo reference. Tempo API method that returns detailed traces for all transactions in a block specified by block hash. This is useful for analyzing all contract interactions within a specific block. ## Parameters * `blockHash` — the hash of the block to trace * `tracerConfig` — (optional) tracer configuration object: * `tracer` — tracer type (e.g., `callTracer`, `prestateTracer`) * `timeout` — (optional) timeout for the trace * `tracerConfig` — (optional) tracer-specific configuration ## Response * `result` — array of trace objects, one for each transaction in the block. The format depends on the tracer used. For `callTracer`, each trace contains: * `from` — sender address * `to` — recipient address * `gas` — gas provided * `gasUsed` — gas consumed * `input` — call data * `output` — return data * `calls` — nested array of internal calls * `type` — call type (CALL, DELEGATECALL, etc.) ## `debug_traceBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlockByHash = async (blockHash) => { const traces = await provider.send("debug_traceBlockByHash", [ blockHash, { tracer: "callTracer" } ]); console.log(`Block contains ${traces.length} transaction traces`); for (let i = 0; i < traces.length; i++) { const trace = traces[i].result; console.log(`\nTx ${i}: ${trace.from} -> ${trace.to}`); console.log(` Type: ${trace.type}, Gas Used: ${trace.gasUsed}`); } }; traceBlockByHash("0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceBlockByHash", [ "0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e", {"tracer": "callTracer"} ]) traces = result['result'] print(f"Block contains {len(traces)} transaction traces") for i, tx_trace in enumerate(traces): trace = tx_trace['result'] print(f"\nTx {i}: {trace['from']} -> {trace.get('to', 'Contract Creation')}") print(f" Type: {trace['type']}, Gas Used: {trace['gasUsed']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceBlockByHash", "params": ["0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e", {"tracer": "callTracer"}], "id": 1 }' ``` # debug_traceBlockByNumber | Tempo Source: https://docs.chainstack.com/reference/tempo-debug-traceblockbynumber openapi/tempo_node_api/debug_and_trace/debug_traceBlockByNumber.json POST / Tempo API method that returns detailed traces for all transactions in a block specified by block number. Chainstack Tempo reference. Tempo API method that returns detailed traces for all transactions in a block specified by block number. This is useful for analyzing all contract interactions within a specific block. ## Parameters * `blockNumber` — the block number (hex) or tag (`latest`, `earliest`, `pending`) * `tracerConfig` — (optional) tracer configuration object: * `tracer` — tracer type (e.g., `callTracer`, `prestateTracer`) * `timeout` — (optional) timeout for the trace * `tracerConfig` — (optional) tracer-specific configuration ## Response * `result` — array of trace objects, one for each transaction in the block. The format depends on the tracer used. For `callTracer`, each trace contains: * `from` — sender address * `to` — recipient address * `gas` — gas provided * `gasUsed` — gas consumed * `input` — call data * `output` — return data * `calls` — nested array of internal calls * `type` — call type (CALL, DELEGATECALL, etc.) ## `debug_traceBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlock = async (blockNumber) => { const traces = await provider.send("debug_traceBlockByNumber", [ blockNumber, { tracer: "callTracer" } ]); console.log(`Block contains ${traces.length} transaction traces`); for (let i = 0; i < traces.length; i++) { const trace = traces[i].result; console.log(`\nTx ${i}: ${trace.from} -> ${trace.to}`); console.log(` Type: ${trace.type}, Gas Used: ${trace.gasUsed}`); if (trace.calls) { console.log(` Internal calls: ${trace.calls.length}`); } } }; traceBlock("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceBlockByNumber", [ "latest", {"tracer": "callTracer"} ]) traces = result['result'] print(f"Block contains {len(traces)} transaction traces") for i, tx_trace in enumerate(traces): trace = tx_trace['result'] print(f"\nTx {i}: {trace['from']} -> {trace.get('to', 'Contract Creation')}") print(f" Type: {trace['type']}, Gas Used: {trace['gasUsed']}") if 'calls' in trace: print(f" Internal calls: {len(trace['calls'])}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceBlockByNumber", "params": ["latest", {"tracer": "callTracer"}], "id": 1 }' ``` # debug_traceCall | Tempo Source: https://docs.chainstack.com/reference/tempo-debug-tracecall openapi/tempo_node_api/debug_and_trace/debug_traceCall.json POST / Tempo API method that traces the execution of a call without creating a transaction on the blockchain. Chainstack Tempo reference. Tempo API method that traces the execution of a call without creating a transaction on the blockchain. This is useful for debugging contract interactions before sending actual transactions. ## Parameters * `callObject` — the call object: * `from` — (optional) sender address * `to` — recipient address * `gas` — (optional) gas limit * `gasPrice` — (optional) gas price * `value` — (optional) value to send * `data` — (optional) call data * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) * `tracerConfig` — (optional) tracer configuration object: * `tracer` — tracer type (e.g., `callTracer`, `prestateTracer`) ## Response * `result` — the trace result object, format depends on the tracer used For the default tracer: * `gas` — gas used * `failed` — whether the call failed * `returnValue` — return data * `structLogs` — array of execution steps ## `debug_traceCall` code examples The following example traces a `balanceOf` call on the pathUSD token: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; const TARGET_ADDRESS = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"; // Encode balanceOf(address) const iface = new ethers.Interface(["function balanceOf(address) view returns (uint256)"]); const data = iface.encodeFunctionData("balanceOf", [TARGET_ADDRESS]); const traceCall = async () => { // Using default tracer const trace = await provider.send("debug_traceCall", [ { to: PATHUSD, data: data }, "latest", {} ]); console.log("Gas used:", trace.gas); console.log("Failed:", trace.failed); console.log("Return value:", trace.returnValue); console.log("Execution steps:", trace.structLogs.length); }; traceCall(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # pathUSD token address PATHUSD = "0x20c0000000000000000000000000000000000000" TARGET_ADDRESS = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294" # balanceOf(address) calldata data = "0x70a08231000000000000000000000000" + TARGET_ADDRESS[2:].lower() result = web3.provider.make_request("debug_traceCall", [ { "to": PATHUSD, "data": data }, "latest", {} ]) trace = result['result'] print(f"Gas used: {trace['gas']}") print(f"Failed: {trace['failed']}") print(f"Return value: {trace['returnValue']}") print(f"Execution steps: {len(trace['structLogs'])}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceCall", "params": [ { "to": "0x20c0000000000000000000000000000000000000", "data": "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" }, "latest", {} ], "id": 1 }' ``` # debug_traceCallMany | Tempo Source: https://docs.chainstack.com/reference/tempo-debug-tracecallmany openapi/tempo_node_api/debug_and_trace/debug_traceCallMany.json POST / Tempo API method that traces multiple calls in sequence at a given block, with each call's state changes visible to subsequent calls. Tempo API method that traces multiple calls in sequence at a given block, with each call's state changes visible to subsequent calls. Useful for simulating complex multi-step interactions. ## Parameters * `calls` — an array of call bundles, where each bundle is an array of `[callObject, traceTypes]` pairs * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — an array of trace results, one for each call in the bundle. ## `debug_traceCallMany` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "debug_traceCallMany", "params": [[[{"to": "0x20c0000000000000000000000000000000000000", "data": "0x95d89b41"}, ["trace"]]], "latest"], "id": 1}' ``` # debug_traceTransaction | Tempo Source: https://docs.chainstack.com/reference/tempo-debug-tracetransaction openapi/tempo_node_api/debug_and_trace/debug_traceTransaction.json POST / Tempo API method that returns a detailed trace of a transaction's execution. Reference for debug_traceTransaction on Tempo via Chainstack. Tempo API method that returns a detailed trace of a transaction's execution. This is useful for debugging failed transactions or understanding contract behavior. ## Parameters * `transactionHash` — the hash of the transaction to trace * `tracerConfig` — (optional) tracer configuration object: * `tracer` — tracer type (e.g., `callTracer`, `prestateTracer`) * `timeout` — (optional) timeout for the trace ## Response * `result` — the trace result object, format depends on the tracer used ## `debug_traceTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txHash) => { const trace = await provider.send("debug_traceTransaction", [ txHash, { tracer: "callTracer" } ]); console.log(JSON.stringify(trace, null, 2)); }; traceTransaction("0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("debug_traceTransaction", [ "0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035", {"tracer": "callTracer"} ]) print(result['result']) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "debug_traceTransaction", "params": ["0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035", {"tracer": "callTracer"}], "id": 1 }' ``` # eth_accounts | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-accounts openapi/tempo_node_api/account_info/eth_accounts.json POST / Tempo API method that returns a list of addresses owned by the node. Since Tempo nodes do not manage private keys, this always returns an empty array. Tempo API method that returns a list of addresses owned by the node. Since Tempo nodes do not manage private keys, this always returns an empty array. ## Parameters * `none` ## Response * `result` — an array of addresses owned by the node. On Tempo, this always returns an empty array since the node does not manage accounts. ## `eth_accounts` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}' ``` # eth_blobBaseFee | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-blobbasefee openapi/tempo_node_api/gas_data/eth_blobBaseFee.json POST / Tempo API method that returns the current blob base fee per gas in wei. Tempo inherits this from the EIP-4844 specification. On Tempo. Tempo API method that returns the current blob base fee per gas in wei. Tempo inherits this from the EIP-4844 specification. ## Parameters * `none` ## Response * `result` — the current blob base fee in wei, encoded as hexadecimal. ## `eth_blobBaseFee` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_blobBaseFee", "params": [], "id": 1}' ``` # eth_blockNumber | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-blocknumber openapi/tempo_node_api/blocks_info/eth_blockNumber.json POST / Tempo API method that returns the latest block number of the blockchain. Tempo developers can call eth_blockNumber via Chainstack. Tempo API method that returns the latest block number of the blockchain. A block number is a hexadecimal number representing the block's position in the blockchain. Tempo produces blocks approximately every 0.5 seconds with sub-second finality using Simplex Consensus. ## Parameters * `none` ## Response * `result` — the integer value of the node's latest block number encoded as hexadecimal. ## `eth_blockNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const eth_getBlockNumber = async () => { const block_Number = await provider.getBlockNumber(); console.log(block_Number); }; eth_getBlockNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.block_number) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1}' ``` ## Use case Tempo has sub-second block times (\~0.5 seconds), making it ideal for applications that need to track real-time payment status. A DApp can use the current block number to determine when a payment transaction has been finalized. # eth_call | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-call openapi/tempo_node_api/transaction_info/eth_call.json POST / Tempo API method that executes a new message call immediately without creating a transaction on the blockchain. Tempo via Chainstack. Tempo API method that executes a new message call immediately without creating a transaction on the blockchain. This is commonly used to read data from smart contracts, such as TIP-20 token balances. ## Parameters * `callObject` — the call object: * `from` — (optional) address the call is sent from * `to` — the address the call is directed to * `gas` — (optional) gas provided for the call * `gasPrice` — (optional) gas price for the call * `value` — (optional) value sent with the call * `data` — (optional) hash of the method signature and encoded parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ### State override (optional) `eth_call` accepts an optional third parameter: a state override set that changes account state for this call only. Nothing is written to the chain. The set is an object keyed by account address, and each address can override: * `balance` — the account balance in wei, hex-encoded * `nonce` — the account nonce, hex-encoded * `code` — the bytecode to run at the address * `state` — storage slot values that replace the account's entire storage * `stateDiff` — storage slot values to change, keeping the rest of the storage This example puts bytecode that returns `42` at an unused address, then calls that address: ```bash theme={"system"} curl --request POST \ --url CHAINSTACK_NODE_URL \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [ { "to": "0x00000000000000000000000000000000c0ffee42", "data": "0x" }, "latest", { "0x00000000000000000000000000000000c0ffee42": { "code": "0x602a60005260206000f3" } } ] }' ``` ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": "0x000000000000000000000000000000000000000000000000000000000000002a" } ``` ## Response * `result` — the return value of the executed contract call ## `eth_call` code examples The following example queries the `balanceOf` function on the pathUSD TIP-20 token contract: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; const getBalance = async (address) => { const abi = ["function balanceOf(address) view returns (uint256)"]; const token = new ethers.Contract(PATHUSD, abi, provider); const balance = await token.balanceOf(address); // TIP-20 tokens use 6 decimals console.log(`pathUSD balance: ${ethers.formatUnits(balance, 6)}`); }; getBalance("0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # pathUSD token address PATHUSD = "0x20c0000000000000000000000000000000000000" # balanceOf function signature: keccak256("balanceOf(address)")[:4] # Plus address padded to 32 bytes address = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294" data = "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" result = web3.eth.call({ 'to': PATHUSD, 'data': data }) balance = int(result.hex(), 16) / 10**6 # TIP-20 uses 6 decimals print(f"pathUSD balance: {balance}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_call", "params": [ { "to": "0x20c0000000000000000000000000000000000000", "data": "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" }, "latest" ], "id": 1 }' ``` # eth_chainId | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-chainid openapi/tempo_node_api/chain_info/eth_chainId.json POST / Tempo API method that returns the chain ID of the current network. The Tempo mainnet chain ID is 4217 (0x1079 in hexadecimal). On Tempo. Tempo API method that returns the chain ID of the current network. The Tempo mainnet chain ID is `4217` (`0x1079` in hexadecimal). The Moderato testnet chain ID is `42431` (`0xa5bf`). ## Parameters * `none` ## Response * `result` — the chain ID encoded as hexadecimal (`0x1079` for Tempo mainnet / `0xa5bf` for Moderato testnet) ## `eth_chainId` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getChainId = async () => { const network = await provider.getNetwork(); console.log(`Chain ID: ${network.chainId}`); }; getChainId(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.chain_id) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_chainId", "params": [], "id": 1}' ``` # eth_createAccessList | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-createaccesslist openapi/tempo_node_api/transaction_info/eth_createAccessList.json POST / Tempo API method that creates an EIP-2930 access list for a transaction. The access list identifies storage slots and addresses the transaction will access. Tempo API method that creates an EIP-2930 access list for a transaction. The access list identifies storage slots and addresses the transaction will access, which can be used to optimize gas costs. ## Parameters * `transactionObject` — the transaction call object: * `from` — (optional) sender address * `to` — recipient address * `gas` — (optional) gas limit * `gasPrice` — (optional) gas price * `value` — (optional) value to send * `data` — (optional) call data * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — an object containing: * `accessList` — an array of access list entries (address and storage keys) * `gasUsed` — the gas used by the transaction with the access list applied ## `eth_createAccessList` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_createAccessList", "params": [{"to": "0x20c0000000000000000000000000000000000000", "data": "0x95d89b41"}, "latest"], "id": 1}' ``` # eth_estimateGas | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-estimategas openapi/tempo_node_api/gas_data/eth_estimateGas.json POST / Tempo API method that estimates the gas needed to execute a transaction. On Tempo, gas fees are paid in TIP-20 stablecoins, not a native token. Tempo API method that estimates the gas needed to execute a transaction. On Tempo, gas fees are paid in TIP-20 stablecoins, not a native token. ## Parameters * `callObject` — the call object: * `from` — (optional) address the transaction is sent from * `to` — (optional) address the transaction is directed to * `gas` — (optional) gas provided for the transaction * `gasPrice` — (optional) gas price * `value` — (optional) value sent with the transaction * `data` — (optional) hash of the method signature and encoded parameters ## Response * `result` — the estimated gas amount encoded as hexadecimal ## `eth_estimateGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const estimateGas = async () => { const gas = await provider.estimateGas({ from: "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294", to: "0x20c0000000000000000000000000000000000000", data: "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" }); console.log(`Estimated gas: ${gas}`); }; estimateGas(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) gas = web3.eth.estimate_gas({ 'from': "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294", 'to': "0x20c0000000000000000000000000000000000000", 'data': "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" }) print(f"Estimated gas: {gas}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_estimateGas", "params": [{"from": "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294", "to": "0x20c0000000000000000000000000000000000000", "data": "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294"}], "id": 1 }' ``` # eth_feeHistory | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-feehistory openapi/tempo_node_api/gas_data/eth_feeHistory.json POST / Tempo API method that returns historical gas information for a range of blocks. Reference for eth_feeHistory on Tempo via Chainstack. Tempo API method that returns historical gas information for a range of blocks. This is useful for estimating appropriate gas fees based on recent network activity. ## Parameters * `blockCount` — number of blocks to return (hex or integer, max 1024) * `newestBlock` — highest block number or tag (`latest`, `pending`) * `rewardPercentiles` — (optional) array of percentiles to calculate priority fees ## Response * `result` — the fee history object: * `baseFeePerGas` — array of base fees for each block (plus next block) * `gasUsedRatio` — array of gas used ratios for each block * `oldestBlock` — the oldest block number in the range * `reward` — (optional) array of priority fee percentiles for each block ## `eth_feeHistory` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getFeeHistory = async () => { // Get fee history for last 10 blocks with 25th, 50th, 75th percentiles const feeHistory = await provider.send("eth_feeHistory", [ "0xa", "latest", [25, 50, 75] ]); console.log(`Oldest block: ${parseInt(feeHistory.oldestBlock, 16)}`); console.log(`\nBase fees (gwei):`); for (let i = 0; i < feeHistory.baseFeePerGas.length; i++) { const baseFee = ethers.formatUnits(feeHistory.baseFeePerGas[i], "gwei"); const gasRatio = feeHistory.gasUsedRatio[i] ? (feeHistory.gasUsedRatio[i] * 100).toFixed(2) : "N/A"; console.log(` Block +${i}: ${baseFee} gwei (${gasRatio}% full)`); } if (feeHistory.reward) { console.log(`\nPriority fee percentiles for latest block (gwei):`); const latest = feeHistory.reward[feeHistory.reward.length - 1]; console.log(` 25th: ${ethers.formatUnits(latest[0], "gwei")}`); console.log(` 50th: ${ethers.formatUnits(latest[1], "gwei")}`); console.log(` 75th: ${ethers.formatUnits(latest[2], "gwei")}`); } }; getFeeHistory(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Get fee history for last 10 blocks result = web3.provider.make_request("eth_feeHistory", [ "0xa", "latest", [25, 50, 75] ]) fee_history = result['result'] oldest = int(fee_history['oldestBlock'], 16) print(f"Oldest block: {oldest}") print("\nBase fees (gwei):") for i, base_fee in enumerate(fee_history['baseFeePerGas']): base_fee_gwei = web3.from_wei(int(base_fee, 16), 'gwei') gas_ratio = fee_history['gasUsedRatio'][i] * 100 if i < len(fee_history['gasUsedRatio']) else 'N/A' print(f" Block +{i}: {base_fee_gwei} gwei ({gas_ratio:.2f}% full)" if isinstance(gas_ratio, float) else f" Block +{i}: {base_fee_gwei} gwei") if fee_history.get('reward'): print("\nPriority fee percentiles for latest block (gwei):") latest = fee_history['reward'][-1] print(f" 25th: {web3.from_wei(int(latest[0], 16), 'gwei')}") print(f" 50th: {web3.from_wei(int(latest[1], 16), 'gwei')}") print(f" 75th: {web3.from_wei(int(latest[2], 16), 'gwei')}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_feeHistory", "params": ["0xa", "latest", [25, 50, 75]], "id": 1 }' ``` # eth_gasPrice | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-gasprice openapi/tempo_node_api/gas_data/eth_gasPrice.json POST / Tempo API method that returns the current gas price in wei. On Tempo, transaction fees are paid in TIP-20 stablecoins, not a native token. Tempo API method that returns the current gas price in wei. On Tempo, transaction fees are paid in TIP-20 stablecoins, not a native token. The gas price returned is used to calculate the USD-denominated fee. ## Parameters * `none` ## Response * `result` — the current gas price in wei encoded as hexadecimal ## `eth_gasPrice` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getGasPrice = async () => { const feeData = await provider.getFeeData(); console.log(`Gas price: ${feeData.gasPrice} wei`); }; getGasPrice(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.eth.gas_price) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_gasPrice", "params": [], "id": 1}' ``` # eth_getBalance | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getbalance openapi/tempo_node_api/account_info/eth_getBalance.json POST / Tempo API method that returns the balance of an account. address — the address to check balance for blockParameter — the block number (hex) or tag (latest. Tempo API method that returns the balance of an account. > [!WARNING] > > **Important**: Tempo has no native token. This method returns a placeholder value instead of an actual balance. To check real balances, query TIP-20 stablecoin contracts using the `balanceOf` function. ## Parameters * `address` — the address to check balance for * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — returns a placeholder value `0x5e5de0bada3a8d...` since Tempo has no native token ## Checking TIP-20 token balances To check actual stablecoin balances on Tempo, use `eth_call` with the TIP-20 `balanceOf` function: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; const checkBalance = async (address) => { const abi = ["function balanceOf(address) view returns (uint256)"]; const token = new ethers.Contract(PATHUSD, abi, provider); const balance = await token.balanceOf(address); // TIP-20 tokens use 6 decimals console.log(`pathUSD balance: ${ethers.formatUnits(balance, 6)}`); }; checkBalance("0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # pathUSD token address PATHUSD = "0x20c0000000000000000000000000000000000000" # ERC20 balanceOf ABI abi = [{"constant": True, "inputs": [{"name": "account", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "", "type": "uint256"}], "type": "function"}] token = web3.eth.contract(address=PATHUSD, abi=abi) balance = token.functions.balanceOf("0x9729187D9E8Bbefa8295F39f5634cA454dd9d294").call() # TIP-20 tokens use 6 decimals print(f"pathUSD balance: {balance / 10**6}") ``` ## Stablecoin tokens Tempo has these predeployed stablecoins (same addresses on mainnet and testnet): | Token | Address | | - | - | | pathUSD | `0x20c0000000000000000000000000000000000000` | | AlphaUSD | `0x20c0000000000000000000000000000000000001` | | BetaUSD | `0x20c0000000000000000000000000000000000002` | | ThetaUSD | `0x20c0000000000000000000000000000000000003` | # eth_getBlockByHash | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getblockbyhash openapi/tempo_node_api/blocks_info/eth_getBlockByHash.json POST / Tempo API method that returns information about a block specified by its hash. Reference for eth_getBlockByHash on Tempo via Chainstack. Tempo API method that returns information about a block specified by its hash. This method can return full transaction objects or just transaction hashes depending on the second parameter. Tempo blocks include additional fields not found in standard Ethereum blocks: `mainBlockGeneralGasLimit`, `sharedGasLimit`, and `timestampMillisPart` for sub-second precision. ## Parameters * `blockHash` — the hash of the block to retrieve * `fullTransactions` — if `true`, returns full transaction objects; if `false`, returns only transaction hashes ## Response * `result` — a block object, or `null` if no block was found: * `number` — the block number * `hash` — the block hash * `parentHash` — hash of the parent block * `timestamp` — block timestamp in seconds * `timestampMillisPart` — sub-second timestamp (0-999 ms), Tempo-specific * `transactions` — array of transaction objects or hashes * Additional Tempo fields for payment lanes gas limits ## `eth_getBlockByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getBlockByHash = async () => { const block = await provider.getBlock("0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e"); console.log(block); }; getBlockByHash(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block = web3.eth.get_block("0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e") print(block) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getBlockByHash", "params": ["0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e", false], "id": 1}' ``` # eth_getBlockByNumber | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getblockbynumber openapi/tempo_node_api/blocks_info/eth_getBlockByNumber.json POST / Tempo API method that returns information about a block specified by its number or tag. eth_getBlockByNumber on Tempo via Chainstack. Tempo API method that returns information about a block specified by its number or tag. This method can return full transaction objects or just transaction hashes depending on the second parameter. Tempo blocks include additional fields not found in standard Ethereum blocks: `mainBlockGeneralGasLimit`, `sharedGasLimit`, and `timestampMillisPart` for sub-second precision. ## Parameters * `blockNumber` — the block number (hex) or tag (`latest`, `earliest`, `pending`) * `fullTransactions` — if `true`, returns full transaction objects; if `false`, returns only transaction hashes ## Response * `result` — a block object, or `null` if no block was found: * `number` — the block number * `hash` — the block hash * `parentHash` — hash of the parent block * `timestamp` — block timestamp in seconds * `timestampMillisPart` — sub-second timestamp (0-999 ms), Tempo-specific * `transactions` — array of transaction objects or hashes * Additional Tempo fields for payment lanes gas limits ## `eth_getBlockByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getBlockByNumber = async () => { const block = await provider.getBlock("latest"); console.log(block); }; getBlockByNumber(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block = web3.eth.get_block("latest") print(block) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getBlockByNumber", "params": ["latest", false], "id": 1}' ``` # eth_getBlockReceipts | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getblockreceipts openapi/tempo_node_api/blocks_info/eth_getBlockReceipts.json POST / Tempo API method that returns all transaction receipts for a given block. Reference for eth_getBlockReceipts on Tempo via Chainstack. Tempo API method that returns all transaction receipts for a given block. This is more efficient than fetching receipts individually when you need all receipts from a block. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — array of receipt objects for all transactions in the block: * `transactionHash` — hash of the transaction * `transactionIndex` — index of the transaction in the block * `blockHash` — hash of the block * `blockNumber` — block number * `from` — sender address * `to` — recipient address * `cumulativeGasUsed` — total gas used in the block up to this transaction * `gasUsed` — gas used by this transaction * `contractAddress` — contract address if this was a deployment * `logs` — array of log objects * `logsBloom` — bloom filter for logs * `status` — `0x1` for success, `0x0` for failure * `effectiveGasPrice` — actual gas price paid * `type` — transaction type * `feeToken` — (Tempo-specific) token used to pay fees * `feePayer` — (Tempo-specific) address that paid the fees ## `eth_getBlockReceipts` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getBlockReceipts = async (blockNumber) => { const receipts = await provider.send("eth_getBlockReceipts", [blockNumber]); console.log(`Block ${blockNumber}: ${receipts.length} transactions`); let totalGasUsed = 0n; let successCount = 0; let failCount = 0; for (const receipt of receipts) { const gasUsed = BigInt(receipt.gasUsed); totalGasUsed += gasUsed; if (receipt.status === "0x1") { successCount++; } else { failCount++; } console.log(`\nTx ${receipt.transactionIndex}: ${receipt.transactionHash}`); console.log(` Status: ${receipt.status === "0x1" ? "Success" : "Failed"}`); console.log(` Gas Used: ${gasUsed}`); console.log(` Logs: ${receipt.logs.length}`); // Tempo-specific fields if (receipt.feeToken) { console.log(` Fee Token: ${receipt.feeToken}`); } if (receipt.feePayer) { console.log(` Fee Payer: ${receipt.feePayer}`); } } console.log(`\n--- Summary ---`); console.log(`Total transactions: ${receipts.length}`); console.log(`Successful: ${successCount}, Failed: ${failCount}`); console.log(`Total gas used: ${totalGasUsed}`); }; getBlockReceipts("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getBlockReceipts", ["latest"]) receipts = result['result'] print(f"Block latest: {len(receipts)} transactions") total_gas_used = 0 success_count = 0 fail_count = 0 for receipt in receipts: gas_used = int(receipt['gasUsed'], 16) total_gas_used += gas_used if receipt['status'] == "0x1": success_count += 1 else: fail_count += 1 print(f"\nTx {receipt['transactionIndex']}: {receipt['transactionHash']}") print(f" Status: {'Success' if receipt['status'] == '0x1' else 'Failed'}") print(f" Gas Used: {gas_used}") print(f" Logs: {len(receipt['logs'])}") # Tempo-specific fields if receipt.get('feeToken'): print(f" Fee Token: {receipt['feeToken']}") if receipt.get('feePayer'): print(f" Fee Payer: {receipt['feePayer']}") print(f"\n--- Summary ---") print(f"Total transactions: {len(receipts)}") print(f"Successful: {success_count}, Failed: {fail_count}") print(f"Total gas used: {total_gas_used}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getBlockReceipts", "params": ["latest"], "id": 1}' ``` # eth_getBlockTransactionCountByHash | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getblocktransactioncountbyhash openapi/tempo_node_api/blocks_info/eth_getBlockTransactionCountByHash.json POST / Tempo API method that returns the number of transactions in a block specified by block hash. blockHash — the hash of the block. On Tempo. Tempo API method that returns the number of transactions in a block specified by block hash. ## Parameters * `blockHash` — the hash of the block ## Response * `result` — the number of transactions in the block encoded as hexadecimal ## `eth_getBlockTransactionCountByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionCount = async (blockHash) => { const count = await provider.send("eth_getBlockTransactionCountByHash", [blockHash]); console.log(`Block ${blockHash}`); console.log(`Transaction count: ${parseInt(count, 16)}`); }; getTransactionCount("0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block_hash = "0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e" result = web3.provider.make_request("eth_getBlockTransactionCountByHash", [block_hash]) count = int(result['result'], 16) print(f"Block {block_hash}") print(f"Transaction count: {count}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByHash", "params": ["0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e"], "id": 1 }' ``` # eth_getBlockTransactionCountByNumber | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getblocktransactioncountbynumber openapi/tempo_node_api/blocks_info/eth_getBlockTransactionCountByNumber.json POST / Tempo API method that returns the number of transactions in a block specified by block number. eth_getBlockTransactionCountByNumber on Tempo via Chainstack. Tempo API method that returns the number of transactions in a block specified by block number. ## Parameters * `blockNumber` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the number of transactions in the block encoded as hexadecimal ## `eth_getBlockTransactionCountByNumber` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionCount = async (blockNumber) => { const count = await provider.send("eth_getBlockTransactionCountByNumber", [blockNumber]); console.log(`Block ${blockNumber}: ${parseInt(count, 16)} transactions`); }; // Get transaction count for latest block getTransactionCount("latest"); // Get transaction count for a specific block getTransactionCount("0x564000"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Get transaction count for latest block result = web3.provider.make_request("eth_getBlockTransactionCountByNumber", ["latest"]) count = int(result['result'], 16) print(f"Latest block: {count} transactions") # Using web3.py method count = web3.eth.get_block_transaction_count('latest') print(f"Latest block (via web3.py): {count} transactions") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByNumber", "params": ["latest"], "id": 1}' ``` # eth_getCode | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getcode openapi/tempo_node_api/account_info/eth_getCode.json POST / Tempo API method that returns the bytecode at a given address. This is commonly used to verify if an address is a contract. On Tempo. Tempo API method that returns the bytecode at a given address. This is commonly used to verify if an address is a contract. ## Parameters * `address` — the address to get the code from * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the bytecode at the given address, or `0x` if no code exists (EOA) ## `eth_getCode` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const PATHUSD = "0x20c0000000000000000000000000000000000000"; const getCode = async () => { const code = await provider.getCode(PATHUSD); console.log(`Is contract: ${code !== '0x'}`); }; getCode(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) PATHUSD = "0x20c0000000000000000000000000000000000000" code = web3.eth.get_code(PATHUSD) print(f"Is contract: {code != b''}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getCode", "params": ["0x20c0000000000000000000000000000000000000", "latest"], "id": 1}' ``` # eth_getFilterChanges | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getfilterchanges openapi/tempo_node_api/filter_handling/eth_getFilterChanges.json POST / Tempo API method that returns an array of logs or block/transaction hashes that occurred since the last poll. Tempo via Chainstack. Tempo API method that returns an array of logs or block/transaction hashes that occurred since the last poll. Used with filters created by `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. ## Parameters * `filterId` — the filter ID returned from a filter creation method ## Response * `result` — array of changes since last poll: * For log filters: array of log objects * For block filters: array of block hashes * For pending transaction filters: array of transaction hashes ## `eth_getFilterChanges` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; const TRANSFER_TOPIC = ethers.id("Transfer(address,address,uint256)"); const pollFilter = async () => { // Create a log filter for Transfer events const filterId = await provider.send("eth_newFilter", [{ address: PATHUSD, topics: [TRANSFER_TOPIC] }]); console.log(`Created filter: ${filterId}`); // Poll for changes every 2 seconds const interval = setInterval(async () => { const changes = await provider.send("eth_getFilterChanges", [filterId]); if (changes.length > 0) { console.log(`\nFound ${changes.length} new Transfer events:`); for (const log of changes) { console.log(` Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`); } } else { console.log("No new changes..."); } }, 2000); // Stop after 20 seconds setTimeout(async () => { clearInterval(interval); await provider.send("eth_uninstallFilter", [filterId]); console.log("\nFilter uninstalled"); }, 20000); }; pollFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 import time node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # pathUSD token address PATHUSD = "0x20c0000000000000000000000000000000000000" TRANSFER_TOPIC = web3.keccak(text="Transfer(address,address,uint256)").hex() # Create a log filter filter_result = web3.provider.make_request("eth_newFilter", [{ "address": PATHUSD, "topics": [TRANSFER_TOPIC] }]) filter_id = filter_result['result'] print(f"Created filter: {filter_id}") # Poll for changes try: for _ in range(10): changes_result = web3.provider.make_request("eth_getFilterChanges", [filter_id]) changes = changes_result['result'] if changes: print(f"\nFound {len(changes)} new Transfer events:") for log in changes: print(f" Block {int(log['blockNumber'], 16)}: {log['transactionHash']}") else: print("No new changes...") time.sleep(2) finally: web3.provider.make_request("eth_uninstallFilter", [filter_id]) print("\nFilter uninstalled") ``` ```bash cURL theme={"system"} # First create a filter curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_newFilter", "params": [{"address": "0x20c0000000000000000000000000000000000000"}], "id": 1 }' # Then poll for changes using the returned filter ID curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x1"], "id": 1 }' ``` # eth_getFilterLogs | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getfilterlogs openapi/tempo_node_api/logs_and_events/eth_getFilterLogs.json POST / Tempo API method that returns an array of all logs matching the filter with the given ID. eth_getFilterLogs on Tempo via Chainstack. Tempo API method that returns an array of all logs matching the filter with the given ID. Unlike `eth_getFilterChanges`, this returns all matching logs, not just new ones since the last poll. ## Parameters * `filterId` — the filter ID returned from `eth_newFilter` ## Response * `result` — array of log objects matching the filter: * `address` — contract address that emitted the log * `topics` — array of indexed log parameters * `data` — non-indexed log parameters * `blockNumber` — block number containing the log * `blockHash` — hash of the block * `transactionHash` — hash of the transaction that emitted the log * `transactionIndex` — index of the transaction in the block * `logIndex` — position of the log in the block * `removed` — `true` if the log was removed due to a chain reorganization ## `eth_getFilterLogs` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; const TRANSFER_TOPIC = ethers.id("Transfer(address,address,uint256)"); const getFilterLogs = async () => { // Create a log filter const filterId = await provider.send("eth_newFilter", [{ address: PATHUSD, topics: [TRANSFER_TOPIC], fromBlock: "0x560000" }]); console.log(`Created filter: ${filterId}`); // Get all matching logs const logs = await provider.send("eth_getFilterLogs", [filterId]); console.log(`Found ${logs.length} Transfer events`); for (const log of logs.slice(0, 5)) { console.log(`\nBlock ${parseInt(log.blockNumber, 16)}:`); console.log(` Tx: ${log.transactionHash}`); console.log(` From: 0x${log.topics[1].slice(26)}`); console.log(` To: 0x${log.topics[2].slice(26)}`); } // Clean up await provider.send("eth_uninstallFilter", [filterId]); }; getFilterLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # pathUSD token address PATHUSD = "0x20c0000000000000000000000000000000000000" TRANSFER_TOPIC = web3.keccak(text="Transfer(address,address,uint256)").hex() # Create a log filter filter_result = web3.provider.make_request("eth_newFilter", [{ "address": PATHUSD, "topics": [TRANSFER_TOPIC], "fromBlock": "0x560000" }]) filter_id = filter_result['result'] print(f"Created filter: {filter_id}") # Get all matching logs logs_result = web3.provider.make_request("eth_getFilterLogs", [filter_id]) logs = logs_result['result'] print(f"Found {len(logs)} Transfer events") for log in logs[:5]: print(f"\nBlock {int(log['blockNumber'], 16)}:") print(f" Tx: {log['transactionHash']}") print(f" From: 0x{log['topics'][1][26:]}") print(f" To: 0x{log['topics'][2][26:]}") # Clean up web3.provider.make_request("eth_uninstallFilter", [filter_id]) ``` ```bash cURL theme={"system"} # First create a filter curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_newFilter", "params": [{"address": "0x20c0000000000000000000000000000000000000", "fromBlock": "0x560000"}], "id": 1 }' # Then get all logs using the returned filter ID curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getFilterLogs", "params": ["0x1"], "id": 1}' ``` # eth_getLogs | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getlogs openapi/tempo_node_api/logs_and_events/eth_getLogs.json POST / Tempo API method that returns an array of logs matching a filter object. This is commonly used to track events emitted by smart contracts. Tempo API method that returns an array of logs matching a filter object. This is commonly used to track events emitted by smart contracts, such as TIP-20 token transfers. ## Parameters * `filterObject` — the filter object: * `fromBlock` — (optional) block number (hex) or tag to start from * `toBlock` — (optional) block number (hex) or tag to end at * `address` — (optional) contract address or array of addresses * `topics` — (optional) array of topic filters ## Response * `result` — array of log objects: * `address` — contract address that emitted the log * `topics` — array of indexed log parameters * `data` — non-indexed log parameters * `blockNumber` — block number containing the log * `transactionHash` — hash of the transaction that emitted the log * `logIndex` — position of the log in the block ## `eth_getLogs` code examples The following example retrieves Transfer events from the pathUSD token: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; // Transfer event topic const TRANSFER_TOPIC = ethers.id("Transfer(address,address,uint256)"); const getLogs = async () => { const logs = await provider.getLogs({ address: PATHUSD, topics: [TRANSFER_TOPIC], fromBlock: "latest" }); for (const log of logs) { console.log(`Transfer in tx: ${log.transactionHash}`); } }; getLogs(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # pathUSD token address PATHUSD = "0x20c0000000000000000000000000000000000000" # Transfer event topic TRANSFER_TOPIC = web3.keccak(text="Transfer(address,address,uint256)").hex() logs = web3.eth.get_logs({ 'address': PATHUSD, 'topics': [TRANSFER_TOPIC], 'fromBlock': 'latest' }) for log in logs: print(f"Transfer in tx: {log['transactionHash'].hex()}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getLogs", "params": [{ "fromBlock": "latest", "toBlock": "latest", "address": "0x20c0000000000000000000000000000000000000", "topics": [] }], "id": 1 }' ``` # eth_getProof | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getproof openapi/tempo_node_api/account_info/eth_getProof.json POST / Tempo API method that returns the Merkle proof for an account and optionally some storage keys. Available on Tempo via Chainstack. Tempo API method that returns the Merkle proof for an account and optionally some storage keys. This is useful for verifying account state without trusting the node. > [!NOTE] > > On Global Nodes, this method returns a proof for the latest block only. A block number behind the tip returns `-32602` `distance to target block exceeds maximum proof window`. See [EVM eth\_getProof history limits](/docs/limits#evm-eth_getproof-history-limits). ## Parameters * `address` — the address to get the proof for * `storageKeys` — array of storage positions to prove (hex strings) * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the proof object: * `address` — the account address * `accountProof` — array of RLP-encoded Merkle Patricia trie nodes from state root to account * `balance` — the account balance * `codeHash` — the hash of the account code * `nonce` — the account nonce * `storageHash` — the hash of the storage trie root * `storageProof` — array of storage proofs, one per requested key: * `key` — the storage key * `value` — the storage value * `proof` — array of RLP-encoded trie nodes from storage root to value ## `eth_getProof` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; const getProof = async () => { // Get proof for storage slot 0 (often totalSupply) const proof = await provider.send("eth_getProof", [ PATHUSD, ["0x0", "0x1"], "latest" ]); console.log(`Account: ${proof.address}`); console.log(`Balance: ${proof.balance}`); console.log(`Nonce: ${parseInt(proof.nonce, 16)}`); console.log(`Code Hash: ${proof.codeHash}`); console.log(`Storage Hash: ${proof.storageHash}`); console.log(`\nAccount Proof nodes: ${proof.accountProof.length}`); for (const sp of proof.storageProof) { console.log(`\nStorage Key: ${sp.key}`); console.log(` Value: ${sp.value}`); console.log(` Proof nodes: ${sp.proof.length}`); } }; getProof(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # pathUSD token address PATHUSD = "0x20c0000000000000000000000000000000000000" result = web3.provider.make_request("eth_getProof", [ PATHUSD, ["0x0", "0x1"], "latest" ]) proof = result['result'] print(f"Account: {proof['address']}") print(f"Balance: {proof['balance']}") print(f"Nonce: {int(proof['nonce'], 16)}") print(f"Code Hash: {proof['codeHash']}") print(f"Storage Hash: {proof['storageHash']}") print(f"\nAccount Proof nodes: {len(proof['accountProof'])}") for sp in proof['storageProof']: print(f"\nStorage Key: {sp['key']}") print(f" Value: {sp['value']}") print(f" Proof nodes: {len(sp['proof'])}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getProof", "params": [ "0x20c0000000000000000000000000000000000000", ["0x0", "0x1"], "latest" ], "id": 1 }' ``` # eth_getStorageAt | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getstorageat openapi/tempo_node_api/account_info/eth_getStorageAt.json POST / Tempo API method that returns the value from a storage position at a given address. This is useful for reading raw contract storage slots. Tempo API method that returns the value from a storage position at a given address. This is useful for reading raw contract storage slots. ## Parameters * `address` — the address of the contract * `storagePosition` — the position in storage (hex) * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the value at the storage position encoded as hexadecimal ## `eth_getStorageAt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; const getStorage = async () => { // Slot 0 typically contains the total supply in ERC20 contracts const value = await provider.getStorage(PATHUSD, 0); console.log(`Storage slot 0: ${value}`); // Read a specific slot const slot5 = await provider.send("eth_getStorageAt", [ PATHUSD, "0x5", "latest" ]); console.log(`Storage slot 5: ${slot5}`); }; getStorage(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # pathUSD token address PATHUSD = "0x20c0000000000000000000000000000000000000" # Read storage slot 0 value = web3.eth.get_storage_at(PATHUSD, 0) print(f"Storage slot 0: {value.hex()}") # Read a specific slot using raw RPC result = web3.provider.make_request("eth_getStorageAt", [ PATHUSD, "0x5", "latest" ]) print(f"Storage slot 5: {result['result']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getStorageAt", "params": ["0x20c0000000000000000000000000000000000000", "0x0", "latest"], "id": 1 }' ``` # eth_getTransactionByBlockHashAndIndex | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-gettransactionbyblockhashandindex openapi/tempo_node_api/transaction_info/eth_getTransactionByBlockHashAndIndex.json POST / Tempo API method that returns information about a transaction by block hash and transaction index position. Chainstack Tempo reference. Tempo API method that returns information about a transaction by block hash and transaction index position. ## Parameters * `blockHash` — the hash of the block * `transactionIndex` — the transaction index position (hex) ## Response * `result` — the transaction object, or `null` if not found: * `hash` — transaction hash * `nonce` — sender's transaction count * `blockHash` — hash of the containing block * `blockNumber` — block number * `transactionIndex` — index in the block * `from` — sender address * `to` — recipient address (null for contract creation) * `value` — value transferred in wei * `gas` — gas limit * `gasPrice` — gas price * `input` — transaction data * `v`, `r`, `s` — signature values ## `eth_getTransactionByBlockHashAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionByIndex = async (blockHash, index) => { const tx = await provider.send("eth_getTransactionByBlockHashAndIndex", [ blockHash, index ]); if (tx) { console.log(`Transaction at index ${parseInt(index, 16)}:`); console.log(` Hash: ${tx.hash}`); console.log(` From: ${tx.from}`); console.log(` To: ${tx.to || 'Contract Creation'}`); console.log(` Value: ${ethers.formatEther(tx.value)} ETH`); } else { console.log("Transaction not found"); } }; getTransactionByIndex( "0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e", "0x0" ); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getTransactionByBlockHashAndIndex", [ "0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e", "0x0" ]) tx = result['result'] if tx: print(f"Transaction at index 0:") print(f" Hash: {tx['hash']}") print(f" From: {tx['from']}") print(f" To: {tx.get('to', 'Contract Creation')}") print(f" Value: {web3.from_wei(int(tx['value'], 16), 'ether')} ETH") else: print("Transaction not found") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getTransactionByBlockHashAndIndex", "params": ["0x1c3830dd03a362ba82e82017a5f4e361c12fc43b64a1e4ebd2902f0c313cad7e", "0x0"], "id": 1 }' ``` # eth_getTransactionByBlockNumberAndIndex | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-gettransactionbyblocknumberandindex openapi/tempo_node_api/transaction_info/eth_getTransactionByBlockNumberAndIndex.json POST / Tempo API method that returns information about a transaction by block number and transaction index position. Tempo via Chainstack. Tempo API method that returns information about a transaction by block number and transaction index position. ## Parameters * `blockNumber` — the block number (hex) or tag (`latest`, `earliest`, `pending`) * `transactionIndex` — the transaction index position (hex) ## Response * `result` — the transaction object, or `null` if not found: * `hash` — transaction hash * `nonce` — sender's transaction count * `blockHash` — hash of the containing block * `blockNumber` — block number * `transactionIndex` — index in the block * `from` — sender address * `to` — recipient address (null for contract creation) * `value` — value transferred in wei * `gas` — gas limit * `gasPrice` — gas price * `input` — transaction data * `v`, `r`, `s` — signature values ## `eth_getTransactionByBlockNumberAndIndex` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransactionByIndex = async (blockNumber, index) => { const tx = await provider.send("eth_getTransactionByBlockNumberAndIndex", [ blockNumber, index ]); if (tx) { console.log(`Transaction at block ${blockNumber}, index ${parseInt(index, 16)}:`); console.log(` Hash: ${tx.hash}`); console.log(` From: ${tx.from}`); console.log(` To: ${tx.to || 'Contract Creation'}`); console.log(` Gas: ${parseInt(tx.gas, 16)}`); } else { console.log("Transaction not found"); } }; // Get first transaction in latest block getTransactionByIndex("latest", "0x0"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("eth_getTransactionByBlockNumberAndIndex", [ "latest", "0x0" ]) tx = result['result'] if tx: print(f"Transaction at latest block, index 0:") print(f" Hash: {tx['hash']}") print(f" From: {tx['from']}") print(f" To: {tx.get('to', 'Contract Creation')}") print(f" Gas: {int(tx['gas'], 16)}") else: print("Transaction not found") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_getTransactionByBlockNumberAndIndex", "params": ["latest", "0x0"], "id": 1 }' ``` # eth_getTransactionByHash | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-gettransactionbyhash openapi/tempo_node_api/transaction_info/eth_getTransactionByHash.json POST / Tempo API method that returns the information about a transaction requested by transaction hash. transactionHash — the hash of the transaction to retrieve. Tempo API method that returns the information about a transaction requested by transaction hash. ## Parameters * `transactionHash` — the hash of the transaction to retrieve ## Response * `result` — a transaction object, or `null` if no transaction was found: * `hash` — the transaction hash * `nonce` — the number of transactions made by the sender prior to this one * `blockHash` — hash of the block containing this transaction * `blockNumber` — block number containing this transaction * `from` — address of the sender * `to` — address of the receiver * `value` — value transferred in wei * `input` — data sent along with the transaction * `type` — transaction type (Tempo supports type `0x76` for TempoTransaction) ## `eth_getTransactionByHash` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTransaction = async () => { const tx = await provider.getTransaction("0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035"); console.log(tx); }; getTransaction(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) tx = web3.eth.get_transaction("0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035") print(tx) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getTransactionByHash", "params": ["0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035"], "id": 1}' ``` # eth_getTransactionCount | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-gettransactioncount openapi/tempo_node_api/account_info/eth_getTransactionCount.json POST / Tempo API method that returns the number of transactions sent from an address (the nonce). This is used to set the correct nonce when sending transactions. Tempo API method that returns the number of transactions sent from an address (the nonce). This is used to set the correct nonce when sending transactions. ## Parameters * `address` — the address to get the transaction count for * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the number of transactions sent from the address encoded as hexadecimal ## `eth_getTransactionCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getNonce = async () => { const nonce = await provider.getTransactionCount("0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"); console.log(`Nonce: ${nonce}`); }; getNonce(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) nonce = web3.eth.get_transaction_count("0x9729187D9E8Bbefa8295F39f5634cA454dd9d294") print(f"Nonce: {nonce}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getTransactionCount", "params": ["0x9729187D9E8Bbefa8295F39f5634cA454dd9d294", "latest"], "id": 1}' ``` # eth_getTransactionReceipt | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-gettransactionreceipt openapi/tempo_node_api/transaction_info/eth_getTransactionReceipt.json POST / Tempo API method that returns the receipt of a transaction by transaction hash. Transaction receipts contain information about the execution of a transaction. Tempo API method that returns the receipt of a transaction by transaction hash. Transaction receipts contain information about the execution of a transaction. > [!NOTE] > > **Tempo-specific fields**: Transaction receipts on Tempo include additional fields: > > * `feeToken` — the TIP-20 token address used to pay transaction fees > * `feePayer` — the address that paid the fees (may differ from sender with fee sponsorship) ## Parameters * `transactionHash` — the hash of the transaction to retrieve the receipt for ## Response * `result` — a receipt object, or `null` if no receipt was found: * `transactionHash` — the transaction hash * `blockHash` — hash of the block containing this transaction * `blockNumber` — block number containing this transaction * `from` — address of the sender * `to` — address of the receiver * `status` — `1` for success, `0` for failure * `gasUsed` — amount of gas used * `logs` — array of log objects * `feeToken` — TIP-20 token address used for fees (Tempo-specific) * `feePayer` — address that paid the fees (Tempo-specific) ## `eth_getTransactionReceipt` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getReceipt = async () => { const receipt = await provider.getTransactionReceipt("0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035"); console.log(receipt); }; getReceipt(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) receipt = web3.eth.get_transaction_receipt("0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035") print(receipt) # Access Tempo-specific fields print(f"Fee token: {receipt.get('feeToken')}") print(f"Fee payer: {receipt.get('feePayer')}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getTransactionReceipt", "params": ["0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035"], "id": 1}' ``` # eth_getUncleCountByBlockNumber | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-getunclecountbyblocknumber openapi/tempo_node_api/blocks_info/eth_getUncleCountByBlockNumber.json POST / Tempo API method that returns the number of uncles in a block by block number. Tempo uses Simplex Consensus and does not produce uncle blocks. Tempo API method that returns the number of uncles in a block by block number. Tempo uses Simplex Consensus and does not produce uncle blocks, so this always returns `0x0`. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the number of uncles in the block, encoded as hexadecimal. On Tempo, this always returns `0x0` since Tempo uses Simplex Consensus and has no uncle blocks. ## `eth_getUncleCountByBlockNumber` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getUncleCountByBlockNumber", "params": ["latest"], "id": 1}' ``` # eth_maxPriorityFeePerGas | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-maxpriorityfeepergas openapi/tempo_node_api/gas_data/eth_maxPriorityFeePerGas.json POST / Tempo API method that returns the current maximum priority fee per gas. This is the fee tip that goes to validators on top of the base fee. Tempo API method that returns the current maximum priority fee per gas. This is the fee tip that goes to validators on top of the base fee. ## Parameters None. ## Response * `result` — the current max priority fee per gas encoded as hexadecimal (in wei) ## `eth_maxPriorityFeePerGas` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getMaxPriorityFee = async () => { const feeData = await provider.getFeeData(); console.log(`Max Priority Fee: ${ethers.formatUnits(feeData.maxPriorityFeePerGas, "gwei")} gwei`); // Or using raw RPC const maxPriorityFee = await provider.send("eth_maxPriorityFeePerGas", []); console.log(`Raw response: ${maxPriorityFee}`); console.log(`In gwei: ${ethers.formatUnits(maxPriorityFee, "gwei")}`); }; getMaxPriorityFee(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Get max priority fee max_priority_fee = web3.eth.max_priority_fee print(f"Max Priority Fee: {web3.from_wei(max_priority_fee, 'gwei')} gwei") # Using raw RPC result = web3.provider.make_request("eth_maxPriorityFeePerGas", []) print(f"Raw response: {result['result']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_maxPriorityFeePerGas", "params": [], "id": 1}' ``` # eth_newBlockFilter | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-newblockfilter openapi/tempo_node_api/filter_handling/eth_newBlockFilter.json POST / Tempo API method that creates a filter for new block notifications. The filter can be polled with eth_getFilterChanges to get new block hashes. Tempo API method that creates a filter for new block notifications. The filter can be polled with `eth_getFilterChanges` to get new block hashes. With Tempo's \~0.5 second block times, this filter will receive frequent updates. ## Parameters * `none` ## Response * `result` — the filter ID ## `eth_newBlockFilter` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const createBlockFilter = async () => { const filterId = await provider.send("eth_newBlockFilter", []); console.log(`Filter ID: ${filterId}`); // Poll for new blocks const newBlocks = await provider.send("eth_getFilterChanges", [filterId]); console.log(`New block hashes: ${newBlocks}`); }; createBlockFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) block_filter = web3.eth.filter('latest') print(f"Filter ID: {block_filter.filter_id}") # Poll for new blocks new_blocks = block_filter.get_new_entries() print(f"New blocks: {new_blocks}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_newBlockFilter", "params": [], "id": 1}' ``` # eth_newFilter | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-newfilter openapi/tempo_node_api/filter_handling/eth_newFilter.json POST / Tempo API method that creates a new filter for logs matching specified criteria. The filter can be polled with eth_getFilterChanges. Tempo API method that creates a new filter for logs matching specified criteria. The filter can be polled with `eth_getFilterChanges`. ## Parameters * `filterObject` — the filter object: * `fromBlock` — (optional) block number (hex) or tag to start from * `toBlock` — (optional) block number (hex) or tag to end at * `address` — (optional) contract address or array of addresses * `topics` — (optional) array of topic filters ## Response * `result` — the filter ID ## `eth_newFilter` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const PATHUSD = "0x20c0000000000000000000000000000000000000"; const createFilter = async () => { const filterId = await provider.send("eth_newFilter", [{ address: PATHUSD, fromBlock: "latest", toBlock: "latest" }]); console.log(`Filter ID: ${filterId}`); }; createFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) PATHUSD = "0x20c0000000000000000000000000000000000000" filter_id = web3.eth.filter({ 'address': PATHUSD, 'fromBlock': 'latest' }) print(f"Filter ID: {filter_id.filter_id}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "eth_newFilter", "params": [{"address": "0x20c0000000000000000000000000000000000000", "fromBlock": "latest"}], "id": 1 }' ``` # eth_newPendingTransactionFilter | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-newpendingtransactionfilter openapi/tempo_node_api/filter_handling/eth_newPendingTransactionFilter.json POST / Tempo API method that creates a filter for pending transactions in the node's transaction pool. Use eth_getFilterChanges to poll for new pending transactions. Tempo API method that creates a filter for pending transactions in the node's transaction pool. Use [`eth_getFilterChanges`](/reference/tempo-eth-getfilterchanges) to poll for new pending transactions. ## Parameters * `none` ## Response * `result` — a filter ID to be used with [`eth_getFilterChanges`](/reference/tempo-eth-getfilterchanges) to poll for pending transactions. ## `eth_newPendingTransactionFilter` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_newPendingTransactionFilter", "params": [], "id": 1}' ``` # eth_protocolVersion | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-protocolversion openapi/tempo_node_api/chain_info/eth_protocolVersion.json POST / Tempo API method that returns the current Ethereum protocol version. result — the current Ethereum protocol version, encoded as hexadecimal. Tempo API method that returns the current Ethereum protocol version. ## Parameters * `none` ## Response * `result` — the current Ethereum protocol version, encoded as hexadecimal. ## `eth_protocolVersion` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_protocolVersion", "params": [], "id": 1}' ``` # eth_sendRawTransaction | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-sendrawtransaction openapi/tempo_node_api/transaction_info/eth_sendRawTransaction.json POST / Tempo API method that sends a signed transaction to the network. Chainstack supports eth_sendRawTransaction on Tempo JSON-RPC nodes. Tempo API method that sends a signed transaction to the network. Tempo supports standard Ethereum transaction types plus type `0x76` (TempoTransaction) with features like passkey authentication, call batching, and fee sponsorship. > [!NOTE] > > **Stablecoin gas fees**: On Tempo, transaction fees are paid in TIP-20 stablecoins like pathUSD, not a native token. The fee is automatically converted using the Fee AMM. ## Parameters * `signedTransaction` — the signed transaction data as a hex string ## Response * `result` — the transaction hash of the submitted transaction ## `eth_sendRawTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const sendTransaction = async () => { const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); const tx = { to: "0xRecipientAddress", value: 0, // Tempo uses TIP-20 tokens for value transfer data: "0x..." // TIP-20 transfer calldata }; const txResponse = await wallet.sendTransaction(tx); console.log(`Transaction hash: ${txResponse.hash}`); const receipt = await txResponse.wait(); console.log(`Confirmed in block: ${receipt.blockNumber}`); }; sendTransaction(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Build and sign transaction account = web3.eth.account.from_key("YOUR_PRIVATE_KEY") tx = { 'nonce': web3.eth.get_transaction_count(account.address), 'to': "0xRecipientAddress", 'value': 0, # Tempo uses TIP-20 tokens for value transfer 'gas': 21000, 'gasPrice': web3.eth.gas_price, 'chainId': 4217 # Tempo mainnet (use 42431 for testnet) } signed = account.sign_transaction(tx) tx_hash = web3.eth.send_raw_transaction(signed.raw_transaction) print(f"Transaction hash: {tx_hash.hex()}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": ["0xf867808502540e841e825208949729187d9e8bbefa8295f39f5634ca454dd9d294808083014b9da00602a6c9850068ac6667c098f65cf061e5e90d7030a63d13396dc6d0522fe517a07a0f9c9455612fcacfce60fba7c6e305728148f3ec345661535d0230f872f224"], "id": 1}' ``` # eth_sendRawTransactionSync | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-sendrawtransactionsync openapi/tempo_node_api/tempo_specific/eth_sendRawTransactionSync.json POST / Tempo-specific API method that sends a signed transaction to the network and blocks until the transaction receipt is returned. On Tempo. Tempo-specific API method that sends a signed transaction to the network and blocks until the transaction receipt is returned. This is a synchronous version of [`eth_sendRawTransaction`](/reference/tempo-eth-sendrawtransaction) — instead of returning just the transaction hash, it waits for the transaction to be included in a block and returns the full receipt. > [!NOTE] > > This method is particularly useful on Tempo because of its \~500ms finality. The synchronous wait adds minimal latency while eliminating the need to poll for receipts. ## Parameters * `signedTransaction` — the signed transaction data as a hex string ## Response * `result` — the full transaction receipt object, returned once the transaction is included in a block. Contains the same fields as the response from [`eth_getTransactionReceipt`](/reference/tempo-eth-gettransactionreceipt), including Tempo-specific fields like `feeToken` and `feePayer`. ## `eth_sendRawTransactionSync` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const sendTransactionSync = async () => { const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider); const tx = { to: "0xRecipientAddress", value: 0, data: "0x..." // TIP-20 transfer calldata }; // Sign the transaction const signedTx = await wallet.signTransaction(tx); // Send synchronously — blocks until receipt is returned const receipt = await provider.send("eth_sendRawTransactionSync", [signedTx]); console.log(`Confirmed in block: ${receipt.blockNumber}`); console.log(`Status: ${receipt.status}`); }; sendTransactionSync(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) account = web3.eth.account.from_key("YOUR_PRIVATE_KEY") tx = { 'nonce': web3.eth.get_transaction_count(account.address), 'to': "0xRecipientAddress", 'value': 0, 'gas': 21000, 'gasPrice': web3.eth.gas_price, 'chainId': 4217 # Tempo mainnet (use 42431 for testnet) } signed = account.sign_transaction(tx) # Send synchronously — blocks until receipt is returned receipt = web3.provider.make_request( "eth_sendRawTransactionSync", [signed.raw_transaction.hex()] ) print(f"Receipt: {receipt['result']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_sendRawTransactionSync", "params": ["SIGNED_TX_DATA"], "id": 1}' ``` ## When to use Use `eth_sendRawTransactionSync` instead of `eth_sendRawTransaction` when: * You need the receipt immediately and want to avoid polling with `eth_getTransactionReceipt` * You are building synchronous request/response flows (like payment APIs) * You want to simplify transaction submission logic by getting the result in a single call For fire-and-forget scenarios or when submitting many transactions in parallel, use [`eth_sendRawTransaction`](/reference/tempo-eth-sendrawtransaction) instead. # eth_syncing | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-syncing openapi/tempo_node_api/chain_info/eth_syncing.json POST / Tempo API method that returns the sync status of the node. Returns false if the node is fully synced, or a sync status object if syncing. Tempo API method that returns the sync status of the node. Returns `false` if the node is fully synced, or a sync status object if syncing. ## Parameters * `none` ## Response * `result` — `false` if not syncing, or a sync status object: * `startingBlock` — block at which sync started * `currentBlock` — current block being synced * `highestBlock` — estimated highest block ## `eth_syncing` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function checkSyncStatus() { const syncing = await provider.send("eth_syncing", []); if (syncing === false) { console.log("Node is fully synced"); } else { console.log(`Syncing: ${Number(syncing.currentBlock)}/${Number(syncing.highestBlock)}`); } } checkSyncStatus() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) syncing = web3.eth.syncing if syncing is False: print("Node is fully synced") else: print(f"Syncing: {syncing['currentBlock']}/{syncing['highestBlock']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1}' ``` # eth_uninstallFilter | Tempo Source: https://docs.chainstack.com/reference/tempo-eth-uninstallfilter openapi/tempo_node_api/filter_handling/eth_uninstallFilter.json POST / Tempo API method that uninstalls a filter created by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter. On Tempo. Tempo API method that uninstalls a filter created by `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. Filters should be uninstalled when no longer needed to free server resources. ## Parameters * `filterId` — the filter ID to uninstall ## Response * `result` — `true` if the filter was successfully uninstalled, `false` otherwise ## `eth_uninstallFilter` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const filterLifecycle = async () => { // Create a block filter const filterId = await provider.send("eth_newBlockFilter", []); console.log(`Created block filter: ${filterId}`); // Use the filter const blocks = await provider.send("eth_getFilterChanges", [filterId]); console.log(`Got ${blocks.length} new blocks`); // Uninstall when done const uninstalled = await provider.send("eth_uninstallFilter", [filterId]); console.log(`Filter uninstalled: ${uninstalled}`); // Trying to use it again will fail try { await provider.send("eth_getFilterChanges", [filterId]); } catch (e) { console.log("Filter no longer exists (expected)"); } }; filterLifecycle(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Create a block filter filter_result = web3.provider.make_request("eth_newBlockFilter", []) filter_id = filter_result['result'] print(f"Created block filter: {filter_id}") # Use the filter changes_result = web3.provider.make_request("eth_getFilterChanges", [filter_id]) print(f"Got {len(changes_result['result'])} new blocks") # Uninstall when done uninstall_result = web3.provider.make_request("eth_uninstallFilter", [filter_id]) print(f"Filter uninstalled: {uninstall_result['result']}") ``` ```bash cURL theme={"system"} # First create a filter curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_newBlockFilter", "params": [], "id": 1}' # Then uninstall it using the returned filter ID curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_uninstallFilter", "params": ["0x1"], "id": 1}' ``` # Tempo API reference: stablecoin payments JSON-RPC Source: https://docs.chainstack.com/reference/tempo-getting-started Get started building on Tempo with Chainstack. Access the JSON-RPC API to build stablecoin payment applications with low-cost gas fees on the Tempo blockchain. ## What is Tempo? Tempo is a Layer-1 blockchain purpose-built for payments. It combines EVM compatibility with payment-specific features like stablecoin gas fees, payment lanes, built-in compliance support, and the Machine Payments Protocol (MPP) for inline HTTP payments. > [!NOTE] > > Tempo is designed for high-throughput payment applications with sub-second finality and the ability to pay transaction fees in stablecoins instead of a volatile native token. ### Key features * **Stablecoin gas fees** — Pay transaction fees in USD stablecoins, eliminating the need for a volatile gas token * **Sub-second finality** — Transactions finalize in approximately 500 milliseconds using Simplex Consensus * **Payment lanes** — Dedicated blockspace ensures payment transactions always have room, even during network congestion * **Tempo Transactions** — Custom EIP-2718 transaction type with fee sponsorship, batch calls, 2D nonces, access keys, and scheduled transactions * **TIP-20 token standard** — Extended ERC-20 with ISO 20022 memos, compliance hooks, and 6-decimal precision for stablecoins * **Machine Payments Protocol (MPP)** — HTTP 402-based inline payments for paid APIs, MCP tools, and agentic commerce * **Enshrined stablecoin DEX** — Protocol-level exchange for stablecoin swaps with limit orders and flip orders * **EVM compatible** — Deploy Solidity contracts using familiar tools like Foundry, Hardhat, and Remix ## Network details > [tab] Mainnet > > | Property | Value | > | - | - | > | Network name | Tempo Mainnet | > | Chain ID | 4217 (0x1079) | > | Currency | USD stablecoins (no native token) | > | Block time | \~0.5 seconds | > | Finality | Sub-second (Simplex Consensus) | > | Explorer | [explore.mainnet.tempo.xyz](https://explore.mainnet.tempo.xyz/) | > [tab] Testnet > > | Property | Value | > | - | - | > | Network name | Tempo Testnet (Moderato) | > | Chain ID | 42431 (0xa5bf) | > | Currency | USD stablecoins (no native token) | > | Block time | \~0.5 seconds | > | Finality | Sub-second (Simplex Consensus) | > | Explorer | [explore.tempo.xyz](https://explore.tempo.xyz/) | ## What is the Tempo API? The Tempo API allows developers to communicate with the Tempo blockchain to build payment applications. To read data from and send transactions to the Tempo blockchain, an application must connect to a Tempo RPC node. When communicating with a Tempo RPC node, the Tempo client implements a JSON-RPC specification, a communication protocol allowing one to make remote calls and execute them as if they were made locally. Tempo supports all standard Ethereum JSON-RPC methods plus Tempo-specific extensions for payments and compliance. ## How to start using the Tempo API To use the Tempo API, you need access to a Tempo RPC node. 1. [Sign up with Chainstack](https://console.chainstack.com/). 2. [Deploy a Tempo node](/docs/manage-your-networks). 3. Use your Chainstack endpoint in your applications. See also [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). ## Predeployed contracts Tempo includes several predeployed system contracts for payments, tokens, and compliance. These addresses are the same on both mainnet and testnet: | Contract | Address | Purpose | | - | - | - | | TIP-20 Factory | `0x20fc000000000000000000000000000000000000` | Create TIP-20 stablecoin tokens | | Fee Manager | `0xfeec000000000000000000000000000000000000` | Handle fee payments and conversions | | Stablecoin DEX | `0xdec0000000000000000000000000000000000000` | Enshrined DEX for stablecoin swaps | | TIP-403 Registry | `0x403c000000000000000000000000000000000000` | Transfer policy registry for compliance | | pathUSD | `0x20c0000000000000000000000000000000000000` | First stablecoin deployed | | Multicall3 | `0xcA11bde05977b3631167028862bE2a173976CA11` | Batch multiple calls | | CreateX | `0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed` | Deterministic contract deployment | | Permit2 | `0x000000000022d473030f116ddee9f6b43ac78ba3` | Token approvals and transfers | ## Get testnet tokens > [!NOTE] > > The faucet is only available on Tempo testnet. On mainnet, acquire stablecoins from issuers or ecosystem partners. To get testnet tokens, use the `tempo_fundAddress` RPC method on the public Tempo RPC: ```bash theme={"system"} curl -X POST https://rpc.moderato.tempo.xyz \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "tempo_fundAddress", "params": ["YOUR_ADDRESS"], "id": 1 }' ``` This funds your address with test stablecoins: pathUSD, AlphaUSD, BetaUSD, and ThetaUSD (1M of each). ## Important differences from Ethereum ### No native token balance Tempo has no native token. The `eth_getBalance` method returns a placeholder value. To check balances, query TIP-20 token contracts directly using `balanceOf`. ### Stablecoin gas fees Transaction fees are paid in TIP-20 stablecoins. The Fee AMM automatically converts between stablecoins, so users can pay in any supported USD token. ### Transaction receipts Tempo transaction receipts include additional fields: * `feeToken` — The TIP-20 token address used to pay fees * `feePayer` — The address that paid the fees (may differ from sender with fee sponsorship) > [!NOTE] > > **See also** > > * [Tempo tooling](/docs/tempo-tooling) > * [Tempo: Building your first payment app](/docs/tempo-tutorial-first-payment-app) > * [Tempo: Basic DEX swap with Foundry](/docs/tempo-tutorial-dex-swap-foundry) > * [Tempo documentation](https://docs.tempo.xyz/) > * [Tempo GitHub](https://github.com/tempoxyz/tempo) # net_listening | Tempo Source: https://docs.chainstack.com/reference/tempo-net-listening openapi/tempo_node_api/client_info/net_listening.json POST / Tempo API method that returns true if the client is actively listening for network connections. Available on Tempo via Chainstack. Tempo API method that returns `true` if the client is actively listening for network connections. ## Parameters None. ## Response * `result` — `true` if the client is listening, `false` otherwise ## `net_listening` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const checkListening = async () => { const listening = await provider.send("net_listening", []); console.log(`Node is listening: ${listening}`); }; checkListening(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) listening = web3.net.listening print(f"Node is listening: {listening}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "net_listening", "params": [], "id": 1}' ``` # net_peerCount | Tempo Source: https://docs.chainstack.com/reference/tempo-net-peercount openapi/tempo_node_api/client_info/net_peerCount.json POST / Tempo API method that returns the number of peers currently connected to the client. Available on Tempo via Chainstack JSON-RPC nodes. Tempo API method that returns the number of peers currently connected to the client. ## Parameters None. ## Response * `result` — the number of connected peers encoded as hexadecimal ## `net_peerCount` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getPeerCount = async () => { const peerCount = await provider.send("net_peerCount", []); console.log(`Connected peers: ${parseInt(peerCount, 16)}`); }; getPeerCount(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) peer_count = web3.net.peer_count print(f"Connected peers: {peer_count}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "net_peerCount", "params": [], "id": 1}' ``` # net_version | Tempo Source: https://docs.chainstack.com/reference/tempo-net-version openapi/tempo_node_api/client_info/net_version.json POST / Tempo API method that returns the current network ID. The Tempo mainnet network ID is 4217. The Moderato testnet network ID is 42431. Tempo API method that returns the current network ID. The Tempo mainnet network ID is `4217`. The Moderato testnet network ID is `42431`. ## Parameters * `none` ## Response * `result` — the network ID as a string (`4217` for Tempo mainnet, `42431` for Moderato testnet) ## `net_version` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getNetworkId() { const network = await provider.getNetwork(); console.log(`Network ID: ${network.chainId}`); } getNetworkId() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.net.version) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "net_version", "params": [], "id": 1}' ``` # tempo_fundAddress | Tempo Source: https://docs.chainstack.com/reference/tempo-tempo-fundaddress openapi/tempo_node_api/tempo_specific/tempo_fundAddress.json POST / Tempo API method that funds an address with testnet stablecoins. tempo_fundAddress JSON-RPC method available on the Tempo blockchain via Chainstack. Tempo API method that funds an address with testnet stablecoins. This is Tempo's faucet mechanism — instead of a web UI, tokens are distributed via this RPC method. > [!WARNING] > > **Testnet only**: This method is only available on the Tempo Moderato testnet via the public RPC endpoint (`https://rpc.moderato.tempo.xyz`). It is not available on Chainstack endpoints or on mainnet. On mainnet, acquire stablecoins from issuers or ecosystem partners. ## Parameters * `address` — the address to fund with testnet stablecoins ## Response * `result` — an array of transaction hashes, one for each funded token ## Tokens funded This method sends the following testnet stablecoins to your address: | Token | Address | | - | - | | pathUSD | `0x20c0000000000000000000000000000000000000` | | AlphaUSD | `0x20c0000000000000000000000000000000000001` | | BetaUSD | `0x20c0000000000000000000000000000000000002` | | ThetaUSD | `0x20c0000000000000000000000000000000000003` | ## `tempo_fundAddress` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "https://rpc.moderato.tempo.xyz"; const provider = new ethers.JsonRpcProvider(NODE_URL); const fundAddress = async (address) => { const result = await provider.send("tempo_fundAddress", [address]); console.log("Funding transaction hashes:"); result.forEach((hash, i) => { console.log(` Token ${i + 1}: ${hash}`); }); }; fundAddress("0xc2F695613de0885dA3bdd18E8c317B9fAf7d4eba"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "https://rpc.moderato.tempo.xyz" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("tempo_fundAddress", ["0xc2F695613de0885dA3bdd18E8c317B9fAf7d4eba"]) print("Funding transaction hashes:") for i, tx_hash in enumerate(result['result']): print(f" Token {i + 1}: {tx_hash}") ``` ```bash cURL theme={"system"} curl -X POST "https://rpc.moderato.tempo.xyz" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "tempo_fundAddress", "params": ["0xc2F695613de0885dA3bdd18E8c317B9fAf7d4eba"], "id": 1 }' ``` ## Example response ```json theme={"system"} { "jsonrpc": "2.0", "id": 1, "result": [ "0x54340f261371117e7d5d39d241a48c595c0626871c38179248262b3a425dd71d", "0x2d8840c6df9fdfe7cbcdca4a4d5b97cb1a520580e2a5be074eb018e2868808f6", "0x5ec61f424fd34aa3faa57eb29204f95de7160bc35f892e358aa23c61e3640fe8", "0xc946437b2fe84d0666bf158e0ed3b8d56f285c440b291b89ea1d2a3487319396" ] } ``` # trace_block | Tempo Source: https://docs.chainstack.com/reference/tempo-trace-block openapi/tempo_node_api/debug_and_trace/trace_block.json POST / Tempo API method that returns execution traces for all transactions in a block. Reference for trace_block on Tempo via Chainstack. Tempo API method that returns execution traces for all transactions in a block. This provides detailed call-level traces using the Parity/OpenEthereum trace format. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — array of trace objects for all transactions in the block: * `action` — the action object: * `from` — sender address * `to` — recipient address * `callType` — type of call (call, delegatecall, staticcall, etc.) * `gas` — gas provided * `input` — call data * `value` — value transferred * `blockHash` — hash of the block * `blockNumber` — block number * `result` — the result object: * `gasUsed` — gas consumed * `output` — return data * `subtraces` — number of child traces * `traceAddress` — position in the trace tree * `transactionHash` — hash of the transaction * `transactionPosition` — index of the transaction in the block * `type` — trace type (call, create, suicide, reward) ## `trace_block` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlock = async (blockNumber) => { const traces = await provider.send("trace_block", [blockNumber]); console.log(`Block has ${traces.length} traces`); // Group traces by transaction const txTraces = {}; for (const trace of traces) { const txHash = trace.transactionHash; if (!txTraces[txHash]) { txTraces[txHash] = []; } txTraces[txHash].push(trace); } console.log(`Across ${Object.keys(txTraces).length} transactions`); for (const [txHash, txTrace] of Object.entries(txTraces)) { console.log(`\nTx: ${txHash}`); for (const trace of txTrace) { const indent = " ".repeat(trace.traceAddress.length); console.log(`${indent}${trace.action.callType}: ${trace.action.from} -> ${trace.action.to}`); } } }; traceBlock("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 from collections import defaultdict node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("trace_block", ["latest"]) traces = result['result'] print(f"Block has {len(traces)} traces") # Group traces by transaction tx_traces = defaultdict(list) for trace in traces: tx_hash = trace['transactionHash'] tx_traces[tx_hash].append(trace) print(f"Across {len(tx_traces)} transactions") for tx_hash, tx_trace in tx_traces.items(): print(f"\nTx: {tx_hash}") for trace in tx_trace: indent = " " * len(trace['traceAddress']) call_type = trace['action'].get('callType', trace['type']) from_addr = trace['action']['from'] to_addr = trace['action'].get('to', 'Contract Creation') print(f"{indent}{call_type}: {from_addr} -> {to_addr}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_block", "params": ["latest"], "id": 1 }' ``` # trace_call | Tempo Source: https://docs.chainstack.com/reference/tempo-trace-call openapi/tempo_node_api/debug_and_trace/trace_call.json POST / Tempo API method that traces a call without creating a transaction. This provides detailed execution traces using the Parity/OpenEthereum trace format. Tempo API method that traces a call without creating a transaction. This provides detailed execution traces using the Parity/OpenEthereum trace format. ## Parameters * `callObject` — the call object: * `from` — (optional) sender address * `to` — recipient address * `gas` — (optional) gas limit * `gasPrice` — (optional) gas price * `value` — (optional) value to send * `data` — (optional) call data * `traceTypes` — array of trace types to include: * `trace` — basic execution trace * `vmTrace` — full VM execution trace * `stateDiff` — state changes * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — the trace result object: * `output` — return data from the call * `trace` — array of trace objects (if requested) * `vmTrace` — VM execution trace (if requested) * `stateDiff` — state differences (if requested) ## `trace_call` code examples The following example traces a `balanceOf` call on the pathUSD token: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; const TARGET_ADDRESS = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294"; // Encode balanceOf(address) const iface = new ethers.Interface(["function balanceOf(address) view returns (uint256)"]); const data = iface.encodeFunctionData("balanceOf", [TARGET_ADDRESS]); const traceCall = async () => { const result = await provider.send("trace_call", [ { to: PATHUSD, data: data }, ["trace", "stateDiff"], "latest" ]); console.log("Output:", result.output); console.log("Traces:", result.trace.length); if (result.stateDiff) { console.log("State changes:", Object.keys(result.stateDiff).length, "addresses affected"); } }; traceCall(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # pathUSD token address PATHUSD = "0x20c0000000000000000000000000000000000000" TARGET_ADDRESS = "0x9729187D9E8Bbefa8295F39f5634cA454dd9d294" # balanceOf(address) calldata data = "0x70a08231000000000000000000000000" + TARGET_ADDRESS[2:].lower() result = web3.provider.make_request("trace_call", [ { "to": PATHUSD, "data": data }, ["trace", "stateDiff"], "latest" ]) trace_result = result['result'] print(f"Output: {trace_result['output']}") print(f"Traces: {len(trace_result['trace'])}") if trace_result.get('stateDiff'): print(f"State changes: {len(trace_result['stateDiff'])} addresses affected") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_call", "params": [ { "to": "0x20c0000000000000000000000000000000000000", "data": "0x70a082310000000000000000000000009729187d9e8bbefa8295f39f5634ca454dd9d294" }, ["trace", "stateDiff"], "latest" ], "id": 1 }' ``` # trace_callMany | Tempo Source: https://docs.chainstack.com/reference/tempo-trace-callmany openapi/tempo_node_api/debug_and_trace/trace_callMany.json POST / Tempo API method that traces multiple calls in sequence using the OpenEthereum trace format. Each call's state changes are visible to subsequent calls. Tempo API method that traces multiple calls in sequence using the OpenEthereum trace format. Each call's state changes are visible to subsequent calls. ## Parameters * `calls` — an array of call bundles, where each bundle is an array of `[callObject, traceTypes]` pairs * `blockParameter` — (optional) the block number (hex) or tag (`latest`, `earliest`, `pending`) ## Response * `result` — an array of trace results, one for each call. ## `trace_callMany` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "trace_callMany", "params": [[[{"to": "0x20c0000000000000000000000000000000000000", "data": "0x95d89b41"}, ["trace"]]], "latest"], "id": 1}' ``` # trace_filter | Tempo Source: https://docs.chainstack.com/reference/tempo-trace-filter openapi/tempo_node_api/debug_and_trace/trace_filter.json POST / Tempo API method that returns execution traces matching specific filter criteria. Available on Tempo via Chainstack JSON-RPC nodes. Tempo API method that returns execution traces matching specific filter criteria. This is useful for finding all interactions with a specific contract or address across multiple blocks. ## Parameters * `filterObject` — the filter object: * `fromBlock` — (optional) starting block number (hex) * `toBlock` — (optional) ending block number (hex) * `fromAddress` — (optional) array of sender addresses to filter * `toAddress` — (optional) array of recipient addresses to filter * `after` — (optional) offset for pagination * `count` — (optional) maximum number of traces to return ## Response * `result` — array of trace objects matching the filter: * `action` — the action object: * `from` — sender address * `to` — recipient address * `callType` — type of call * `gas` — gas provided * `input` — call data * `value` — value transferred * `blockHash` — hash of the block * `blockNumber` — block number * `result` — the result object * `subtraces` — number of child traces * `traceAddress` — position in the trace tree * `transactionHash` — hash of the transaction * `transactionPosition` — index in the block * `type` — trace type ## `trace_filter` code examples The following example finds all calls to the pathUSD token contract: ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // pathUSD token address const PATHUSD = "0x20c0000000000000000000000000000000000000"; const traceFilter = async () => { const currentBlock = await provider.getBlockNumber(); const fromBlock = "0x" + (currentBlock - 1000).toString(16); const toBlock = "0x" + currentBlock.toString(16); const traces = await provider.send("trace_filter", [{ fromBlock: fromBlock, toBlock: toBlock, toAddress: [PATHUSD], count: 20 }]); console.log(`Found ${traces.length} traces to pathUSD`); for (const trace of traces) { console.log(`Block ${trace.blockNumber}: ${trace.action.from} -> ${trace.action.to}`); console.log(` Call type: ${trace.action.callType}`); console.log(` Tx: ${trace.transactionHash}`); } }; traceFilter(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # pathUSD token address PATHUSD = "0x20c0000000000000000000000000000000000000" current_block = web3.eth.block_number from_block = hex(current_block - 1000) to_block = hex(current_block) result = web3.provider.make_request("trace_filter", [{ "fromBlock": from_block, "toBlock": to_block, "toAddress": [PATHUSD], "count": 20 }]) traces = result['result'] print(f"Found {len(traces)} traces to pathUSD") for trace in traces: print(f"Block {trace['blockNumber']}: {trace['action']['from']} -> {trace['action']['to']}") print(f" Call type: {trace['action']['callType']}") print(f" Tx: {trace['transactionHash']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_filter", "params": [{ "fromBlock": "0x564000", "toBlock": "0x565000", "toAddress": ["0x20c0000000000000000000000000000000000000"], "count": 20 }], "id": 1 }' ``` # trace_get | Tempo Source: https://docs.chainstack.com/reference/tempo-trace-get openapi/tempo_node_api/debug_and_trace/trace_get.json POST / Tempo API method that returns a specific trace from a transaction by its trace address. Available on Tempo via Chainstack JSON-RPC nodes. Tempo API method that returns a specific trace from a transaction by its trace address. The trace address is an array of indices that identifies a particular internal call within the transaction execution tree. ## Parameters * `transactionHash` — the hash of the transaction * `traceAddress` — an array of index positions describing the trace address within the transaction ## Response * `result` — the trace object at the specified address, or `null` if not found. ## `trace_get` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "trace_get", "params": ["0x0000000000000000000000000000000000000000000000000000000000000000", ["0x0"]], "id": 1}' ``` # trace_rawTransaction | Tempo Source: https://docs.chainstack.com/reference/tempo-trace-rawtransaction openapi/tempo_node_api/debug_and_trace/trace_rawTransaction.json POST / Tempo API method that traces a signed transaction without broadcasting it to the network. trace_rawTransaction on Tempo via Chainstack. Tempo API method that traces a signed transaction without broadcasting it to the network. Returns the execution trace as if the transaction were executed at the current block. ## Parameters * `signedTransaction` — the signed transaction data as a hex string * `traceTypes` — an array of trace types to include (e.g., `trace`, `vmTrace`, `stateDiff`) ## Response * `result` — the trace result object containing the requested trace types. ## `trace_rawTransaction` code examples ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "trace_rawTransaction", "params": ["0x00", ["trace"]], "id": 1}' ``` # trace_replayBlockTransactions | Tempo Source: https://docs.chainstack.com/reference/tempo-trace-replayblocktransactions openapi/tempo_node_api/debug_and_trace/trace_replayBlockTransactions.json POST / Tempo API method that replays all transactions in a block and returns execution traces for each. Available on Tempo via Chainstack. Tempo API method that replays all transactions in a block and returns execution traces for each. This is useful for detailed analysis of block execution with customizable trace options. ## Parameters * `blockParameter` — the block number (hex) or tag (`latest`, `earliest`, `pending`) * `traceTypes` — array of trace types to include: * `trace` — basic execution trace * `vmTrace` — full VM execution trace * `stateDiff` — state changes ## Response * `result` — array of trace results, one per transaction: * `output` — return data from the transaction * `trace` — array of trace objects (if requested) * `vmTrace` — VM execution trace (if requested) * `stateDiff` — state differences (if requested) * `transactionHash` — hash of the transaction ## `trace_replayBlockTransactions` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const replayBlock = async (blockNumber) => { const results = await provider.send("trace_replayBlockTransactions", [ blockNumber, ["trace"] ]); console.log(`Replayed ${results.length} transactions`); for (let i = 0; i < results.length; i++) { const result = results[i]; console.log(`\nTransaction ${i}: ${result.transactionHash}`); console.log(` Output: ${result.output}`); console.log(` Traces: ${result.trace?.length || 0}`); if (result.trace && result.trace.length > 0) { for (const trace of result.trace) { const callType = trace.action.callType || trace.type; console.log(` ${callType}: ${trace.action.from} -> ${trace.action.to || 'Create'}`); } } } }; replayBlock("latest"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("trace_replayBlockTransactions", [ "latest", ["trace"] ]) results = result['result'] print(f"Replayed {len(results)} transactions") for i, tx_result in enumerate(results): print(f"\nTransaction {i}: {tx_result['transactionHash']}") print(f" Output: {tx_result['output']}") traces = tx_result.get('trace', []) print(f" Traces: {len(traces)}") for trace in traces: call_type = trace['action'].get('callType', trace['type']) to = trace['action'].get('to', 'Create') print(f" {call_type}: {trace['action']['from']} -> {to}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_replayBlockTransactions", "params": ["latest", ["trace"]], "id": 1 }' ``` # trace_replayTransaction | Tempo Source: https://docs.chainstack.com/reference/tempo-trace-replaytransaction openapi/tempo_node_api/debug_and_trace/trace_replayTransaction.json POST / Tempo API method that replays a specific transaction and returns detailed execution traces. trace_replayTransaction on Tempo via Chainstack. Tempo API method that replays a specific transaction and returns detailed execution traces. This is useful for debugging transaction behavior with customizable trace options. ## Parameters * `transactionHash` — the hash of the transaction to replay * `traceTypes` — array of trace types to include: * `trace` — basic execution trace * `vmTrace` — full VM execution trace * `stateDiff` — state changes ## Response * `result` — trace result object: * `output` — return data from the transaction * `trace` — array of trace objects (if requested) * `vmTrace` — VM execution trace (if requested) * `stateDiff` — state differences (if requested) ## `trace_replayTransaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const replayTransaction = async (txHash) => { // Replay with all trace types const result = await provider.send("trace_replayTransaction", [ txHash, ["trace", "stateDiff"] ]); console.log("Transaction Output:", result.output); console.log("Traces:", result.trace?.length || 0); if (result.trace) { for (const trace of result.trace) { const callType = trace.action.callType || trace.type; const to = trace.action.to || "Contract Creation"; console.log(` ${callType}: ${trace.action.from} -> ${to}`); } } if (result.stateDiff) { console.log("\nState changes:"); for (const [address, diff] of Object.entries(result.stateDiff)) { console.log(` ${address}:`); if (diff.balance) { console.log(` Balance: ${diff.balance['*']?.from || 'new'} -> ${diff.balance['*']?.to || diff.balance['+']}`); } } } }; replayTransaction("0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("trace_replayTransaction", [ "0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035", ["trace", "stateDiff"] ]) trace_result = result['result'] print(f"Transaction Output: {trace_result['output']}") print(f"Traces: {len(trace_result.get('trace', []))}") for trace in trace_result.get('trace', []): call_type = trace['action'].get('callType', trace['type']) to = trace['action'].get('to', 'Contract Creation') print(f" {call_type}: {trace['action']['from']} -> {to}") if trace_result.get('stateDiff'): print("\nState changes:") for address, diff in trace_result['stateDiff'].items(): print(f" {address}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_replayTransaction", "params": ["0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035", ["trace", "stateDiff"]], "id": 1 }' ``` # trace_transaction | Tempo Source: https://docs.chainstack.com/reference/tempo-trace-transaction openapi/tempo_node_api/debug_and_trace/trace_transaction.json POST / Tempo API method that returns execution traces for a specific transaction. Reference for trace_transaction on Tempo via Chainstack. Tempo API method that returns execution traces for a specific transaction. This provides detailed call-level traces using the Parity/OpenEthereum trace format. ## Parameters * `transactionHash` — the hash of the transaction to trace ## Response * `result` — array of trace objects for the transaction: * `action` — the action object: * `from` — sender address * `to` — recipient address * `callType` — type of call (call, delegatecall, staticcall, etc.) * `gas` — gas provided * `input` — call data * `value` — value transferred * `blockHash` — hash of the block * `blockNumber` — block number * `result` — the result object: * `gasUsed` — gas consumed * `output` — return data * `subtraces` — number of child traces * `traceAddress` — position in the trace tree * `transactionHash` — hash of the transaction * `transactionPosition` — index of the transaction in the block * `type` — trace type (call, create, suicide, reward) ## `trace_transaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txHash) => { const traces = await provider.send("trace_transaction", [txHash]); console.log(`Transaction has ${traces.length} traces`); for (const trace of traces) { const indent = " ".repeat(trace.traceAddress.length); const callType = trace.action.callType || trace.type; const to = trace.action.to || "Contract Creation"; console.log(`${indent}[${trace.traceAddress.join(",")}] ${callType}`); console.log(`${indent} From: ${trace.action.from}`); console.log(`${indent} To: ${to}`); console.log(`${indent} Gas Used: ${trace.result?.gasUsed || "N/A"}`); if (trace.error) { console.log(`${indent} Error: ${trace.error}`); } } }; traceTransaction("0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035"); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("trace_transaction", [ "0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035" ]) traces = result['result'] print(f"Transaction has {len(traces)} traces") for trace in traces: indent = " " * len(trace['traceAddress']) call_type = trace['action'].get('callType', trace['type']) to = trace['action'].get('to', 'Contract Creation') print(f"{indent}[{','.join(map(str, trace['traceAddress']))}] {call_type}") print(f"{indent} From: {trace['action']['from']}") print(f"{indent} To: {to}") if trace.get('result'): print(f"{indent} Gas Used: {trace['result'].get('gasUsed', 'N/A')}") if trace.get('error'): print(f"{indent} Error: {trace['error']}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "trace_transaction", "params": ["0xb3e821e696897b02283b7b2d602941b1d3cb08448d3a204bab05955215fc2035"], "id": 1 }' ``` # txpool_content | Tempo Source: https://docs.chainstack.com/reference/tempo-txpool-content openapi/tempo_node_api/txpool/txpool_content.json POST / Tempo API method that returns the contents of the transaction pool. This includes both pending and queued transactions. Tempo via Chainstack. > [!NOTE] > > Chainstack plans to deprecate Trader Nodes on December 1, 2026. We recommend moving your workloads to [Global Nodes](/docs/global-elastic-node) before then, as described in [Trader Node deprecation](/docs/trader-node#trader-node-deprecation). Tempo API method that returns the contents of the transaction pool. This includes both pending and queued transactions. > [!WARNING] > > `txpool_content` is not available on Tempo Global Nodes and Trader Nodes — the call returns `-32601`. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access). ## Parameters None. ## Response * `result` — object containing pending and queued transactions: * `pending` — object mapping addresses to their pending transactions (keyed by nonce) * `queued` — object mapping addresses to their queued transactions (keyed by nonce) Each transaction object contains: * `hash` — transaction hash * `nonce` — sender's nonce * `from` — sender address * `to` — recipient address * `value` — value in wei * `gas` — gas limit * `gasPrice` — gas price * `input` — transaction data ## `txpool_content` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTxpoolContent = async () => { const content = await provider.send("txpool_content", []); const pendingAddresses = Object.keys(content.pending); const queuedAddresses = Object.keys(content.queued); console.log(`Pending transactions from ${pendingAddresses.length} addresses`); console.log(`Queued transactions from ${queuedAddresses.length} addresses`); // Show details for pending transactions for (const [address, txs] of Object.entries(content.pending)) { console.log(`\nPending from ${address}:`); for (const [nonce, tx] of Object.entries(txs)) { console.log(` Nonce ${nonce}: ${tx.hash}`); console.log(` To: ${tx.to || 'Contract Creation'}`); console.log(` Gas: ${parseInt(tx.gas, 16)}`); } } }; getTxpoolContent(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("txpool_content", []) content = result['result'] pending_addresses = list(content['pending'].keys()) queued_addresses = list(content['queued'].keys()) print(f"Pending transactions from {len(pending_addresses)} addresses") print(f"Queued transactions from {len(queued_addresses)} addresses") # Show details for pending transactions for address, txs in content['pending'].items(): print(f"\nPending from {address}:") for nonce, tx in txs.items(): print(f" Nonce {nonce}: {tx['hash']}") print(f" To: {tx.get('to', 'Contract Creation')}") print(f" Gas: {int(tx['gas'], 16)}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "txpool_content", "params": [], "id": 1}' ``` # txpool_status | Tempo Source: https://docs.chainstack.com/reference/tempo-txpool-status openapi/tempo_node_api/txpool/txpool_status.json POST / Tempo API method that returns the number of transactions currently pending or queued in the transaction pool. Tempo via Chainstack. Tempo API method that returns the number of transactions currently pending or queued in the transaction pool. ## Parameters * `none` ## Response * `result` — an object with: * `pending` — number of pending transactions (hex) * `queued` — number of queued transactions (hex) ## `txpool_status` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const getTxpoolStatus = async () => { const status = await provider.send("txpool_status", []); console.log(`Pending: ${parseInt(status.pending, 16)}`); console.log(`Queued: ${parseInt(status.queued, 16)}`); }; getTxpoolStatus(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) result = web3.provider.make_request("txpool_status", []) status = result['result'] print(f"Pending: {int(status['pending'], 16)}") print(f"Queued: {int(status['queued'], 16)}") ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "txpool_status", "params": [], "id": 1}' ``` # web3_clientVersion | Tempo Source: https://docs.chainstack.com/reference/tempo-web3-clientversion openapi/tempo_node_api/client_info/web3_clientVersion.json POST / Tempo API method that returns the current client version. Tempo is built on the Reth SDK. result — the current client version string. Tempo API method that returns the current client version. Tempo is built on the Reth SDK. ## Parameters * `none` ## Response * `result` — the current client version string ## `web3_clientVersion` code examples ```javascript ethers.js theme={"system"} const { ethers } = require("ethers"); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function getClientVersion() { const version = await provider.send("web3_clientVersion", []); console.log(`Client version: ${version}`); } getClientVersion() ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) print(web3.client_version) ``` ```bash cURL theme={"system"} curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "web3_clientVersion", "params": [], "id": 1}' ``` # web3_sha3 | Tempo Source: https://docs.chainstack.com/reference/tempo-web3-sha3 openapi/tempo_node_api/client_info/web3_sha3.json POST / Tempo API method that returns the Keccak-256 hash of the given data. This is the same hashing algorithm used by Ethereum for various purposes. Tempo API method that returns the Keccak-256 hash of the given data. This is the same hashing algorithm used by Ethereum for various purposes. ## Parameters * `data` — the data to hash (hex encoded) ## Response * `result` — the Keccak-256 hash of the input data ## Use case The `web3_sha3` method is useful for: * Computing function selectors (first 4 bytes of keccak256 of function signature) * Computing event topics * General-purpose hashing ## `web3_sha3` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const sha3Example = async () => { // Hash hex-encoded data via RPC const hash = await provider.send("web3_sha3", ["0x68656c6c6f"]); // "hello" in hex console.log(`Hash of 'hello': ${hash}`); // Compare with ethers.js local computation const localHash = ethers.keccak256("0x68656c6c6f"); console.log(`Local hash: ${localHash}`); console.log(`Hashes match: ${hash === localHash}`); // Compute a function selector const transferSelector = await provider.send("web3_sha3", [ ethers.toUtf8Bytes("transfer(address,uint256)").reduce( (hex, byte) => hex + byte.toString(16).padStart(2, '0'), '0x' ) ]); console.log(`\nTransfer function selector: ${transferSelector.slice(0, 10)}`); }; sha3Example(); ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Hash hex-encoded data data = "0x68656c6c6f" # "hello" in hex result = web3.provider.make_request("web3_sha3", [data]) print(f"Hash of 'hello': {result['result']}") # Compare with local computation local_hash = web3.keccak(hexstr=data).hex() print(f"Local hash: {local_hash}") # Compute a function selector transfer_sig = "transfer(address,uint256)" selector = web3.keccak(text=transfer_sig).hex()[:10] print(f"\nTransfer function selector: {selector}") ``` ```bash cURL theme={"system"} # Hash "hello" (0x68656c6c6f in hex) curl -X POST "CHAINSTACK_NODE_URL" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x68656c6c6f"], "id": 1}' ``` # account | TON v3 Source: https://docs.chainstack.com/reference/ton-account-v3 openapi/ton_node_api/v3/getAccountInfo.json GET /api/v3/addressInformation The account endpoint retrieves detailed information about a specific account on the TON blockchain. Use it on TON v3 via Chainstack. # Get Account Information The `account` endpoint retrieves detailed information about a specific account on the TON blockchain. This endpoint allows you to fetch the current state, balance, and other relevant details of an account. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The address of the account to retrieve information for. ## Response * `address` (string) — The account address. * `balance` (string) — The account balance in nanotons. * `last_activity` (integer) — The timestamp of the last account activity. * `status` (string) — The status of the account (e.g., active, frozen). * `code_hash` (string) — The hash of the account's code. * `data_hash` (string) — The hash of the account's data. ## Use case The `account` endpoint is useful for various applications that need to retrieve or verify account information on the TON blockchain: 1. Wallet applications can use this to display account balances and status to users. 2. Decentralized applications (dApps) can verify account existence and status before interacting with them. 3. Block explorers can use this to show detailed account information to users. 4. Analytics platforms can track account balances and activities over time. 5. Developers can use this endpoint to build account monitoring or notification systems. 6. Compliance tools can use this data to verify account status and activity. 7. Smart contract interfaces can use this to check if an account is active before allowing interactions. # accountStates | TON v3 Source: https://docs.chainstack.com/reference/ton-accountstates-v3 openapi/ton_node_api/v3/getAccountStates.json GET /accountStates The accountStates endpoint queries account states from the TON blockchain, including historical state data. TON v3 via Chainstack. # Account States The `accountStates` endpoint queries account states from the TON blockchain, including historical state data. This endpoint provides detailed information about account balances, status, and optionally the code and data cells. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `account_address` (string, optional) — Account address to query. * `include_boc` (boolean, optional) — Include code and data BoC in response. Default: `false`. * `limit` (integer, optional) — Maximum number of states to return. Default: `10`. * `offset` (integer, optional) — Number of states to skip for pagination. Default: `0`. * `sort` (string, optional) — Sort order: `asc` or `desc`. Default: `desc`. ## Response * `account_states` (array) — Array of account state objects: * `account` (string) — Account address. * `hash` (string) — State hash. * `balance` (string) — Account balance in nanotons. * `account_status` (string) — Account status (`active`, `uninit`, `frozen`). * `last_trans_lt` (integer) — Logical time of last transaction. * `last_trans_hash` (string) — Hash of last transaction. * `frozen_hash` (string) — Hash if account is frozen. * `code_hash` (string) — Hash of the code cell. * `data_hash` (string) — Hash of the data cell. * `code_boc` (string) — Code BoC (if `include_boc` is true). * `data_boc` (string) — Data BoC (if `include_boc` is true). * `address_book` (object) — Address book mapping. ## Use case The `accountStates` endpoint is useful for applications that need detailed account information: 1. Block explorers displaying account history and state changes. 2. Smart contract analysis tools examining code and data. 3. Wallet applications showing detailed account information. 4. Auditing tools tracking account state over time. 5. Research applications studying account behavior patterns. Here's an example of querying account state: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/accountStates?account_address=EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs&include_boc=true&limit=1' \ -H 'accept: application/json' ``` > [!TIP] > > Set `include_boc` to `true` only when you need the actual code and data cells, as this significantly increases response size. # actions | TON v3 Source: https://docs.chainstack.com/reference/ton-actions-v3 openapi/ton_node_api/v3/getActions.json GET /actions The actions endpoint retrieves decoded, human-readable actions from TON blockchain transactions. The actions endpoint retrieves decoded. # Actions The `actions` endpoint retrieves decoded, human-readable actions from TON blockchain transactions. Actions represent high-level operations like Jetton transfers, NFT transfers, and TON transfers, parsed from raw transaction data. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `action_id` (string, optional) — Filter by specific action ID. * `trace_id` (string, optional) — Get actions for a specific trace. * `tx_hash` (string, optional) — Get actions for a specific transaction hash. * `tx_lt` (integer, optional) — Filter by transaction logical time. * `account` (string, optional) — Filter by account address. * `action_type` (string, optional) — Filter by action type (e.g., `JettonTransfer`, `NftItemTransfer`, `TonTransfer`). * `start_utime` (integer, optional) — Start of time range (unix timestamp). * `end_utime` (integer, optional) — End of time range (unix timestamp). * `limit` (integer, optional) — Maximum number of actions to return. Default: `10`. * `offset` (integer, optional) — Number of actions to skip for pagination. Default: `0`. * `sort` (string, optional) — Sort order: `asc` or `desc`. Default: `desc`. ## Response * `actions` (array) — Array of action objects containing: * `action_id` (string) — Unique identifier for the action. * `trace_id` (string) — Trace ID this action belongs to. * `tx_hash` (string) — Hash of the transaction containing this action. * `type` (string) — Type of action (e.g., `JettonTransfer`, `NftItemTransfer`, `TonTransfer`, `ContractDeploy`). * `status` (string) — Action status (e.g., `ok`, `failed`). * `value` (object) — Action-specific data (varies by action type). * `start_lt` (integer) — Logical time when the action started. * `start_utime` (integer) — Unix timestamp when the action started. * `end_lt` (integer) — Logical time when the action ended. * `end_utime` (integer) — Unix timestamp when the action ended. * `address_book` (object) — Address book mapping raw addresses to user-friendly information. ## Use case The `actions` endpoint provides a higher-level view of blockchain activity than raw transactions: 1. Building user-friendly transaction histories showing "sent 100 USDT" instead of raw message data. 2. Filtering specific action types like all Jetton transfers for an account. 3. Creating analytics dashboards for token activity. 4. Implementing notification systems for specific action types. 5. Building portfolio trackers that understand different asset movements. Here's an example of getting Jetton transfer actions: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/actions?action_type=JettonTransfer&limit=5' \ -H 'accept: application/json' ``` > [!TIP] > > Actions are decoded from traces, so they provide the same completeness as traces but in a more digestible format. Use actions for user-facing features and traces for deep debugging. # addressBook | TON v3 Source: https://docs.chainstack.com/reference/ton-addressbook-v3 openapi/ton_node_api/v3/getAddressBookInfo.json GET /addressBook The addressBook endpoint retrieves information about a specific address from the TON blockchain's address book. TON v3 via Chainstack. The `addressBook` endpoint retrieves information about a specific address from the TON blockchain's address book. This can be useful for identifying known entities or contracts on the network. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The address to look up in the address book. This should be a raw address format. Example: `0:ed1691307050047117b998b561d8de82d31fbf84910ced6eb5fc92e7485ef8a7`. ## Response * `name` (string) — The name associated with the address, if available. * `description` (string) — A description of the address or the entity it represents. * `icon` (string) — URL of an icon associated with the address or entity. * `website` (string) — Website associated with the address or entity. * `address` (string) — The queried address, confirming the lookup. ## Use case The `addressBook` endpoint can be used in various scenarios: 1. Wallet applications can use it to display human-readable names and descriptions for known addresses. 2. Block explorers can enhance their UI by showing additional information about significant addresses. 3. DApps can use it to verify if they're interacting with known, trusted contracts or entities. # addressInformation | TON v3 Source: https://docs.chainstack.com/reference/ton-addressinformation-v3 openapi/ton_node_api/v3/getAddressInformation.json GET /addressInformation The addressInformation endpoint retrieves detailed information about a smart contract address, including its balance, code, and data cells. # Address Information The `addressInformation` endpoint retrieves detailed information about a smart contract address, including its balance, code, and data cells. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The account address to query. ## Response * `balance` (string) — Account balance in nanotons. * `code` (string) — Smart contract code in base64 format. * `data` (string) — Smart contract data in base64 format. * `last_transaction_lt` (string) — Logical time of last transaction. * `last_transaction_hash` (string) — Hash of last transaction. * `status` (string) — Account status (active, uninit, frozen). ## Use case The `addressInformation` endpoint is useful for applications that need complete contract details: 1. Block explorers displaying full contract information. 2. Smart contract analysis tools examining code and data. 3. Wallet applications verifying contract state. 4. Development tools debugging contract behavior. 5. Security audits examining deployed contracts. Here's an example of getting address information: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/addressInformation?address=EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs' \ -H 'accept: application/json' ``` > [!TIP] > > This endpoint returns the raw code and data cells. Use [runGetMethod](/reference/ton-rungetmethod-v3) to interact with the contract's get methods for parsed data. # adjacentTransactions | TON v3 Source: https://docs.chainstack.com/reference/ton-adjacenttransactions-v3 openapi/ton_node_api/v3/getAdjacentTransactions.json GET /adjacentTransactions The adjacentTransactions endpoint retrieves a list of transactions adjacent to a specific transaction in the TON blockchain. On TON v3. The `adjacentTransactions` endpoint retrieves a list of transactions adjacent to a specific transaction in the TON blockchain. This endpoint allows you to fetch transactions that occurred before or after a given transaction, providing context and enabling transaction flow analysis. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `hash` (string, required) — The hash of the reference transaction. Example: `a9d39a7f1e5f849835496b052885ed2ac07d54d5e0e11f2b17c3b00e3295a2b0`. * `direction` (string, required) — The direction of adjacent transactions to retrieve. Possible values: `before`, `after`, or `both`. Default: `both`. * `limit` (integer, optional) — The maximum number of transactions to return. Default: `128`. * `offset` (integer, optional) — The number of transactions to skip before starting to return results. Default: `0`. * `sort` (string, optional) — The sorting order of the transactions. Possible values: `asc` (ascending) or `desc` (descending). Default: `desc`. ## Response * `transactions` (array) — An array of transaction objects, each containing: * `hash` (string) — The transaction hash. * `lt` (string) — The logical time of the transaction. * `account` (string) — The account address involved in the transaction. * `now` (integer) — The timestamp of the transaction. * `origStatus` (string) — The original status of the account. * `endStatus` (string) — The end status of the account. * `totalFees` (string) — The total fees for the transaction. ## Use case The `adjacentTransactions` endpoint is useful for various applications that need to analyze transaction sequences or provide context around specific transactions: 1. Block explorers can use this to show transactions that occurred before and after a specific transaction, helping users understand the transaction flow. 2. Analytics tools can use this endpoint to track transaction chains and analyze patterns in transaction sequences. 3. Wallet applications can provide users with a broader context of their transactions by showing adjacent transactions from the same account or related accounts. 4. Debugging tools for smart contracts can use this to trace the execution flow of transactions and their effects on the blockchain state. # blocks | TON v3 Source: https://docs.chainstack.com/reference/ton-blocks-v3 openapi/ton_node_api/v3/getBlocks.json GET /blocks The blocks endpoint retrieves a list of blocks from the TON blockchain. This endpoint allows you to fetch multiple blocks at once. # blocks The `blocks` endpoint retrieves a list of blocks from the TON blockchain. This endpoint allows you to fetch multiple blocks at once, with options for pagination and sorting. ## Parameters * `limit` (integer, optional) — The maximum number of blocks to return. Default: 128. * `offset` (integer, optional) — The number of blocks to skip before starting to return results. Default: 0. * `sort` (string, optional) — The sorting order of the blocks. Possible values: "asc" (ascending) or "desc" (descending). Default: "desc". ## Response The response is an object containing an array of `blocks`. Each block in the array has the following properties: * `seqno` (integer) — The sequence number of the block. * `shard` (string) — The shard identifier of the block. * `workchain` (integer) — The workchain ID of the block. * `fileHash` (string) — The file hash of the block. * `rootHash` (string) — The root hash of the block. * `timestamp` (integer) — The timestamp of the block. ## Use case The `blocks` endpoint is essential for applications that need to analyze or display information about multiple blocks in the TON blockchain. Some common use cases include: 1. Block explorers: To show a list of recent blocks and their basic information. 2. Analytics tools: To gather data about block production rates, sizes, and other metrics. 3. Monitoring services: To track the progress of the blockchain and detect any anomalies in block production. 4. Synchronization services: To help light clients or other services catch up with the latest state of the blockchain. 5. Historical analysis: To study patterns in block production over time. By using this endpoint, developers can efficiently retrieve information about multiple blocks at once, enabling them to build comprehensive and performant applications that interact with or analyze the TON blockchain. ## Pagination and sorting The `blocks` endpoint supports pagination through the `limit` and `offset` parameters. This allows you to retrieve blocks in manageable chunks and implement features like "load more" or paginated views in your application. The `sort` parameter allows you to control the order in which blocks are returned. Setting it to "desc" (the default) will return the most recent blocks first, which is useful for displaying the latest blockchain activity. Setting it to "asc" will return older blocks first, which can be helpful for historical analysis or synchronization purposes. # detectAddress | TON v2 Source: https://docs.chainstack.com/reference/ton-detectaddress-v2 openapi/ton_node_api/v2/detectAddress.json GET /detectAddress The detectAddress method is used to detect the type and format of a TON address. Available on TON v2 via Chainstack JSON-RPC nodes. The `detectAddress` method is used to detect the type and format of a TON address. This method provides detailed information about the given address, including its raw form, bounceable and non-bounceable representations, and whether it's a testnet address. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The address to detect. Example: `EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2`. ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "detectAddress", "params": { "address": "EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2" } }' ``` ## Response * `raw_form` (string) — The raw form of the address. * `bounceable` (object) — The bounceable representations of the address: * `b64` (string) — The bounceable address in base64 format. * `b64url` (string) — The bounceable address in base64url format. * `non_bounceable` (object) — The non-bounceable representations of the address: * `b64` (string) — The non-bounceable address in base64 format. * `b64url` (string) — The non-bounceable address in base64url format. * `given_type` (string) — The type of the given address (e.g., "friendly", "raw"). * `test_only` (boolean) — Indicates whether the address is for testnet use only. ## Use case A possible use case for the `detectAddress` method in TON is for applications that need to handle various types of TON addresses. This method can be used to: 1. Validate and normalize user-input addresses. 2. Determine the appropriate format to use when interacting with smart contracts. 3. Display different address representations to users based on context. 4. Detect if an address is meant for testnet, preventing accidental use on mainnet. # estimateFee | TON v2 Source: https://docs.chainstack.com/reference/ton-estimatefee-v2 openapi/ton_node_api/v2/estimateFee.json POST /estimateFee The estimateFee method estimates the fee required to send a message to the TON blockchain. Available on TON v2 via Chainstack JSON-RPC nodes. The `estimateFee` method estimates the fee required to send a message to the TON blockchain. This method allows you to calculate expected transaction costs before actually sending the transaction. > [!NOTE] > > There's no difference between a full node an archive node in data availability or pricing. All data is always available and all node requests are consumed as 1 request unit. ## Request body * `address` (string, required) — The address to send the message from. Example: `EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs`. * `body` (string, required) — The body of the message in base64 format. * `init_code` (string, optional) — The init code in base64 format. Required when deploying a new contract. * `init_data` (string, optional) — The init data in base64 format. Required when deploying a new contract. * `ignore_chksig` (boolean, optional) — Whether to ignore the signature check. Set to `true` when estimating fees without a valid signature. Default: `true`. ## JSON-RPC ```shell shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "estimateFee", "params": { "address": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", "body": "te6cckEBAQEAAgAAAEysuc0=", "ignore_chksig": true } }' ``` ## Response * `ok` (boolean) — Whether the operation was successful. * `result` (object) — The result of the operation. Contains: * `@type` (string) — The type of the result, typically `query.fees`. * `source_fees` (object) — The fees charged to the source account: * `in_fwd_fee` (integer) — The incoming forward fee in nanotons. * `storage_fee` (integer) — The storage fee in nanotons. * `gas_fee` (integer) — The gas fee in nanotons. * `fwd_fee` (integer) — The forward fee in nanotons. * `destination_fees` (array) — Array of fee objects for destination accounts (for multi-hop messages). * `@extra` (string) — Extra information about the operation. ## Use case The `estimateFee` method is essential for applications that need to calculate transaction costs: 1. Wallet applications can display estimated fees to users before they confirm transactions. 2. Smart contract developers can estimate deployment costs before deploying contracts. 3. DApps can optimize transaction parameters to minimize fees. 4. Trading bots can factor in transaction costs when calculating profitability. 5. Payment services can include accurate fee estimates in invoices. Here's an example of estimating fees for a simple transaction: ```shell shell theme={"system"} curl -X 'POST' \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/estimateFee' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "address": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", "body": "te6cckEBAQEAAgAAAEysuc0=", "ignore_chksig": true }' ``` > [!TIP] > > The total fee is approximately the sum of `in_fwd_fee`, `storage_fee`, `gas_fee`, and `fwd_fee`. However, actual fees may vary slightly due to network conditions. # estimateFee | TON v3 Source: https://docs.chainstack.com/reference/ton-estimatefee-v3 openapi/ton_node_api/v3/estimateFee.json POST /estimateFee The estimateFee endpoint estimates the fee for a transaction on the TON blockchain. Available on TON v3 via Chainstack JSON-RPC nodes. # Estimate Fee The `estimateFee` endpoint estimates the fee for a transaction on the TON blockchain. This endpoint allows you to calculate the expected fees before actually sending a transaction. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The address of the smart contract. * `body` (string, required) — The body of the transaction in base64 format. * `ignore_chksig` (boolean, optional) — Whether to ignore the signature check. Default: `false`. ## Response The response includes the estimated fees for both the source and destination of the transaction. ### Source Fees * `in_fwd_fee` (integer) — The incoming forward fee. * `storage_fee` (integer) — The storage fee. * `gas_fee` (integer) — The gas fee. * `fwd_fee` (integer) — The forward fee. ### Destination Fees * `in_fwd_fee` (integer) — The incoming forward fee. * `storage_fee` (integer) — The storage fee. * `gas_fee` (integer) — The gas fee. * `fwd_fee` (integer) — The forward fee. ## Use case The `estimateFee` endpoint is useful for various applications that need to estimate transaction costs on the TON blockchain: 1. Wallet applications can use this to show users the expected cost of a transaction before they send it. 2. Smart contract developers can use this to estimate the cost of deploying or interacting with contracts. 3. DApp developers can integrate this to provide users with accurate fee estimates for transactions. 4. Automated trading systems can use this to factor in transaction costs when making trading decisions. 5. Fee optimization tools can use this endpoint to suggest optimal transaction parameters. # events | TON v3 Source: https://docs.chainstack.com/reference/ton-events-v3 openapi/ton_node_api/v3/getEvents.json GET /events The events endpoint queries blockchain events, which combine traces with their decoded actions. The events endpoint queries blockchain events. # Events The `events` endpoint queries blockchain events, which combine traces with their decoded actions. Events provide a complete view of what happened in a transaction flow, including all resulting actions. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `account` (string, optional) — Filter by account address. * `start_utime` (integer, optional) — Start of time range (unix timestamp). * `end_utime` (integer, optional) — End of time range (unix timestamp). * `limit` (integer, optional) — Maximum number of events to return. Default: `10`. * `offset` (integer, optional) — Number of events to skip for pagination. Default: `0`. * `sort` (string, optional) — Sort order: `asc` or `desc`. Default: `desc`. ## Response * `events` (array) — Array of event objects: * `trace_id` (string) — Trace ID. * `external_hash` (string) — Hash of the external message. * `mc_seqno_start` (string) — Start masterchain sequence number. * `mc_seqno_end` (string) — End masterchain sequence number. * `start_lt` (string) — Start logical time. * `start_utime` (integer) — Start unix timestamp. * `end_lt` (string) — End logical time. * `end_utime` (integer) — End unix timestamp. * `trace_info` (object) — Trace state and statistics. * `actions` (array) — Decoded actions from the event. * `is_incomplete` (boolean) — Whether the event is still processing. * `address_book` (object) — Address book mapping. ## Use case The `events` endpoint is useful for applications that need combined trace and action data: 1. Building activity feeds showing user transaction history. 2. Notification systems alerting on specific events. 3. Analytics dashboards tracking blockchain activity. 4. Block explorers showing transaction details with decoded actions. 5. Debugging complex multi-contract interactions. Here's an example of getting recent events: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/events?limit=5' \ -H 'accept: application/json' ``` > [!TIP] > > Events combine the structure of [traces](/reference/ton-traces-v3) with the decoded information from [actions](/reference/ton-actions-v3), making them ideal for building user-facing transaction histories. # getAddressBalance | TON v2 Source: https://docs.chainstack.com/reference/ton-getaddressbalance-v2 openapi/ton_node_api/v2/getAddressBalance.json GET /getAddressBalance The getAddressBalance method retrieves the balance of a specific address on the TON blockchain. Available on TON v2 via Chainstack. The `getAddressBalance` method retrieves the balance of a specific address on the TON blockchain. This method provides a straightforward way to check the current balance of any TON address. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getAddressBalance", "params": { "address": "EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2" } }' ``` ## Parameters * `address` (string, required) — The address for which to retrieve the balance. Example: `EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2`. ## Response * `balance` (string) — The balance of the address in nanotons. 1 TON = 1,000,000,000 nanotons. ## Use case A possible use case for the `getAddressBalance` method in TON is for wallet applications, blockchain explorers, or any DApp that needs to display or work with account balances. This method can be used to: 1. Show users their current balance. 2. Check if an account has sufficient funds before initiating a transaction. 3. Monitor balance changes over time for analytics purposes. # getAddressInformation | TON v2 Source: https://docs.chainstack.com/reference/ton-getaddressinformation-v2 openapi/ton_node_api/v2/getAddressInformation.json GET /getAddressInformation The getAddressInformation method retrieves information about a specific address on the TON blockchain. Chainstack TON v2 reference. The `getAddressInformation` method retrieves information about a specific address on the TON blockchain. This includes details such as the balance, state, code, and data associated with the address, as well as the last transaction ID. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The address for which information is being requested. Example: `EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2`. * `seqno` (int, optional) — the Masterchain seqno. You can get the Masterchain seqno with [getMasterchainInfo | TON v2](/reference/ton-getmasterchaininfo-v2). Example with `seqno`: ```shell Shell theme={"system"} curl --request GET \ --url 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/getAddressInformation?address=EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2&seqno=45792554' \ --header 'accept: application/json' ``` ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getAddressInformation", "params": { "address": "EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2" } }' ``` ## Response * `balance` (string) — The balance of the address. * `state` (string) — The state of the address. * `code` (string) — The code of the address. * `data` (string) — The data of the address. * `last_transaction_id` (object) — The last transaction ID, containing: * `lt` (string) — The logical time of the last transaction. * `hash` (string) — The hash of the last transaction. * `error` (object) — Error information, if any. * `id` (integer) — The request ID. ## Use case A possible use case for the `getAddressInformation` method in TON is for applications that need to display or utilize the current state and balance of a specific address. For example, a wallet application may use this method to show the user's balance and transaction history. # getAddressState | TON v2 Source: https://docs.chainstack.com/reference/ton-getaddressstate-v2 openapi/ton_node_api/v2/getAddressState.json GET /getAddressState The getAddressState method retrieves the current state of a specific address on the TON blockchain. Use it on TON v2 via Chainstack. The `getAddressState` method retrieves the current state of a specific address on the TON blockchain. This method provides a quick way to check whether an address is active, uninitialized, or frozen. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getAddressState", "params": { "address": "EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2" } }' ``` ## Parameters * `address` (string, required) — The address for which to retrieve the state. Example: `EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2`. ## Response * `state` (string) — The current state of the address. Possible values are: * `active`: The address is active and can participate in transactions. * `uninitialized`: The address has not been initialized yet (no outgoing transactions). * `frozen`: The address is frozen and cannot participate in transactions. ## Use case A possible use case for the `getAddressState` method in TON is for wallet applications, blockchain explorers, or smart contract interactions. This method can be used to: 1. Verify if an address is ready to receive transactions. 2. Check if a smart contract has been deployed and is active. 3. Determine if an address needs to be initialized before use. 4. Monitor the state of important addresses for security or operational purposes. # getBlockHeader | TON v2 Source: https://docs.chainstack.com/reference/ton-getblockheader-v2 openapi/ton_node_api/v2/getBlockHeader.json GET /getBlockHeader The getBlockHeader method retrieves the header of a specific block in the TON blockchain. getBlockHeader on TON v2 via Chainstack. The `getBlockHeader` method retrieves the header of a specific block in the TON blockchain. This method provides detailed metadata about a block, including its identifiers, state flags, and timing information. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `workchain` (integer, required) — The ID of the workchain. Example: `-1` for the masterchain. * `shard` (string, required) — The ID of the shard. Example: `"-9223372036854775808"` for the masterchain. * `seqno` (integer, required) — The sequence number of the block. Example: `39064874`. ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getBlockHeader", "params": { "workchain": -1, "shard": "-9223372036854775808", "seqno": 39064874 } }' ``` ## Response * `id` (object) — The identifier of the block, containing: * `workchain` (integer) — The workchain ID of the block. * `shard` (string) — The shard ID of the block. * `seqno` (integer) — The sequence number of the block. * `root_hash` (string) — The root hash of the block. * `file_hash` (string) — The file hash of the block. * `global_id` (integer) — The global ID of the block. * `version` (integer) — The version of the block. * `after_merge` (boolean) — Whether the block was created after a merge. * `before_split` (boolean) — Whether the block was created before a split. * `after_split` (boolean) — Whether the block was created after a split. * `want_merge` (boolean) — Whether the block wants to merge. * `want_split` (boolean) — Whether the block wants to split. * `validator_list_hash_short` (integer) — The short hash of the validator list. * `catchain_seqno` (integer) — The sequence number in the catchain. * `min_ref_mc_seqno` (integer) — The minimum referenced masterchain sequence number. * `is_key_block` (boolean) — Whether the block is a key block. * `prev_key_block_seqno` (integer) — The sequence number of the previous key block. * `start_lt` (string) — The start logical time of the block. * `end_lt` (string) — The end logical time of the block. * `gen_utime` (integer) — The generation time of the block. * `vert_seqno` (integer) — The vertical sequence number of the block. ## Use case A possible use case for the `getBlockHeader` method in TON is for blockchain explorers, validators, or any application that needs detailed metadata about specific blocks. This method can be used to: 1. Display comprehensive block information in a block explorer. 2. Analyze the structure and evolution of the blockchain, including shard splits and merges. 3. Verify the integrity and sequence of blocks in a custom validation process. 4. Track key blocks and their relationships in the blockchain. # getBlockTransactions | TON v2 Source: https://docs.chainstack.com/reference/ton-getblocktransactions-v2 openapi/ton_node_api/v2/getBlockTransactions.json GET /getBlockTransactions The getBlockTransactions method retrieves transactions from a specific block in the TON blockchain. Use it on TON v2 via Chainstack. The `getBlockTransactions` method retrieves transactions from a specific block in the TON blockchain. This method allows you to fetch transaction details for a given block, specified by its workchain, shard, and sequence number. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `workchain` (integer, required) — The ID of the workchain. Example: `-1` for the masterchain. * `shard` (string, required) — The ID of the shard. Example: `"-9223372036854775808"` for the masterchain. * `seqno` (integer, required) — The sequence number of the block. Example: `39064874`. * `count` (integer, optional) — The maximum number of transactions to return. Default: `40`. ## JSON-RPC ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getBlockTransactions", "params": { "workchain": -1, "shard": "-9223372036854775808", "seqno": 39064874, "count": 40 } }' ``` ## Response * `id` (object) — The identifier of the block, containing: * `workchain` (integer) — The workchain ID of the block. * `shard` (string) — The shard ID of the block. * `seqno` (integer) — The sequence number of the block. * `root_hash` (string) — The root hash of the block. * `file_hash` (string) — The file hash of the block. * `transactions` (array of objects) — An array of transaction objects, each containing: * `account` (string) — The account address involved in the transaction. * `hash` (string) — The transaction hash. * `lt` (string) — The logical time of the transaction. * `incomplete` (boolean) — Indicates if the returned list of transactions is incomplete. ## Use case A possible use case for the `getBlockTransactions` method in TON is for blockchain explorers, analytical tools, or any application that needs to examine transactions within specific blocks. This method can be used to: 1. Display transaction details for a given block in a block explorer. 2. Analyze transaction patterns or volumes within specific blocks or shards. 3. Monitor specific accounts by tracking their transactions across blocks. 4. Implement custom indexing or caching mechanisms for transaction data. # getBlockTransactionsExt | TON v2 Source: https://docs.chainstack.com/reference/ton-getblocktransactionsext-v2 openapi/ton_node_api/v2/getBlockTransactionsExt.json GET /getBlockTransactionsExt The getBlockTransactionsExt method retrieves extended transaction information from a specific block in the TON blockchain. On TON v2. The `getBlockTransactionsExt` method retrieves extended transaction information from a specific block in the TON blockchain. This method provides more detailed transaction data compared to the basic `getBlockTransactions` method. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `workchain` (integer, required) — The ID of the workchain. Example: `-1` for the masterchain. * `shard` (string, required) — The ID of the shard. Example: `"-9223372036854775808"` for the masterchain. * `seqno` (integer, required) — The sequence number of the block. Example: `39064874`. * `count` (integer, optional) — The maximum number of transactions to return. Default: `40`. ## JSON-RPC example ``` curl -X POST \ 'https://ton-mainnet.core.chainstack.com/api/v2/5628c87910d405d931dffcdf7578732f/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getBlockTransactionsExt", "params": { "workchain": -1, "shard": "-9223372036854775808", "seqno": 39064874, "count": 40 } }' ``` ## Response * `id` (object) — The identifier of the block, containing: * `workchain` (integer) — The workchain ID of the block. * `shard` (string) — The shard ID of the block. * `seqno` (integer) — The sequence number of the block. * `root_hash` (string) — The root hash of the block. * `file_hash` (string) — The file hash of the block. * `transactions` (array of objects) — An array of transaction objects, each containing: * `account` (string) — The account address involved in the transaction. * `hash` (string) — The transaction hash. * `lt` (string) — The logical time of the transaction. * `transaction` (object) — Detailed transaction information. * `incomplete` (boolean) — Indicates if the returned list of transactions is incomplete. ## Use case A possible use case for the `getBlockTransactionsExt` method in TON is for advanced blockchain explorers, analytical tools, or any application that needs comprehensive transaction data within specific blocks. This method can be used to: 1. Display detailed transaction information for a given block in a block explorer. 2. Perform in-depth analysis of transaction patterns, including message contents and computation details. 3. Monitor specific accounts by tracking their detailed transaction activities across blocks. 4. Implement advanced indexing or data processing mechanisms for transaction data. # getConfigParam | TON v2 Source: https://docs.chainstack.com/reference/ton-getconfigparam-v2 openapi/ton_node_api/v2/getConfigParam.json GET /getConfigParam The getConfigParam method retrieves a specific configuration parameter from the TON blockchain. Available on TON v2 via Chainstack. The `getConfigParam` method retrieves a specific configuration parameter from the TON blockchain. This method allows you to access various system-wide settings and parameters that govern the behavior of the TON network. ## Parameters * `config_id` (integer, required) — The ID of the configuration parameter to retrieve. Example: `5`. ## JSON-RPC ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getConfigParam", "params": { "config_id": 5 } }' ``` ## Response * `config_id` (integer) — The ID of the configuration parameter that was requested. * `value` (object) — The value of the configuration parameter. The structure of this object varies depending on the specific parameter requested. ## Use case A possible use case for the `getConfigParam` method in TON is for applications or services that need to access and utilize system-wide configuration parameters. This method can be used to: 1. Retrieve network-wide settings for validators, such as minimum stake requirements. 2. Access global parameters that affect transaction processing or smart contract execution. 3. Obtain information about the current configuration of the TON network. 4. Build tools for network analysis and monitoring that require access to system parameters. # getConsensusBlock | TON v2 Source: https://docs.chainstack.com/reference/ton-getconsensusblock-v2 openapi/ton_node_api/v2/getConsensusBlock.json GET /getConsensusBlock The getConsensusBlock method retrieves information about the latest consensus block in the TON blockchain. Chainstack TON v2 reference. The `getConsensusBlock` method retrieves information about the latest consensus block in the TON blockchain. This method provides crucial details about the most recent block that has achieved consensus across the network. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getConsensusBlock", "params": {} }' ``` ## Parameters This method doesn't require any parameters. ## Response * `consensus_block` (integer) — The sequence number of the latest consensus block. * `timestamp` (integer) — The Unix timestamp of the latest consensus block. * `timestamp_string` (string) — The human-readable timestamp of the latest consensus block. ## Use case A possible use case for the `getConsensusBlock` method in TON is for applications or services that need to track the current state of the blockchain consensus. This method can be used to: 1. Determine the latest block that has achieved network-wide consensus. 2. Synchronize application state with the confirmed blockchain state. 3. Implement waiting mechanisms for transaction confirmation based on consensus blocks. 4. Monitor the progress and health of the TON network by tracking consensus block updates. # getExtendedAddressInformation | TON v2 Source: https://docs.chainstack.com/reference/ton-getextendedaddressinformation-v2 openapi/ton_node_api/v2/getExtendedAddressInformation.json GET /getExtendedAddressInformation The getExtendedAddressInformation method retrieves extended information about a specific address on the TON blockchain. On TON v2. The `getExtendedAddressInformation` method retrieves extended information about a specific address on the TON blockchain. This method provides more detailed information compared to the basic `getAddressInformation` method, including the account type, code hash, and data hash. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The address for which extended information is being requested. Example: `EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2`. * `seqno` (int, optional) — the Masterchain seqno. You can get the Masterchain seqno with [getMasterchainInfo | TON v2](/reference/ton-getmasterchaininfo-v2). Example with `seqno`: ```shell Shell theme={"system"} curl --request GET \ --url 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/getExtendedAddressInformation?address=EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2&seqno=45792554' \ --header 'accept: application/json' ``` ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getExtendedAddressInformation", "params": { "address": "EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2" } }' ``` ## Response * `address` (string) — The address in human-readable form. * `balance` (string) — The balance of the address in nanotons. * `state` (string) — The state of the address (active, uninitialized, etc.). * `last_transaction_id` (object) — The last transaction ID, containing: * `lt` (string) — The logical time of the last transaction. * `hash` (string) — The hash of the last transaction. * `block_id` (object) — The block ID where the address state was last updated, containing: * `workchain` (integer) — The workchain ID. * `shard` (string) — The shard ID. * `seqno` (integer) — The sequence number. * `code_hash` (string) — The hash of the contract code. * `data_hash` (string) — The hash of the contract data. * `acc_type` (integer) — The account type. * `acc_type_name` (string) — The human-readable account type name. ## Use case A possible use case for the `getExtendedAddressInformation` method in TON is for applications that need detailed information about an address, such as wallet applications or blockchain explorers. This method can be used to display comprehensive account information, including the contract details and the latest state of the address. # getMasterchainBlockSignatures | TON v2 Source: https://docs.chainstack.com/reference/ton-getmasterchainblocksignatures-v2 openapi/ton_node_api/v2/getMasterchainBlockSignatures.json GET /getMasterchainBlockSignatures The getMasterchainBlockSignatures method retrieves the signatures of a specific masterchain block in the TON blockchain. On TON v2. The `getMasterchainBlockSignatures` method retrieves the signatures of a specific masterchain block in the TON blockchain. This method is crucial for verifying the authenticity and consensus of masterchain blocks. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `seqno` (integer, required) — The sequence number of the masterchain block for which to retrieve signatures. Example: `39064874`. ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getMasterchainBlockSignatures", "params": { "seqno": 39064874 } }' ``` ## Response * `signatures` (array of objects) — An array of signature objects, each containing: * `node_id` (string) — The ID of the validator node that provided the signature. * `r` (string) — The 'r' component of the signature. * `s` (string) — The 's' component of the signature. * `validator_set_hash` (string) — The hash of the validator set that signed this block. * `catchain_seqno` (integer) — The sequence number in the catchain (consensus protocol). ## Use case A possible use case for the `getMasterchainBlockSignatures` method in TON is for applications or services that need to verify the authenticity and consensus of masterchain blocks. This method can be used to: 1. Implement custom block validation logic in light clients or explorers. 2. Verify the consensus reached for a specific block by checking the signatures. 3. Analyze validator participation and performance in block signing. 4. Provide transparency and auditability for critical blockchain operations. # getMasterchainInfo | TON v2 Source: https://docs.chainstack.com/reference/ton-getmasterchaininfo-v2 openapi/ton_node_api/v2/getMasterchainInfo.json GET /getMasterchainInfo The getMasterchainInfo method retrieves information about the current state of the TON masterchain. Use it on TON v2 via Chainstack. The `getMasterchainInfo` method retrieves information about the current state of the TON masterchain. This method provides crucial details about the latest masterchain block and the initial state of the masterchain. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters This method doesn't require any parameters. ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getMasterchainInfo", "params": {} }' ``` ## Response * `last` (object) — Information about the last masterchain block: * `seqno` (integer) — The sequence number of the last masterchain block. * `shard` (string) — The shard identifier of the last masterchain block. * `workchain` (integer) — The workchain identifier of the last masterchain block. * `fileHash` (string) — The file hash of the last masterchain block. * `rootHash` (string) — The root hash of the last masterchain block. * `init` (object) — Information about the initial masterchain block: * `fileHash` (string) — The file hash of the initial masterchain block. * `rootHash` (string) — The root hash of the initial masterchain block. * `stateRootHash` (string) — The state root hash of the masterchain. * `version` (integer) — The version of the masterchain. ## Use case A possible use case for the `getMasterchainInfo` method in TON is for applications that need to monitor or sync with the current state of the TON blockchain. This method can be used to: 1. Track the latest block in the masterchain for synchronization purposes. 2. Verify the current state of the blockchain against a known state. 3. Monitor the progress of the masterchain in terms of block sequence numbers. 4. Obtain necessary information for validating and processing transactions. # getShardBlockProof | TON v2 Source: https://docs.chainstack.com/reference/ton-getshardblockproof-v2 openapi/ton_node_api/v2/getShardBlockProof.json GET /getShardBlockProof The getShardBlockProof method retrieves the proof of a specific shard block in the TON blockchain. Use it on TON v2 via Chainstack. The `getShardBlockProof` method retrieves the proof of a specific shard block in the TON blockchain. This method is crucial for verifying the authenticity and inclusion of shard blocks within the overall TON blockchain structure. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## JSON-RPC examples ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getShardBlockProof", "params": { "workchain": -1, "shard": "-9223372036854775808", "seqno": 39064874 } }' ``` ## Parameters * `workchain` (integer, required) — The ID of the workchain. Example: `-1` for the masterchain. * `shard` (integer, required) — The ID of the shard. Example: `-9223372036854775808` for the masterchain. * `seqno` (integer, required) — The sequence number of the block. Example: `39064874`. ## Response * `masterchain_block_root_hash` (string) — The root hash of the masterchain block that includes this shard block. * `masterchain_block_seq_no` (integer) — The sequence number of the masterchain block. * `links` (array of objects) — An array of link objects, each containing: * `from` (string) — The source block hash. * `to` (string) — The destination block hash. * `dest_proof` (string) — The proof of the destination block. * `proof` (string) — The proof of the link. * `block_header` (string) — The header of the shard block. ## Use case A possible use case for the `getShardBlockProof` method in TON is for applications or services that need to verify the authenticity and inclusion of shard blocks within the TON blockchain. This method can be used to: 1. Implement light clients that can verify shard blocks without downloading the entire blockchain. 2. Provide cryptographic proof of a shard block's inclusion in the blockchain. 3. Verify cross-shard transactions by proving the existence of blocks in different shards. 4. Build block explorers that can show detailed proof information for each block. # getTokenData | TON v2 Source: https://docs.chainstack.com/reference/ton-gettokendata-v2 openapi/ton_node_api/v2/getTokenData.json GET /getTokenData The getTokenData method retrieves data about a specific token on the TON blockchain. Available on TON v2 via Chainstack JSON-RPC nodes. The `getTokenData` method retrieves data about a specific token on the TON blockchain. This method is useful for obtaining detailed information about TON-based tokens, including their name, symbol, supply, and other characteristics. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The address of the token contract. Example: `EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs`. ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "unpackAddress", "params": { "address": "EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2" } }' ``` ## Response * `name` (string) — The name of the token. * `symbol` (string) — The symbol or ticker of the token. * `decimals` (integer) — The number of decimal places for the token. * `totalSupply` (string) — The total supply of the token. * `mintable` (boolean) — Indicates whether new tokens can be minted. * `burnable` (boolean) — Indicates whether tokens can be burned (destroyed). * `address` (string) — The address of the token contract. ## Use case A possible use case for the `getTokenData` method in TON is for applications that deal with TON-based tokens, such as: 1. Wallet applications displaying token information to users. 2. Decentralized exchanges (DEXs) fetching token data for listing and trading purposes. 3. Block explorers providing detailed token information. 4. DApps that need to interact with various tokens and require their metadata. # getTransactions | TON v2 Source: https://docs.chainstack.com/reference/ton-gettransactions-v2 openapi/ton_node_api/v2/getTransactions.json GET /getTransactions The getTransactions method retrieves the transaction history for a specific address on the TON blockchain. Chainstack TON v2 reference. The `getTransactions` method retrieves the transaction history for a specific address on the TON blockchain. This method allows you to fetch a list of transactions associated with an address, with options to limit the number of results and specify the starting point of the search. > [!NOTE] > > **TON billing: this method is always archive (2 RUs)** > > Account transaction history can reach arbitrarily far back, so this method is always billed as archive. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getTransactions", "params": { "address": "EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2", "limit": 10, "to_lt": 0, "archival": false } }' ``` ## Parameters * `address` (string, required) — The address for which to retrieve transactions. Example: `EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2`. * `limit` (integer, optional) — The maximum number of transactions to return. Default: 10. * `to_lt` (integer, optional) — The logical time to start the search from (0 means from the latest). Default: 0. * `archival` (boolean, optional) — Whether to use an archival node for the query. Default: false. ## Response An array of transaction objects, each containing: * `transaction_id` (object) — The unique identifier of the transaction, containing: * `lt` (string) — The logical time of the transaction. * `hash` (string) — The hash of the transaction. * `utime` (integer) — Unix timestamp of the transaction. * `fee` (string) — The fee for the transaction in nanotons. * `storage_fee` (string) — The storage fee for the transaction in nanotons. * `other_fee` (string) — Other fees for the transaction in nanotons. * `transaction_type` (string) — The type of the transaction. * `compute` (object) — Computation details of the transaction. * `action` (object) — Action details of the transaction. * `in_msg` (object) — Details of the incoming message. * `out_msgs` (array) — Details of the outgoing messages. ## Use case A possible use case for the `getTransactions` method in TON is for blockchain explorers or wallet applications that need to display transaction history for a specific address. This method can be used to show recent transactions, calculate total incoming/outgoing amounts, or analyze transaction patterns for a given address. # getWalletInformation | TON v2 Source: https://docs.chainstack.com/reference/ton-getwalletinformation-v2 openapi/ton_node_api/v2/getWalletInformation.json GET /getWalletInformation The getWalletInformation method retrieves wallet-specific information about a TON address. getWalletInformation on TON v2 via Chainstack. The `getWalletInformation` method retrieves wallet-specific information about a TON address. This method is particularly useful for obtaining details about wallet contracts, including their type, balance, and current state. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). * `seqno` (int, optional) — the Masterchain seqno. You can get the Masterchain seqno with [getMasterchainInfo | TON v2](/reference/ton-getmasterchaininfo-v2). Example with `seqno`: ```shell Shell theme={"system"} curl --request GET \ --url 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/getWalletInformation?address=EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2&seqno=45792554' \ --header 'accept: application/json' ``` ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "getWalletInformation", "params": { "address": "EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2" } }' ``` ## Parameters * `address` (string, required) — The address for which wallet information is being requested. Example: `EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2`. ## Response * `wallet` (boolean) — Indicates if the address is a wallet. * `balance` (string) — The balance of the wallet in nanotons. * `account_state` (string) — The state of the account (active, uninitialized, etc.). * `wallet_type` (string) — The type of the wallet contract. * `seqno` (integer) — The current sequence number of the wallet. * `last_transaction_id` (object) — The last transaction ID, containing: * `lt` (string) — The logical time of the last transaction. * `hash` (string) — The hash of the last transaction. ## Use case A possible use case for the `getWalletInformation` method in TON is for wallet applications or blockchain explorers that need to display specific information about wallet contracts. This method can be used to show the current balance, wallet type, and transaction sequence number, which are crucial for interacting with the wallet or monitoring its state. # jetton/burns | TON v3 Source: https://docs.chainstack.com/reference/ton-jetton-burns-v3 openapi/ton_node_api/v3/getJettonBurns.json GET /jetton/burns The jetton/burns endpoint retrieves a list of Jetton burn events from the TON blockchain. Available on TON v3 via Chainstack JSON-RPC nodes. The `jetton/burns` endpoint retrieves a list of Jetton burn events from the TON blockchain. This endpoint allows you to fetch information about Jettons that have been permanently removed from circulation and provides options for pagination and sorting. Running the interactive example will return the amount of [DOGS burned](https://tonviewer.com/EQCvxJy4eG8hyHBFsZ7eePxrRsUQSFE_jpptRAYBmcG_DOGS). > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `limit` (integer, optional) — The maximum number of burn events to return. Default: `128`. * `offset` (integer, optional) — The number of burn events to skip before starting to return results. Default: `0`. * `sort` (string, optional) — The sorting order of the burn events. Possible values: `asc` (ascending) or `desc` (descending). Default: `desc`. ## Response * `burns` (array) — An array of Jetton burn event objects, each containing: * `jetton` (object) — Information about the burned Jetton: * `address` (string) — The address of the Jetton master contract. * `name` (string) — The name of the Jetton. * `symbol` (string) — The symbol of the Jetton. * `burner` (object) — Information about the account that burned the Jettons: * `address` (string) — The address of the burner. * `transaction` (object) — Information about the transaction: * `hash` (string) — The hash of the transaction. * `time` (integer) — The timestamp of the transaction. * `amount` (string) — The amount of Jettons burned. * `comment` (string) — Any comment attached to the burn event. ## Use case The `jetton/burns` endpoint is useful for various applications that need to track or analyze Jetton burn activities on the TON blockchain: 1. Analytics platforms can use this to monitor the reduction in circulating supply of various Jettons. 2. Wallet applications can show users their history of Jetton burn transactions. 3. Developers can use this endpoint to build notification systems for significant burn events. 4. Token issuers can track burn events to manage their token's supply and verify proper functioning of burn mechanisms. 5. Researchers can analyze burn patterns to study token economics and user behavior. 6. Compliance tools can use this data to monitor large burns or burns from specific addresses. # jetton/masters | TON v3 Source: https://docs.chainstack.com/reference/ton-jetton-masters-v3 openapi/ton_node_api/v3/getJettonMasters.json GET /jetton/masters The jetton/masters endpoint retrieves a list of Jetton master contracts from the TON blockchain. Available on TON v3 via Chainstack. The `jetton/masters` endpoint retrieves a list of Jetton master contracts from the TON blockchain. Jettons are the TON equivalent of fungible tokens (similar to ERC-20 tokens on Ethereum). This endpoint allows you to fetch information about various Jetton types and provides options for pagination. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `limit` (integer, optional) — The maximum number of Jetton masters to return. Default: `128`. * `offset` (integer, optional) — The number of Jetton masters to skip before starting to return results. Default: `0`. ## Response * `masters` (array) — An array of Jetton master objects, each containing: * `address` (string) — The address of the Jetton master contract. * `name` (string) — The name of the Jetton. * `symbol` (string) — The symbol of the Jetton. * `decimals` (integer) — The number of decimal places for the Jetton. * `totalSupply` (string) — The total supply of the Jetton. * `mintable` (boolean) — Whether new tokens can be minted. * `admin` (string) — The address of the Jetton admin. * `metadata` (object) — Additional metadata of the Jetton. * `contentUri` (string) — The URI of the Jetton's content. ## Use case The `jetton/masters` endpoint is useful for various applications that need to interact with or display information about Jettons on the TON blockchain: 1. Decentralized exchanges (DEXs) can use this to list available Jettons for trading. 2. Wallet applications can display Jetton information and allow users to add custom Jettons. 3. Analytics platforms can gather data on Jetton distribution, total supply, and metadata patterns. 4. Developers can use this endpoint to integrate Jetton information into their dApps or financial services. 5. Token creators can verify the on-chain status and details of their published Jettons. 6. Investors can research and discover new Jettons available on the TON blockchain. # jetton/transfers | TON v3 Source: https://docs.chainstack.com/reference/ton-jetton-transfers-v3 openapi/ton_node_api/v3/getJettonTransfers.json GET /jetton/transfers The jetton/transfers endpoint retrieves a list of Jetton transfers from the TON blockchain. jetton/transfers on TON v3 via Chainstack. The `jetton/transfers` endpoint retrieves a list of Jetton transfers from the TON blockchain. This endpoint allows you to fetch information about Jetton transfer activities and provides options for filtering, pagination, and sorting. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `direction` (string, required) — The direction of transfers to retrieve. Possible values: `in`, `out`, or `both`. Default: `both`. * `limit` (integer, optional) — The maximum number of transfers to return. Default: `128`. * `offset` (integer, optional) — The number of transfers to skip before starting to return results. Default: `0`. * `sort` (string, optional) — The sorting order of the transfers. Possible values: `asc` (ascending) or `desc` (descending). Default: `desc`. ## Response * `transfers` (array) — An array of Jetton transfer objects, each containing: * `jetton` (object) — Information about the transferred Jetton: * `address` (string) — The address of the Jetton master contract. * `name` (string) — The name of the Jetton. * `symbol` (string) — The symbol of the Jetton. * `from` (object) — Information about the sender: * `address` (string) — The address of the sender. * `to` (object) — Information about the recipient: * `address` (string) — The address of the recipient. * `transaction` (object) — Information about the transaction: * `hash` (string) — The hash of the transaction. * `time` (integer) — The timestamp of the transaction. * `amount` (string) — The amount of Jettons transferred. * `comment` (string) — Any comment attached to the transfer. ## Use case The `jetton/transfers` endpoint is useful for various applications that need to track or analyze Jetton transfer activities on the TON blockchain: 1. Wallet applications can show users their history of Jetton transactions. 2. Decentralized exchanges (DEXs) can use this to display recent trades or transfers of Jettons. 3. Analytics platforms can track Jetton transfer patterns, popular tokens, and market trends. 4. Developers can use this endpoint to build notification systems for Jetton transfers. 5. Compliance tools can use this data to monitor large transfers or specific addresses. 6. Portfolio tracking applications can use this to update users' Jetton balances in real-time. # jetton/wallets | TON v3 Source: https://docs.chainstack.com/reference/ton-jetton-wallets-v3 openapi/ton_node_api/v3/getJettonWallets.json GET /jetton/wallets The jetton/wallets endpoint retrieves a list of Jetton wallets from the TON blockchain. Available on TON v3 via Chainstack JSON-RPC nodes. The `jetton/wallets` endpoint retrieves a list of Jetton wallets from the TON blockchain. Jetton wallets are smart contracts that hold balances of specific Jettons for users. This endpoint allows you to fetch information about various Jetton wallets and provides options for pagination. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `limit` (integer, optional) — The maximum number of Jetton wallets to return. Default: `128`. * `offset` (integer, optional) — The number of Jetton wallets to skip before starting to return results. Default: `0`. ## Response * `wallets` (array) — An array of Jetton wallet objects, each containing: * `address` (string) — The address of the Jetton wallet. * `balance` (string) — The balance of the Jetton wallet. * `owner` (string) — The address of the wallet owner. * `jetton` (object) — Information about the Jetton: * `address` (string) — The address of the Jetton master contract. * `name` (string) — The name of the Jetton. * `symbol` (string) — The symbol of the Jetton. * `decimals` (integer) — The number of decimal places for the Jetton. ## Use case The `jetton/wallets` endpoint is useful for various applications that need to interact with or display information about Jetton holdings on the TON blockchain: 1. Wallet applications can use this to display users' Jetton balances across different tokens. 2. Decentralized exchanges (DEXs) can fetch users' Jetton wallet information for trading purposes. 3. Analytics platforms can gather data on Jetton distribution and user holdings. 4. Developers can use this endpoint to integrate Jetton wallet information into their dApps or financial services. 5. Portfolio tracking applications can use this to monitor users' Jetton holdings. 6. Compliance tools can use this data to track Jetton transactions and holdings for specific addresses. # lookupBlock | TON v2 Source: https://docs.chainstack.com/reference/ton-lookupblock-v2 openapi/ton_node_api/v2/lookupBlock.json GET /lookupBlock The lookupBlock method retrieves detailed information about a specific block in the TON blockchain. Use it on TON v2 via Chainstack. The `lookupBlock` method retrieves detailed information about a specific block in the TON blockchain. This method allows you to query block data by specifying the workchain, shard, and sequence number of the block. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "lookupBlock", "params": { "workchain": -1, "shard": "-9223372036854775808", "seqno": 39064874 } }' ``` ## Parameters * `workchain` (integer, required) — The ID of the workchain. Example: `-1` for the masterchain. * `shard` (string, required) — The ID of the shard. Example: `"-9223372036854775808"` for the masterchain. * `seqno` (integer, required) — The sequence number of the block. Example: `39064874`. ## Response * `id` (object) — The identifier of the block, containing: * `workchain` (integer) — The workchain ID of the block. * `shard` (string) — The shard ID of the block. * `seqno` (integer) — The sequence number of the block. * `root_hash` (string) — The root hash of the block. * `file_hash` (string) — The file hash of the block. * `status` (integer) — The status of the block. * `global_id` (integer) — The global ID of the block. * `version` (integer) — The version of the block. * `after_merge` (boolean) — Whether the block was created after a merge. * `before_split` (boolean) — Whether the block was created before a split. * `want_merge` (boolean) — Whether the block wants to merge. * `want_split` (boolean) — Whether the block wants to split. * `gen_utime` (integer) — The generation time of the block. * `start_lt` (string) — The start logical time of the block. * `end_lt` (string) — The end logical time of the block. ## Use case A possible use case for the `lookupBlock` method in TON is for blockchain explorers, analytical tools, or any application that needs detailed information about specific blocks. This method can be used to: 1. Display detailed block information in a block explorer. 2. Analyze block characteristics for research or optimization purposes. 3. Verify block properties in custom validation processes. 4. Track the evolution of the blockchain by examining block metadata over time. # masterchainBlockShards | TON v3 Source: https://docs.chainstack.com/reference/ton-masterchainblockshards-v3 openapi/ton_node_api/v3/getMasterchainBlockShards.json GET /masterchainBlockShards The masterchainBlockShards endpoint retrieves information about the shards of a specific masterchain block in the TON blockchain. On TON v3. The `masterchainBlockShards` endpoint retrieves information about the shards of a specific masterchain block in the TON blockchain. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `seqno` (integer, required) — The sequence number of the masterchain block. Example: `40467413`. ## Response * `shards` (array) — An array of shard objects, each containing: * `workchain` (integer) — The workchain ID. * `shard` (string) — The shard identifier. * `seqno` (integer) — The sequence number of the shard block. * `rootHash` (string) — The root hash of the shard block. * `fileHash` (string) — The file hash of the shard block. ## Use case The `masterchainBlockShards` endpoint is useful for applications that need to analyze or track the state of shards within a specific masterchain block. This information can be valuable for: 1. Blockchain explorers that want to display detailed information about the structure of the TON blockchain at a specific point. 2. Analytics tools that need to monitor the distribution and state of shards across workchains for a given masterchain block. 3. Developers building applications that require knowledge of the shard structure for optimization or routing purposes. # masterchainBlockShardState | TON v3 Source: https://docs.chainstack.com/reference/ton-masterchainblockshardstate-v3 openapi/ton_node_api/v3/getMasterchainBlockShardState.json GET /masterchainBlockShardState The masterchainBlockShardState method retrieves the shard state of a specific masterchain block in the TON blockchain. TON v3 via Chainstack. The `masterchainBlockShardState` method retrieves the shard state of a specific masterchain block in the TON blockchain. This method provides information about the shards associated with the given masterchain block. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `seqno` (integer, required) — The sequence number of the masterchain block. Example: `40467413`. ## Response * `shards` (array) — An array of shard objects, each containing: * `workchain` (integer) — The workchain ID. * `shard` (string) — The shard identifier. * `seqno` (integer) — The sequence number of the shard block. * `root_hash` (string) — The root hash of the shard block. * `file_hash` (string) — The file hash of the shard block. ## Use case The `masterchainBlockShardState` method is useful for applications that need to analyze or track the state of shards within the TON blockchain. This information can be valuable for: 1. Blockchain explorers that want to display detailed information about the structure of the TON blockchain. 2. Analytics tools that need to monitor the distribution of shards across workchains. 3. Developers building applications that require knowledge of the current state of shards for optimization or routing purposes. # masterchainInfo | TON v3 Source: https://docs.chainstack.com/reference/ton-masterchaininfo-v3 openapi/ton_node_api/v3/getMasterchainInfo.json GET /masterchainInfo The masterchainInfo endpoint retrieves information about the current state of the masterchain in the TON blockchain. TON v3 via Chainstack. The `masterchainInfo` endpoint retrieves information about the current state of the masterchain in the TON blockchain. This includes details about the last masterchain block, the initial masterchain block, and the current state root hash. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters This endpoint doesn't require any parameters. ## Response * `last` (object) — Information about the last masterchain block: * `seqno` (integer) — The sequence number of the last masterchain block. * `shard` (string) — The shard identifier of the last masterchain block. * `workchain` (integer) — The workchain ID of the last masterchain block. * `fileHash` (string) — The file hash of the last masterchain block. * `rootHash` (string) — The root hash of the last masterchain block. * `init` (object) — Information about the initial masterchain block: * `fileHash` (string) — The file hash of the initial masterchain block. * `rootHash` (string) — The root hash of the initial masterchain block. * `stateRootHash` (string) — The state root hash of the masterchain. * `now` (integer) — The current timestamp. ## Use case The `masterchainInfo` endpoint is crucial for applications that need to stay synchronized with the current state of the TON blockchain. Some common use cases include: 1. Block explorers: To display the latest block information and blockchain statistics. 2. Wallet applications: To ensure that transactions are being processed against the most recent blockchain state. 3. DApps: To verify the current state of the blockchain before executing smart contracts or transactions. 4. Monitoring tools: To track the progress and health of the TON network. 5. Synchronization services: To help light clients or other services stay up-to-date with the latest masterchain state. By using this endpoint, developers can easily access critical information about the current state of the TON masterchain, enabling them to build robust and reliable applications that interact with the TON blockchain. # message | TON v3 Source: https://docs.chainstack.com/reference/ton-message-v3 openapi/ton_node_api/v3/sendMessage.json POST /api/v3/message The message endpoint sends an external message to the TON network. This is the v3 API method for submitting signed transactions to the blockchain. # Send Message The `message` endpoint sends an external message to the TON network. This is the v3 API method for submitting signed transactions to the blockchain. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `boc` (string, required) — The serialized bag-of-cells in base64 format containing the signed external message. ## Response * `message_hash` (string) — The hash of the sent message, which can be used to track the transaction. ## Use case The `message` endpoint is essential for any application that needs to send transactions via the v3 API: 1. Wallet applications broadcasting user-signed transactions. 2. Smart contract deployment and interaction. 3. DApps submitting user transactions to the network. 4. Automated systems executing on-chain operations. 5. Token transfer services sending Jetton or NFT transfers. Here's an example of sending a transaction: ```shell shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/message' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "boc": "te6ccgEBAgEAqwAB4YgBVbC7Hu+g3htYXzst8L5ucV76NMzeBK3URJKebN2Y1kwADRfFu/r0HxgrOp/Iml8Li5x4gR10J5XYFS8z0fMWJ6JPeUF5xNSLrSDGFIqhJqkW1SEYQF8Dw5p4dI3oaFs4LCmpoxdkfKw0AAAAGAAcAQBoQgAwwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL68=" }' ``` > [!TIP] > > Use the returned `message_hash` with the [transactions](/reference/ton-transactions-v3) or [traces](/reference/ton-traces-v3) endpoints to track transaction status and execution results. # messages | TON v3 Source: https://docs.chainstack.com/reference/ton-messages-v3 openapi/ton_node_api/v3/getMessages.json GET /messages The messages endpoint retrieves a list of messages from the TON blockchain. Chainstack supports messages on TON v3 JSON-RPC nodes. The `messages` endpoint retrieves a list of messages from the TON blockchain. This endpoint allows you to fetch recent messages and provides options for pagination. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `limit` (integer, optional) — The maximum number of messages to return. Default: `128`. * `offset` (integer, optional) — The number of messages to skip before starting to return results. Default: `0`. ## Response * `messages` (array) — An array of message objects, each containing: * `hash` (string) — The message hash. * `source` (string) — The source address of the message. * `destination` (string) — The destination address of the message. * `value` (string) — The value transferred in the message. * `fwd_fee` (string) — The forward fee for the message. * `ihr_fee` (string) — The IHR (Instant Hypercube Routing) fee for the message. * `created_lt` (string) — The logical creation time of the message. * `body_hash` (string) — The hash of the message body. ## Use case The `messages` endpoint is useful for various applications that need to monitor or analyze message activity on the TON blockchain: 1. Block explorers can use this to display recent messages and allow users to browse through message history. 2. Wallet applications can fetch and display recent messages related to a user's account. 3. Analytics tools can use this endpoint to gather data on message volumes, fees, and patterns over time. 4. Monitoring systems can track specific types of messages or activity between particular accounts. 5. Developers can use this endpoint to debug and verify message processing in smart contracts. # metadata | TON v3 Source: https://docs.chainstack.com/reference/ton-metadata-v3 openapi/ton_node_api/v3/getMetadata.json GET /metadata The metadata endpoint queries address metadata from the TON blockchain indexer. Available on TON v3 via Chainstack JSON-RPC nodes. # Metadata The `metadata` endpoint queries address metadata from the TON blockchain indexer. This includes human-readable names, scam flags, icons, and other identifying information for known addresses. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The address to get metadata for. ## Response * `address` (string) — The queried address. * `name` (string) — Human-readable name for the address (if known). * `is_scam` (boolean) — Whether the address is flagged as a scam. * `icon` (string) — URL to the address icon (if available). * `is_wallet` (boolean) — Whether the address is a wallet contract. ## Use case The `metadata` endpoint is valuable for improving user experience and security: 1. Wallet applications displaying friendly names instead of raw addresses. 2. Scam detection by checking addresses before sending funds. 3. Block explorers showing rich information about addresses. 4. DApps displaying icons for known contracts and tokens. 5. Security tools identifying potentially malicious addresses. Here's an example of getting address metadata: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/metadata?address=EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs' \ -H 'accept: application/json' ``` > [!WARNING] > > Always check the `is_scam` flag before interacting with unknown addresses. This can help protect users from known malicious actors on the network. # multisig/orders | TON v3 Source: https://docs.chainstack.com/reference/ton-multisig-orders-v3 openapi/ton_node_api/v3/getMultisigOrders.json GET /multisig/orders The multisig/orders endpoint retrieves pending and executed orders for multisig wallets. multisig/orders on TON v3 via Chainstack. # Multisig Orders The `multisig/orders` endpoint retrieves pending and executed orders for multisig wallets. Orders represent proposed transactions that require multiple signatures before execution. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `multisig_address` (string, optional) — Filter by multisig wallet address. * `order_address` (string, optional) — Filter by specific order contract address. * `is_executed` (boolean, optional) — Filter by execution status. * `limit` (integer, optional) — Maximum number of orders to return. Default: `10`. * `offset` (integer, optional) — Number of orders to skip for pagination. Default: `0`. ## Response * `multisig_orders` (array) — Array of order objects: * `address` (string) — Order contract address. * `multisig_address` (string) — Parent multisig wallet address. * `order_seqno` (integer) — Order sequence number. * `threshold` (integer) — Required number of signatures. * `signers_num` (integer) — Total number of possible signers. * `approvals_num` (integer) — Current number of approvals. * `approvals` (array) — List of addresses that have approved. * `expiration_date` (integer) — Order expiration timestamp. * `is_executed` (boolean) — Whether the order has been executed. * `address_book` (object) — Address book mapping. ## Use case The `multisig/orders` endpoint is crucial for multisig transaction management: 1. Signer interfaces showing orders awaiting their approval. 2. Treasury dashboards displaying pending transactions. 3. Alert systems notifying when orders need signatures. 4. Audit tools tracking multisig transaction history. 5. Governance tools managing DAO proposals and executions. Here's an example of getting pending orders for a multisig wallet: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/multisig/orders?is_executed=false&limit=5' \ -H 'accept: application/json' ``` > [!TIP] > > Monitor `expiration_date` to ensure orders are signed before they expire. Orders that expire without enough signatures will need to be recreated. # multisig/wallets | TON v3 Source: https://docs.chainstack.com/reference/ton-multisig-wallets-v3 openapi/ton_node_api/v3/getMultisigWallets.json GET /multisig/wallets The multisig/wallets endpoint retrieves multisig wallet contracts from the TON blockchain. multisig/wallets on TON v3 via Chainstack. # Multisig Wallets The `multisig/wallets` endpoint retrieves multisig wallet contracts from the TON blockchain. Multisig wallets require multiple signatures to execute transactions, providing enhanced security for shared funds. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, optional) — Filter by multisig wallet address. * `proposer_address` (string, optional) — Filter by proposer address. * `limit` (integer, optional) — Maximum number of wallets to return. Default: `10`. * `offset` (integer, optional) — Number of wallets to skip for pagination. Default: `0`. ## Response * `multisig_wallets` (array) — Array of multisig wallet objects: * `address` (string) — Multisig wallet address. * `seqno` (integer) — Current sequence number. * `threshold` (integer) — Required number of signatures. * `signers` (array) — List of signer addresses. * `proposers` (array) — List of proposer addresses. * `orders_count` (integer) — Number of pending orders. * `address_book` (object) — Address book mapping. ## Use case The `multisig/wallets` endpoint is essential for managing multisig wallets: 1. Treasury management interfaces displaying wallet configuration. 2. DAO tools showing governance wallet status. 3. Security audits reviewing multisig setup and signers. 4. Wallet applications supporting multisig functionality. 5. Organization tools managing shared funds. Here's an example of getting multisig wallets: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/multisig/wallets?limit=5' \ -H 'accept: application/json' ``` > [!TIP] > > Use the [multisig/orders](/reference/ton-multisig-orders-v3) endpoint to see pending transactions that require signatures for these wallets. # nft/collections | TON v3 Source: https://docs.chainstack.com/reference/ton-nft-collections-v3 openapi/ton_node_api/v3/getNFTCollections.json GET /nft/collections The nft/collections endpoint retrieves a list of NFT (Non-Fungible Token) collections from the TON blockchain. TON v3 via Chainstack. The `nft/collections` endpoint retrieves a list of NFT (Non-Fungible Token) collections from the TON blockchain. This endpoint allows you to fetch information about various NFT collections and provides options for pagination. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `limit` (integer, optional) — The maximum number of NFT collections to return. Default: `128`. * `offset` (integer, optional) — The number of NFT collections to skip before starting to return results. Default: `0`. ## Response * `collections` (array) — An array of NFT collection objects, each containing: * `address` (string) — The address of the NFT collection. * `name` (string) — The name of the NFT collection. * `description` (string) — The description of the NFT collection. * `metadata` (object) — Additional metadata of the NFT collection. * `owner` (string) — The owner's address of the NFT collection. * `raw_collection_content` (string) — The raw content of the NFT collection. * `next_item_index` (integer) — The index of the next item in the collection. * `icon` (string) — The URL of the collection's icon. ## Use case The `nft/collections` endpoint is useful for various applications that need to interact with or display information about NFT collections on the TON blockchain: 1. NFT marketplaces can use this to list available collections for browsing or trading. 2. Wallet applications can display NFT collections owned by a user or allow users to explore popular collections. 3. Analytics platforms can gather data on NFT collections, such as ownership distribution, collection sizes, and metadata patterns. 4. Developers can use this endpoint to integrate NFT collection information into their dApps or games. 5. Collection creators can verify the on-chain status and details of their published collections. # nft/items | TON v3 Source: https://docs.chainstack.com/reference/ton-nft-items-v3 openapi/ton_node_api/v3/getNFTItems.json GET /nft/items The nft/items endpoint retrieves a list of NFT (Non-Fungible Token) items from the TON blockchain. Use it on TON v3 via Chainstack. The `nft/items` endpoint retrieves a list of NFT (Non-Fungible Token) items from the TON blockchain. This endpoint allows you to fetch information about individual NFT items and provides options for pagination. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (array of strings, optional) — NFT item address in any form. Max: 1000. * `owner_address` (array of strings, optional) — Address of NFT item owner in any form. Max: 1000. * `collection_address` (array of strings, optional) — Collection address in any form. * `index` (array of strings, optional) — Index of item for given collection. Max: 1000. * `sort_by_last_transaction_lt` (boolean, optional) — Sort NFT items by last transaction lt descending. **Warning:** results may be inconsistent during pagination with limit and offset. * `limit` (integer, optional) — The maximum number of NFT items to return. Default: `10`. * `offset` (integer, optional) — The number of NFT items to skip before starting to return results. Default: `0`. ## Response * `nft_items` (array) — An array of NFT item objects, each containing: * `address` (string) — The address of the NFT item. * `auction_contract_address` (string) — The address of the auction contract, if applicable. * `code_hash` (string) — The code hash of the NFT item. * `collection` (object) — Information about the collection this item belongs to: * `address` (string) — The address of the collection. * `code_hash` (string) — The code hash of the collection. * `collection_content` (object) — The content/metadata of the collection. * `data_hash` (string) — The data hash of the collection. * `last_transaction_lt` (string) — The logical time of the last transaction. * `next_item_index` (string) — The index of the next item in the collection. * `owner_address` (string) — The address of the collection owner. * `collection_address` (string) — The address of the collection. * `content` (object) — The content of the NFT item. * `data_hash` (string) — The data hash of the NFT item. * `index` (string) — The index of the NFT item within its collection. * `init` (boolean) — Whether the NFT item is initialized. * `last_transaction_lt` (string) — The logical time of the last transaction. * `on_sale` (boolean) — Whether the NFT item is on sale. * `owner_address` (string) — The address of the current owner. * `real_owner` (string) — The address of the real owner (if different from owner\_address, e.g. when on sale). * `sale_contract_address` (string) — The address of the sale contract, if applicable. * `address_book` (object) — Address book information. * `metadata` (object) — Metadata information. ## Use case The `nft/items` endpoint is useful for various applications that need to interact with or display information about individual NFT items on the TON blockchain: 1. NFT marketplaces can use this to list available NFTs for browsing or trading. 2. Wallet applications can display NFT items owned by a user or allow users to explore popular NFTs. 3. Analytics platforms can gather data on NFT items, such as ownership distribution, rarity, and metadata patterns. 4. Developers can use this endpoint to integrate NFT item information into their dApps or games. 5. Collectors can verify the on-chain status and details of their owned NFTs. 6. Artists and creators can check the current status and ownership of their minted NFTs. # nft/transfers | TON v3 Source: https://docs.chainstack.com/reference/ton-nft-transfers-v3 openapi/ton_node_api/v3/getNFTTransfers.json GET /nft/transfers The nft/transfers endpoint retrieves a list of NFT (Non-Fungible Token) transfers from the TON blockchain. Chainstack TON v3 reference. The `nft/transfers` endpoint retrieves a list of NFT (Non-Fungible Token) transfers from the TON blockchain. This endpoint allows you to fetch information about NFT transfer activities and provides options for filtering, pagination, and sorting. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `direction` (string, required) — The direction of transfers to retrieve. Possible values: `in`, `out`, or `both`. Default: `both`. * `limit` (integer, optional) — The maximum number of transfers to return. Default: `128`. * `offset` (integer, optional) — The number of transfers to skip before starting to return results. Default: `0`. * `sort` (string, optional) — The sorting order of the transfers. Possible values: `asc` (ascending) or `desc` (descending). Default: `desc`. ## Response * `transfers` (array) — An array of NFT transfer objects, each containing: * `nft` (object) — Information about the transferred NFT: * `address` (string) — The address of the NFT item. * `collection` (object) — Information about the collection: * `address` (string) — The address of the collection. * `from` (object) — Information about the sender: * `address` (string) — The address of the sender. * `to` (object) — Information about the recipient: * `address` (string) — The address of the recipient. * `transaction` (object) — Information about the transaction: * `hash` (string) — The hash of the transaction. * `time` (integer) — The timestamp of the transaction. * `value` (string) — The value of the transfer (if any). ## Use case The `nft/transfers` endpoint is useful for various applications that need to track or analyze NFT transfer activities on the TON blockchain: 1. NFT marketplaces can use this to display recent sales or transfers of NFTs. 2. Wallet applications can show users their history of NFT acquisitions and disposals. 3. Analytics platforms can track NFT transfer patterns, popular collections, and market trends. 4. Developers can use this endpoint to build notification systems for NFT transfers. 5. Collectors can monitor transfers of specific NFTs or within particular collections. 6. Compliance tools can use this data to track the provenance of NFTs. # packAddress | TON v2 Source: https://docs.chainstack.com/reference/ton-packaddress-v2 openapi/ton_node_api/v2/packAddress.json GET /packAddress The packAddress method converts a raw TON address to a user-friendly format. TON v2 developers can call packAddress via Chainstack. The `packAddress` method converts a raw TON address to a user-friendly format. This method is useful for presenting TON addresses in a more readable and standardized way. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "packAddress", "params": { "address": "0:ed1691307050047117b998b561d8de82d31fbf84910ced6eb5fc92e7485ef8a7" } }' ``` ## Parameters * `address` (string, required) — The raw address to pack. Example: `0:ed1691307050047117b998b561d8de82d31fbf84910ced6eb5fc92e7485ef8a7`. ## Response * `address` (string) — The packed address in user-friendly format. This format typically includes a checksum and is easier for users to read and verify. ## Use case A possible use case for the `packAddress` method in TON is for wallet applications, blockchain explorers, or any DApp that needs to display TON addresses to users. This method can be used to: 1. Convert raw addresses to a more user-friendly format before displaying them in a UI. 2. Standardize address representation across different parts of an application. 3. Prepare addresses for QR code generation or other visual representations. 4. Improve user experience by making addresses easier to read and verify visually. # pendingTraces | TON v3 Source: https://docs.chainstack.com/reference/ton-pendingtraces-v3 openapi/ton_node_api/v3/getPendingTraces.json GET /pendingTraces The pendingTraces endpoint retrieves traces that are pending completion in the TON blockchain. Available on TON v3 via Chainstack. # Pending Traces The `pendingTraces` endpoint retrieves traces that are pending completion in the TON blockchain. These represent transaction execution flows that have not yet fully propagated through all necessary contracts. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `trace_id` (string, optional) — Filter by specific trace ID. * `limit` (integer, optional) — Maximum number of traces to return. Default: `10`. * `offset` (integer, optional) — Number of traces to skip for pagination. Default: `0`. ## Response * `traces` (array) — Array of pending trace objects: * `trace_id` (string) — Trace ID. * `external_hash` (string) — Hash of the external message. * `mc_seqno_start` (integer) — Start masterchain sequence number. * `start_lt` (integer) — Start logical time. * `start_utime` (integer) — Start unix timestamp. * `state` (string) — Trace state (e.g., `pending`). * `pending_edges` (integer) — Number of pending message edges. * `edges` (integer) — Total edges so far. * `nodes` (integer) — Total nodes so far. * `address_book` (object) — Address book mapping. ## Use case The `pendingTraces` endpoint is essential for tracking in-progress complex operations: 1. Monitoring multi-hop transactions that involve several contracts. 2. Tracking Jetton transfers waiting for completion across wallet contracts. 3. Debugging stuck transactions by identifying pending message edges. 4. Building real-time dashboards showing transaction progress. 5. Implementing timeout alerts for transactions taking too long. Here's an example of getting pending traces: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/pendingTraces?limit=5' \ -H 'accept: application/json' ``` > [!TIP] > > The `pending_edges` field indicates how many messages are still being processed. A trace completes when this reaches zero. # pendingTransactions | TON v3 Source: https://docs.chainstack.com/reference/ton-pendingtransactions-v3 openapi/ton_node_api/v3/getPendingTransactions.json GET /pendingTransactions The pendingTransactions endpoint retrieves transactions that are pending finalization in the TON blockchain. TON v3 via Chainstack. # Pending Transactions The `pendingTransactions` endpoint retrieves transactions that are pending finalization in the TON blockchain. These are transactions that have been included in a block but may not yet be fully confirmed. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `account` (string, optional) — Filter by account address. * `limit` (integer, optional) — Maximum number of transactions to return. Default: `10`. * `offset` (integer, optional) — Number of transactions to skip for pagination. Default: `0`. ## Response * `transactions` (array) — Array of pending transaction objects: * `account` (string) — Account address. * `hash` (string) — Transaction hash. * `lt` (integer) — Logical time. * `now` (integer) — Unix timestamp. * `mc_block_seqno` (integer) — Masterchain block sequence number. * `trace_id` (string) — Associated trace ID. * `address_book` (object) — Address book mapping. ## Use case The `pendingTransactions` endpoint is useful for monitoring real-time transaction status: 1. Wallet applications showing recently submitted transactions before full confirmation. 2. Trading bots tracking order execution in real-time. 3. Payment systems providing instant feedback on transaction status. 4. Block explorers showing live transaction feeds. 5. Monitoring systems tracking specific account activity. Here's an example of getting pending transactions: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/pendingTransactions?limit=5' \ -H 'accept: application/json' ``` > [!TIP] > > Pending transactions will eventually move to the regular [transactions](/reference/ton-transactions-v3) endpoint once fully confirmed. Use this endpoint for real-time monitoring. # runGetMethod | TON v2 Source: https://docs.chainstack.com/reference/ton-rungetmethod-v2 openapi/ton_node_api/v2/runGetMethod.json POST /runGetMethod The runGetMethod method executes a get method on a smart contract in the TON blockchain. Available on TON v2 via Chainstack JSON-RPC nodes. The `runGetMethod` method executes a get method on a smart contract in the TON blockchain. This method allows you to retrieve data from smart contracts without modifying their state. > [!NOTE] > > There's no difference between a full node an archive node in data availability or pricing. All data is always available and all node requests are consumed as 1 request unit. ## Request Body * `address` (string, required) — The address of the smart contract to interact with. Example: `EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs`. * `method` (string, required) — The name of the get method to execute. Example: `get_wallet_address`. * `stack` (array, optional) — An array of arguments to pass to the method, if any. Default is an empty array. * `seqno` (int, optional) — the Masterchain seqno. You can get the Masterchain seqno with [getMasterchainInfo | TON v2](/reference/ton-getmasterchaininfo-v2). ## JSON-RPC ```shell shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "runGetMethod", "params": { "address": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", "method": "get_wallet_address", "stack": [ [ "tvm.Slice", "te6cckEBAQEAJAAAQ4AbUzrTQYTUv8s/I9ds2TSZgRjyrgl2S2LKcZMEFcxj6PARy3rF" ] ], "seqno": 45792554 } }' ``` ## Response * `exit_code` (integer) — The exit code of the method execution. A value of 0 typically indicates successful execution. * `stack` (array of objects) — The resulting stack after method execution. Each object in the array represents a stack item and contains: * `type` (string) — The type of the stack item. * `value` (string) — The value of the stack item. * `gas_used` (integer) — The amount of gas used for the execution. ## Use case A possible use case for the `runGetMethod` method in TON is for applications or services that need to interact with smart contracts to retrieve data. This method can be used to: 1. Fetch the current state or data from a smart contract. 2. Implement read operations for decentralized applications (dApps). 3. Query token balances, metadata, or other contract-specific information. 4. Debug or test smart contract functionality without modifying the blockchain state. Here's another with a `get_jetton_data` method: ```shell shell theme={"system"} curl -X 'POST' \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/runGetMethod' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "address": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", "method": "get_jetton_data", "stack": []}' ``` # runGetMethod | TON v3 Source: https://docs.chainstack.com/reference/ton-rungetmethod-v3 openapi/ton_node_api/v3/runGetMethod.json POST /runGetMethod The runGetMethod endpoint allows you to execute a get method on a smart contract in the TON blockchain. Chainstack TON v3 reference. The `runGetMethod` endpoint allows you to execute a get method on a smart contract in the TON blockchain. This is useful for retrieving data from smart contracts without modifying their state. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Request Body * `address` (string, required) — The address of the smart contract to interact with. * `method` (string, required) — The name of the get method to execute on the smart contract. * `stack` (array, optional) — An array of stack items to pass as parameters to the method. Each item is an object with: * `type` (string) — The type of the stack item (e.g., "num", "cell", "slice"). * `value` (string) — The value of the stack item. ## Response * `exitCode` (integer) — The exit code of the method execution. 0 typically indicates success. * `gasUsed` (integer) — The amount of gas used for the execution. * `stack` (array) — The resulting stack after method execution. Each item is an object with: * `type` (string) — The type of the stack item. * `value` (string) — The value of the stack item. ## Use case The `runGetMethod` endpoint is crucial for interacting with smart contracts on the TON blockchain: 1. Developers can use this to test and debug their smart contracts by calling get methods. 2. dApps can retrieve data from smart contracts to display information to users. 3. Wallets can use this to fetch account balances or other relevant data from custom contracts. 4. Analytical tools can gather data from multiple contracts to provide insights into the blockchain state. 5. Automated systems can periodically check contract states without initiating transactions. ## Example Here's an example of how to use the `runGetMethod` endpoint: # sendBoc | TON v2 Source: https://docs.chainstack.com/reference/ton-sendboc-v2 openapi/ton_node_api/v2/sendBoc.json POST /sendBoc The sendBoc method sends a serialized bag-of-cells (BoC) to the TON blockchain. This is the primary method for submitting signed transactions to the network. The `sendBoc` method sends a serialized bag-of-cells (BoC) to the TON blockchain. This is the primary method for submitting signed transactions to the network. > [!NOTE] > > There's no difference between a full node an archive node in data availability or pricing. All data is always available and all node requests are consumed as 1 request unit. ## Request body * `boc` (string, required) — The serialized bag-of-cells in base64 format. This contains the signed external message to be sent to the blockchain. ## JSON-RPC ```shell shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "sendBoc", "params": { "boc": "te6ccgEBAgEAqwAB4YgBVbC7Hu+g3htYXzst8L5ucV76NMzeBK3URJKebN2Y1kwADRfFu/r0HxgrOp/Iml8Li5x4gR10J5XYFS8z0fMWJ6JPeUF5xNSLrSDGFIqhJqkW1SEYQF8Dw5p4dI3oaFs4LCmpoxdkfKw0AAAAGAAcAQBoQgAwwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL68=" } }' ``` ## Response * `ok` (boolean) — Whether the operation was successful. * `result` (object) — The result of the operation. Contains: * `@type` (string) — The type of the result, typically `ok` for successful submissions. * `@extra` (string) — Extra information about the operation. ## Use case The `sendBoc` method is essential for any application that needs to send transactions to the TON blockchain: 1. Wallet applications use this to broadcast user-signed transactions. 2. Smart contract deployment tools send compiled contracts to the network. 3. DApps submit user interactions with smart contracts. 4. Automated trading bots execute trades on DEXs. 5. Token transfer services broadcast transfer transactions. Here's an example of sending a simple TON transfer: ```shell shell theme={"system"} curl -X 'POST' \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/sendBoc' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "boc": "te6ccgEBAgEAqwAB4YgBVbC7Hu+g3htYXzst8L5ucV76NMzeBK3URJKebN2Y1kwADRfFu/r0HxgrOp/Iml8Li5x4gR10J5XYFS8z0fMWJ6JPeUF5xNSLrSDGFIqhJqkW1SEYQF8Dw5p4dI3oaFs4LCmpoxdkfKw0AAAAGAAcAQBoQgAwwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL68=" }' ``` > [!TIP] > > To create a valid BoC for sending, you typically need to: > > 1. Create the message payload > 2. Sign it with your private key > 3. Serialize it to base64 format > > Libraries like TonWeb, ton-core, or the TON SDK handle this process. # sendBocReturnHash | TON v2 Source: https://docs.chainstack.com/reference/ton-sendbocreturnhash-v2 openapi/ton_node_api/v2/sendBocReturnHash.json POST /sendBocReturnHash The sendBocReturnHash method sends a serialized bag-of-cells (BoC) to the TON blockchain and returns the message hash. TON v2 via Chainstack. The `sendBocReturnHash` method sends a serialized bag-of-cells (BoC) to the TON blockchain and returns the message hash. This method is similar to [sendBoc](/reference/ton-sendboc-v2) but additionally returns the hash of the sent message for tracking purposes. > [!NOTE] > > There's no difference between a full node an archive node in data availability or pricing. All data is always available and all node requests are consumed as 1 request unit. ## Request body * `boc` (string, required) — The serialized bag-of-cells in base64 format. This contains the signed external message to be sent to the blockchain. ## JSON-RPC ```shell shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "sendBocReturnHash", "params": { "boc": "te6ccgEBAgEAqwAB4YgBVbC7Hu+g3htYXzst8L5ucV76NMzeBK3URJKebN2Y1kwADRfFu/r0HxgrOp/Iml8Li5x4gR10J5XYFS8z0fMWJ6JPeUF5xNSLrSDGFIqhJqkW1SEYQF8Dw5p4dI3oaFs4LCmpoxdkfKw0AAAAGAAcAQBoQgAwwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL68=" } }' ``` ## Response * `ok` (boolean) — Whether the operation was successful. * `result` (object) — The result of the operation. Contains: * `@type` (string) — The type of the result, typically `raw.extMessageInfo`. * `hash` (string) — The hash of the sent message. This can be used to track the transaction status. * `@extra` (string) — Extra information about the operation. ## Use case The `sendBocReturnHash` method is preferred over `sendBoc` when you need to track transaction status: 1. Wallet applications can display the transaction hash to users for tracking. 2. Backend services can store the hash for transaction monitoring and confirmation. 3. Trading bots can track order execution status using the returned hash. 4. Payment systems can correlate transaction hashes with internal records. 5. DApps can provide users with explorer links using the transaction hash. Here's an example of sending a transaction and receiving the hash: ```shell shell theme={"system"} curl -X 'POST' \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/sendBocReturnHash' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "boc": "te6ccgEBAgEAqwAB4YgBVbC7Hu+g3htYXzst8L5ucV76NMzeBK3URJKebN2Y1kwADRfFu/r0HxgrOp/Iml8Li5x4gR10J5XYFS8z0fMWJ6JPeUF5xNSLrSDGFIqhJqkW1SEYQF8Dw5p4dI3oaFs4LCmpoxdkfKw0AAAAGAAcAQBoQgAwwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL68=" }' ``` > [!TIP] > > Use the returned hash with [getTransactions](/reference/ton-gettransactions-v2) or a block explorer to confirm your transaction was processed successfully. # sendQuery | TON v2 Source: https://docs.chainstack.com/reference/ton-sendquery-v2 openapi/ton_node_api/v2/sendQuery.json POST /sendQuery The sendQuery method sends an unpacked external message to the TON blockchain. Unlike sendBoc which requires a fully serialized BoC. The `sendQuery` method sends an unpacked external message to the TON blockchain. Unlike [sendBoc](/reference/ton-sendboc-v2) which requires a fully serialized BoC, this method accepts individual message components and constructs the message on the server side. > [!NOTE] > > There's no difference between a full node an archive node in data availability or pricing. All data is always available and all node requests are consumed as 1 request unit. ## Request body * `address` (string, required) — The destination address for the message. Example: `EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs`. * `body` (string, required) — The body of the message in base64 format. * `init_code` (string, optional) — The init code in base64 format. Required when deploying a new contract. * `init_data` (string, optional) — The init data in base64 format. Required when deploying a new contract. ## JSON-RPC ```shell shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "sendQuery", "params": { "address": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", "body": "te6cckEBAQEAAgAAAEysuc0=" } }' ``` ## Response * `ok` (boolean) — Whether the operation was successful. * `result` (object) — The result of the operation. Contains: * `@type` (string) — The type of the result, typically `ok` for successful submissions. * `@extra` (string) — Extra information about the operation. ## Use case The `sendQuery` method is useful when you have the message components but haven't serialized them into a BoC: 1. Smart contract deployment when you have separate code and data cells. 2. Simplified transaction sending without manual BoC construction. 3. Testing and debugging with individual message components. 4. Integration with systems that provide message parts separately. Here's an example of deploying a smart contract using sendQuery: ```shell shell theme={"system"} curl -X 'POST' \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/sendQuery' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "address": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", "body": "te6cckEBAQEAAgAAAEysuc0=", "init_code": "te6cckEBAQEAXAAAsFF/APSkE/S88sgL...", "init_data": "te6cckEBAQEAJAAAQ4AW..." }' ``` > [!TIP] > > For most use cases, [sendBoc](/reference/ton-sendboc-v2) or [sendBocReturnHash](/reference/ton-sendbocreturnhash-v2) are preferred as they give you full control over the message serialization. Use `sendQuery` when working with systems that provide unpacked message components. # shards | TON v2 Source: https://docs.chainstack.com/reference/ton-shards-v2 openapi/ton_node_api/v2/shards.json GET /shards The shards method retrieves information about shards for a specific masterchain block in the TON blockchain. TON v2 via Chainstack. The `shards` method retrieves information about shards for a specific masterchain block in the TON blockchain. This method provides details about the current state of shards at a given masterchain block height. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `seqno` (integer, required) — The sequence number of the masterchain block. Example: `39064874`. ## JSON-RPC examples ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "shards", "params": { "seqno": 39064874 } }' ``` ## Response * `shards` (array of objects) — An array containing information about each shard, where each object includes: * `workchain` (integer) — The ID of the workchain to which the shard belongs. * `shard` (string) — The ID of the shard. * `seqno` (integer) — The sequence number of the shard block. * `root_hash` (string) — The root hash of the shard block. * `file_hash` (string) — The file hash of the shard block. ## Use case A possible use case for the `shards` method in TON is for applications or services that need to monitor or analyze the sharding structure of the TON blockchain. This method can be used to: 1. Track the evolution of shards over time by querying this endpoint for different masterchain block heights. 2. Implement shard-aware applications that need to route transactions or queries to specific shards. 3. Analyze the load distribution across different shards in the TON network. 4. Build block explorers that can display detailed shard information for each masterchain block. # topAccountsByBalance | TON v3 Source: https://docs.chainstack.com/reference/ton-topaccountsbybalance-v3 openapi/ton_node_api/v3/getTopAccountsByBalance.json GET /topAccountsByBalance The topAccountsByBalance endpoint retrieves accounts with the highest TON balances on the network. Use it on TON v3 via Chainstack. # Top Accounts by Balance The `topAccountsByBalance` endpoint retrieves accounts with the highest TON balances on the network. This provides a snapshot of the wealthiest addresses on the TON blockchain. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `limit` (integer, optional) — Maximum number of accounts to return. Default: `10`. * `offset` (integer, optional) — Number of accounts to skip for pagination. Default: `0`. ## Response * `accounts` (array) — Array of account objects sorted by balance descending: * `address` (string) — Account address. * `balance` (string) — Balance in nanotons. * `account_status` (string) — Account status (`active`, `uninit`, `frozen`). * `last_activity` (integer) — Last activity timestamp. * `address_book` (object) — Address book mapping with names for known addresses. ## Use case The `topAccountsByBalance` endpoint is valuable for network analysis and research: 1. Network statistics dashboards showing wealth distribution. 2. Research tools analyzing TON token concentration. 3. Block explorers displaying rich list pages. 4. Market analysis tools tracking whale wallets. 5. Ecosystem health monitoring and reporting. Here's an example of getting the top 10 accounts: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/topAccountsByBalance?limit=10' \ -H 'accept: application/json' ``` > [!TIP] > > The `address_book` in the response will include names for well-known addresses like exchanges and major contracts, making it easier to identify the top holders. # traces | TON v3 Source: https://docs.chainstack.com/reference/ton-traces-v3 openapi/ton_node_api/v3/getTraces.json GET /traces The traces endpoint retrieves transaction traces from the TON blockchain. A trace represents the complete execution path of a transaction. # Traces The `traces` endpoint retrieves transaction traces from the TON blockchain. A trace represents the complete execution path of a transaction, including all resulting internal messages and their effects across the network. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `trace_id` (string, optional) — Filter by specific trace ID. * `tx_hash` (string, optional) — Get traces for a specific transaction hash. * `tx_lt` (integer, optional) — Filter by transaction logical time. * `limit` (integer, optional) — Maximum number of traces to return. Default: `10`. * `offset` (integer, optional) — Number of traces to skip for pagination. Default: `0`. * `sort` (string, optional) — Sort order: `asc` or `desc`. Default: `desc`. ## Response * `traces` (array) — Array of trace objects containing: * `trace_id` (string) — Unique identifier for the trace. * `external_hash` (string) — Hash of the external message that initiated the trace. * `mc_seqno_start` (integer) — Masterchain sequence number when the trace started. * `mc_seqno_end` (integer) — Masterchain sequence number when the trace ended. * `start_lt` (integer) — Logical time when the trace started. * `start_utime` (integer) — Unix timestamp when the trace started. * `end_lt` (integer) — Logical time when the trace ended. * `end_utime` (integer) — Unix timestamp when the trace ended. * `state` (string) — Current state of the trace (e.g., `complete`, `pending`). * `pending_edges` (integer) — Number of pending message edges. * `edges` (integer) — Total number of message edges in the trace. * `nodes` (integer) — Total number of transaction nodes in the trace. * `classification_state` (string) — Classification state of the trace. * `address_book` (object) — Address book mapping raw addresses to user-friendly information. ## Use case The `traces` endpoint is essential for understanding the complete execution flow of transactions: 1. Debugging failed transactions by tracing the exact execution path. 2. Analyzing multi-hop transactions across multiple smart contracts. 3. Understanding fee distribution across a transaction's execution. 4. Building transaction explorers that show complete message flows. 5. Auditing smart contract interactions and their side effects. Here's an example of getting a trace for a specific transaction: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/traces?tx_hash=BhLJTdV2R6Vr5c3BAOjH8nFpnMYOuPcJvRlagGSKU88%3D&limit=1' \ -H 'accept: application/json' ``` > [!TIP] > > Traces are particularly useful for understanding Jetton and NFT transfers, which typically involve multiple internal messages between minter, wallet, and recipient contracts. # transactions | TON v3 Source: https://docs.chainstack.com/reference/ton-transactions-v3 openapi/ton_node_api/v3/getTransactions.json GET /transactions The transactions endpoint retrieves a list of transactions from the TON blockchain. Available on TON v3 via Chainstack JSON-RPC nodes. The `transactions` endpoint retrieves a list of transactions from the TON blockchain. This endpoint allows you to fetch recent transactions and provides options for pagination and sorting. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `limit` (integer, optional) — The maximum number of transactions to return. Default: `128`. * `offset` (integer, optional) — The number of transactions to skip before starting to return results. Default: `0`. * `sort` (string, optional) — The sorting order of the transactions. Possible values: `asc` (ascending) or `desc` (descending). Default: `desc`. ## Response * `transactions` (array) — An array of transaction objects, each containing: * `hash` (string) — The transaction hash. * `lt` (string) — The logical time of the transaction. * `account` (string) — The account address involved in the transaction. * `now` (integer) — The timestamp of the transaction. * `origStatus` (string) — The original status of the account. * `endStatus` (string) — The end status of the account. * `totalFees` (string) — The total fees for the transaction. ## Use case The `transactions` endpoint is useful for various applications that need to monitor or analyze transaction activity on the TON blockchain: 1. Block explorers can use this to display recent transactions and allow users to browse through transaction history. 2. Wallet applications can fetch and display recent transactions for a user's account. 3. Analytics tools can use this endpoint to gather data on transaction volumes, fees, and patterns over time. 4. Monitoring systems can track specific types of transactions or activity from particular accounts. # transactionsByMasterchainBlock | TON v3 Source: https://docs.chainstack.com/reference/ton-transactionsbymasterchainblock-v3 openapi/ton_node_api/v3/getTransactionsByMasterchainBlock.json GET /transactionsByMasterchainBlock The transactionsByMasterchainBlock endpoint retrieves a list of transactions for a specific masterchain block in the TON blockchain. The `transactionsByMasterchainBlock` endpoint retrieves a list of transactions for a specific masterchain block in the TON blockchain. This endpoint allows you to fetch transactions associated with a particular block and provides options for pagination and sorting. > [!NOTE] > > **TON billing: full or archive by block age** > > This method is billed as archive (2 RUs) when the requested block is 128 or more seqno behind the tip, and full (1 RU) otherwise. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `seqno` (integer, required) — The sequence number of the masterchain block. Example: `40467413`. * `limit` (integer, optional) — The maximum number of transactions to return. Default: `128`. * `offset` (integer, optional) — The number of transactions to skip before starting to return results. Default: `0`. * `sort` (string, optional) — The sorting order of the transactions. Possible values: `asc` (ascending) or `desc` (descending). Default: `desc`. ## Response * `transactions` (array) — An array of transaction objects, each containing: * `hash` (string) — The transaction hash. * `lt` (string) — The logical time of the transaction. * `account` (string) — The account address involved in the transaction. * `now` (integer) — The timestamp of the transaction. * `origStatus` (string) — The original status of the account. * `endStatus` (string) — The end status of the account. * `totalFees` (string) — The total fees for the transaction. ## Use case The `transactionsByMasterchainBlock` endpoint is useful for various applications that need to analyze or display transaction data for specific blocks: 1. Block explorers can use this to show all transactions included in a particular masterchain block. 2. Researchers and analysts can study transaction patterns and volumes for specific blocks. 3. Developers can use this endpoint to verify transaction inclusion in a specific block. 4. Auditing tools can track all transactions processed in a given block for compliance or security purposes. # transactionsByMessage | TON v3 Source: https://docs.chainstack.com/reference/ton-transactionsbymessage-v3 openapi/ton_node_api/v3/getTransactionsByMessage.json GET /transactionsByMessage The transactionsByMessage endpoint retrieves a list of transactions related to a specific message in the TON blockchain. On TON v3. The `transactionsByMessage` endpoint retrieves a list of transactions related to a specific message in the TON blockchain. This endpoint allows you to fetch transactions associated with a particular message hash and provides options for pagination. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `direction` (string, required) — The direction of the message. Possible values: `in` or `out`. Default: `out`. * `msg_hash` (string, required) — The hash of the message. Example: `po4/F9OgHrJGTUCtobScZrW/gOwVPj7s3f40HV32EII=`. * `limit` (integer, optional) — The maximum number of transactions to return. Default: `128`. * `offset` (integer, optional) — The number of transactions to skip before starting to return results. Default: `0`. ## Response * `transactions` (array) — An array of transaction objects, each containing: * `hash` (string) — The transaction hash. * `lt` (string) — The logical time of the transaction. * `account` (string) — The account address involved in the transaction. * `now` (integer) — The timestamp of the transaction. * `origStatus` (string) — The original status of the account. * `endStatus` (string) — The end status of the account. * `totalFees` (string) — The total fees for the transaction. ## Use case The `transactionsByMessage` endpoint is useful for various applications that need to track or analyze specific messages and their related transactions: 1. Wallet applications can use this to track the status of sent or received messages and their associated transactions. 2. Block explorers can provide detailed information about message propagation and the resulting transactions. 3. Developers can use this endpoint to debug and verify message processing in smart contracts. 4. Analytics tools can track the impact of specific messages on the blockchain state. # tryLocateResultTx | TON v2 Source: https://docs.chainstack.com/reference/ton-trylocateresulttx-v2 openapi/ton_node_api/v2/tryLocateResultTx.json GET /tryLocateResultTx The tryLocateResultTx method attempts to locate the result transaction in the TON blockchain based on the source address, destination address. The `tryLocateResultTx` method attempts to locate the result transaction in the TON blockchain based on the source address, destination address, and the logical time of the original transaction. This method is useful for finding the outcome or response to a specific transaction. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `source` (string, required) — The source address of the original transaction. Example: `UQAX2d4C2d48JeA-0y2_peGabRVumnKY22xPvkuEayC3zTI1`. * `destination` (string, required) — The destination address of the original transaction. Example: `UQBP4mzpDIywL1SV-Wp9ZuBBlzprR9eXQgSYGEXiUEHm73vA`. * `created_lt` (string, required) — The logical time when the original transaction was created. Example: `47597573000002`. ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tryLocateResultTx", "params": { "source": "UQAX2d4C2d48JeA-0y2_peGabRVumnKY22xPvkuEayC3zTI1", "destination": "UQBP4mzpDIywL1SV-Wp9ZuBBlzprR9eXQgSYGEXiUEHm73vA", "created_lt": "47597573000002" } }' ``` ## Response * `block` (object) — Information about the block containing the result transaction: * `workchain` (integer) — The workchain ID of the block. * `shard` (string) — The shard ID of the block. * `seqno` (integer) — The sequence number of the block. * `root_hash` (string) — The root hash of the block. * `file_hash` (string) — The file hash of the block. * `transaction_id` (object) — The identifier of the result transaction: * `lt` (string) — The logical time of the result transaction. * `hash` (string) — The hash of the result transaction. ## Use case A possible use case for the `tryLocateResultTx` method in TON is for applications or services that need to track the outcomes of transactions. This method can be used to: 1. Verify the completion and result of a smart contract execution. 2. Track the flow of funds in complex multi-step transactions. 3. Implement transaction status tracking in wallet applications. 4. Analyze the effects of transactions in blockchain analytics tools. # tryLocateSourceTx | TON v2 Source: https://docs.chainstack.com/reference/ton-trylocatesourcetx-v2 openapi/ton_node_api/v2/tryLocateSourceTx.json GET /tryLocateSourceTx The tryLocateSourceTx method attempts to locate the source transaction in the TON blockchain based on the source address, destination address. The `tryLocateSourceTx` method attempts to locate the source transaction in the TON blockchain based on the source address, destination address, and the logical time of a result transaction. This method is useful for tracing back the origin of a transaction. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `source` (string, required) — The source address of the result transaction. Example: `UQAX2d4C2d48JeA-0y2_peGabRVumnKY22xPvkuEayC3zTI1`. * `destination` (string, required) — The destination address of the result transaction. Example: `UQBP4mzpDIywL1SV-Wp9ZuBBlzprR9eXQgSYGEXiUEHm73vA`. * `created_lt` (string, required) — The logical time when the result transaction was created. Example: `47597573000002`. ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tryLocateSourceTx", "params": { "source": "UQAX2d4C2d48JeA-0y2_peGabRVumnKY22xPvkuEayC3zTI1", "destination": "UQBP4mzpDIywL1SV-Wp9ZuBBlzprR9eXQgSYGEXiUEHm73vA", "created_lt": "47597573000002" } }' ``` ## Response * `block` (object) — Information about the block containing the source transaction: * `workchain` (integer) — The workchain ID of the block. * `shard` (string) — The shard ID of the block. * `seqno` (integer) — The sequence number of the block. * `root_hash` (string) — The root hash of the block. * `file_hash` (string) — The file hash of the block. * `transaction_id` (object) — The identifier of the source transaction: * `lt` (string) — The logical time of the source transaction. * `hash` (string) — The hash of the source transaction. ## Use case A possible use case for the `tryLocateSourceTx` method in TON is for applications or services that need to trace the origin of transactions. This method can be used to: 1. Implement transaction history tracing in blockchain explorers. 2. Analyze the chain of events leading to a specific transaction. 3. Verify the authenticity of a transaction by tracing it back to its source. 4. Debug smart contract interactions by identifying the initiating transaction. # tryLocateTx | TON v2 Source: https://docs.chainstack.com/reference/ton-trylocatetx-v2 openapi/ton_node_api/v2/tryLocateTx.json GET /tryLocateTx The tryLocateTx method attempts to locate a specific transaction in the TON blockchain based on the source address, destination address. The `tryLocateTx` method attempts to locate a specific transaction in the TON blockchain based on the source address, destination address, and the logical time when the transaction was created. This method is useful for finding detailed information about a transaction when you have partial information about it. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `source` (string, required) — The source address of the transaction. Example: `UQAX2d4C2d48JeA-0y2_peGabRVumnKY22xPvkuEayC3zTI1`. * `destination` (string, required) — The destination address of the transaction. Example: `UQBP4mzpDIywL1SV-Wp9ZuBBlzprR9eXQgSYGEXiUEHm73vA`. * `created_lt` (string, required) — The logical time when the transaction was created. Example: `47597573000002`. ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tryLocateTx", "params": { "source": "UQAX2d4C2d48JeA-0y2_peGabRVumnKY22xPvkuEayC3zTI1", "destination": "UQBP4mzpDIywL1SV-Wp9ZuBBlzprR9eXQgSYGEXiUEHm73vA", "created_lt": "47597573000002" } }' ``` ## Response * `block` (object) — Information about the block containing the transaction: * `workchain` (integer) — The workchain ID of the block. * `shard` (string) — The shard ID of the block. * `seqno` (integer) — The sequence number of the block. * `root_hash` (string) — The root hash of the block. * `file_hash` (string) — The file hash of the block. * `transaction_id` (object) — The identifier of the transaction: * `lt` (string) — The logical time of the transaction. * `hash` (string) — The hash of the transaction. ## Use case A possible use case for the `tryLocateTx` method in TON is for blockchain explorers, wallet applications, or any service that needs to locate a specific transaction with partial information. This method can be used to: 1. Find the full details of a transaction when only the source, destination, and approximate time are known. 2. Verify the existence and details of a transaction in dispute resolution scenarios. 3. Implement advanced search functionality in blockchain explorers. 4. Cross-reference transactions across different data sources or systems. # unpackAddress | TON v2 Source: https://docs.chainstack.com/reference/ton-unpackaddress-v2 openapi/ton_node_api/v2/unpackAddress.json GET /unpackAddress The unpackAddress method converts a user-friendly TON address to its raw format. Available on TON v2 via Chainstack JSON-RPC nodes. The `unpackAddress` method converts a user-friendly TON address to its raw format. This method is useful for converting human-readable addresses into the format required for certain low-level operations or for consistency in data storage. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## JSON-RPC example ```shell Shell theme={"system"} curl -X POST \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v2/jsonRPC' \ -H 'Content-Type: application/json' \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "unpackAddress", "params": { "address": "EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2" } }' ``` ## Parameters * `address` (string, required) — The user-friendly address to unpack. Example: `EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2`. ## Response * `address` (string) — The unpacked address in raw format. This format typically includes the workchain ID and the raw address without any encoding or checksum. ## Use case A possible use case for the `unpackAddress` method in TON is for developers building applications that need to interact with the TON blockchain at a lower level. This method can be used to: 1. Convert user-input addresses to the raw format required for certain blockchain operations. 2. Standardize address storage in databases or other backend systems. 3. Prepare addresses for use in smart contract interactions that require the raw format. 4. Verify the validity of a user-friendly address by unpacking it and checking its structure. # wallet | TON v3 Source: https://docs.chainstack.com/reference/ton-wallet-v3 openapi/ton_node_api/v3/getWalletInfo.json GET /api/v3/walletInformation The wallet endpoint retrieves detailed information about a specific wallet on the TON blockchain. Use it on TON v3 via Chainstack. # Get Wallet Information The `wallet` endpoint retrieves detailed information about a specific wallet on the TON blockchain. This endpoint allows you to fetch the current state, balance, and other relevant details of a wallet. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The address of the wallet to retrieve information for. ## Response * `wallet` (boolean) — Indicates if the address is a wallet. * `balance` (string) — The balance of the wallet in nanotons. * `account_state` (string) — The state of the account (e.g., active, uninitialized). * `wallet_type` (string) — The type of the wallet contract. * `seqno` (integer) — The current sequence number of the wallet. * `last_transaction_lt` (string) — The logical time of the last transaction. * `last_transaction_hash` (string) — The hash of the last transaction. ## Use case The `wallet` endpoint is useful for various applications that need to retrieve or verify wallet information on the TON blockchain: 1. Wallet applications can use this to display wallet balances and status to users. 2. Decentralized applications (dApps) can verify wallet existence and status before interacting with them. 3. Block explorers can use this to show detailed wallet information to users. 4. Analytics platforms can track wallet balances and activities over time. 5. Developers can use this endpoint to build wallet monitoring or notification systems. 6. Exchange platforms can use this to verify user wallets and balances. 7. Smart contract interfaces can use this to check if a wallet is active and has sufficient balance before allowing interactions. # walletStates | TON v3 Source: https://docs.chainstack.com/reference/ton-walletstates-v3 openapi/ton_node_api/v3/getWalletStates.json GET /walletStates The walletStates endpoint queries wallet state information for addresses, including balance, status, and whether the address is a recognized wallet contract. # Wallet States The `walletStates` endpoint queries wallet state information for addresses, including balance, status, and whether the address is a recognized wallet contract. > [!NOTE] > > **TON billing: full (1 RU)** > > This method is always billed as full. See [Request units — TON method scope](/docs/request-units#ton-method-scope). ## Parameters * `address` (string, required) — The wallet address to query. * `limit` (integer, optional) — Maximum number of results. Default: `10`. * `offset` (integer, optional) — Number of results to skip. Default: `0`. ## Response * `wallets` (array) — Array of wallet state objects: * `address` (string) — Wallet address. * `is_wallet` (boolean) — Whether the address is a recognized wallet contract. * `balance` (string) — Balance in nanotons. * `status` (string) — Account status (active, uninit, frozen). * `code_hash` (string) — Hash of the code cell. * `last_transaction_hash` (string) — Hash of last transaction. * `last_transaction_lt` (string) — Logical time of last transaction. * `address_book` (object) — Address book mapping with names for known addresses. ## Use case The `walletStates` endpoint is useful for applications working with wallet contracts: 1. Wallet applications displaying account status and balance. 2. Portfolio trackers monitoring multiple addresses. 3. Payment systems verifying wallet state before transactions. 4. Block explorers identifying wallet types. 5. Analytics tools categorizing addresses as wallets vs contracts. Here's an example of getting wallet state: ```shell shell theme={"system"} curl -X GET \ 'https://ton-mainnet.core.chainstack.com/f2a2411bce1e54a2658f2710cd7969c3/api/v3/walletStates?address=EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs' \ -H 'accept: application/json' ``` > [!TIP] > > The `is_wallet` field indicates whether the contract matches a known wallet implementation (v3, v4, etc.). Jetton masters and other contracts will show `false`. # trace filter | Fantom Source: https://docs.chainstack.com/reference/trace-filter-fantom Fantom trace_filter method — query transaction execution traces across a block range, filtered by sender, receiver, or other criteria for EVM analysis. ## Parameters * `fromBlock` — the starting block number (in hexadecimal) from which to filter. * `toBlock` — the ending block number (in hexadecimal) to filter to. * `toAddress` — array of addresses involved in the transactions (sender or receiver). * `fromAddress` — array of addresses that initiated the transactions. * `offset` — the offset trace number, indicating the starting point for trace retrieval. This is useful for pagination. * `count` — the integer number of traces to display in a batch, allowing for controlled data retrieval. ## JSON-RPC example ```shell theme={"system"} curl https://fantom-mainnet.core.chainstack.com/4ab982aa70a7baead515ffdb5915df3f \ -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"trace_filter","params":[{"fromBlock":"0x4bbb701","toBlock":"0x4bbb701","toAddress":["0x5dD4A25B354993e3201419db5172dfEEDaF39AdF"],"offset":2,"count":1}]}' ``` ## Response * `blockHash` — the hash of the block in which the trace was included. * `blockNumber` — the number of the block in which the trace occurred. * `subtraces` — the number of sub-traces spawned by the trace. * `traceAddress` — an array that indicates the hierarchical position of the trace. * `transactionHash` — the hash of the transaction. * `transactionPosition` — the position of the transaction within the block. * `action` — an object that describes the action taken by the trace: * `callType` — the type of call, such as `call`, `delegatecall`, or `staticcall`. * `from` — the address of the sender who initiated the trace. * `gas` — the units of gas used by the transaction. * `input` — the data sent with the transaction. * `to` — the recipient of the transaction if applicable. * `value` — the value transferred in Wei. * `result` — an object that contains the outcome of the trace: * `gasUsed` — the gas used by the transaction, encoded as hexadecimal. * `output` — the return value of the execution, encoded as a hexadecimal string. * `error` — a string indicating any errors that occurred during the transaction execution. * type`— the type of action taken by the transaction,`call`or`create`. `call `is the most common type of trace and occurs when a smart contract invokes another contract's function.`create\` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain. ## `trace_filter` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Function to use traceFilter const traceFilter = async (options) => { const params = { fromBlock: options.fromBlock, toBlock: options.toBlock, toAddress: options.toAddress, fromAddress: options.fromAddress, offset: options.offset, count: options.count }; const traces = await provider.send('trace_filter', [params]); console.log(traces); }; // Example usage of traceFilter const filterOptions = { fromBlock: '0x4bbb701', toBlock: '0x4bbb701', toAddress: ['0x5dD4A25B354993e3201419db5172dfEEDaF39AdF'], offset: 2, count: 1 }; traceFilter(filterOptions); ``` ```python web3.py theme={"system"} from web3 import Web3 # Setup Web3 connection node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Define the filter options filter_options = { 'fromBlock': '0x4bbb701', 'toBlock': '0x4bbb701', 'toAddress': ['0x5dD4A25B354993e3201419db5172dfEEDaF39AdF'], 'offset': 2, 'count': 1 } # Function to use traceFilter def trace_filter(options): result = web3.provider.make_request('trace_filter', [options]) return result # Example usage of traceFilter traces = trace_filter(filter_options) print(traces) ``` ## Use case The `trace_filter` method is invaluable for developers and analysts who must conduct thorough investigations of transaction flows and state changes in the EVM, particularly over a specific block range. For example, this method can help audit smart contract interactions over time, investigate potential security breaches or unexpected behaviors in deployed contracts, and deeply analyze complex transaction sequences. ## Try the `trace_filter` RPC method yourself # trace get | Fantom Source: https://docs.chainstack.com/reference/trace-get-fantom Fantom trace_get method — retrieve a specific internal trace from a transaction by index for detailed analysis of nested calls and EVM operations. ## Parameters * `txHash` — the hash of the transaction from which to retrieve the trace. * `traceIndex` — an array of integers specifying the zero-based indices of the traces to retrieve. ## JSON-RPC example ```shell theme={"system"} curl https://fantom-mainnet.core.chainstack.com/4ab982aa70a7baead515ffdb5915df3f \ -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"trace_get","params":["0xa2b1e35f59d184da1996b37803ed2e8e81057be7d07a93af48a410fd9338d616", ["0x1"]]}' ``` ## Response * `action` — an object that describes the action taken by the transaction: * `callType` — the type of call, such as `call`, `delegatecall`, or `staticcall`. * `from` — the address of the sender who initiated the transaction. * `gas` — the units of gas used by the transaction. * `input` — the data sent with the transaction. * `to` — the recipient of the transaction, if applicable. * `value` — the value transferred in Wei. * `blockHash` — the hash of the block in which the transaction was included. * `blockNumber` — the number of the block in which the transaction was included. * `result` — an object that contains the outcome of the trace: * `gasUsed` — the gas used by the transaction, encoded as hexadecimal. * `output` — the return value of the execution, encoded as a hexadecimal string. * `subtraces` — the number of sub-traces spawned by the trace. * `traceAddress` — an array that indicates the hierarchical position of the trace. * `transactionHash` — the hash of the transaction. * `transactionPosition` — the position of the transaction within the block. * `type` — the type of action taken by the transaction, `call` or `create`. ## `trace_get` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); // Function to use traceGet async function traceGet(txHash, transactionPositions) { const method = 'trace_get'; const params = [txHash, transactionPositions]; // Using the provider to send a custom JSON-RPC request const result = await provider.send(method, params); console.log(result); } // Example usage of traceGet const txHash = '0xa2b1e35f59d184da1996b37803ed2e8e81057be7d07a93af48a410fd9338d616'; // Example block hash const transactionPositions = ["0x1"]; // Example transaction index traceGet(txHash, transactionPositions); ``` ```python web3.py theme={"system"} from web3 import Web3 # Setup Web3 connection node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) # Function to use trace_get def trace_get(tx_hash, transaction_positions): method = 'trace_get' params = [tx_hash, transaction_positions] # Using the provider to send a custom JSON-RPC request result = web3.manager.request_blocking(method, params) print(result) # Example usage of trace_get tx_hash = '0xa2b1e35f59d184da1996b37803ed2e8e81057be7d07a93af48a410fd9338d616' # Example block hash transaction_positions = ['0x1'] # Example transaction index trace_get(tx_hash, transaction_positions) ``` ## Use case The `trace_get` method is useful for developers and analysts who must investigate the detailed execution of specific transactions, particularly those involving complex interactions and multiple steps within the EVM. It can help audit smart contract functions, verify transaction logic, and understand the flow of transactions that involve several contract calls. ## Try the `trace_get` RPC method yourself # trace transaction | Fantom Source: https://docs.chainstack.com/reference/trace-transaction-fantom Fantom trace_transaction method — get a complete trace of all internal calls and state changes for a specific transaction. Available on Erigon nodes only. ## Parameters * `hash` — the hash identifying a transaction ## JSON-RPC example ```shell theme={"system"} curl https://fantom-mainnet.core.chainstack.com/4ab982aa70a7baead515ffdb5915df3f \ -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"trace_transaction","params":["0xa2b1e35f59d184da1996b37803ed2e8e81057be7d07a93af48a410fd9338d616"]}' ``` ## Response * `action` — an object that describes the action taken by the transaction: * `from` — the address of the sender who initiated the transaction. * `callType` — the type of call, `call` or `delegatecall`, two ways to invoke a function in a smart contract. `call` creates a new environment for the function to work in, so changes made in that function won't affect the environment where the function was called. `delegatecall` doesn't create a new environment. Instead, it runs the function within the environment of the caller, so changes made in that function will affect the caller's environment. * `gas` — the units of gas included in the transaction by the sender. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is null. * `value` — the value of the native token transferred along with the transaction, in Wei. * `blockHash` — the hash of the block in which the transaction was included. * `blockNumber` — the number of the block in which the transaction was included. * `error` — a string that indicates whether the transaction was successful or not. `null` if successful, `Reverted` if not. * `result` — an object that contains additional data about the execution of the transaction: * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `output` — the return value of the call, encoded as a hexadecimal string. * `subtraces` — the number of sub-traces created during execution. When a transaction is executed on the EVM, it may trigger additional sub-executions, such as when a smart contract calls another smart contract or when an external account is accessed. * `traceAddress` — an array that indicates the position of the transaction in the trace. * `transactionHash` — the hash of the transaction. * `transactionPosition` — the position of the transaction in the block. * `type` — the type of action taken by the transaction, `call` or `create`. `call `is the most common type of trace and occurs when a smart contract invokes another contract's function. `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain. ## `trace_transaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txHash) => { const traces = await provider.send("trace_transaction", [txHash]); console.log(traces); }; traceTransaction("0xa2b1e35f59d184da1996b37803ed2e8e81057be7d07a93af48a410fd9338d616") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) tx_hash = "0xa2b1e35f59d184da1996b37803ed2e8e81057be7d07a93af48a410fd9338d616" traces = web3.provider.make_request('trace_transaction', [tx_hash]) print(traces) ``` ## Use case A practical use case of the `trace_transaction` method can inspect NFT transfers, allowing you to identify internal transactions, the sender and receiver, and the amounts transferred. This can be especially useful when analyzing an ERC-721 token purchase on a marketplace like Opensea, where funds transfer is not always straightforward due to multiple internal calls for transferring funds to the NFT owner, paying royalties to the creator, and other factors. By using `trace_transaction`, you can gain a more comprehensive understanding of the transaction flow. ## Try the `trace_transaction` RPC method yourself # trace block | Fantom Source: https://docs.chainstack.com/reference/trace_block-fantom Fantom trace_block method — retrieve execution traces for all transactions in a specific block to analyze smart contract behavior and gas usage patterns. ## Parameters * `quantity or tag` — the integer of a block encoded as hexadecimal or the string with: * `latest` — the most recent block in the blockchain and the current state of the blockchain at the most recent block. A chain reorganization is to be expected. * `safe` — the block that received justification from the beacon chain. Although this block could be involved in a chain reorganization, it would necessitate either a coordinated attack by the majority of validators or an instance of severe propagation latency. * `finalized` — the block accepted as canonical by more than 2/3 of the validators. A chain reorganization is extremely unlikely, and it would require at least 1/3 of the staked ETH to be burned. * `earliest` — the earliest available or genesis block * `pending` — the pending state and transactions block. The current state of transactions that have been broadcast to the network but have not yet been included in a block. > [!NOTE] > > See the [default block parameter](https://eth.wiki/json-rpc/API#the-default-block-parameter) and [How The Merge Impacts Ethereum’s Application Layer](https://blog.ethereum.org/2021/11/29/how-the-merge-impacts-app-layer). ## JSON-RPC example ```shell theme={"system"} curl https://fantom-mainnet.core.chainstack.com/4ab982aa70a7baead515ffdb5915df3f \ -X POST \ -H "Content-Type: application/json" \ --data '{"jsonrpc":"2.0","id":1,"method":"trace_block","params":["latest"]}' ``` ## Response * `result` — an object containing the traces of all of the transactions in the block: * `action` — an object that describes the action taken by the transaction: * `from` — the address of the sender who initiated the transaction. * `callType` — the type of call, `call` or `delegatecall`, two ways to invoke a function in a smart contract. `call` creates a new environment for the function to work in, so changes made in that function won't affect the environment where the function was called. `delegatecall` doesn't create a new environment. Instead, it runs the function within the environment of the caller, so changes made in that function will affect the caller's environment. * `gas` — the units of gas included in the transaction by the sender. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is null. * `value` — the value of the native token transferred along with the transaction, in Wei. * `blockHash` — the hash of the block in which the transaction was included. * `blockNumber` — the number of the block in which the transaction was included. * `error` — a string that indicates whether the transaction was successful or not. `null` if successful, `Reverted` if not. * `result` — an object that contains additional data about the execution of the transaction: * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `output` — the return value of the call, encoded as a hexadecimal string. * `subtraces` — the number of sub-traces created during execution. When a transaction is executed on the EVM, it may trigger additional sub-executions, such as when a smart contract calls another smart contract or when an external account is accessed. * `traceAddress` — an array that indicates the position of the transaction in the trace. * `transactionHash` — the hash of the transaction. * `transactionPosition` — the position of the transaction in the block. * `type` — the type of action taken by the transaction, `call` or `create`. `call `is the most common type of trace and occurs when a smart contract invokes another contract's function. `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain. ## `trace_block` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceBlock = async (block) => { const traces = await provider.send("trace_block", [block]); console.log(traces); }; traceBlock("latest") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) block = "latest" traces = web3.provider.make_request('trace_block', [block]) print(traces) ``` ## Use case A practical use case for the `trace_block` method on Fantom can be to analyze the most recent block on the network. For example, developers can gain valuable insights into the block's behavior by examining the amount of gas supplied by all the transactions and the actual amount of gas utilized. This can be the first step to developing a chain explorer. ## Try the `trace_block` RPC method yourself # trace_transaction | BNB Chain Source: https://docs.chainstack.com/reference/trace_transaction openapi/bnb_node_api/trace_transaction.json POST /35848e183f3e3303c8cfeacbea831cab BNB API method that traces a specific transaction. trace_transaction JSON-RPC method available on the BNB Chain blockchain via Chainstack. BNB API method that traces a specific transaction. It provides a detailed record of all the steps the Ethereum Virtual Machine (EVM) took during the execution, including all the operations performed and the changes made to the blockchain state. This method is available on Erigon only. ## Parameters * `hash` — the hash identifying a transaction ## Response * `action` — an object that describes the action taken by the transaction: * `from` — the address of the sender who initiated the transaction. * `callType` — the type of call, `call` or `delegatecall`, two ways to invoke a function in a smart contract. `call` creates a new environment for the function to work in, so changes made in that function won't affect the environment where the function was called. `delegatecall` doesn't create a new environment. Instead, it runs the function within the environment of the caller, so changes made in that function will affect the caller's environment. * `gas` — the units of gas included in the transaction by the sender. * `input` — the optional input data sent with the transaction, usually used to interact with smart contracts. * `to` — the address of the recipient of the transaction if it was a transaction to an address. For contract creation transactions, this field is null. * `value` — the value of the native token transferred along with the transaction, in Wei. * `blockHash` — the hash of the block in which the transaction was included. * `blockNumber` — the number of the block in which the transaction was included. * `error` — a string that indicates whether the transaction was successful or not. `null` if successful, `Reverted` if not. * `result` — an object that contains additional data about the execution of the transaction: * `gasUsed` — the total used gas by the call, encoded as hexadecimal. * `output` — the return value of the call, encoded as a hexadecimal string. * `subtraces` — the number of sub-traces created during execution. When a transaction is executed on the EVM, it may trigger additional sub-executions, such as when a smart contract calls another smart contract or when an external account is accessed. * `traceAddress` — an array that indicates the position of the transaction in the trace. * `transactionHash` — the hash of the transaction. * `transactionPosition` — the position of the transaction in the block. * `type` — the type of action taken by the transaction, `call` or `create`. `call` is the most common type of trace and occurs when a smart contract invokes another contract's function. `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain. ## `trace_transaction` code examples ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); const traceTransaction = async (txHash) => { const traces = await provider.send("trace_transaction", [txHash]); console.log(traces); }; traceTransaction("0x0e77e2cf5a6999f616b56f1378b6c7b127b5a4e20cb2e12611d808af0979dacf") ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3.HTTPProvider(node_url) tx_hash = "0x0e77e2cf5a6999f616b56f1378b6c7b127b5a4e20cb2e12611d808af0979dacf" traces = web3.provider.make_request('trace_transaction', [tx_hash]) print(traces) ``` ## Use case A practical use case of the `trace_transaction` method can inspect NFT transfers, allowing you to identify internal transactions, the sender and receiver, and the amounts transferred. This can be especially useful when analyzing an ERC-721 token purchase on a marketplace like Opensea, where funds transfer is not always straightforward due to multiple internal calls for transferring funds to the NFT owner, paying royalties to the creator, and other factors. By using `trace_transaction`, you can gain a more comprehensive understanding of the transaction flow. # debug_traceBlockByHash | Optimism Source: https://docs.chainstack.com/reference/traceblockbyhash openapi/optimism_node_api/debug_traceBlockByHash.json POST /efb0a5eccd2caa5135eb54eba6f7f300 The debug_traceBlockByHash RPC method is used to retrieve detailed execution traces for all transactions. debug_traceBlockByHash on Optimism via Chainstack. The `debug_traceBlockByHash` RPC method is used to retrieve detailed execution traces for all transactions included in a block, identified by the block's hash. This method is particularly useful for developers and analysts who need to deeply understand transaction execution and state changes within a specific block. ## Request To use `debug_traceBlockByHash`, send a POST request with a JSON RPC call in the body. ## Parameters * **Block Hash** (`string`): The hash of the block for which to retrieve the execution traces. ## Response The response includes detailed execution traces for all transactions in the specified block. * **result** (`array`): An array of execution traces for each transaction in the block. Each trace provides comprehensive details about the execution, including calls, state changes, and gas usage. ## Use case The `debug_traceBlockByHash` method is essential for: * Developers debugging complex interactions within a block. * Analysts conducting forensic analysis of block transactions. * Tools and services that provide insights into the Ethereum Virtual Machine (EVM) execution and state changes. # wallet/accountpermissionupdate | TRON Source: https://docs.chainstack.com/reference/tron-accountpermissionupdate openapi/tron_node_api/accountpermissionupdate.json post /95e61622bf6a8af293978377718e3b77/wallet/accountpermissionupdate TRON API method that updates the permissions of an account on the TRON blockchain. wallet/accountpermissionupdate on TRON via Chainstack. TRON API method that updates the permissions of an account on the TRON blockchain. This method allows modification of owner, witness, and active permissions for multi-signature account management. ## Parameters * `owner_address` — the account address whose permissions are being updated * `owner` — optional object containing owner permission settings with threshold and keys * `witness` — optional object containing witness permission settings with threshold and keys * `actives` — optional array of active permission objects with threshold, operations, and keys * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `visible` — indicates the address format used in the response * `txID` — the transaction hash * `raw_data` — raw transaction data including: * `contract` — array containing the permission update contract * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — hexadecimal representation of the raw transaction ## Use case The `wallet/accountpermissionupdate` method is used for: * Setting up multi-signature accounts with custom permission structures * Modifying existing account permissions and thresholds * Managing witness permissions for Super Representative accounts * Implementing advanced security models for institutional accounts Note: The returned transaction must be signed and broadcast to complete the permission update. ## curl example Provide concrete keys and a valid operations bitmask. The example below builds an unsigned permission update transaction that the node can parse. Replace the sample addresses with your own and sign the returned transaction before broadcasting. ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/accountpermissionupdate' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g", "owner": { "type": 0, "permission_name": "owner", "threshold": 1, "keys": [ { "address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g", "weight": 1 } ] }, "actives": [ { "type": 2, "permission_name": "active", "threshold": 1, "operations": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "keys": [ { "address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g", "weight": 1 } ] } ], "visible": true }' ``` > [!NOTE] > > * `operations` must be a 32‑byte hex string (64 hex chars) representing the allowed operations bitmask. > * Each `keys` entry requires an `address` and `weight`. The sum of weights for a permission must be greater than or equal to its `threshold`. > * Use base58 addresses with `visible: true`, or hex addresses with `visible: false`. # TRON API reference: HTTP and JSON-RPC quickstart Source: https://docs.chainstack.com/reference/tron-api-reference Use the TRON API on Chainstack to query the DPoS blockchain, send transactions, work with TRC-20 tokens, and build DApps with low-fee throughput. 168 methods across HTTP wallet, walletsolidity, Ethereum-compatible JSON-RPC, and gRPC, at 1 RU per request. The TRON API gives developers programmatic access to the TRON blockchain across four surfaces on one node endpoint: the HTTP `/wallet` and `/walletsolidity` APIs, an Ethereum-compatible JSON-RPC interface, and gRPC. Chainstack serves all four through a single TRON endpoint at a flat 1 RU per request. ## TRON API at a glance | Property | Value | | - | - | | Chain | TRON — Layer 1, Delegated Proof of Stake (DPoS) consensus | | Resource model | Energy and bandwidth, obtained by staking TRX (no per-call gas in TRX for resourced accounts) | | API surfaces | HTTP `/wallet`, HTTP `/walletsolidity`, Ethereum-compatible JSON-RPC, and gRPC | | JSON-RPC chain ID | `0x2b6653dc` (728126428) mainnet, `0xcd8690dc` (3448148188) Nile testnet | | Methods served by Chainstack | 168 — 101 `/wallet`, 34 `/walletsolidity`, 33 JSON-RPC | | Token standards | TRC-20, TRC-10, and shielded TRC-20 | | Networks | Mainnet and Nile testnet | | Real-time data | Polling — TRON nodes do not serve WebSocket event subscriptions | | Request units | 1 RU per request, no archive split | ## What is the TRON protocol TRON is a decentralized Layer 1 blockchain platform designed to build a free, global digital content entertainment system with distributed storage technology. TRON enables cost-effective sharing of digital content and supports smart contracts, various kinds of blockchain systems, and decentralized applications (DApps). TRON uses a Delegated Proof of Stake (DPoS) consensus mechanism for high throughput and scalability. Token holders stake TRX to vote for super representatives, who produce blocks on the network. Instead of charging gas in TRX on every call, TRON meters execution through two stakeable resources: * Bandwidth — consumed by transaction byte size, and used for transfers and most non-contract operations. * Energy — consumed by smart contract execution. You obtain energy by staking TRX, which makes contract calls effectively free once an account is resourced. This resource model is what lets TRON sustain low-cost, high-frequency transactions. For a hands-on walkthrough, see [managing energy and bandwidth with Python](/docs/tron-mastering-energy-bandwidth-with-python-and-chainstack). ## What is the TRON API The TRON API lets developers interact with the TRON blockchain to build applications. It provides functionality for managing accounts, transactions, smart contracts, and blockchain data. To read data and submit transactions, an application connects to a TRON node endpoint, then calls the surface it needs: * The HTTP `/wallet` API exposes the full node — accounts, blocks, contracts, resource staking, and transaction creation and broadcasting. * The HTTP `/walletsolidity` API serves solidified (confirmed) data, read-only. * The JSON-RPC interface offers Ethereum-compatible read methods, so Ethereum-native tooling can query a TRON node. * The gRPC interface uses HTTP/2 and Protocol Buffers for high-performance, high-throughput access and indexing. ## API surfaces Chainstack exposes all four surfaces over a single node endpoint. Append the path you need to your endpoint URL, except for gRPC, which connects to a dedicated host on port `443`. | API surface | Endpoint path | Use it for | Served by | | - | - | - | - | | HTTP wallet | `/wallet` | Full node — accounts, blocks, contracts, transactions | Chainstack | | HTTP walletsolidity | `/walletsolidity` | Confirmed (solidified) read-only data | Chainstack | | Ethereum JSON-RPC | `/jsonrpc` | Ethereum-compatible read operations | Chainstack | | gRPC | host on port `443` | High-performance binary access and indexing | Chainstack | > [!NOTE] > > TRON's JSON-RPC is read-only by design. The methods required for transaction submission, such as `eth_sendRawTransaction` and `eth_getTransactionCount`, are not available — use the native `/wallet` API or [TronWeb.js](/docs/tron-tooling#tronwebjs) for writes. See [TRON tooling](/docs/tron-tooling#tooling-compatibility) for the full compatibility notes. ### What you can build With a Chainstack TRON endpoint you can: * Create and manage accounts, set multi-signature permissions, and validate addresses through the `/wallet` API. * Build, sign, and broadcast transactions, and track them with `gettransactioninfobyid`, `gettransactionbyid`, and the JSON-RPC `eth_getTransactionReceipt`. * Transfer and interact with TRC-20 and TRC-10 tokens, and run privacy-preserving shielded TRC-20 operations. * Deploy and call smart contracts with `deploycontract`, `triggersmartcontract`, and `triggerconstantcontract`. * Manage energy and bandwidth — freeze and stake TRX, delegate resources, and query account resources with `freezebalancev2`, `delegateresource`, and `getaccountresource`. * Participate in governance — vote for witnesses, and create, approve, and query proposals. * Read chain state through Ethereum-compatible JSON-RPC, compatible with web3.py, Foundry, and Hardhat for read operations. * Poll for token transfers and new blocks — see [polling for TRC-20 transfers in Node.js](/docs/tron-polling-for-trc20-transfers). ## Methods Chainstack serves 168 TRON methods across the four surfaces: | API surface | Endpoint | Methods | Availability | | - | - | - | - | | HTTP wallet | `/wallet` | 101 | All served by Chainstack | | HTTP walletsolidity | `/walletsolidity` | 34 | All served by Chainstack | | Ethereum JSON-RPC | `/jsonrpc` | 33 | All served by Chainstack | The 33 JSON-RPC methods cover 27 `eth_*` read methods, plus `net_*`, `web3_*`, and `buildTransaction`. gRPC exposes the `protocol.Wallet`, `protocol.Database`, and `protocol.WalletSolidity` services on the same endpoint. For the complete, per-method list, see [TRON methods](/docs/tron-methods). > [!NOTE] > > TRON pricing is simple: 1 RU per request, with no archive split. TRON nodes run in archive mode — which for TRON means the complete block and transaction history from genesis, not historical state — and every request is billed as full. See [Request units](/docs/request-units#full-vs-archive-by-protocol). ## How to start using the TRON API To use the TRON API, you need access to a TRON node endpoint. Follow these steps to sign up, deploy a node, and find your credentials: **Step 1** [Sign up with Chainstack](https://console.chainstack.com/user/account/create). **Step 2** [Deploy a node](/docs/manage-your-networks). **Step 3** [View node access and credentials](/docs/manage-your-node#view-node-access-and-credentials). Now you are ready to connect to the TRON blockchain and build. ## SDKs and tooling You can call the API directly over HTTP and gRPC, or use a maintained library: * TronWeb.js — the official [TronWeb](https://tronweb.network/) JavaScript library. It supports all TRON operations, including contract deployment and transactions, through the `/wallet` API. * web3.py and Ethereum tooling — [web3.py](https://github.com/ethereum/web3.py), Foundry, and Hardhat can run read operations against the `/jsonrpc` endpoint, but cannot deploy or send transactions directly. Compile and test with Foundry or Hardhat, then deploy with TronWeb.js in a hybrid workflow. * gRPC clients — generate typed clients from the [TRON protocol buffer definitions](https://github.com/tronprotocol/protocol) for Python, Node.js, or Go. Point any tool at your Chainstack endpoint. See [TRON tooling](/docs/tron-tooling) for setup, the [gRPC API section](/docs/tron-tooling#grpc-api) for generating clients, and the [hybrid workflow](/docs/tron-tooling#hybrid-workflow) for deploying compiled contracts. ## Networks Chainstack supports TRON mainnet and Nile testnet as Global Nodes. TRON nodes run in archive mode, which for TRON means the complete block and transaction history from genesis rather than historical state — there is no separate full versus archive split. | Feature | Mainnet | Nile testnet | | - | - | - | | JSON-RPC chain ID | `0x2b6653dc` (728126428) | `0xcd8690dc` (3448148188) | | Block and transaction history | From genesis | From genesis | | gRPC endpoint | `tron-mainnet.core.chainstack.com:443` | `tron-nile.core.chainstack.com:443` | | Faucet | N/A | [Nile faucet](https://nileex.io/join/getJoinPage) | > [!NOTE] > > TRON archive means full block and transaction history, not historical state. java-tron has no state-at-block queries — `eth_getBalance`, `eth_call`, `eth_getCode`, and `eth_getStorageAt` accept only the `latest` block. This is a protocol limitation ([java-tron#6289](https://github.com/tronprotocol/java-tron/issues/6289)), not a Chainstack one. See [Historical data availability](/docs/tron-tooling#historical-data-availability). ## Frequently asked questions ### What API surfaces does TRON expose on Chainstack? Four, all on one node endpoint: the HTTP `/wallet` API (full node), the HTTP `/walletsolidity` API (confirmed read-only data), an Ethereum-compatible JSON-RPC interface at `/jsonrpc`, and gRPC on port `443`. Chainstack serves 168 methods across the three HTTP and JSON-RPC surfaces. ### Is TRON's JSON-RPC fully Ethereum-compatible? No. TRON's JSON-RPC provides Ethereum-compatible methods for read operations only. The methods required to submit transactions, such as `eth_sendRawTransaction` and `eth_getTransactionCount`, are not available. For writes, use the native `/wallet` API or TronWeb.js. The JSON-RPC chain ID is `0x2b6653dc` (728126428) on mainnet. ### What is the difference between energy and bandwidth? Both are stakeable resources that meter execution instead of charging gas in TRX on every call. Bandwidth is consumed by transaction byte size and covers transfers and most non-contract operations. Energy is consumed by smart contract execution. You obtain both by staking TRX with `freezebalancev2`, which makes operations effectively free once an account is resourced. See [managing energy and bandwidth](/docs/tron-mastering-energy-bandwidth-with-python-and-chainstack). ### How do I work with TRC-20 tokens? Use the `/wallet` API and TronWeb.js to transfer and call TRC-20 contracts with `triggersmartcontract` and `triggerconstantcontract`. TRON also supports the TRC-10 native token standard and privacy-preserving shielded TRC-20 operations. To monitor transfers, poll the chain — see [polling for TRC-20 transfers in Node.js](/docs/tron-polling-for-trc20-transfers). ### Does TRON support WebSocket subscriptions? No. TRON nodes do not serve WebSocket connections for event subscriptions, so track on-chain activity by polling block and transaction methods. To follow this capability, see the feature request for [TRON event plugin support](https://ideas.chainstack.com/p/implement-tron-event-plugin). ### Why is there no archive versus full pricing for TRON? TRON pricing is a flat 1 RU per request, with no archive split. TRON nodes run in archive mode, but for TRON that means the complete block and transaction history from genesis rather than historical state — java-tron has no state-at-block queries. Billing is independent of node mode, so every request is billed as full. See [Request units](/docs/request-units#full-vs-archive-by-protocol). ### When should I use gRPC instead of HTTP? Use gRPC for high-throughput applications and data indexing, where its HTTP/2 transport and binary Protocol Buffers serialization are more efficient than HTTP. Chainstack serves the `protocol.Wallet`, `protocol.Database`, and `protocol.WalletSolidity` services on the same `:443` endpoint. See the [gRPC API section](/docs/tron-tooling#grpc-api). ## Related TRON resources * [TRON methods](/docs/tron-methods) — complete per-surface method reference * [TRON tooling](/docs/tron-tooling) — TronWeb.js, gRPC, web3.py, Foundry, and Hardhat setup * [Managing energy and bandwidth with Python](/docs/tron-mastering-energy-bandwidth-with-python-and-chainstack) — TRON's resource and fee model * [Polling for TRC-20 transfers in Node.js](/docs/tron-polling-for-trc20-transfers) — monitor token transfers without WebSocket * [Request units](/docs/request-units#full-vs-archive-by-protocol) — how TRON requests are billed > [!NOTE] > > **See also** > > [Build better with TRON](https://chainstack.com/build-better-with-tron/) # wallet/broadcasthex | TRON Source: https://docs.chainstack.com/reference/tron-broadcasthex openapi/tron_node_api/broadcasthex.json post /95e61622bf6a8af293978377718e3b77/wallet/broadcasthex TRON API method that broadcasts a transaction to the TRON network using hexadecimal format. wallet/broadcasthex on TRON via Chainstack. TRON API method that broadcasts a transaction to the TRON network using hexadecimal format. This method allows you to submit pre-signed transactions directly to the blockchain without needing to use the standard JSON transaction format. ## Parameters * `transaction` — the complete transaction data encoded in hexadecimal format, including all signatures and necessary fields ## Response * `result` — boolean indicating whether the broadcast was successful * `txid` — the transaction hash if the broadcast was successful * `message` — descriptive message about the broadcast status ## Use case The `wallet/broadcasthex` method is used for: * Broadcasting pre-signed transactions in hex format to the TRON network. * Submitting transactions that were created and signed offline for security purposes. * Integrating with external signing tools and hardware wallets that output hex-encoded transactions. * Building custom transaction broadcasting workflows that require hex format compatibility. * Supporting legacy systems or tools that work with hexadecimal transaction data. ## curl example The example below uses a syntactically valid transaction hex, so the node parses it and returns a JSON result. Because the signature is a placeholder, you should expect a logical error (for example, `SIGERROR`) rather than a parsing exception. Replace the `transaction` value with the full hex of a transaction you have actually signed to get `result: true`. ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/broadcasthex' \ --header 'Content-Type: application/json' \ --data '{ "transaction": "0a84010a025e4b220847c9dc89341b300d40f8fed3a2a72e5a66080112620a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412310a1541608f8da72479edc7dd921e4c30bb7e7cddbe722e121541e9d79cc47518930bc322d9bf7cddd260a0260a8d18e8077093afd0a2a72e1241deadbeefcafebabefacec0011234abcd0badf00d9999777755553333222211114321dcba0f0ff0f013579bdf2468ace0987654321a2b3c4d5e6f7a8b9c0deeff1b" }' ``` > [!TIP] > > to build the `transaction` hex from a signed transaction object: > > * take the raw transaction bytes (`raw_data_hex`) > * prefix with `0a` then a protobuf varint of the raw bytes length > * append each signature as `12` + `41` + 65‑byte signature hex (repeat for multisig) > > Most users should prefer creating the transaction with [`wallet/createtransaction`](/reference/tron-createtransaction), signing the exact `raw_data`, and then broadcasting via this method. # wallet/broadcasttransaction | TRON Source: https://docs.chainstack.com/reference/tron-broadcasttransaction openapi/tron_node_api/broadcasttransaction.json post /95e61622bf6a8af293978377718e3b77/wallet/broadcasttransaction TRON API method that broadcasts a signed transaction to the TRON network. Reference for wallet/broadcasttransaction on TRON via Chainstack. TRON API method that broadcasts a signed transaction to the TRON network. This method submits a fully constructed and signed transaction for processing and inclusion in the blockchain. ## Parameters The transaction object should contain: * `raw_data` — the raw transaction data object containing: * `contract` — array of contract objects with transaction details * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration time in milliseconds * `timestamp` — transaction creation timestamp * `raw_data_hex` — hexadecimal representation of the raw transaction data * `signature` — array of transaction signatures (required for signed transactions) * `visible` — optional boolean for address format ## Response * `result` — boolean indicating broadcast success * `code` — error code if broadcast failed * `message` — hexadecimal error message if broadcast failed * `txid` — transaction ID if broadcast succeeded ## Use case The `wallet/broadcasttransaction` method is used for: * Submitting signed transactions to the TRON network for processing. * Broadcasting transfers, smart contract interactions, and other operations. * Finalizing transactions after they have been created and signed offline. * Implementing transaction submission in wallets and DApps. ## curl example Use a fully signed transaction object. The example below is syntactically valid and will return a JSON result from the node. If the signature does not match the `raw_data`, the node responds with `SIGERROR`. Replace the `signature` value with a real signature produced from the exact `raw_data` to get `result: true`. ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/broadcasttransaction' \ --header 'Content-Type: application/json' \ --data '{ "raw_data": { "contract": [ { "parameter": { "value": { "amount": 1000, "owner_address": "41608f8da72479edc7dd921e4c30bb7e7cddbe722e", "to_address": "41e9d79cc47518930bc322d9bf7cddd260a0260a8d" }, "type_url": "type.googleapis.com/protocol.TransferContract" }, "type": "TransferContract" } ], "ref_block_bytes": "5e4b", "ref_block_hash": "47c9dc89341b300d", "expiration": 1591089627000, "timestamp": 1591089567635 }, "raw_data_hex": "0a025e4b220847c9dc89341b300d40f8fed3a2a72e5a66080112620a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412310a1541608f8da72479edc7dd921e4c30bb7e7cddbe722e121541e9d79cc47518930bc322d9bf7cddd260a0260a8d18e8077093afd0a2a72e", "signature": [ "deadbeefcafebabefacec0011234abcd0badf00d9999777755553333222211114321dcba0f0ff0f013579bdf2468ace0987654321a2b3c4d5e6f7a8b9c0deeff1b" ], "visible": false }' ``` Example response with an invalid signature (connectivity and payload are correct, signature is not): ```json theme={"system"} { "result": false, "code": "SIGERROR", "message": "..." } ``` > [!TIP] > > to get a successful `result: true`, create the transaction with [`wallet/createtransaction`](/reference/tron-createtransaction), sign the exact `raw_data` with your private key, then broadcast the signed transaction here. Keep your private key secure and never paste it in client-side code. # wallet/cancelallunfreezev2 | TRON Source: https://docs.chainstack.com/reference/tron-cancelallunfreezev2 openapi/tron_node_api/cancelallunfreezev2.json post /95e61622bf6a8af293978377718e3b77/wallet/cancelallunfreezev2 TRON API method that cancels all unfreezing operations for an account under the Stake 2.0 mechanism. Use it on TRON via Chainstack. TRON API method that cancels all unfreezing operations for an account under the Stake 2.0 mechanism. This allows users to cancel pending unfreeze requests and return resources to the frozen state. ## Parameters * `owner_address` — the account address that wants to cancel all unfreezing operations * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `visible` — indicates the address format used in the response * `txID` — the transaction hash * `raw_data` — raw transaction data including: * `contract` — array containing the cancel unfreeze contract * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — hexadecimal representation of the raw transaction ## Use case The `wallet/cancelallunfreezev2` method is used for: * Canceling all pending unfreeze requests to maintain staked resources * Responding to market conditions by keeping resources frozen * Managing resource allocation in DeFi protocols * Implementing dynamic staking strategies * Preventing accidental resource unfreezing Note: The returned transaction must be signed and broadcast to complete the cancellation. # wallet/clearabi | TRON Source: https://docs.chainstack.com/reference/tron-clearabi openapi/tron_node_api/clearabi.json post /95e61622bf6a8af293978377718e3b77/wallet/clearabi TRON API method that clears (removes) the ABI (Application Binary Interface) information from a smart contract. TRON via Chainstack. TRON API method that clears (removes) the ABI (Application Binary Interface) information from a smart contract. This operation removes the contract's ABI data from the blockchain, making the contract functions less discoverable but still executable if the function signatures are known. ## Parameters * `owner_address` — address of the contract owner who can clear the ABI * `contract_address` — address of the smart contract to clear ABI for * `visible` — boolean indicating whether to use visible (Base58) address format instead of hex ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the ABI clearing transaction * `raw_data` — raw transaction data containing: * `contract` — array with contract ABI clearing details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/clearabi` method is used for: * Removing contract ABI information for privacy or security reasons. * Cleaning up contract metadata when ABI information is no longer needed. * Implementing obfuscation strategies for proprietary smart contracts. * Managing contract visibility and discoverability in explorers. * Reducing on-chain storage by removing unnecessary ABI data. * Preparing contracts for migration or deprecation processes. > [!WARNING] > > Clearing the ABI makes the contract less user-friendly as tools and interfaces will not be able to automatically understand the contract's functions. However, the contract remains fully functional and can still be called using raw function signatures and data. ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/clearabi' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC", "contract_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "visible": true }' ``` # wallet/createaccount | TRON Source: https://docs.chainstack.com/reference/tron-createaccount openapi/tron_node_api/createaccount.json post /95e61622bf6a8af293978377718e3b77/wallet/createaccount TRON API method that creates a new account on the TRON blockchain. Chainstack supports wallet/createaccount on TRON JSON-RPC nodes. TRON API method that creates a new account on the TRON blockchain. This method generates an unsigned transaction to activate a new account address by sending it TRX for the first time. ## Parameters * `owner_address` — the address that will create and pay for the new account. Must have sufficient TRX balance to cover the account creation fee. * `account_address` — the new account address to be created and activated on the blockchain. * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `visible` — indicates the address format used in the response * `txID` — the transaction hash * `raw_data` — raw transaction data including: * `contract` — array containing the account creation contract * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — hexadecimal representation of the raw transaction ## Use case The `wallet/createaccount` method is used for: * Activating new TRON addresses on the blockchain. * Creating accounts for new users in wallet applications. * Setting up multi-signature or contract accounts. * Onboarding new users to the TRON ecosystem. Note: The returned transaction must be signed and broadcast to complete the account creation. ## curl example The node validates that the `owner_address` can cover the protocol’s account creation fee. If the owner is not funded, you will get a validation error instead of an unsigned transaction. ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/createaccount' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g", "account_address": "TFgY1uN8buRxAtV2r6Zy5sG3ACko6pJT1y", "visible": true }' ``` Example error when the owner address lacks enough TRX to cover the account creation fee: ```json theme={"system"} { "Error": "class org.tron.core.exception.ContractValidateException : Validate CreateAccountActuator error, insufficient fee." } ``` When the owner is funded, the response is an unsigned transaction that you must sign and then broadcast using [`wallet/broadcasttransaction`](/reference/tron-broadcasttransaction) or [`wallet/broadcasthex`](/reference/tron-broadcasthex). > [!TIP] > > you can also create an account implicitly by sending TRX to a new address; on first receipt the account is created and the sender pays the creation fee. Addresses can be provided in base58 with `visible: true` or in hex with `visible: false`. ## end-to-end steps **1. check the payer balance** Use `wallet/getaccount` to confirm the `owner_address` has enough TRX to cover the account creation fee. ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getaccount' \ --header 'Content-Type: application/json' \ --data '{ "address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g", "visible": true }' ``` **2. option: use nile testnet** For testing, you can use a Nile endpoint and get test TRX from the faucet. Replace with your own Nile endpoint from the console. ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-nile.core.chainstack.com/11112222333444555666677778888/wallet/createaccount' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "TXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "account_address": "TYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY", "visible": true }' ``` Fund `owner_address` with test TRX from the Nile faucet, then run the call again. **3. sign and broadcast** On success, you receive an unsigned transaction. Sign the `raw_data` with your private key and broadcast via [`wallet/broadcasttransaction`](/reference/tron-broadcasttransaction) or provide the built hex to [`wallet/broadcasthex`](/reference/tron-broadcasthex). > [!WARNING] > > getting `insufficient fee` is expected when the payer has no TRX. Fund the `owner_address` first or create the account implicitly by sending TRX to the new address. # wallet/createassetissue | TRON Source: https://docs.chainstack.com/reference/tron-createassetissue openapi/tron_node_api/createassetissue.json post /95e61622bf6a8af293978377718e3b77/wallet/createassetissue TRON API method that creates an unsigned transaction for issuing a new TRC10 token on the TRON network. Chainstack TRON reference. TRON API method that creates an unsigned transaction for issuing a new TRC10 token on the TRON network. TRC10 tokens are native TRON assets that can be created without deploying smart contracts. ## Parameters * `owner_address` — the address that will issue and own the token * `name` — the full name of the token (string) * `abbr` — the token symbol or abbreviation (string) * `total_supply` — the total number of tokens to be issued (integer) * `trx_num` — the TRX amount used in the exchange rate calculation (integer) * `num` — the token amount used in the exchange rate calculation (integer) * `precision` — the number of decimal places supported by the token (integer, 0-6) * `start_time` — ICO start timestamp in milliseconds (integer) * `end_time` — ICO end timestamp in milliseconds (integer) * `description` — detailed description of the token project (string) * `url` — official website URL for the token project (string) * `free_asset_net_limit` — bandwidth provided by token issuer for token operations (integer, optional) * `public_free_asset_net_limit` — public bandwidth limit available for this token (integer, optional) * `frozen_supply` — array of frozen supply configurations (optional) * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `visible` — indicates the address format used in the response * `txID` — the transaction hash * `raw_data` — raw transaction data including: * `contract` — array containing the asset issue contract * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `fee_limit` — maximum fee allowed for this transaction * `raw_data_hex` — hexadecimal representation of the raw transaction ## Use case The `wallet/createassetissue` method is used for: * Creating new TRC10 tokens for projects and organizations * Launching Initial Coin Offerings (ICOs) and token sales * Building token issuance functionality in wallet applications * Developing automated token creation systems for DeFi platforms # wallet/createtransaction | TRON Source: https://docs.chainstack.com/reference/tron-createtransaction openapi/tron_node_api/createtransaction.json post /95e61622bf6a8af293978377718e3b77/wallet/createtransaction TRON API method that creates an unsigned TRX transfer transaction. This method generates a transaction object that transfers TRX from one address to another. TRON API method that creates an unsigned TRX transfer transaction. This method generates a transaction object that transfers TRX from one address to another, which can then be signed and broadcast to the network. ## Parameters * `to_address` — the recipient's TRON address (hex format) * `owner_address` — the sender's TRON address (hex format) * `amount` — the amount of TRX to transfer (in sun, where 1 TRX = 1,000,000 sun) * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `visible` — boolean indicating address format used * `txID` — transaction ID hash * `raw_data` — raw transaction data object containing: * `contract` — array with transfer contract details * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/createtransaction` method is used for: * Creating unsigned TRX transfer transactions that need to be signed separately. * Building transactions for offline signing in secure environments. * Implementing wallet functionality that separates transaction creation from signing. * Preparing transactions for multi-signature workflows. # wallet/createwitness | TRON Source: https://docs.chainstack.com/reference/tron-createwitness openapi/tron_node_api/createwitness.json post /95e61622bf6a8af293978377718e3b77/wallet/createwitness TRON API method that creates a transaction to register an account as a witness (validator) on the TRON network. TRON via Chainstack. TRON API method that creates a transaction to register an account as a witness (validator) on the TRON network. Witnesses participate in block production and network governance through the Delegated Proof of Stake (DPoS) consensus mechanism. ## Parameters * `owner_address` — address of the account that will become a witness. Use base58 with `visible: true`, or hex with `visible: false`. * `url` — witness website URL providing information about the witness (must be a valid URL) * `visible` — optional boolean. When `true`, addresses are base58; when `false`, hex. Default is `true`. ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the witness creation transaction * `raw_data` — raw transaction data containing: * `contract` — array with witness creation contract details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/createwitness` method is used for: * Registering an account as a witness to participate in TRON network governance. * Setting up block production capabilities for earning rewards from the network. * Establishing a public presence for community voting and trust building. * Preparing to compete for one of the 27 Super Representative positions. * Contributing to network security and decentralization through validation services. * Building reputation and credibility within the TRON ecosystem. > [!NOTE] > > This method only creates the witness registration transaction. You must sign the transaction and use `wallet/broadcasttransaction` to complete the witness registration. Becoming an active block producer requires receiving votes from TRX holders. ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/createwitness' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC", "url": "https://mywitness.example.com", "visible": true }' ``` > [!NOTE] > > seeing `INVALID hex String`? Provide a base58 address with `visible: true`, or a 21‑byte hex address (starts with `41…`) with `visible: false`. # wallet/delegateresource | TRON Source: https://docs.chainstack.com/reference/tron-delegateresource openapi/tron_node_api/delegateresource.json post /95e61622bf6a8af293978377718e3b77/wallet/delegateresource TRON API method that delegates resources to another account. wallet/delegateresource JSON-RPC method available on the TRON blockchain via Chainstack. TRON API method that delegates resources to another account. This method allows you to delegate bandwidth or energy resources obtained from staking TRX to another address, enabling that address to use your resources for transactions. ## Parameters * `owner_address` — the address that owns the resources to delegate (hex format) * `receiver_address` — the address that will receive the delegated resources (hex format) * `balance` — the amount of TRX equivalent resources to delegate (in sun) * `resource` — the resource type to delegate ("BANDWIDTH" or "ENERGY") * `lock` — optional boolean to lock the delegation (default: false) * `lock_period` — optional lock period in seconds (if lock is true) * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `visible` — boolean indicating address format used * `txID` — transaction ID hash * `raw_data` — raw transaction data object containing delegation contract details * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/delegateresource` method is used for: * Sharing bandwidth or energy resources with other accounts that need them. * Implementing resource rental or delegation services in DApps. * Allowing users to sponsor transaction costs for other accounts. * Building resource management systems that optimize usage across multiple addresses. # wallet/deploycontract | TRON Source: https://docs.chainstack.com/reference/tron-deploycontract openapi/tron_node_api/deploycontract.json post /95e61622bf6a8af293978377718e3b77/wallet/deploycontract TRON API method that creates a transaction to deploy a new smart contract to the TRON network. wallet/deploycontract on TRON via Chainstack. TRON API method that creates a transaction to deploy a new smart contract to the TRON network. This method prepares the deployment transaction with all necessary parameters, which must then be signed and broadcast to complete the contract deployment. ## Parameters * `owner_address` — address of the account deploying the contract in hexadecimal format * `abi` — contract ABI (Application Binary Interface) as a JSON string defining contract methods and events * `bytecode` — compiled contract bytecode in hexadecimal format * `parameter` — (optional) constructor parameters encoded in hexadecimal format * `call_value` — (optional) amount of TRX to transfer to the contract during deployment (in SUN, 1 TRX = 1,000,000 SUN) * `consume_user_resource_percent` — (optional) percentage of caller's resources used for contract calls (0-100, default 100) * `fee_limit` — (optional) maximum energy fee willing to pay for deployment (in SUN) * `origin_energy_limit` — (optional) energy limit provided by the contract creator for future calls * `name` — (optional) human-readable name for the contract ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the deployment transaction * `raw_data` — raw transaction data containing all deployment parameters * `raw_data_hex` — raw transaction data encoded in hexadecimal format ## Use case The `wallet/deploycontract` method is used for: * Deploying new smart contracts to the TRON blockchain. * Creating TRC20 tokens, NFT contracts, and other decentralized applications. * Setting up contract resource limits and fee structures during deployment. * Preparing deployment transactions that can be signed offline for security. * Building deployment tools and contract management platforms. * Establishing smart contract infrastructure for DApps and services. > [!NOTE] > > This method only creates the deployment transaction. You must sign the transaction and use `wallet/broadcasttransaction` to actually deploy the contract to the network. # wallet/estimateenergy | TRON Source: https://docs.chainstack.com/reference/tron-estimateenergy openapi/tron_node_api/estimateenergy.json post /95e61622bf6a8af293978377718e3b77/wallet/estimateenergy TRON API method that estimates the energy consumption for executing a smart contract function call. Use it on TRON via Chainstack. TRON API method that estimates the energy consumption for executing a smart contract function call. This method helps predict the energy costs before actually executing a transaction, enabling better cost planning and user experience optimization. ## Parameters * `owner_address` — address of the account that would execute the contract call * `contract_address` — address of the smart contract to estimate energy for * `function_selector` — function signature of the contract method to call (e.g., "balanceOf(address)") * `parameter` — hex-encoded parameters to pass to the function * `visible` — boolean indicating whether to use visible (Base58) address format instead of hex ## Response * `result` — estimation result object containing: * `result` — boolean indicating if estimation was successful * `energy_required` — estimated energy consumption for the function call * `energy_used` — total energy that would be consumed * `constant_result` — array of return values if the function is a view/pure function * `transaction` — transaction object with estimated costs if applicable ## Use case The `wallet/estimateenergy` method is used for: * Predicting transaction costs before executing smart contract calls. * Building user interfaces that show estimated fees upfront. * Optimizing contract interactions by choosing energy-efficient functions. * Implementing cost-aware transaction batching and prioritization. * Creating accurate fee estimation tools for wallets and dApps. * Analyzing contract performance and identifying gas-intensive operations. > [!TIP] > > Use this method before calling expensive contract functions to give users accurate cost estimates. This is especially useful for functions that might fail due to insufficient energy, allowing you to prevent failed transactions. ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/estimateenergy' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC", "contract_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "function_selector": "balanceOf(address)", "parameter": "000000000000000000000000a614f803b6fd780986a42c78ec9c7f77e6ded13c", "visible": true }' ``` # wallet/exchangecreate | TRON Source: https://docs.chainstack.com/reference/tron-exchangecreate openapi/tron_node_api/exchangecreate.json post /95e61622bf6a8af293978377718e3b77/wallet/exchangecreate TRON API method that creates a new trading pair on the TRON decentralized exchange with initial liquidity provision. Returns a transaction object containing:. TRON API method that creates a new trading pair on the TRON decentralized exchange with initial liquidity provision. ## Parameters * `owner_address` — the address that will own and control the exchange pair (base58 with `visible: true`, or hex with `visible: false`). * `first_token_id` — TRC10 token ID of the first token in the trading pair. * `first_token_balance` — initial amount for the first token as an integer (token base units). * `second_token_id` — TRC10 token ID of the second token; use `_` to denote TRX. * `second_token_balance` — initial amount for the second token as an integer (if TRX, specify in sun). * `permission_id` — optional permission ID for multi‑signature accounts. ## Response Returns a transaction object containing: * `txID` — unique transaction identifier * `raw_data` — raw transaction data including contract details * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/exchangecreate` method is used for: * Creating new trading pairs on the TRON decentralized exchange * Establishing initial liquidity pools for token pairs * Launching new markets for custom tokens * Setting up automated market maker (AMM) pools with initial pricing ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/exchangecreate' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH", "first_token_id": "1000001", "first_token_balance": 1000000000000, "second_token_id": "_", "second_token_balance": 5000000000, "visible": true }' ``` > [!NOTE] > > * balances must be integers (do not quote numbers). When the second token is TRX (denoted by `_`), provide `second_token_balance` in sun (1 TRX = 1,000,000 sun). > * use `_` for TRX or a TRC10 token ID string for both sides to create TRC10–TRC10 pairs. > * the call builds an unsigned transaction; you must sign and broadcast it. On mainnet, additional on‑chain validations apply (token existence, ownership, pair uniqueness, sufficient balances). # wallet/exchangeinject | TRON Source: https://docs.chainstack.com/reference/tron-exchangeinject openapi/tron_node_api/exchangeinject.json post /95e61622bf6a8af293978377718e3b77/wallet/exchangeinject TRON API method that adds liquidity to an existing exchange trading pair on the TRON decentralized exchange. Returns a transaction object containing:. TRON API method that adds liquidity to an existing exchange trading pair on the TRON decentralized exchange. ## Parameters * `owner_address` — the address providing liquidity (base58 with `visible: true`, or hex with `visible: false`). * `exchange_id` — unique identifier of the exchange pair to add liquidity to. * `token_id` — TRC10 token ID; use `_` to denote TRX. * `quant` — integer amount to add (token base units; if TRX, specify in sun). * `permission_id` — optional permission ID for multi‑signature accounts. ## Response Returns a transaction object containing: * `txID` — unique transaction identifier * `raw_data` — raw transaction data including contract details * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/exchangeinject` method is used for: * Adding liquidity to existing trading pairs to earn trading fees * Increasing the depth and stability of token markets * Participating in liquidity mining and yield farming programs * Providing capital to support decentralized exchange operations ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/exchangeinject' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH", "exchange_id": 1, "token_id": "1000001", "quant": 500000000000, "visible": true }' ``` > [!NOTE] > > * balances must be integers (do not quote numbers). When injecting TRX (using `token_id: "_"`), provide `quant` in sun (1 TRX = 1,000,000 sun). > * the call returns an unsigned transaction; sign and broadcast it to finalize. > * on mainnet, the exchange must exist, the token must match the pair, and the account must hold sufficient balance. # wallet/exchangetransaction | TRON Source: https://docs.chainstack.com/reference/tron-exchangetransaction openapi/tron_node_api/exchangetransaction.json post /95e61622bf6a8af293978377718e3b77/wallet/exchangetransaction TRON API method that executes a token swap transaction on the TRON decentralized exchange. Returns a transaction object containing:. TRON API method that executes a token swap transaction on the TRON decentralized exchange. ## Parameters * `owner_address` — the address performing the token swap (base58 with `visible: true`, or hex with `visible: false`). * `exchange_id` — unique identifier of the exchange pair to trade on. * `token_id` — token ID being sold; use `_` to denote TRX. * `quant` — integer amount being sold (token base units; if TRX, specify in sun). * `expected` — integer minimum to receive (token base units; if TRX, sun). Protects against slippage. * `permission_id` — optional permission ID for multi‑signature accounts. ## Response Returns a transaction object containing: * `txID` — unique transaction identifier * `raw_data` — raw transaction data including contract details * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/exchangetransaction` method is used for: * Swapping tokens directly on the TRON decentralized exchange * Converting between TRX and TRC20 tokens at current market rates * Building arbitrage bots and automated trading strategies * Implementing token exchange functionality in decentralized applications ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/exchangetransaction' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH", "exchange_id": 1, "token_id": "1000001", "quant": 100000000000, "expected": 95000000, "visible": true }' ``` > [!NOTE] > > * amounts must be integers (do not quote numbers). If TRX is involved (token\_id is "\_" on either side of the pair), express TRX values in sun (1 TRX = 1,000,000 sun). > * the call returns an unsigned transaction; sign and broadcast it. On mainnet, the exchange must exist and you must have sufficient balance; slippage protection may reject if market has moved. # wallet/exchangewithdraw | TRON Source: https://docs.chainstack.com/reference/tron-exchangewithdraw openapi/tron_node_api/exchangewithdraw.json post /95e61622bf6a8af293978377718e3b77/wallet/exchangewithdraw TRON API method that removes liquidity from an existing exchange trading pair on the TRON decentralized exchange. Returns a transaction object containing:. TRON API method that removes liquidity from an existing exchange trading pair on the TRON decentralized exchange. ## Parameters * `owner_address` — the address withdrawing liquidity (base58 with `visible: true`, or hex with `visible: false`). * `exchange_id` — unique identifier of the exchange pair to remove liquidity from. * `token_id` — TRC10 token ID being withdrawn; use `_` to denote TRX. * `quant` — integer amount to withdraw (token base units; if TRX, specify in sun). * `permission_id` — optional permission ID for multi‑signature accounts. ## Response Returns a transaction object containing: * `txID` — unique transaction identifier * `raw_data` — raw transaction data including contract details * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/exchangewithdraw` method is used for: * Removing liquidity from trading pairs to claim earned fees * Withdrawing capital from underperforming or deprecated markets * Rebalancing liquidity provider positions across different pairs * Exiting liquidity positions before market volatility or impermanent loss ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/exchangewithdraw' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH", "exchange_id": 1, "token_id": "1000001", "quant": 250000000000, "visible": true }' ``` > [!NOTE] > > * balances must be integers (do not quote numbers). When withdrawing TRX (using `token_id: "_"`), provide `quant` in sun (1 TRX = 1,000,000 sun). > * the call returns an unsigned transaction; sign and broadcast it. On mainnet, the exchange must exist, token must match the pair, and you must have sufficient LP position to withdraw. # wallet/freezebalance | TRON Source: https://docs.chainstack.com/reference/tron-freezebalance openapi/tron_node_api/freezebalance.json post /95e61622bf6a8af293978377718e3b77/wallet/freezebalance TRON API method that stakes TRX for bandwidth or energy resources (deprecated method). wallet/freezebalance on TRON via Chainstack. TRON API method that stakes TRX for bandwidth or energy resources (deprecated method). This method freezes TRX tokens to obtain bandwidth or energy resources, which are required for transaction execution. > [!WARNING] > > legacy staking closed on mainnet > > The legacy `wallet/freezebalance` endpoint is disabled on mainnet and returns: > > ```json theme={"system"} > {"Error":"class org.tron.core.exception.ContractValidateException : freeze v2 is open, old freeze is closed"} > ``` > > Use the current staking method instead: [`wallet/freezebalancev2`](/reference/tron-freezebalancev2). The examples below are kept for historical context and may only work on specific networks where legacy staking is still enabled. ## Parameters * `owner_address` — the address that owns the TRX to freeze (hex format) * `frozen_balance` — the amount of TRX to freeze (in sun, where 1 TRX = 1,000,000 sun) * `frozen_duration` — the duration to freeze for (minimum 3 days) * `resource` — the resource type to obtain ("BANDWIDTH" or "ENERGY") * `receiver_address` — optional address to receive the resources. Omit this field to freeze for yourself. If provided, it must be different from `owner_address`. * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `visible` — boolean indicating address format used * `txID` — transaction ID hash * `raw_data` — raw transaction data object * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/freezebalance` method is used for: * Staking TRX to obtain bandwidth for free transactions (legacy method). * Freezing TRX to get energy for smart contract execution (legacy method). * Supporting older applications that still use the original staking mechanism. * Migrating from the deprecated staking system to the new `freezebalancev2` method. ## curl examples freeze for yourself (omit `receiver_address`): ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/freezebalance' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "41608f8da72479edc7dd921e4c30bb7e7cddbe722e", "frozen_balance": 1000000, "frozen_duration": 3, "resource": "BANDWIDTH", "visible": false }' ``` delegate resources to another account (receiver must differ from owner): ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/freezebalance' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "41608f8da72479edc7dd921e4c30bb7e7cddbe722e", "frozen_balance": 1000000, "frozen_duration": 3, "resource": "ENERGY", "receiver_address": "41e9d79cc47518930bc322d9bf7cddd260a0260a8d", "visible": false }' ``` > [!NOTE] > > if you pass `receiver_address` equal to `owner_address`, the node returns: > > ```json theme={"system"} > { > "Error": "class org.tron.core.exception.ContractValidateException : receiverAddress must not be the same as ownerAddress" > } > ``` > > To freeze for yourself, simply omit `receiver_address`. For new staking, prefer [`wallet/freezebalancev2`](/reference/tron-freezebalancev2). ## recommended v2 equivalent Use the v2 staking endpoint (no duration or receiver in the body): ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/freezebalancev2' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "41608f8da72479edc7dd921e4c30bb7e7cddbe722e", "frozen_balance": 1000000, "resource": "BANDWIDTH", "visible": false }' ``` To unstake and withdraw in v2, use [`wallet/unfreezebalancev2`](/reference/tron-unfreezebalancev2) followed by [`wallet/withdrawexpireunfreeze`](/reference/tron-withdrawexpireunfreeze) after the waiting period. # wallet/freezebalancev2 | TRON Source: https://docs.chainstack.com/reference/tron-freezebalancev2 openapi/tron_node_api/freezebalancev2.json post /95e61622bf6a8af293978377718e3b77/wallet/freezebalancev2 TRON API method that stakes TRX for resources using the new staking mechanism. Reference for wallet/freezebalancev2 on TRON via Chainstack. TRON API method that stakes TRX for resources using the new staking mechanism. This method freezes TRX tokens to obtain bandwidth or energy resources with improved flexibility, including the ability to unfreeze resources at any time. ## Parameters * `owner_address` — the address that owns the TRX to freeze (hex format) * `frozen_balance` — the amount of TRX to freeze (in sun, where 1 TRX = 1,000,000 sun) * `resource` — the resource type to obtain ("BANDWIDTH" or "ENERGY") * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `visible` — boolean indicating address format used * `txID` — transaction ID hash * `raw_data` — raw transaction data object containing freeze contract details * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/freezebalancev2` method is used for: * Staking TRX to obtain bandwidth for free transactions using the new mechanism. * Freezing TRX to get energy for smart contract execution with improved flexibility. * Taking advantage of the ability to unfreeze resources without waiting periods. * Implementing modern TRON staking in wallets and DApps with better resource management. # wallet/getaccount | TRON Source: https://docs.chainstack.com/reference/tron-getaccount openapi/tron_node_api/getaccount.json post /95e61622bf6a8af293978377718e3b77/wallet/getaccount TRON API method that retrieves detailed account information for a given address. Reference for wallet/getaccount on TRON via Chainstack. TRON API method that retrieves detailed account information for a given address. This method returns comprehensive data about an account including balances, resources, and account metadata. ## Parameters * `address` — the TRON account address to query. Can be in base58 or hex format. * `visible` — optional boolean parameter. When set to `true`, the address should be in base58 format. Default is `false`. ## Response * `address` — the account address in hex format * `balance` — the TRX balance in sun (1 TRX = 1,000,000 sun) * `create_time` — account creation timestamp * `latest_operation_time` — timestamp of the last operation * `free_net_usage` — free bandwidth used * `latest_consume_free_time` — last free bandwidth consumption time * `account_resource` — resource information including energy and bandwidth * `owner_permission` — owner permission settings * `active_permission` — active permission settings * `frozenV2` — frozen balance details for resource delegation * `asset` — TRC10 token balances * `assetV2` — detailed TRC10 token information ## Use case The `wallet/getaccount` method is essential for: * Retrieving account balances and resource information. * Checking account permissions and multi-signature settings. * Monitoring frozen balances and resource delegations. * Displaying account information in wallets and explorers. * Verifying account existence before sending transactions. # wallet/getaccountbalance | TRON Source: https://docs.chainstack.com/reference/tron-getaccountbalance openapi/tron_node_api/getaccountbalance.json post /95e61622bf6a8af293978377718e3b77/wallet/getaccountbalance TRON API method that retrieves the TRX balance and related balance information for a specific account. Use it on TRON via Chainstack. TRON API method that retrieves the TRX balance and related balance information for a specific account. This method provides comprehensive balance data including available balance, frozen balance, and delegated resources. ## Parameters * `account_identifier.address` — the account address to query. Use base58 with `visible: true`, or hex with `visible: false`. * `block_identifier` — required object specifying the block to query. Provide both the 32‑byte `hash` (64 hex chars) and the matching `number`. * `visible` — optional boolean for address format. Default is `false` (hex). ## Response * `balance` — available TRX balance (in sun, where 1 TRX = 1,000,000 sun) * `frozen` — array of frozen balance information * `frozen_balance` — amount of TRX frozen * `expire_time` — expiration timestamp for frozen balance * `delegated_frozenV2` — delegated frozen balance information for v2 staking * `undelegated_frozenV2` — undelegated frozen balance information for v2 staking ## Use case The `wallet/getaccountbalance` method is used for: * Checking available TRX balance for transactions and transfers. * Monitoring frozen balance and staking information. * Analyzing delegated resource allocations. * Managing account liquidity and resource planning. ## curl examples Get current balance with a base58 address (at a specific block): ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getaccountbalance' \ --header 'Content-Type: application/json' \ --data '{ "account_identifier": { "address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g" }, "block_identifier": { "hash": "0000000004986736812cbf15ffbcdd229bd3d76a595db895719867cc2da3a5bd", "number": 77096758 }, "visible": true }' ``` Get balance at a specific block using a hex address: ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getaccountbalance' \ --header 'Content-Type: application/json' \ --data '{ "account_identifier": { "address": "41608f8da72479edc7dd921e4c30bb7e7cddbe722e" }, "block_identifier": { "hash": "0000000004986736812cbf15ffbcdd229bd3d76a595db895719867cc2da3a5bd", "number": 77096758 }, "visible": false }' ``` > [!NOTE] > > * avoid `INVALID hex String` by providing a real 32‑byte block hash (64 hex chars). > * avoid `account_identifier is null` by passing `account_identifier: { address: ... }` rather than a top‑level `address`. > * avoid `block_identifier null` and `hash length not equals 32` by always including `block_identifier.hash` with 64 hex chars, and `block_identifier.number` that matches the same block. > > ### find a valid block hash > > Use [`wallet/getnowblock`](/reference/tron-getnowblock) to fetch the latest block and copy the `blockID` field as the `block_identifier.hash` value. > > ```shell Shell theme={"system"} > curl --request POST \ > --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getnowblock' \ > --header 'Content-Type: application/json' > ``` > > Alternatively, get a specific block with [`wallet/getblockbynum`](/reference/tron-getblockbynum) and use its `blockID`. > > ## working example (auto picks latest block) > > Run this to fetch the latest `blockID`, resolve its block `number`, and immediately query the balance at that block. > > ```shell Shell theme={"system"} > # requires jq > BLOCK_ID=$(curl -s -X POST \ > 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getnowblock' \ > -H 'Content-Type: application/json' | jq -r '.blockID') > NUMBER=$(curl -s -X POST \ > 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getblockbyid' \ > -H 'Content-Type: application/json' \ > --data "{\"value\": \"$BLOCK_ID\"}" | jq -r '.block_header.raw_data.number') > > curl --request POST \ > --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getaccountbalance' \ > --header 'Content-Type: application/json' \ > --data "{\n \"account_identifier\": { \"address\": \"41608f8da72479edc7dd921e4c30bb7e7cddbe722e\" },\n \"block_identifier\": { \"hash\": \"$BLOCK_ID\", \"number\": $NUMBER },\n \"visible\": false\n}" > ``` > > If `jq` is not available, use Python instead: > > ```shell Shell theme={"system"} > BLOCK_ID=$(curl -s -X POST \ > 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getnowblock' \ > -H 'Content-Type: application/json' | python3 -c 'import sys,json; print(json.load(sys.stdin)["blockID"])') > NUMBER=$(curl -s -X POST \ > 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getblockbyid' \ > -H 'Content-Type: application/json' \ > --data "{\"value\": \"$BLOCK_ID\"}" | python3 -c 'import sys,json; print(json.load(sys.stdin)["block_header"]["raw_data"]["number"])') > > curl --request POST \ > --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getaccountbalance' \ > --header 'Content-Type: application/json' \ > --data "{\n \"account_identifier\": { \"address\": \"41608f8da72479edc7dd921e4c30bb7e7cddbe722e\" },\n \"block_identifier\": { \"hash\": \"$BLOCK_ID\", \"number\": $NUMBER },\n \"visible\": false\n}" > ``` # wallet/getaccountnet | TRON Source: https://docs.chainstack.com/reference/tron-getaccountnet openapi/tron_node_api/getaccountnet.json post /95e61622bf6a8af293978377718e3b77/wallet/getaccountnet TRON API method that retrieves network resource information for a specific account. Available on TRON via Chainstack JSON-RPC nodes. TRON API method that retrieves network resource information for a specific account. This method provides detailed information about an account's bandwidth usage, including free bandwidth, frozen bandwidth, and network resource consumption statistics. ## Parameters * `address` — the account address to query network resources for (hex format) * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `freeNetUsed` — amount of free bandwidth used * `freeNetLimit` — total free bandwidth limit available * `NetUsed` — amount of staked bandwidth used * `NetLimit` — total staked bandwidth limit available * `TotalNetLimit` — total network bandwidth limit * `TotalNetWeight` — total network weight for bandwidth calculation * `netUsed` — current bandwidth usage * `netLimit` — current bandwidth limit * `TotalEnergyLimit` — total energy limit (if applicable) * `TotalEnergyWeight` — total energy weight (if applicable) ## Use case The `wallet/getaccountnet` method is used for: * Monitoring bandwidth usage and limits for transaction planning. * Checking free bandwidth allocation and consumption. * Analyzing network resource utilization for cost optimization. * Determining if additional bandwidth staking is needed for smooth operations. # wallet/getaccountresource | TRON Source: https://docs.chainstack.com/reference/tron-getaccountresource openapi/tron_node_api/getaccountresource.json post /95e61622bf6a8af293978377718e3b77/wallet/getaccountresource TRON API method that queries the resource information of an account, including bandwidth, energy, and TRON Power (voting power). On TRON. TRON API method that queries the resource information of an account, including bandwidth, energy, and TRON Power (voting power). This endpoint provides comprehensive resource metrics for managing transaction costs and network participation. ## Parameters * `address` — the account address in hex format (41 prefix) or base58check format * `visible` — whether the address is in readable base58check format (optional, default: true) ## Response * `freeNetUsed` — free bandwidth used by the account * `freeNetLimit` — total free bandwidth available to the account * `NetUsed` — used amount of bandwidth obtained by staking * `NetLimit` — total bandwidth obtained by staking * `TotalNetLimit` — total network bandwidth limit * `TotalNetWeight` — total network weight for bandwidth * `EnergyUsed` — energy consumed by the account * `EnergyLimit` — total energy obtained by staking * `TotalEnergyLimit` — total energy limit on the network * `TotalEnergyWeight` — total energy weight on the network * `tronPowerLimit` — TRON Power available for voting * `tronPowerUsed` — TRON Power used for voting * `totalTronPowerWeight` — total TRON Power weight on the network * `assetNetUsed` — bandwidth used for TRC10 assets (array) * `assetNetLimit` — bandwidth limit for TRC10 assets (array) ## Use case The `wallet/getaccountresource` method is used for: * Monitoring account bandwidth and energy consumption * Calculating transaction fees based on available resources * Managing resource delegation and staking strategies * Tracking voting power (TRON Power) for network governance * Analyzing resource usage for TRC10 token transfers * Building wallet interfaces that display resource availability # wallet/getapprovedlist | TRON Source: https://docs.chainstack.com/reference/tron-getapprovedlist openapi/tron_node_api/getapprovedlist.json post /95e61622bf6a8af293978377718e3b77/wallet/getapprovedlist TRON API method that returns the addresses that signed (approved) a specific signed transaction. Available on TRON via Chainstack. TRON API method that returns the addresses that signed (approved) a specific signed transaction. Submit the fully signed transaction object and the endpoint derives the approver addresses from the signatures. ## Parameters * `txID` — the transaction ID (hash). * `raw_data` — the raw transaction object. * `raw_data_hex` — the raw transaction in hexadecimal. * `signature` — array of 65‑byte hex signatures. * `visible` — optional boolean. When `true`, addresses in `raw_data` use base58; when `false`, hex. Defaults to `false`. ## Response * `approved_list` — array of addresses (hex) recovered from the signatures * `transaction` — the echoed transaction payload and validation result ## Use case The `wallet/getapprovedlist` method is used for: * Verifying which address(es) signed a transaction * Auditing multi‑signature workflows * Building wallet UIs that display who approved a transaction ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getapprovedlist' \ --header 'Content-Type: application/json' \ --data '{ "txID": "4ad97d49409cfff338d2f9e71d4019c70e82030b52e15e208b1808f2f3ea782e", "raw_data": { "contract": [ { "parameter": { "value": { "data": "a9059cbb0000000000000000000000415d4a41a3b0cdda5c03df72b38a20aa1b88777c7500000000000000000000000000000000000000000000000000000000f9cd95c0", "owner_address": "41cf2e99409d2a9a9190197ab0bff35ca87818c41e", "contract_address": "41a614f803b6fd780986a42c78ec9c7f77e6ded13c" }, "type_url": "type.googleapis.com/protocol.TriggerSmartContract" }, "type": "TriggerSmartContract" } ], "ref_block_bytes": "68bd", "ref_block_hash": "bc830d2c09fa23d1", "expiration": 1761952567698, "fee_limit": 225000000, "timestamp": 1761952507695 }, "raw_data_hex": "0a0268bd2208bc830d2c09fa23d14092a3bae4a3335aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a1541cf2e99409d2a9a9190197ab0bff35ca87818c41e121541a614f803b6fd780986a42c78ec9c7f77e6ded13c2244a9059cbb0000000000000000000000415d4a41a3b0cdda5c03df72b38a20aa1b88777c7500000000000000000000000000000000000000000000000000000000f9cd95c070afceb6e4a3339001c0f4a46b", "signature": [ "c99cdc8af6f47dc65a75aa0c6da1c5be8510736e581043162c3e8525c6e0ed8f592adaa74ba250136ea6e1fb125b3d3d024ac6abd3e24c28097d66a8e2a0c0aa01" ], "visible": false }' ``` > [!TIP] > > to avoid errors, make sure the transaction object is complete and the `signature` matches the `raw_data`. A quick way to test is to: > > * call [`wallet/getnowblock`](/reference/tron-getnowblock) to get the latest block > * copy a transaction from `transactions[0]` and POST its `txID`, `raw_data`, `raw_data_hex`, and `signature` to this method # wallet/getassetissuebyid | TRON Source: https://docs.chainstack.com/reference/tron-getassetissuebyid openapi/tron_node_api/getassetissuebyid.json post /95e61622bf6a8af293978377718e3b77/wallet/getassetissuebyid TRON API method that retrieves detailed information about a TRC10 token using its unique asset ID. Available on TRON via Chainstack. TRON API method that retrieves detailed information about a TRC10 token using its unique asset ID. TRC10 tokens are native TRON assets that can be issued directly on the blockchain without requiring smart contracts. ## Parameters * `value` — the TRC10 token ID to query (string format) ## Response * `owner_address` — address of the account that issued the token * `name` — full name of the token * `abbr` — token symbol or abbreviation * `total_supply` — total number of tokens issued * `trx_num` — TRX amount used in the exchange rate calculation * `precision` — number of decimal places supported by the token * `num` — token amount used in the exchange rate calculation (tokens per TRX) * `start_time` — ICO start timestamp (when token sale began) * `end_time` — ICO end timestamp (when token sale ended) * `description` — detailed description of the token project * `url` — official website URL for the token project * `id` — unique identifier for the token * `free_asset_net_limit` — bandwidth provided by token issuer for token operations * `public_free_asset_net_limit` — public bandwidth limit available for this token * `public_free_asset_net_usage` — amount of public bandwidth currently used * `public_latest_free_net_time` — timestamp of the latest free bandwidth usage ## Use case The `wallet/getassetissuebyid` method is used for: * Retrieving comprehensive information about TRC10 tokens for display in wallets and exchanges. * Analyzing token economics including supply, exchange rates, and ICO details. * Building token explorers and analytical tools for the TRON ecosystem. * Verifying token authenticity and project information before trading or investing. * Understanding bandwidth allocation and usage patterns for specific tokens. * Implementing token management features in decentralized applications. # wallet/getassetissuebyname | TRON Source: https://docs.chainstack.com/reference/tron-getassetissuebyname openapi/tron_node_api/getassetissuebyname.json post /95e61622bf6a8af293978377718e3b77/wallet/getassetissuebyname TRON API method that retrieves information about a TRC10 token by its name. Reference for wallet/getassetissuebyname on TRON via Chainstack. TRON API method that retrieves information about a TRC10 token by its name. ## Parameters * `value` — the name of the TRC10 token to retrieve information for * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `assetIssue` — array of asset issue information containing: * `owner_address` — token creator address * `name` — token name * `abbr` — token abbreviation * `total_supply` — total token supply * `trx_num` — TRX amount for exchange rate * `num` — token amount for exchange rate * `start_time` — ICO start time * `end_time` — ICO end time * `description` — token description * `url` — token website URL * `id` — token ID ## Use case The `wallet/getassetissuebyname` method is used for: * Retrieving detailed information about TRC10 tokens * Building token information displays in wallets and DApps * Verifying token authenticity and properties * Creating token discovery and analysis tools * Implementing token trading interfaces ## curl examples Because TRC10 names are not unique on mainnet, calling `wallet/getassetissuebyname` with a plain name often returns a non‑unique error. A reliable flow is: 1. search by name to see all matches ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getassetissuelistbyname' \ --header 'Content-Type: application/json' \ --data '{ "value": "SEED", "visible": true }' ``` This returns an `assetIssue` array with multiple SEED tokens. Each item has an `id` field (for example, `1000001`). 2. fetch the exact token by id ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getassetissuebyid' \ --header 'Content-Type: application/json' \ --data '{ "value": "1000001" }' ``` One‑liner using `jq` to pick the first match and query by id: ```shell Shell theme={"system"} ASSET_ID=$(curl -s -X POST \ 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getassetissuelistbyname' \ -H 'Content-Type: application/json' \ -d '{"value":"SEED","visible":true}' | jq -r '.assetIssue[0].id') curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getassetissuebyid' \ --header 'Content-Type: application/json' \ --data '{"value":"'"$ASSET_ID"'"}' ``` > [!NOTE] > > * if you call `wallet/getassetissuebyname` with a non‑unique name (for example, `SEED`), the node can respond with: > > ```json theme={"system"} > {"Error":"class org.tron.core.exception.NonUniqueObjectException : To get more than one asset, please use getAssetIssueById syntax"} > ``` > > * prefer `wallet/getassetissuelistbyname` to discover candidates, then `wallet/getassetissuebyid` to retrieve a specific token. ### direct working example (hex name) You can also query a known unique token by providing the name hex‑encoded with `visible: false`. Example for `TRXTestCoin` (asset id `1000006`): ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getassetissuebyname' \ --header 'Content-Type: application/json' \ --data '{ "value": "54525854657374436f696e", "visible": false }' ``` # wallet/getassetissuelist | TRON Source: https://docs.chainstack.com/reference/tron-getassetissuelist openapi/tron_node_api/getassetissuelist.json post /95e61622bf6a8af293978377718e3b77/wallet/getassetissuelist TRON API method that retrieves a list of all TRC10 tokens that have been issued on the TRON network. No parameters required for this method. TRON API method that retrieves a list of all TRC10 tokens that have been issued on the TRON network. ## Parameters No parameters required for this method. ## Response Returns an array of asset objects, each containing: * `owner_address` — address of the account that issued the token * `name` — full name of the token * `abbr` — token symbol or abbreviation * `total_supply` — total number of tokens issued * `trx_num` — TRX amount used in the exchange rate calculation * `precision` — number of decimal places supported by the token * `num` — token amount used in the exchange rate calculation (tokens per TRX) * `start_time` — ICO start timestamp (when token sale began) * `end_time` — ICO end timestamp (when token sale ended) * `description` — detailed description of the token project * `url` — official website URL for the token project * `id` — unique identifier for the token * `free_asset_net_limit` — bandwidth provided by token issuer for token operations * `public_free_asset_net_limit` — public bandwidth limit available for this token * `public_free_asset_net_usage` — amount of public bandwidth currently used * `public_latest_free_net_time` — timestamp of the latest free bandwidth usage ## Use case The `wallet/getassetissuelist` method is used for: * Building comprehensive token explorers and market overview dashboards * Retrieving all available TRC10 tokens for wallet and exchange integrations * Analyzing the complete TRC10 token ecosystem on TRON * Creating token discovery interfaces for DeFi applications # wallet/getassetissuelistbyname | TRON Source: https://docs.chainstack.com/reference/tron-getassetissuelistbyname openapi/tron_node_api/getassetissuelistbyname.json post /95e61622bf6a8af293978377718e3b77/wallet/getassetissuelistbyname TRON API method that retrieves a list of TRC10 tokens that match a specified name pattern. wallet/getassetissuelistbyname on TRON via Chainstack. TRON API method that retrieves a list of TRC10 tokens that match a specified name pattern. This method allows searching for tokens by their names and is useful for discovering assets with similar or related names. ## Parameters * `value` — token name or pattern. Use plain text with `visible: true`, or hex‑encoded UTF‑8 with `visible: false`. * `visible` — optional boolean. When `true`, `value` is plain text; when `false`, hex‑encoded. Default is `true`. ## Response * `assetIssue` — array of TRC10 token information objects, each containing: * `id` — unique token ID * `owner_address` — address of the token issuer * `name` — token name in hex format * `abbr` — token abbreviation in hex format * `total_supply` — total supply of the token * `trx_num` — TRX amount in exchange ratio * `precision` — token decimal places * `num` — token amount in exchange ratio * `start_time` — token sale start timestamp * `end_time` — token sale end timestamp * `description` — token description in hex format * `url` — token website URL in hex format * `free_asset_net_limit` — free bandwidth allocation * `public_free_asset_net_limit` — public free bandwidth limit * `frozen_supply` — frozen token supply details ## Use case The `wallet/getassetissuelistbyname` method is used for: * Searching for TRC10 tokens by name pattern or partial name matches. * Discovering tokens with similar names or themes in the ecosystem. * Building token discovery and search functionality in applications. * Validating token names before creating new assets to avoid conflicts. * Implementing auto-complete features for token selection interfaces. * Analyzing token naming patterns and trends in the TRON network. ## curl examples Search by plain name (recommended): ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getassetissuelistbyname' \ --header 'Content-Type: application/json' \ --data '{ "value": "SEED", "visible": true }' ``` The response lists multiple `SEED` tokens; each entry includes an `id`. To fetch a specific token, use [`wallet/getassetissuebyid`](/reference/tron-getassetissuebyid) with that `id`. Query with a hex‑encoded name: ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getassetissuelistbyname' \ --header 'Content-Type: application/json' \ --data '{ "value": "54525854657374436f696e", "visible": false }' ``` > [!NOTE] > > TRC10 names are not unique. Prefer this endpoint to discover candidates, then call [`wallet/getassetissuebyid`](/reference/tron-getassetissuebyid) to retrieve the exact token by `id`. # wallet/getavailableunfreezecount | TRON Source: https://docs.chainstack.com/reference/tron-getavailableunfreezecount openapi/tron_node_api/getavailableunfreezecount.json post /95e61622bf6a8af293978377718e3b77/wallet/getavailableunfreezecount TRON API method that retrieves the available unfreeze count for an account under the Stake 2.0 mechanism. Chainstack TRON reference. TRON API method that retrieves the available unfreeze count for an account under the Stake 2.0 mechanism. This shows how many unfreeze operations can still be performed. ## Parameters * `owner_address` — the account address to query for available unfreeze count * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `count` — the number of available unfreeze operations remaining for this account ## Use case The `wallet/getavailableunfreezecount` method is used for: * Checking how many more unfreeze operations an account can perform * Planning resource management strategies * Building user interfaces that show unfreeze limitations * Implementing safeguards against exceeding unfreeze limits * Monitoring account resource flexibility # wallet/getbandwidthprices | TRON Source: https://docs.chainstack.com/reference/tron-getbandwidthprices openapi/tron_node_api/getbandwidthprices.json post /95e61622bf6a8af293978377718e3b77/wallet/getbandwidthprices TRON API method that retrieves the current bandwidth prices on the TRON network, showing the cost of bandwidth for transaction processing. TRON API method that retrieves the current bandwidth prices on the TRON network, showing the cost of bandwidth for transaction processing. ## Parameters This method does not require any parameters. ## Response * `prices` — bandwidth pricing information including current rates and historical data ## Use case The `wallet/getbandwidthprices` method is used for: * Monitoring current bandwidth costs for transaction processing * Building transaction fee estimation tools * Analyzing network congestion through bandwidth pricing * Optimizing transaction timing based on cost * Creating cost-aware transaction batching systems # wallet/getblock | TRON Source: https://docs.chainstack.com/reference/tron-getblock openapi/tron_node_api/getblock.json post /95e61622bf6a8af293978377718e3b77/wallet/getblock TRON API method that retrieves detailed block information from the TRON blockchain by block number or block hash. TRON via Chainstack. TRON API method that retrieves detailed block information from the TRON blockchain by block number or block hash. ## Parameters * `id_or_num` — block hash (as hex string) or block number to retrieve * `detail` — optional boolean. When true, returns detailed transaction information. Default is false. * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `blockID` — the block hash * `block_header` — block header information including: * `raw_data` — raw header data with timestamp, number, parent hash, etc. * `witness_signature` — block producer signature * `transactions` — array of transactions in the block (detailed if detail=true) ## Use case The `wallet/getblock` method is used for: * Retrieving specific block information for analysis * Building block explorers and blockchain visualization tools * Verifying block contents and transaction inclusion * Monitoring blockchain state at specific block heights * Analyzing block production patterns and timing # wallet/getblockbalance | TRON Source: https://docs.chainstack.com/reference/tron-getblockbalance openapi/tron_node_api/getblockbalance.json post /95e61622bf6a8af293978377718e3b77/wallet/getblockbalance TRON API method that returns the balance deltas within a specific block. It shows, per transaction, how account balances changed in that block. TRON API method that returns the balance deltas within a specific block. It shows, per transaction, how account balances changed in that block. ## Parameters * `hash` — block hash as a 64‑character hex string. * `number` — block number. Must match the provided `hash`. * `visible` — optional boolean. When `true`, addresses are base58; when `false`, hex. Default is `true`. ## Response * `timestamp` — block timestamp in milliseconds. * `block_identifier` — object with `hash` and `number`. * `transaction_balance_trace` — array per transaction with: * `transaction_identifier` — transaction hash. * `operation` — list with `operation_identifier`, `address`, and `amount` (sun; negative for debits, positive for credits). * `type` — contract type (for example, `TransferContract`). * `status` — execution status. ## Use case * Auditing balance changes for a given block. * Building explorers or analytics that summarize per‑address deltas. * Reconciling application balances against chain state. ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getblockbalance' \ --header 'Content-Type: application/json' \ --data '{ "hash": "0000000004986736812cbf15ffbcdd229bd3d76a595db895719867cc2da3a5bd", "number": 77096758, "visible": true }' ``` ## get a valid block pair automatically ```shell Shell theme={"system"} # requires jq BLOCK_ID=$(curl -s -X POST \ 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getnowblock' \ -H 'Content-Type: application/json' --data '{}' | jq -r '.blockID') NUMBER=$(curl -s -X POST \ 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getblockbyid' \ -H 'Content-Type: application/json' \ --data "{\"value\": \"$BLOCK_ID\"}" | jq -r '.block_header.raw_data.number') curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getblockbalance' \ --header 'Content-Type: application/json' \ --data "{\n \"hash\": \"$BLOCK_ID\",\n \"number\": $NUMBER,\n \"visible\": true\n}" ``` > [!NOTE] > > * provide both `hash` (64 hex chars) and `number` that match the same block to avoid errors like `INVALID hex String`, `hash length not equals 32`, or `number and hash do not match`. > * amounts are in sun (1 TRX = 1,000,000 sun). # wallet/getblockbyid | TRON Source: https://docs.chainstack.com/reference/tron-getblockbyid openapi/tron_node_api/getblockbyid.json post /95e61622bf6a8af293978377718e3b77/wallet/getblockbyid TRON API method that retrieves a specific block from the TRON blockchain using its block ID (hash). Use it on TRON via Chainstack. TRON API method that retrieves a specific block from the TRON blockchain using its block ID (hash). This method allows you to fetch detailed information about a particular block, including its header, transactions, and metadata. ## Parameters * `value` — the block ID (hash) in hexadecimal format to retrieve ## Response * `blockID` — the block hash identifier * `block_header` — block header information containing: * `raw_data` — raw block header data including: * `timestamp` — block generation timestamp in milliseconds * `txTrieRoot` — transaction trie root hash * `parentHash` — parent block hash * `number` — block height/number * `witness_address` — address of the block producer (witness) * `version` — block version * `witness_signature` — digital signature of the block producer * `transactions` — array of all transactions included in the block ## Use case The `wallet/getblockbyid` method is used for: * Retrieving detailed information about a specific block using its hash. * Building block explorers that need to display block details by ID. * Verifying block contents and validating transactions within a specific block. * Analyzing historical blockchain data for auditing and compliance purposes. * Implementing blockchain synchronization tools that need to fetch blocks by their identifiers. # wallet/getblockbylatestnum | TRON Source: https://docs.chainstack.com/reference/tron-getblockbylatestnum openapi/tron_node_api/getblockbylatestnum.json post /95e61622bf6a8af293978377718e3b77/wallet/getblockbylatestnum TRON API method that retrieves the latest blocks from the TRON blockchain up to a specified number of blocks. TRON via Chainstack. TRON API method that retrieves the latest blocks from the TRON blockchain up to a specified number of blocks. ## Parameters * `num` — number of latest blocks to retrieve (maximum 100) * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `block` — array of block objects, each containing: * `blockID` — the block hash * `block_header` — block header with raw data and witness signature * `transactions` — array of transactions in each block ## Use case The `wallet/getblockbylatestnum` method is used for: * Retrieving recent blockchain activity and transactions * Building real-time blockchain monitoring dashboards * Analyzing recent network activity patterns * Creating live feeds of blockchain events * Monitoring latest block production and validation # wallet/getblockbylimitnext | TRON Source: https://docs.chainstack.com/reference/tron-getblockbylimitnext openapi/tron_node_api/getblockbylimitnext.json post /95e61622bf6a8af293978377718e3b77/wallet/getblockbylimitnext TRON API method that retrieves a range of blocks from the TRON blockchain starting from a specific block number with a specified limit. TRON API method that retrieves a range of blocks from the TRON blockchain starting from a specific block number with a specified limit. ## Parameters * `startNum` — the starting block number to retrieve from * `endNum` — the ending block number (range limit) * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `block` — array of block objects within the specified range, each containing: * `blockID` — the block hash * `block_header` — block header with raw data and witness signature * `transactions` — array of transactions in each block ## Use case The `wallet/getblockbylimitnext` method is used for: * Retrieving specific ranges of blocks for analysis * Building paginated block explorers * Analyzing blockchain activity within specific time periods * Creating historical data analysis tools * Implementing efficient block synchronization mechanisms # wallet/getblockbynum | TRON Source: https://docs.chainstack.com/reference/tron-getblockbynum openapi/tron_node_api/getblockbynum.json post /95e61622bf6a8af293978377718e3b77/wallet/getblockbynum TRON API method that retrieves a block by its number. This method returns detailed information about a specific block in the TRON blockchain. TRON API method that retrieves a block by its number. This method returns detailed information about a specific block in the TRON blockchain, including all transactions contained within that block. ## Parameters * `num` — the block number to retrieve (integer) * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `blockID` — unique block identifier hash * `block_header` — block header information containing: * `raw_data` — raw header data with timestamp, number, and parent hash * `witness_signature` — block producer's signature * `transactions` — array of transaction objects in the block * `visible` — boolean indicating address format used ## Use case The `wallet/getblockbynum` method is used for: * Retrieving specific blocks for blockchain analysis and monitoring. * Accessing transaction history within particular blocks. * Building block explorers and analytics tools. * Verifying block data and transaction inclusion for auditing purposes. # wallet/getBrokerage | TRON Source: https://docs.chainstack.com/reference/tron-getbrokerage openapi/tron_node_api/getbrokerage.json GET /95e61622bf6a8af293978377718e3b77/wallet/getBrokerage TRON API method that returns a witness’s brokerage percentage (0–100). TRON developers can call wallet/getBrokerage via Chainstack. TRON API method that returns a witness’s brokerage percentage (0–100). Witnesses set this rate to determine how much of their block rewards they keep before sharing the remainder with voters. ## Parameters * `address` — witness address in hex format (21‑byte, starts with `41`). ## Response * `brokerage` — percentage (0–100) kept by the witness. ## Use case The `wallet/getBrokerage` method is used for: * Checking the current reward sharing rate of a witness before voting. * Comparing brokerage rates between different witnesses to maximize voting rewards. * Monitoring changes in witness brokerage policies over time. * Building witness comparison tools and voting recommendation systems. ## curl example ```shell Shell theme={"system"} curl --request GET \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getBrokerage?address=41928c9af0651632157ef27a2cf17ca72c575a4d21' \ --header 'accept: application/json' ``` > [!NOTE] > > if you have a base58 witness address, convert it to a 21‑byte hex address (leading `41…`) before calling this endpoint. You can obtain witness addresses via [`wallet/listwitnesses`](/reference/tron-listwitnesses). # wallet/getburntrx | TRON Source: https://docs.chainstack.com/reference/tron-getburntrx openapi/tron_node_api/getburntrx.json post /95e61622bf6a8af293978377718e3b77/wallet/getburntrx TRON API method that retrieves information about burned TRX tokens, showing the total amount of TRX permanently removed from circulation. TRON API method that retrieves information about burned TRX tokens, showing the total amount of TRX permanently removed from circulation. ## Parameters This method does not require any parameters. ## Response * `burnTrxAmount` — the total amount of TRX that has been burned (in sun units) ## Use case The `wallet/getburntrx` method is used for: * Monitoring the deflationary mechanism of the TRON network * Analyzing the total supply reduction over time * Building economic analysis tools for TRON tokenomics * Creating dashboards showing network burn statistics * Tracking the impact of transaction fees on TRX supply # wallet/getcandelegatedmaxsize | TRON Source: https://docs.chainstack.com/reference/tron-getcandelegatedmaxsize openapi/tron_node_api/getcandelegatedmaxsize.json post /95e61622bf6a8af293978377718e3b77/wallet/getcandelegatedmaxsize TRON API method that retrieves the maximum amount that can be delegated by an account for bandwidth and energy resources. On TRON. TRON API method that retrieves the maximum amount that can be delegated by an account for bandwidth and energy resources. ## Parameters * `owner_address` — the account address to query for maximum delegation capacity * `type` — resource type: 0 for bandwidth, 1 for energy * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `max_size` — the maximum amount that can be delegated for the specified resource type ## Use case The `wallet/getcandelegatedmaxsize` method is used for: * Determining delegation capacity before attempting resource delegation * Building user interfaces with delegation limits * Implementing resource delegation validation * Planning resource sharing strategies * Preventing delegation failures due to insufficient capacity # wallet/getcanwithdrawunfreezeamount | TRON Source: https://docs.chainstack.com/reference/tron-getcanwithdrawunfreezeamount openapi/tron_node_api/getcanwithdrawunfreezeamount.json post /95e61622bf6a8af293978377718e3b77/wallet/getcanwithdrawunfreezeamount TRON API method that retrieves the amount that can be withdrawn after unfreezing resources under the Stake 2.0 mechanism. On TRON. TRON API method that retrieves the amount that can be withdrawn after unfreezing resources under the Stake 2.0 mechanism. ## Parameters * `owner_address` — the account address to query for withdrawable unfreeze amount * `timestamp` — optional timestamp to check withdrawable amount at a specific time * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `amount` — the total amount of TRX that can be withdrawn from completed unfreeze operations ## Use case The `wallet/getcanwithdrawunfreezeamount` method is used for: * Checking how much TRX is available for withdrawal after unfreezing * Planning liquidity management for accounts * Building user interfaces showing withdrawable balances * Implementing automatic withdrawal triggers * Monitoring completed unfreeze operations # wallet/getchainparameters | TRON Source: https://docs.chainstack.com/reference/tron-getchainparameters openapi/tron_node_api/getchainparameters.json get /95e61622bf6a8af293978377718e3b77/wallet/getchainparameters TRON API method that retrieves the current blockchain parameters and configuration settings. wallet/getchainparameters on TRON via Chainstack. TRON API method that retrieves the current blockchain parameters and configuration settings. This method provides access to important network constants, fees, limits, and other configurable parameters that govern the TRON blockchain operation. ## Parameters This method does not require any parameters. ## Response * `chainParameter` — array of blockchain parameters * `key` — parameter name * `value` — parameter value * Common parameters include: * `getMaintenanceTimeInterval` — maintenance period interval * `getAccountUpgradeCost` — cost to upgrade account * `getCreateAccountFee` — fee for creating new accounts * `getTransactionFee` — base transaction fee * `getAssetIssueFee` — fee for issuing assets/tokens * `getWitnessPayPerBlock` — witness reward per block * `getWitnessStandbyAllowance` — standby witness allowance * `getCreateNewAccountFeeInSystemContract` — system contract account creation fee * `getCreateNewAccountBandwidthRate` — bandwidth rate for account creation * `getAllowCreationOfContracts` — whether contract creation is allowed * `getRemoveThePowerOfTheGr` — governance parameter * `getEnergyFee` — energy fee rate * `getExchangeCreateFee` — fee for creating exchanges * `getMaxCpuTimeOfOneTx` — maximum CPU time per transaction * `getAllowUpdateAccountName` — whether account name updates are allowed * `getAllowSameTokenName` — whether duplicate token names are allowed * `getAllowDelegateResource` — whether resource delegation is allowed * `getTotalEnergyLimit` — total energy limit for the network * `getAllowTvmTransferTrc10` — whether TVM can transfer TRC10 tokens * `getTotalEnergyCurrentLimit` — current total energy limit * `getAllowMultiSign` — whether multi-signature is allowed * `getAllowAdaptiveEnergy` — whether adaptive energy is enabled * `getUpdateAccountPermissionFee` — fee for updating account permissions * `getMultiSignFee` — fee for multi-signature transactions ## Use case The `wallet/getchainparameters` method is used for: * Retrieving current network fees and limits for transaction planning. * Checking governance parameters and network settings. * Understanding network constraints for application development. * Monitoring parameter changes and network upgrades. # wallet/getcontract | TRON Source: https://docs.chainstack.com/reference/tron-getcontract openapi/tron_node_api/getcontract.json post /95e61622bf6a8af293978377718e3b77/wallet/getcontract TRON API method that retrieves detailed information about a smart contract deployed on the TRON network. Chainstack TRON reference. TRON API method that retrieves detailed information about a smart contract deployed on the TRON network. This method provides comprehensive contract data including bytecode, ABI, deployment information, and resource configuration. ## Parameters * `value` — the smart contract address in hexadecimal format ## Response * `bytecode` — compiled contract bytecode in hexadecimal format * `name` — human-readable name of the smart contract * `origin_address` — address of the account that deployed the contract * `contract_address` — the contract's address in hexadecimal format * `abi` — Application Binary Interface defining contract methods and events * `origin_energy_limit` — maximum energy limit set during contract deployment * `consume_user_resource_percent` — percentage of caller's resources used for contract execution (0-100) * `code_hash` — unique hash identifier of the contract's bytecode * `trx_hash` — transaction hash of the original contract deployment transaction ## Use case The `wallet/getcontract` method is used for: * Retrieving comprehensive information about deployed smart contracts. * Obtaining contract ABI for building user interfaces and interaction tools. * Analyzing contract resource configuration and deployment details. * Verifying contract bytecode and comparing with expected implementations. * Building block explorers and contract verification tools. * Understanding contract resource consumption settings for optimization. # wallet/getcontractinfo | TRON Source: https://docs.chainstack.com/reference/tron-getcontractinfo openapi/tron_node_api/getcontractinfo.json post /95e61622bf6a8af293978377718e3b77/wallet/getcontractinfo TRON API method that retrieves detailed information about a smart contract, including its ABI, bytecode, source code, and metadata. TRON API method that retrieves detailed information about a smart contract, including its ABI, bytecode, source code, and metadata. This method is essential for understanding and interacting with deployed contracts on the TRON network. ## Parameters * `value` — contract address to retrieve information for * `visible` — boolean indicating whether to use visible (Base58) address format instead of hex ## Response * `contract_address` — the contract address * `bytecode` — contract bytecode in hex format * `name` — contract name if available * `abi` — Application Binary Interface defining contract functions and events * `source_map` — source code mapping for debugging purposes * `source_code` — original contract source code if available * `compiler_version` — Solidity compiler version used * `consume_user_resource_percent` — percentage of user resources consumed * `origin_address` — address that deployed the contract * `origin_energy_limit` — energy limit set by contract creator * `contract_state` — current state of the contract * `code_hash` — hash of the contract code ## Use case The `wallet/getcontractinfo` method is used for: * Retrieving contract ABI for building function calls and parsing events. * Verifying contract source code and understanding contract functionality. * Building contract explorers and analysis tools for developers. * Debugging contract deployments and understanding energy consumption settings. * Implementing contract interaction interfaces in wallets and dApps. * Auditing smart contracts and understanding their implementation details. # wallet/getdelegatedresource | TRON Source: https://docs.chainstack.com/reference/tron-getdelegatedresource openapi/tron_node_api/getdelegatedresource.json post /95e61622bf6a8af293978377718e3b77/wallet/getdelegatedresource TRON API method that retrieves information about delegated resources (bandwidth and energy) between accounts on the TRON blockchain. TRON API method that retrieves information about delegated resources (bandwidth and energy) between accounts on the TRON blockchain. ## Parameters * `fromAddress` — the address that delegated resources to another account * `toAddress` — the address that received the delegated resources * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `delegatedResource` — array of delegated resource information containing: * `from` — the delegator address * `to` — the recipient address * `frozen_balance_for_bandwidth` — amount of TRX frozen for bandwidth delegation * `frozen_balance_for_energy` — amount of TRX frozen for energy delegation * `expire_time_for_bandwidth` — expiration timestamp for bandwidth delegation * `expire_time_for_energy` — expiration timestamp for energy delegation ## Use case The `wallet/getdelegatedresource` method is used for: * Checking delegated bandwidth and energy between specific accounts * Monitoring resource delegation relationships * Verifying delegation amounts and expiration times * Building resource management dashboards and tools * Analyzing resource sharing patterns in applications # wallet/getdelegatedresourceaccountindex | TRON Source: https://docs.chainstack.com/reference/tron-getdelegatedresourceaccountindex openapi/tron_node_api/getdelegatedresourceaccountindex.json post /95e61622bf6a8af293978377718e3b77/wallet/getdelegatedresourceaccountindex Reference docs for the wallet/getdelegatedresourceaccountindex JSON-RPC method on the TRON blockchain, available via Chainstack JSON-RPC nodes. TRON API method that retrieves the delegation index of an account, showing all accounts that have delegated resources to or received resources from the specified account. ## Parameters * `value` — the account address to query for delegation relationships * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `account` — the queried account address * `fromAccounts` — array of accounts that have delegated resources to this account * `toAccounts` — array of accounts that have received delegated resources from this account ## Use case The `wallet/getdelegatedresourceaccountindex` method is used for: * Discovering all delegation relationships for a specific account * Building resource delegation network graphs * Analyzing delegation patterns and resource flow * Monitoring accounts involved in resource sharing * Creating comprehensive resource management interfaces # wallet/getdelegatedresourceaccountindexv2 | TRON Source: https://docs.chainstack.com/reference/tron-getdelegatedresourceaccountindexv2 openapi/tron_node_api/getdelegatedresourceaccountindexv2.json post /95e61622bf6a8af293978377718e3b77/wallet/getdelegatedresourceaccountindexv2 Reference docs for the wallet/getdelegatedresourceaccountindexv2 JSON-RPC method on the TRON blockchain, available via Chainstack JSON-RPC nodes. TRON API method that retrieves the delegation index of an account using the Stake 2.0 mechanism, showing all accounts involved in resource delegation relationships. ## Parameters * `value` — the account address to query for delegation relationships * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `account` — the queried account address * `fromAccounts` — array of accounts that have delegated resources to this account * `toAccounts` — array of accounts that have received delegated resources from this account ## Use case The `wallet/getdelegatedresourceaccountindexv2` method is used for: * Discovering all Stake 2.0 delegation relationships for a specific account * Building updated resource delegation network graphs * Analyzing modern delegation patterns and resource flow * Monitoring accounts involved in Stake 2.0 resource sharing * Creating comprehensive resource management interfaces for the new staking system # wallet/getdelegatedresourcev2 | TRON Source: https://docs.chainstack.com/reference/tron-getdelegatedresourcev2 openapi/tron_node_api/getdelegatedresourcev2.json post /95e61622bf6a8af293978377718e3b77/wallet/getdelegatedresourcev2 TRON API method that retrieves delegated resource information using the Stake 2.0 mechanism between accounts on the TRON blockchain. TRON API method that retrieves delegated resource information using the Stake 2.0 mechanism between accounts on the TRON blockchain. ## Parameters * `fromAddress` — the address that delegated resources to another account * `toAddress` — the address that received the delegated resources * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `delegatedResource` — array of delegated resource information containing: * `from` — the delegator address * `to` — the recipient address * `frozen_balance_for_bandwidth` — amount of TRX frozen for bandwidth delegation * `frozen_balance_for_energy` — amount of TRX frozen for energy delegation * `expire_time_for_bandwidth` — expiration timestamp for bandwidth delegation * `expire_time_for_energy` — expiration timestamp for energy delegation ## Use case The `wallet/getdelegatedresourcev2` method is used for: * Checking Stake 2.0 delegated resources between specific accounts * Monitoring resource delegation relationships under the new staking mechanism * Verifying delegation amounts and expiration times * Building modern resource management dashboards * Analyzing updated resource sharing patterns # wallet/getenergyprices | TRON Source: https://docs.chainstack.com/reference/tron-getenergyprices openapi/tron_node_api/getenergyprices.json post /95e61622bf6a8af293978377718e3b77/wallet/getenergyprices TRON API method that retrieves the current energy prices on the TRON network, showing the cost of energy for smart contract execution. TRON API method that retrieves the current energy prices on the TRON network, showing the cost of energy for smart contract execution. ## Parameters This method does not require any parameters. ## Response * `prices` — energy pricing information including current rates and historical data ## Use case The `wallet/getenergyprices` method is used for: * Monitoring current energy costs for smart contract operations * Building fee estimation tools for DApp users * Analyzing network congestion through energy pricing * Optimizing smart contract execution timing * Creating cost-aware transaction scheduling systems # wallet/getexchangebyid | TRON Source: https://docs.chainstack.com/reference/tron-getexchangebyid openapi/tron_node_api/getexchangebyid.json post /95e61622bf6a8af293978377718e3b77/wallet/getexchangebyid TRON API method that retrieves detailed information about a specific exchange trading pair by its unique identifier. Returns an exchange object containing:. TRON API method that retrieves detailed information about a specific exchange trading pair by its unique identifier. ## Parameters * `id` — the unique exchange ID to query ## Response Returns an exchange object containing: * `exchange_id` — unique identifier for the exchange pair * `creator_address` — address that created the exchange pair * `create_time` — timestamp when the exchange was created * `first_token_id` — token ID of the first token in the pair * `first_token_balance` — current balance of the first token * `second_token_id` — token ID of the second token in the pair * `second_token_balance` — current balance of the second token ## Use case The `wallet/getexchangebyid` method is used for: * Retrieving specific exchange pair details for trading interfaces * Checking current liquidity balances before executing trades * Monitoring individual exchange pair performance and metrics * Validating exchange pair existence before performing operations # wallet/getmarketorderbyaccount | TRON Source: https://docs.chainstack.com/reference/tron-getmarketorderbyaccount openapi/tron_node_api/getmarketorderbyaccount.json post /95e61622bf6a8af293978377718e3b77/wallet/getmarketorderbyaccount TRON API method that retrieves all market orders associated with a specific account. wallet/getmarketorderbyaccount on TRON via Chainstack. TRON API method that retrieves all market orders associated with a specific account. This includes both active and historical buy/sell orders placed on the TRON DEX marketplace. ## Parameters * `address` — hexadecimal address of the account whose market orders you want to query * `offset` — (optional) starting position for pagination (default: 0) * `limit` — (optional) maximum number of orders to return (default: 20, max: 100) ## Response * `orders` — array of market order objects, each containing: * `order_id` — unique identifier for the market order * `owner_address` — hexadecimal address of the order creator * `create_time` — timestamp when the order was created * `sell_token_id` — token ID being sold (empty for TRX) * `sell_token_quantity` — quantity of tokens being sold * `buy_token_id` — token ID being purchased (empty for TRX) * `buy_token_quantity` — quantity of tokens being purchased * `sell_token_quantity_remain` — remaining quantity available for sale * `buy_token_quantity_remain` — remaining quantity to be purchased * `state` — current order status (ACTIVE, INACTIVE, CANCELED, etc.) ## Use case The `wallet/getmarketorderbyaccount` method is used for: * Viewing all trading orders for a specific account on TRON DEX. * Building portfolio management dashboards showing order history. * Tracking order execution status and remaining quantities. * Analyzing trading patterns and market participation. * Creating automated trading monitoring systems. > [!NOTE] > > Market orders on TRON DEX can trade TRX against TRC-10 tokens. Empty token IDs represent TRX in the trading pair. The order state indicates whether the order is still active and available for matching with other orders. # wallet/getmarketorderbyid | TRON Source: https://docs.chainstack.com/reference/tron-getmarketorderbyid openapi/tron_node_api/getmarketorderbyid.json post /95e61622bf6a8af293978377718e3b77/wallet/getmarketorderbyid TRON API method that retrieves detailed information for a specific market order by its unique order ID. Chainstack TRON reference. TRON API method that retrieves detailed information for a specific market order by its unique order ID. This provides comprehensive details about order status, quantities, and trading pair information. ## Parameters * `order_id` — unique identifier of the market order to query (as hexadecimal string) ## Response * `order_id` — unique identifier for the market order * `owner_address` — hexadecimal address of the order creator * `create_time` — timestamp when the order was created * `sell_token_id` — token ID being sold (empty for TRX) * `sell_token_quantity` — original quantity of tokens being sold * `buy_token_id` — token ID being purchased (empty for TRX) * `buy_token_quantity` — original quantity of tokens being purchased * `sell_token_quantity_remain` — remaining quantity available for sale * `buy_token_quantity_remain` — remaining quantity to be purchased * `state` — current order status (ACTIVE, INACTIVE, CANCELED, etc.) * `prev` — previous order ID in the order book (if applicable) * `next` — next order ID in the order book (if applicable) ## Use case The `wallet/getmarketorderbyid` method is used for: * Checking the current status and execution progress of a specific order. * Monitoring order fulfillment and remaining quantities. * Building order tracking interfaces for trading applications. * Verifying order details before attempting modifications or cancellations. * Investigating order book structure and linked order relationships. > [!NOTE] > > The `prev` and `next` fields help navigate the order book structure, showing how orders are linked in the matching system. Empty token IDs represent TRX in trading pairs. Order states indicate the current execution status and availability for matching. # wallet/getmarketorderlistbypair | TRON Source: https://docs.chainstack.com/reference/tron-getmarketorderlistbypair openapi/tron_node_api/getmarketorderlistbypair.json post /95e61622bf6a8af293978377718e3b77/wallet/getmarketorderlistbypair TRON API method that retrieves all active market orders for a specific trading pair on the TRON DEX. Use it on TRON via Chainstack. TRON API method that retrieves all active market orders for a specific trading pair on the TRON DEX. This provides order book information showing current buy and sell orders available for matching. ## Parameters * `sell_token_id` — TRC10 token ID being sold, as a numeric string (for example, "1000006"). * `buy_token_id` — TRC10 token ID being purchased, as a numeric string. * `offset` — (optional) starting position for pagination (default: 0) * `limit` — (optional) maximum number of orders to return (default: 20, max: 100) * `visible` — (optional) when `true`, some nodes accept the request more reliably. ## Response * `orders` — array of market order objects, each containing: * `order_id` — unique identifier for the market order * `owner_address` — hexadecimal address of the order creator * `create_time` — timestamp when the order was created * `sell_token_id` — token ID being sold * `sell_token_quantity` — quantity of tokens being sold * `buy_token_id` — token ID being purchased * `buy_token_quantity` — quantity of tokens being purchased * `sell_token_quantity_remain` — remaining quantity available for sale * `buy_token_quantity_remain` — remaining quantity to be purchased * `state` — current order status (typically ACTIVE for this query) ## Use case The `wallet/getmarketorderlistbypair` method is used for: * Building order book displays for trading interfaces. * Analyzing market depth and liquidity for specific trading pairs. * Finding matching orders for automated trading strategies. * Monitoring market activity and order placement patterns. * Implementing market making and arbitrage detection systems. ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getmarketorderlistbypair' \ --header 'Content-Type: application/json' \ --data '{ "sell_token_id": "1000006", "buy_token_id": "1000001", "offset": 0, "limit": 20, "visible": true }' ``` # wallet/getmarketpairlist | TRON Source: https://docs.chainstack.com/reference/tron-getmarketpairlist openapi/tron_node_api/getmarketpairlist.json post /95e61622bf6a8af293978377718e3b77/wallet/getmarketpairlist TRON API method that retrieves all available trading pairs on the TRON DEX marketplace. wallet/getmarketpairlist on TRON via Chainstack. TRON API method that retrieves all available trading pairs on the TRON DEX marketplace. This provides a comprehensive list of all token pairs that can be traded on the decentralized exchange. ## Parameters This method does not require any parameters. ## Response * `orderPair` — array of trading pair objects, each containing: * `sell_token_id` — token ID for the sell side of the pair (empty for TRX) * `buy_token_id` — token ID for the buy side of the pair (empty for TRX) ## Use case The `wallet/getmarketpairlist` method is used for: * Discovering all available trading pairs on TRON DEX. * Building trading interface dropdown menus and pair selection lists. * Analyzing market coverage and available token combinations. * Implementing comprehensive market monitoring across all pairs. * Creating trading pair validation for order placement systems. * Building market overview dashboards showing all tradeable assets. > [!NOTE] > > Trading pairs on TRON DEX consist of combinations between TRX and TRC-10 tokens. Empty token IDs represent TRX. Each pair represents a market where users can exchange one asset for another through the decentralized order book system. # wallet/getmarketpricebypair | TRON Source: https://docs.chainstack.com/reference/tron-getmarketpricebypair openapi/tron_node_api/getmarketpricebypair.json post /95e61622bf6a8af293978377718e3b77/wallet/getmarketpricebypair TRON API method that retrieves the current market price for a specific TRC10↔TRC10 trading pair on the TRON DEX. TRON via Chainstack. TRON API method that retrieves the current market price for a specific TRC10↔TRC10 trading pair on the TRON DEX. ## Parameters * `sell_token_id` — TRC10 token ID being sold, as a numeric string (for example, "1000006"). * `buy_token_id` — TRC10 token ID being purchased, as a numeric string. * `visible` — optional boolean. Some node builds require `visible: true`. ## Response * `sell_token_id` — token ID being sold * `buy_token_id` — token ID being purchased * `prices` — array of recent price points containing: * `sell_token_quantity` — quantity of sell token * `buy_token_quantity` — quantity of buy token * `timestamp` — when this price point was recorded ## Use case The `wallet/getmarketpricebypair` method is used for: * Getting current market rates for trading pair price calculations. * Building price tracking and charting applications for TRON DEX. * Implementing automated trading strategies based on market prices. * Displaying real-time pricing information in trading interfaces. * Analyzing price trends and market volatility for specific token pairs. ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getmarketpricebypair' \ --header 'Content-Type: application/json' \ --data '{ "sell_token_id": "1000006", "buy_token_id": "1000001", "visible": true }' ``` # merkle tree voucher info (client‑side) | TRON Source: https://docs.chainstack.com/reference/tron-getmerkletreevoucherinfo Retrieve merkle tree voucher information client‑side. Public TRON nodes generally do not expose an RPC for wallet/getmerkletreevoucherinfo. Retrieve merkle tree voucher information client‑side. Public TRON nodes generally do not expose an RPC for `wallet/getmerkletreevoucherinfo`; attempting to POST to that route returns HTTP 405. Build voucher data locally from shielded commitments and anchors. ## Inputs * `output_point` — the output commitment/point for which you need a Merkle path. * `ak` — authentication key (64‑hex) used in verification. * `nk` — nullifier key (64‑hex) used in verification. * `position` — leaf index in the commitment tree. ## Outputs * `voucher_info` — voucher data structure used in verification circuits. * `paths[]` — Merkle path elements (sibling hashes) from leaf to root. * `anchors[]` — accepted anchor(s) (tree roots) for the proof context. * `rt` — Merkle root. * `output_point_info` — metadata about the output commitment. ## How to build it locally 1. Maintain or query a local view of the shielded commitment tree (from blocks and shielded notes). 2. Locate the `position` (leaf index) for `output_point`. 3. Ascend the tree computing sibling hashes to assemble `paths[]` and the root `rt`. 4. Package `voucher_info` with `ak`, `nk`, `position`, `paths[]`, and `rt` according to your proof/verifier requirements. Consider using or extending an existing Sapling tree implementation. The derivation follows other Sapling‑based systems: compute diversifyHash where applicable, hash pairs per level, and collect siblings up to the root. ### Pseudocode sketch ```python theme={"system"} # Given: output_point (leaf), position, and a commitment tree abstraction def merkle_path(tree, position): path = [] node = tree.leaf(position) while node.parent is not None: path.append(node.sibling.hash) node = node.parent return path, node.hash # path elements, root paths, rt = merkle_path(commitment_tree, position) voucher = { 'voucher_info': { 'output_point': output_point }, 'paths': paths, 'anchors': [rt], 'rt': rt, 'output_point_info': { 'position': position } } ``` > [!NOTE] > > Public nodes typically return HTTP 405 for this route. Build voucher data client‑side using a local tree or a dedicated indexer, then submit proofs with your transaction flows. # wallet/getnextmaintenancetime | TRON Source: https://docs.chainstack.com/reference/tron-getnextmaintenancetime openapi/tron_node_api/getnextmaintenancetime.json post /95e61622bf6a8af293978377718e3b77/wallet/getnextmaintenancetime TRON API method that retrieves the timestamp for the next scheduled maintenance window. wallet/getnextmaintenancetime on TRON via Chainstack. TRON API method that retrieves the timestamp for the next scheduled maintenance window. Maintenance periods occur every 6 hours and are when witness elections, rewards distribution, and other network governance updates take effect. ## Parameters This method does not require any parameters. ## Response * `num` — timestamp of the next maintenance window in milliseconds since Unix epoch ## Use case The `wallet/getnextmaintenancetime` method is used for: * Planning witness operations and vote changes before maintenance periods. * Timing strategic voting decisions to take effect in the next election cycle. * Building maintenance schedule tracking for automated systems. * Coordinating network governance activities with maintenance windows. * Calculating time remaining until witness election results are finalized. > [!NOTE] > > TRON network maintenance occurs every 6 hours (at 00:00, 06:00, 12:00, and 18:00 UTC). During maintenance, witness rankings are updated, rewards are distributed, and governance changes take effect. The returned timestamp is in milliseconds. # wallet/getnodeinfo | TRON Source: https://docs.chainstack.com/reference/tron-getnodeinfo openapi/tron_node_api/getnodeinfo.json get /95e61622bf6a8af293978377718e3b77/wallet/getnodeinfo TRON API method that retrieves information about the current TRON node. This method provides details about the node's version, configuration, network status. TRON API method that retrieves information about the current TRON node. This method provides details about the node's version, configuration, network status, and operational parameters. ## Parameters This method does not require any parameters. ## Response * `beginSyncNum` — the block number from which synchronization started * `block` — current block height information * `solidityBlock` — current solidity block height information * `currentConnectCount` — number of current peer connections * `activeConnectCount` — number of active peer connections * `passiveConnectCount` — number of passive peer connections * `totalFlow` — total network traffic flow * `peerInfoList` — list of connected peer information * `configNodeInfo` — node configuration information * `machineInfo` — machine and system information * `cheatWitnessInfoMap` — information about witnesses flagged for cheating ## Use case The `wallet/getnodeinfo` method is used for: * Monitoring node health and synchronization status. * Checking network connectivity and peer connections. * Gathering node configuration and version information. * Debugging network issues and performance monitoring. # wallet/getnowblock | TRON Source: https://docs.chainstack.com/reference/tron-getnowblock openapi/tron_node_api/getnowblock.json post /95e61622bf6a8af293978377718e3b77/wallet/getnowblock TRON API method that retrieves the current latest block from the TRON blockchain. Available on TRON via Chainstack JSON-RPC nodes. TRON API method that retrieves the current latest block from the TRON blockchain. This method returns the most recently produced block with all its transactions and metadata. ## Parameters This method accepts no parameters. ## Response * `blockID` — the block hash identifier * `block_header` — block header information containing: * `raw_data` — raw block header data including: * `timestamp` — block generation timestamp * `txTrieRoot` — transaction trie root hash * `parentHash` — parent block hash * `number` — block height * `witness_address` — address of the block producer * `version` — block version * `witness_signature` — block producer's signature * `transactions` — array of transactions included in the block ## Use case The `wallet/getnowblock` method is used for: * Monitoring the latest state of the blockchain in real-time. * Retrieving the current block height and latest transactions. * Synchronizing applications with the blockchain's current state. * Implementing block explorers and monitoring tools. * Watching for new transactions as they are confirmed. # wallet/getpaginatedassetissuelist | TRON Source: https://docs.chainstack.com/reference/tron-getpaginatedassetissuelist openapi/tron_node_api/getpaginatedassetissuelist.json post /95e61622bf6a8af293978377718e3b77/wallet/getpaginatedassetissuelist TRON API method that retrieves a paginated list of all TRC10 tokens issued on the TRON network. wallet/getpaginatedassetissuelist on TRON via Chainstack. TRON API method that retrieves a paginated list of all TRC10 tokens issued on the TRON network. This method provides efficient pagination support for browsing through large numbers of tokens without overwhelming the response. ## Parameters * `offset` — number of records to skip from the beginning (starting point for pagination) * `limit` — maximum number of TRC10 tokens to return in the response ## Response * `assetIssue` — array of TRC10 token information objects, each containing: * `id` — unique token ID * `owner_address` — address of the token issuer * `name` — token name in hex format * `abbr` — token abbreviation in hex format * `total_supply` — total supply of the token * `trx_num` — TRX amount in exchange ratio * `precision` — token decimal places * `num` — token amount in exchange ratio * `start_time` — token sale start timestamp * `end_time` — token sale end timestamp * `description` — token description in hex format * `url` — token website URL in hex format * `free_asset_net_limit` — free bandwidth allocation per account * `public_free_asset_net_limit` — total public free bandwidth limit * `frozen_supply` — array of frozen token supply information ## Use case The `wallet/getpaginatedassetissuelist` method is used for: * Building comprehensive TRC10 token listings and directories. * Implementing paginated token browsers for better user experience. * Creating analytics dashboards that analyze token distribution patterns. * Developing token discovery platforms with efficient data loading. * Building portfolio trackers that need to browse available tokens. * Creating market data aggregators for TRC10 token ecosystems. # wallet/getpaginatedproposallist | TRON Source: https://docs.chainstack.com/reference/tron-getpaginatedproposallist openapi/tron_node_api/getpaginatedproposallist.json post /95e61622bf6a8af293978377718e3b77/wallet/getpaginatedproposallist TRON API method that retrieves governance proposals with pagination support, allowing efficient browsing of large proposal lists. On TRON. TRON API method that retrieves governance proposals with pagination support, allowing efficient browsing of large proposal lists. ## Parameters * `offset` — the starting index for pagination (default: 0). * `limit` — the maximum number of proposals to return (default: 10, max: 100). ## Response * `proposals` — array of proposal objects, each containing: * `proposal_id` — the unique identifier of the proposal * `proposer_address` — the address of the Super Representative who created the proposal * `parameters` — object containing the parameter changes proposed * `expiration_time` — timestamp when the proposal expires * `create_time` — timestamp when the proposal was created * `approvals` — array of Super Representatives who have approved the proposal * `state` — current state of the proposal (PENDING, DISAPPROVED, APPROVED, CANCELED) ## Use case The `wallet/getpaginatedproposallist` method is used for: * Implementing paginated proposal browsing in applications * Efficiently loading large proposal datasets * Building scalable governance interfaces with pagination * Creating proposal exploration tools with performance optimization # wallet/getpendingsize | TRON Source: https://docs.chainstack.com/reference/tron-getpendingsize openapi/tron_node_api/getpendingsize.json get /95e61622bf6a8af293978377718e3b77/wallet/getpendingsize TRON API method that returns the current size of the pending transaction pool (mempool). wallet/getpendingsize on TRON via Chainstack. TRON API method that returns the current size of the pending transaction pool (mempool). This provides insight into network congestion and the number of unconfirmed transactions awaiting processing. ## Parameters This method requires no parameters. It returns the current size of the pending transaction pool. ## Response * `pendingSize` — number representing the current count of pending transactions in the mempool ## Use case The `wallet/getpendingsize` method is used for: * Monitoring network congestion by tracking the number of pending transactions. * Building network analytics dashboards that display mempool statistics. * Implementing adaptive fee strategies based on current network load. * Creating alerts for high network congestion periods. * Optimizing transaction timing for applications based on mempool size. > [!NOTE] > > The pending pool size fluctuates constantly as new transactions are submitted and existing ones are confirmed or dropped. A larger pending size typically indicates higher network congestion and may suggest longer confirmation times. # wallet/getproposalbyid | TRON Source: https://docs.chainstack.com/reference/tron-getproposalbyid openapi/tron_node_api/getproposalbyid.json post /95e61622bf6a8af293978377718e3b77/wallet/getproposalbyid TRON API method that retrieves detailed information about a specific governance proposal using its ID. Use it on TRON via Chainstack. TRON API method that retrieves detailed information about a specific governance proposal using its ID. ## Parameters * `id` — the ID of the proposal to retrieve information for. ## Response * `proposal_id` — the unique identifier of the proposal * `proposer_address` — the address of the Super Representative who created the proposal * `parameters` — object containing the parameter changes proposed * `expiration_time` — timestamp when the proposal expires * `create_time` — timestamp when the proposal was created * `approvals` — array of Super Representatives who have approved the proposal * `state` — current state of the proposal (PENDING, DISAPPROVED, APPROVED, CANCELED) ## Use case The `wallet/getproposalbyid` method is used for: * Retrieving detailed proposal information for governance interfaces * Checking proposal status and approval progress * Monitoring proposal parameters and voting results * Building governance dashboards and voting applications # wallet/getReward | TRON Source: https://docs.chainstack.com/reference/tron-getreward openapi/tron_node_api/getreward.json GET /95e61622bf6a8af293978377718e3b77/wallet/getReward TRON API method that retrieves witness rewards information for a specific account. Available on TRON via Chainstack JSON-RPC nodes. TRON API method that retrieves witness rewards information for a specific account. This includes both unclaimed voting rewards and witness block production rewards. ## Parameters * `address` — account address in hex format (21‑byte, starts with `41`). ## Response * `reward` — total amount of TRX rewards available for withdrawal (in sun units, where 1 TRX = 1,000,000 sun) ## Use case The `wallet/getReward` method is used for: * Checking available voting rewards before claiming them. * Monitoring reward accumulation from witness voting activities. * Building reward tracking dashboards for voter accounts. * Calculating potential returns from voting for specific witnesses. * Automating reward claim notifications and processes. ## curl example ```shell Shell theme={"system"} curl --request GET \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getReward?address=41b487cdb2d8dc7b2a8e5e7e7b4e3e8b8b8b8b8b' \ --header 'accept: application/json' ``` > [!NOTE] > > rewards are returned in sun (1 TRX = 1,000,000 sun). If you have a base58 address, convert it to a 21‑byte hex address (leading `41…`) before calling this endpoint. Claim rewards with [`wallet/withdrawbalance`](/reference/tron-withdrawbalance). # wallet/gettransactionbyid | TRON Source: https://docs.chainstack.com/reference/tron-gettransactionbyid openapi/tron_node_api/gettransactionbyid.json post /95e61622bf6a8af293978377718e3b77/wallet/gettransactionbyid TRON API method that retrieves transaction details by transaction ID. This method returns comprehensive information about a specific transaction. TRON API method that retrieves transaction details by transaction ID. This method returns comprehensive information about a specific transaction, including its execution results, resource consumption, and contract details. ## Parameters * `value` — the transaction ID (hash) to retrieve * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `ret` — array of execution results containing: * `contractRet` — contract execution result (SUCCESS, REVERT, etc.) * `fee` — transaction fee paid * `signature` — array of transaction signatures * `txID` — transaction ID hash * `net_usage` — bandwidth consumed * `raw_data_hex` — hexadecimal representation of raw transaction data * `net_fee` — network fee paid * `energy_usage` — energy consumed (for smart contracts) * `blockNumber` — block number containing the transaction * `block_timestamp` — block timestamp * `contract_result` — contract execution return data (for smart contracts) ## Use case The `wallet/gettransactionbyid` method is used for: * Checking transaction status and execution results after broadcasting. * Retrieving detailed transaction information for wallets and explorers. * Analyzing transaction fees and resource consumption. * Debugging smart contract execution and validating transaction outcomes. # wallet/gettransactionfrompending | TRON Source: https://docs.chainstack.com/reference/tron-gettransactionfrompending openapi/tron_node_api/gettransactionfrompending.json post /95e61622bf6a8af293978377718e3b77/wallet/gettransactionfrompending TRON API method that retrieves a specific pending transaction from the mempool by its transaction ID. Use it on TRON via Chainstack. TRON API method that retrieves a specific pending transaction from the mempool by its transaction ID. This allows applications to check the status and details of an unconfirmed transaction. ## Parameters * `value` — transaction ID hash of the pending transaction to retrieve ## Response * `txID` — unique transaction identifier hash * `raw_data` — raw transaction data containing: * `contract` — array with transaction contract details * `ref_block_bytes` — reference block bytes for validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `fee_limit` — maximum fee allowed for this transaction * `signature` — array of transaction signatures * `ret` — transaction result information (if available) ## Use case The `wallet/gettransactionfrompending` method is used for: * Checking the status of a specific pending transaction by its ID. * Retrieving detailed information about unconfirmed transactions for verification. * Building transaction tracking systems that monitor pending transactions. * Validating transaction parameters before confirmation in custom applications. * Implementing transaction status polling for user interfaces and notifications. > [!NOTE] > > If the transaction is no longer in the pending pool (either confirmed or dropped), this method will return an error. Use this method immediately after broadcasting a transaction for best results. # wallet/gettransactioninfobyblocknum | TRON Source: https://docs.chainstack.com/reference/tron-gettransactioninfobyblocknum openapi/tron_node_api/gettransactioninfobyblocknum.json post /95e61622bf6a8af293978377718e3b77/wallet/gettransactioninfobyblocknum TRON API method that retrieves detailed transaction information for all transactions within a specific block number. TRON via Chainstack. TRON API method that retrieves detailed transaction information for all transactions within a specific block number. ## Parameters * `num` — the block number to retrieve transaction information from * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * Array of transaction info objects, each containing: * `id` — transaction hash * `fee` — transaction fee in sun * `blockNumber` — block number containing the transaction * `blockTimeStamp` — block timestamp * `contractResult` — contract execution result * `receipt` — transaction receipt with energy usage and result * `log` — event logs from smart contract execution ## Use case The `wallet/gettransactioninfobyblocknum` method is used for: * Analyzing all transaction details within a specific block * Building block explorers with detailed transaction information * Monitoring transaction fees and energy consumption patterns * Auditing smart contract execution results in a block * Creating comprehensive transaction analysis tools # wallet/gettransactioninfobyid | TRON Source: https://docs.chainstack.com/reference/tron-gettransactioninfobyid openapi/tron_node_api/gettransactioninfobyid.json post /95e61622bf6a8af293978377718e3b77/wallet/gettransactioninfobyid TRON API method that retrieves detailed transaction receipt and execution information by transaction ID. Chainstack TRON reference. TRON API method that retrieves detailed transaction receipt and execution information by transaction ID. This method provides comprehensive data about a transaction's execution status, resource usage, fees, and any smart contract events that occurred. ## Parameters * `value` — the transaction ID (hash) in hexadecimal format ## Response * `id` — transaction ID (hash) * `blockNumber` — block number where the transaction was included * `blockTimeStamp` — timestamp when the block was created (milliseconds) * `contractResult` — array of smart contract execution results in hex format * `contract_address` — address of the deployed contract (for contract creation transactions) * `receipt` — transaction receipt containing: * `energy_usage` — total energy consumed by the transaction * `energy_fee` — energy fee paid in SUN (1 TRX = 1,000,000 SUN) * `origin_energy_usage` — original energy usage before optimizations * `energy_usage_total` — total energy usage including all operations * `net_usage` — bandwidth consumed by the transaction * `net_fee` — bandwidth fee paid in SUN * `result` — execution result status (SUCCESS, REVERT, etc.) * `log` — array of event logs emitted by smart contracts during execution * `result` — overall transaction execution result * `resMessage` — error message if transaction failed (hex-encoded) ## Use case The `wallet/gettransactioninfobyid` method is used for: * Getting detailed transaction execution information and resource consumption data. * Monitoring transaction success/failure status and error messages. * Analyzing smart contract execution results and event logs. * Calculating actual fees paid for energy and bandwidth usage. * Building transaction explorers and analytics tools that need execution details. * Debugging smart contract interactions by examining logs and results. # wallet/gettransactionlistfrompending | TRON Source: https://docs.chainstack.com/reference/tron-gettransactionlistfrompending openapi/tron_node_api/gettransactionlistfrompending.json get /95e61622bf6a8af293978377718e3b77/wallet/gettransactionlistfrompending TRON API method that retrieves a list of all pending transactions currently in the mempool. wallet/gettransactionlistfrompending on TRON via Chainstack. TRON API method that retrieves a list of all pending transactions currently in the mempool. This allows applications to monitor unconfirmed transactions awaiting inclusion in a block. ## Parameters This method requires no parameters. It returns all pending transactions in the current mempool. ## Response * `transactions` — array of pending transaction objects, each containing: * `txID` — unique transaction identifier hash * `raw_data` — raw transaction data including: * `contract` — array with transaction contract details * `ref_block_bytes` — reference block bytes for validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `fee_limit` — maximum fee allowed for this transaction * `signature` — array of transaction signatures * `ret` — transaction result information (if available) ## Use case The `wallet/gettransactionlistfrompending` method is used for: * Monitoring pending transactions in real-time for transaction status tracking. * Building transaction pool analytics and network congestion monitoring tools. * Implementing custom mempool explorers and pending transaction dashboards. * Analyzing transaction patterns and fee structures before confirmation. * Creating alerts for specific pending transactions or transaction types. > [!NOTE] > > The pending transaction pool is dynamic and constantly changing as transactions get confirmed or dropped. The response represents a snapshot at the time of the request and may vary between consecutive calls. # jsonrpc eth_blockNumber | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-blocknumber openapi/tron_node_api/jsonrpc_eth_blocknumber.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the current block number in hexadecimal format. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns the current block number, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_blockNumber") * `params` — array containing method parameters (empty for this method) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the current block number in hexadecimal format ## Use case The `jsonrpc eth_blockNumber` method is used for: * Monitoring the current state of the TRON blockchain through Ethereum-compatible interfaces * Implementing block synchronization logic in Web3 applications * Building real-time dashboards that display current blockchain height * Validating transaction confirmations by comparing block numbers # jsonrpc eth_call | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-call openapi/tron_node_api/jsonrpc_eth_call.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON eth_call JSON-RPC method — execute a read-only message call on the TRON blockchain without creating a transaction, via the Ethereum-compatible interface. TRON API method that executes a contract function call without sending a transaction, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_call") * `params` — array containing method parameters * `transaction` — transaction object containing to, from, gas, gasPrice, value, and data fields * `blockParameter` — only `"latest"` is supported; TRON has no historical state queries, see [Historical data availability](/docs/tron-tooling#historical-data-availability) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the return value of the executed contract function as hex string ## Use case The `jsonrpc eth_call` method is used for: * Reading data from smart contracts without spending gas through Web3 interfaces * Implementing view/pure function calls in dApps and frontend applications * Testing contract interactions and validating parameters before sending transactions * Building real-time data displays that query contract state continuously # jsonrpc web3_clientVersion | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-clientversion openapi/tron_node_api/jsonrpc_web3_clientVersion.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the current client version string. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns the current client version string, providing an Ethereum-compatible interface for identifying TRON node software and version information. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "web3\_clientVersion") * `params` — empty array (no parameters required) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — string containing the client version information ## Use case The `jsonrpc web3_clientVersion` method is used for: * Identifying the TRON client software and version for compatibility checks * Implementing version-specific logic in dApps that need to handle different client capabilities * Building monitoring and diagnostic tools that track node software versions through Web3 interfaces * Creating support systems that can provide version-specific assistance and troubleshooting # jsonrpc eth_estimateGas | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-estimategas openapi/tron_node_api/jsonrpc_eth_estimateGas.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that estimates the amount of gas needed to execute a transaction. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that estimates the gas required to execute a transaction, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_estimateGas") * `params` — array containing method parameters * `transaction` — transaction object containing to, from, gas, gasPrice, value, and data fields * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the estimated gas amount as hex string ## Use case The `jsonrpc eth_estimateGas` method is used for: * Calculating gas requirements before sending transactions through Web3 interfaces * Building transaction fee estimation features in wallets and dApps * Optimizing gas usage and preventing failed transactions due to insufficient gas * Implementing dynamic gas pricing and transaction cost calculations # jsonrpc eth_gasPrice | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-gasprice openapi/tron_node_api/jsonrpc_eth_gasPrice.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the current gas price in wei. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns the current gas price for transactions, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_gasPrice") * `params` — array containing method parameters (empty for this method) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the current gas price in wei as hex string ## Use case The `jsonrpc eth_gasPrice` method is used for: * Retrieving current gas pricing information through Web3 interfaces * Implementing dynamic gas price adjustments in wallets and dApps * Building transaction cost calculators and fee estimation tools * Monitoring gas price trends for optimal transaction timing # jsonrpc eth_getBalance | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getbalance openapi/tron_node_api/jsonrpc_eth_getbalance.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the TRX balance of an account. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns the TRX balance of a specified account in wei format, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getBalance") * `params` — array containing method parameters * `address` — the account address to check balance for * `blockParameter` — only `"latest"` is supported; TRON has no historical state queries, see [Historical data availability](/docs/tron-tooling#historical-data-availability) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the account balance in wei (as hexadecimal string) ## Use case The `jsonrpc eth_getBalance` method is used for: * Checking TRX account balances through Web3-compatible interfaces in wallets and dApps * Implementing balance validation logic before executing transactions * Building portfolio tracking and financial analysis tools for TRON assets * Monitoring account balances for payment processing and settlement systems # jsonrpc eth_getBlockByHash | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getblockbyhash openapi/tron_node_api/jsonrpc_eth_getBlockByHash.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns block information by block hash. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns detailed block information by block hash, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getBlockByHash") * `params` — array containing method parameters * `blockHash` — the block hash as a hex string * `fullTransactionObjects` — boolean indicating whether to return full transaction objects (true) or just transaction hashes (false) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — block object containing block information including hash, number, parent hash, timestamp, transactions, and other block metadata ## Use case The `jsonrpc eth_getBlockByHash` method is used for: * Retrieving specific block information when you know the block hash through Web3 interfaces * Verifying block data integrity and exploring blockchain structure * Building block explorer functionality that allows hash-based block lookups * Implementing transaction confirmation logic that tracks specific blocks # jsonrpc eth_getBlockByNumber | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getblockbynumber openapi/tron_node_api/jsonrpc_eth_getBlockByNumber.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns block information by block number. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns detailed block information by block number, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getBlockByNumber") * `params` — array containing method parameters * `blockNumber` — the block number in hexadecimal format or "latest", "earliest", "pending" * `fullTransactionObjects` — boolean indicating whether to return full transaction objects (true) or just transaction hashes (false) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — block object containing block information including hash, number, parent hash, timestamp, transactions, and other block metadata ## Use case The `jsonrpc eth_getBlockByNumber` method is used for: * Retrieving detailed block information for analysis and monitoring through Web3 interfaces * Implementing block explorers and blockchain data visualization tools * Validating transaction inclusion and block confirmations in dApps * Building synchronization mechanisms for off-chain systems tracking TRON blockchain state # jsonrpc eth_getBlockTransactionCountByHash | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getblocktransactioncountbyhash openapi/tron_node_api/jsonrpc_eth_getBlockTransactionCountByHash.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the number of transactions in a block by block hash using Ethereum-compatible JSON-RPC format. On TRON. TRON API method that returns the number of transactions in a block by block hash using Ethereum-compatible JSON-RPC format. This method provides Ethereum tooling compatibility for block transaction counting on TRON. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getBlockTransactionCountByHash") * `params` — array containing method parameters * `blockHash` — the hash of the target block as a hexadecimal string * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the number of transactions in the block as a hexadecimal string ## Use case The `jsonrpc eth_getBlockTransactionCountByHash` method is used for: * Getting transaction count for specific blocks using Ethereum-compatible tools * Analyzing block capacity and transaction throughput on TRON * Supporting Ethereum-based block explorers and analytics tools on TRON * Enabling existing Ethereum applications to work with TRON blockchain data # jsonrpc eth_getBlockTransactionCountByNumber | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getblocktransactioncountbynumber openapi/tron_node_api/jsonrpc_eth_getBlockTransactionCountByNumber.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the number of transactions in a block by block number using Ethereum-compatible JSON-RPC format. On TRON. TRON API method that returns the number of transactions in a block by block number using Ethereum-compatible JSON-RPC format. This method provides Ethereum tooling compatibility for block transaction counting on TRON. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getBlockTransactionCountByNumber") * `params` — array containing method parameters * `blockNumber` — the block number as a hexadecimal string or block tag ("latest", "earliest", "pending") * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the number of transactions in the block as a hexadecimal string ## Use case The `jsonrpc eth_getBlockTransactionCountByNumber` method is used for: * Getting transaction count for blocks by number using Ethereum-compatible tools * Monitoring blockchain activity and transaction throughput on TRON * Supporting Ethereum-based analytics and monitoring applications on TRON * Enabling seamless integration of existing Ethereum tools with TRON # jsonrpc eth_getCode | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getcode openapi/tron_node_api/jsonrpc_eth_getCode.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the contract code at a given address. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns the bytecode of a smart contract at a specified address, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getCode") * `params` — array containing method parameters * `address` — the contract address to retrieve code from * `blockParameter` — only `"latest"` is supported; TRON has no historical state queries, see [Historical data availability](/docs/tron-tooling#historical-data-availability) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the contract bytecode as a hex string (returns "0x" for non-contract addresses) ## Use case The `jsonrpc eth_getCode` method is used for: * Verifying smart contract deployment and retrieving contract bytecode through Web3 interfaces * Building contract verification and analysis tools for TRON smart contracts * Implementing contract existence checks in dApps and development tools * Developing security analysis tools that examine contract code patterns # jsonrpc eth_getFilterChanges | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getfilterchanges openapi/tron_node_api/jsonrpc_eth_getFilterChanges.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that polls for filter changes and returns new logs or blocks. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that polls for changes on a previously created filter and returns new logs or block hashes, providing an Ethereum-compatible interface for monitoring TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getFilterChanges") * `params` — array containing method parameters * `filterId` — the filter ID returned by eth\_newFilter or eth\_newBlockFilter * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — array of log objects (for log filters) or block hashes (for block filters) ## Use case The `jsonrpc eth_getFilterChanges` method is used for: * Polling for new logs or blocks that match previously created filter criteria * Building efficient event monitoring systems that check for updates periodically * Implementing real-time dApp functionality through Web3 interfaces without constant re-filtering * Creating blockchain monitoring applications that track specific events or block production # jsonrpc eth_getFilterLogs | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getfilterlogs openapi/tron_node_api/jsonrpc_eth_getFilterLogs.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns all logs matching a specific filter. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns all logs matching a previously created filter, providing an Ethereum-compatible interface for retrieving historical TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getFilterLogs") * `params` — array containing method parameters * `filterId` — the filter ID returned by eth\_newFilter * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — array of all log objects that match the filter criteria ## Use case The `jsonrpc eth_getFilterLogs` method is used for: * Retrieving all historical logs that match a previously created filter * Building comprehensive audit trails and analytics for smart contract interactions * Implementing batch processing systems that need complete log datasets through Web3 interfaces * Creating blockchain explorers and reporting tools that analyze contract event histories # jsonrpc eth_getLogs | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getlogs openapi/tron_node_api/jsonrpc_eth_getLogs.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns an array of logs matching the specified filter criteria. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that retrieves event logs based on filter criteria, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getLogs") * `params` — array containing method parameters * `filterObject` — object containing filter criteria including fromBlock, toBlock, address, and topics * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — array of log objects matching the filter criteria ## Use case The `jsonrpc eth_getLogs` method is used for: * Retrieving smart contract event logs for dApp functionality through Web3 interfaces * Building event-driven applications that react to on-chain activities * Implementing transaction and event monitoring systems for compliance and analytics * Creating blockchain explorers and audit tools that track contract interactions # jsonrpc eth_getStorageAt | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getstorageat openapi/tron_node_api/jsonrpc_eth_getStorageAt.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the value from a storage position at a given address. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns the value stored at a specific storage position of a smart contract, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getStorageAt") * `params` — array containing method parameters * `address` — the contract address to read storage from * `position` — the storage position (slot) as hex string * `blockParameter` — only `"latest"` is supported; TRON has no historical state queries, see [Historical data availability](/docs/tron-tooling#historical-data-availability) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the value at the storage position as a hex string ## Use case The `jsonrpc eth_getStorageAt` method is used for: * Reading smart contract state variables directly through Web3 interfaces * Debugging and analyzing contract storage layout and values * Building advanced dApp functionality that requires direct storage access * Implementing contract monitoring tools that track state changes # jsonrpc eth_getTransactionByBlockHashAndIndex | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-gettransactionbyblockhashandindex openapi/tron_node_api/jsonrpc_eth_getTransactionByBlockHashAndIndex.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns information about a transaction by block hash and transaction index using Ethereum-compatible JSON-RPC format. TRON API method that returns information about a transaction by block hash and transaction index using Ethereum-compatible JSON-RPC format. This method provides Ethereum tooling compatibility for retrieving specific transactions from blocks on TRON. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getTransactionByBlockHashAndIndex") * `params` — array containing method parameters * `blockHash` — the hash of the target block as a hexadecimal string * `transactionIndex` — the transaction index position in the block as a hexadecimal string * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — transaction object with Ethereum-compatible fields or null if not found ## Use case The `jsonrpc eth_getTransactionByBlockHashAndIndex` method is used for: * Retrieving specific transactions from blocks using Ethereum-compatible tools * Building block explorers and transaction analysis tools for TRON * Supporting Ethereum-based indexing and analytics applications on TRON * Enabling precise transaction lookups by block hash and position # jsonrpc eth_getTransactionByBlockNumberAndIndex | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-gettransactionbyblocknumberandindex openapi/tron_node_api/jsonrpc_eth_getTransactionByBlockNumberAndIndex.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns information about a transaction by block number and transaction index using Ethereum-compatible JSON-RPC format. TRON API method that returns information about a transaction by block number and transaction index using Ethereum-compatible JSON-RPC format. This method provides Ethereum tooling compatibility for retrieving specific transactions from blocks on TRON. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getTransactionByBlockNumberAndIndex") * `params` — array containing method parameters * `blockNumber` — the block number as a hexadecimal string or block tag ("latest", "earliest", "pending") * `transactionIndex` — the transaction index position in the block as a hexadecimal string * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — transaction object with Ethereum-compatible fields or null if not found ## Use case The `jsonrpc eth_getTransactionByBlockNumberAndIndex` method is used for: * Retrieving specific transactions from blocks by number using Ethereum-compatible tools * Building transaction indexing and monitoring systems for TRON * Supporting Ethereum-based analytics and block explorer applications on TRON * Enabling efficient transaction lookups by block number and position # jsonrpc eth_getTransactionByHash | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-gettransactionbyhash openapi/tron_node_api/jsonrpc_eth_getTransactionByHash.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns transaction information by transaction hash. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns detailed transaction information by transaction hash, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getTransactionByHash") * `params` — array containing method parameters * `transactionHash` — the transaction hash as a hex string * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — transaction object containing transaction details including hash, from, to, value, gas, and other transaction metadata ## Use case The `jsonrpc eth_getTransactionByHash` method is used for: * Retrieving detailed transaction information for verification and monitoring through Web3 interfaces * Implementing transaction tracking functionality in dApps and wallets * Building payment confirmation systems that verify transaction details * Developing blockchain analytics tools that analyze transaction patterns # jsonrpc eth_getTransactionReceipt | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-gettransactionreceipt openapi/tron_node_api/jsonrpc_eth_getTransactionReceipt.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the receipt of a transaction by transaction hash. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns the transaction receipt containing execution results and event logs, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getTransactionReceipt") * `params` — array containing method parameters * `transactionHash` — the transaction hash as a hex string * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — receipt object containing transaction execution results including status, gas used, logs, and contract address (if applicable) ## Use case The `jsonrpc eth_getTransactionReceipt` method is used for: * Confirming transaction execution status and retrieving execution results through Web3 interfaces * Extracting event logs from smart contract interactions for dApp functionality * Calculating actual gas costs and monitoring transaction efficiency * Building transaction confirmation systems that verify successful execution # jsonrpc eth_getWork | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-getwork openapi/tron_node_api/jsonrpc_eth_getWork.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the hash of the current block, the seed hash, and the boundary condition using Ethereum-compatible JSON-RPC format. TRON API method that returns the hash of the current block, the seed hash, and the boundary condition using Ethereum-compatible JSON-RPC format. This method provides Ethereum tooling compatibility for mining-related operations on TRON. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_getWork") * `params` — parameters array (empty for this method) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — array containing work data: \[current block hash, seed hash, boundary condition] or null ## Use case The `jsonrpc eth_getWork` method is used for: * Supporting Ethereum-compatible mining pool integration on TRON * Providing work data for proof-of-work mining operations with Ethereum tools * Enabling existing Ethereum mining software to work with TRON networks * Supporting mining pool management and monitoring applications # jsonrpc net_listening | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-listening openapi/tron_node_api/jsonrpc_net_listening.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON net_listening JSON-RPC method — check if the TRON client is actively accepting network connections via the Ethereum-compatible JSON-RPC interface. TRON API method that returns whether the client is actively listening for network connections, providing an Ethereum-compatible interface for checking TRON node connectivity status. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "net\_listening") * `params` — empty array (no parameters required) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — boolean value indicating whether the client is listening for connections ## Use case The `jsonrpc net_listening` method is used for: * Checking if the TRON node is actively listening for network connections * Implementing health checks and monitoring systems for node connectivity through Web3 interfaces * Building node diagnostics tools that verify network participation status * Creating resilient dApp architectures that can detect and respond to node connectivity issues # jsonrpc net_version | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-net-version openapi/tron_node_api/jsonrpc_net_version.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the network ID using Ethereum-compatible JSON-RPC format. jsonrpc net_version on TRON via Chainstack. TRON API method that returns the network ID using Ethereum-compatible JSON-RPC format. This provides network identification for TRON using familiar Ethereum network conventions. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "net\_version") * `params` — parameters array (empty for this method) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the network ID as a string ## Use case The `jsonrpc net_version` method is used for: * Identifying the TRON network (mainnet/testnet) for Ethereum-compatible applications * Supporting network-specific configuration in wallets and tools * Enabling proper network detection for multi-chain applications * Providing network validation for transaction routing # jsonrpc eth_newBlockFilter | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-newblockfilter openapi/tron_node_api/jsonrpc_eth_newBlockFilter.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that creates a filter object for new block notifications. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that creates a filter object for receiving new block notifications, providing an Ethereum-compatible interface for monitoring TRON blockchain blocks. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_newBlockFilter") * `params` — empty array (no parameters required) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — a filter ID (hex-encoded string) for use with other filter methods ## Use case The `jsonrpc eth_newBlockFilter` method is used for: * Creating filter objects to receive notifications about new blocks as they are mined * Building real-time blockchain monitoring applications through Web3 interfaces * Implementing block-based event systems for dApps that need to react to new blocks * Setting up efficient polling mechanisms for blockchain synchronization and data processing # jsonrpc eth_newFilter | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-newfilter openapi/tron_node_api/jsonrpc_eth_newFilter.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that creates a filter object for event log notifications. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that creates a filter object for receiving event log notifications, providing an Ethereum-compatible interface for monitoring TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_newFilter") * `params` — array containing method parameters * `filterObject` — object containing filter criteria including address, fromBlock, toBlock, and topics * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — a filter ID (hex-encoded string) for use with other filter methods ## Use case The `jsonrpc eth_newFilter` method is used for: * Creating filter objects to receive notifications about new logs that match specific criteria * Setting up event monitoring for smart contract interactions through Web3 interfaces * Building real-time dApp functionality that responds to blockchain events * Implementing efficient event polling systems for transaction monitoring and analytics # jsonrpc net_peerCount | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-peercount openapi/tron_node_api/jsonrpc_net_peerCount.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the number of peers connected to the client. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns the number of peers currently connected to the client, providing an Ethereum-compatible interface for monitoring TRON network connectivity. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "net\_peerCount") * `params` — empty array (no parameters required) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — hex-encoded string representing the number of connected peers ## Use case The `jsonrpc net_peerCount` method is used for: * Monitoring the number of peers connected to the TRON node for network health assessment * Implementing node diagnostics and monitoring systems through Web3 interfaces * Building network analysis tools that track peer connectivity and network participation * Creating alerts and monitoring dashboards for node operators and infrastructure managers # jsonrpc eth_protocolVersion | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-protocolversion openapi/tron_node_api/jsonrpc_eth_protocolVersion.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the current Ethereum protocol version using Ethereum-compatible JSON-RPC format. TRON via Chainstack. TRON API method that returns the current Ethereum protocol version using Ethereum-compatible JSON-RPC format. This method provides Ethereum tooling compatibility for protocol version identification on TRON. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_protocolVersion") * `params` — parameters array (empty for this method) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the current Ethereum protocol version as a hexadecimal string ## Use case The `jsonrpc eth_protocolVersion` method is used for: * Identifying the Ethereum protocol version for compatibility with Ethereum tools * Supporting multi-protocol applications that need to adapt to different versions * Enabling Ethereum-compatible wallet and dApp integration with TRON * Providing protocol version information for debugging and development tools # jsonrpc eth_sendRawTransaction | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-sendrawtransaction TRON API — eth_sendRawTransaction is not supported on TRON's JSON-RPC endpoint. Use the native /wallet/broadcasttransaction endpoint instead. > [!WARNING] > > ### Method not supported > > The `eth_sendRawTransaction` method is **not available** on TRON's `/jsonrpc` endpoint. TRON's JSON-RPC implementation provides limited Ethereum compatibility for **read operations only**. > > To broadcast transactions on TRON, use the native [broadcasttransaction](/reference/tron-broadcasttransaction) method via the `/wallet` endpoint. ## Why this method is not supported TRON's JSON-RPC endpoint does not support transaction submission methods because: * TRON uses a different transaction format (Protobuf) than Ethereum (RLP-encoded) * TRON addresses use Base58 encoding natively, not hex * Transaction nonces work differently on TRON ## Alternative: Use TronWeb.js To send transactions on TRON, use [TronWeb.js](https://tronweb.network/) with your Chainstack node: ```js theme={"system"} const { TronWeb } = require('tronweb'); const tronWeb = new TronWeb({ fullHost: 'YOUR_CHAINSTACK_ENDPOINT', privateKey: 'YOUR_PRIVATE_KEY' }); async function sendTRX() { const transaction = await tronWeb.transactionBuilder.sendTrx( 'RECIPIENT_ADDRESS', 1000000, // Amount in SUN (1 TRX = 1,000,000 SUN) tronWeb.defaultAddress.base58 ); const signedTx = await tronWeb.trx.sign(transaction); const result = await tronWeb.trx.sendRawTransaction(signedTx); console.log('Transaction ID:', result.txid); } sendTRX(); ``` where YOUR\_CHAINSTACK\_ENDPOINT is your TRON node **base** endpoint without the `/jsonrpc`, `/wallet`, or `/walletsolidity` postfixes. See also: * [TRON tooling](/docs/tron-tooling) for complete examples * [broadcasttransaction](/reference/tron-broadcasttransaction) for the native TRON API # jsonrpc web3_sha3 | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-sha3 openapi/tron_node_api/jsonrpc_web3_sha3.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the Keccak-256 hash of the given data. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that returns the Keccak-256 hash of the given data, providing an Ethereum-compatible interface for cryptographic hashing on TRON blockchain. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "web3\_sha3") * `params` — array containing method parameters * `data` — hex-encoded data to be hashed * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — hex-encoded Keccak-256 hash of the input data ## Use case The `jsonrpc web3_sha3` method is used for: * Computing Keccak-256 hashes for data verification and integrity checks * Generating hash values for smart contract function signatures and event topics through Web3 interfaces * Implementing cryptographic operations in dApps that require consistent hashing algorithms * Creating data fingerprints and merkle tree components for blockchain applications # jsonrpc eth_syncing | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-syncing openapi/tron_node_api/jsonrpc_eth_syncing.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns an object describing the sync status or false if not syncing, using Ethereum-compatible JSON-RPC format. TRON API method that returns an object describing the sync status or false if not syncing, using Ethereum-compatible JSON-RPC format. This method provides Ethereum tooling compatibility for synchronization status monitoring on TRON. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_syncing") * `params` — parameters array (empty for this method) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — sync status object with startingBlock, currentBlock, highestBlock fields, or false if not syncing ## Use case The `jsonrpc eth_syncing` method is used for: * Monitoring blockchain synchronization status with Ethereum-compatible tools * Supporting node health checks and monitoring applications on TRON * Enabling Ethereum-based infrastructure monitoring tools to work with TRON * Providing sync status information for wallet and dApp integration # jsonrpc eth_uninstallFilter | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-uninstallfilter openapi/tron_node_api/jsonrpc_eth_uninstallFilter.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that uninstalls a previously created filter. This method provides Ethereum-compatible interface for TRON blockchain. TRON API method that uninstalls a previously created filter and frees up resources, providing an Ethereum-compatible interface for filter lifecycle management on TRON blockchain. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "eth\_uninstallFilter") * `params` — array containing method parameters * `filterId` — the filter ID returned by eth\_newFilter or eth\_newBlockFilter * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — boolean value indicating whether the filter was successfully uninstalled ## Use case The `jsonrpc eth_uninstallFilter` method is used for: * Properly cleaning up filter resources when they are no longer needed * Implementing good resource management practices in dApp development through Web3 interfaces * Preventing memory leaks and resource exhaustion in long-running blockchain monitoring applications * Managing filter lifecycle as part of efficient event monitoring systems # jsonrpc net_version | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-version openapi/tron_node_api/jsonrpc_net_version.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON net_version JSON-RPC method — retrieve the current TRON network ID for chain identification via the Ethereum-compatible JSON-RPC interface on Chainstack. TRON API method that returns the network ID of the TRON network, providing an Ethereum-compatible interface for accessing TRON blockchain data. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "net\_version") * `params` — array containing method parameters (empty for this method) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the network ID as a string ## Use case The `jsonrpc net_version` method is used for: * Identifying the TRON network through Web3-compatible interfaces * Implementing network validation logic in multi-chain applications * Building network-aware dApps that adapt behavior based on the connected network * Ensuring transaction safety by verifying the correct network before operations # jsonrpc web3_clientVersion | TRON Source: https://docs.chainstack.com/reference/tron-jsonrpc-web3-clientversion openapi/tron_node_api/jsonrpc_web3_clientVersion.json POST /95e61622bf6a8af293978377718e3b77/jsonrpc TRON API method that returns the client version using Ethereum-compatible JSON-RPC format. jsonrpc web3_clientVersion on TRON via Chainstack. TRON API method that returns the client version using Ethereum-compatible JSON-RPC format. This provides information about the TRON node software version and capabilities. ## Parameters * `jsonrpc` — the JSON-RPC protocol version (always "2.0") * `method` — the method name (always "web3\_clientVersion") * `params` — parameters array (empty for this method) * `id` — request identifier (number or string) ## Response * `jsonrpc` — the JSON-RPC protocol version ("2.0") * `id` — the request identifier that matches the request * `result` — the client version string ## Use case The `jsonrpc web3_clientVersion` method is used for: * Identifying TRON node software version for compatibility checks * Supporting Ethereum-compatible client identification * Enabling version-specific functionality in applications * Providing debugging information for development and support # wallet/listexchanges | TRON Source: https://docs.chainstack.com/reference/tron-listexchanges openapi/tron_node_api/listexchanges.json post /95e61622bf6a8af293978377718e3b77/wallet/listexchanges TRON API method that retrieves a list of all trading pairs available on the TRON decentralized exchange. This method does not require any parameters. TRON API method that retrieves a list of all trading pairs available on the TRON decentralized exchange. ## Parameters This method does not require any parameters. ## Response Returns an array of exchange objects, each containing: * `exchange_id` — unique identifier for the exchange pair * `creator_address` — address that created the exchange pair * `create_time` — timestamp when the exchange was created * `first_token_id` — token ID of the first token in the pair * `first_token_balance` — current balance of the first token * `second_token_id` — token ID of the second token in the pair * `second_token_balance` — current balance of the second token ## Use case The `wallet/listexchanges` method is used for: * Displaying all available trading pairs on decentralized exchange interfaces * Market discovery and analysis of available token pairs * Building exchange aggregators that need to know all active pairs * Monitoring liquidity pools and their current token balances # wallet/listnodes | TRON Source: https://docs.chainstack.com/reference/tron-listnodes openapi/tron_node_api/listnodes.json post /95e61622bf6a8af293978377718e3b77/wallet/listnodes TRON API method that retrieves a list of all nodes in the TRON network, including Super Representative nodes and full nodes. On TRON. TRON API method that retrieves a list of all nodes in the TRON network, including Super Representative nodes and full nodes. ## Parameters This method does not require any parameters. ## Response * `nodes` — array of node information objects containing: * `address` — node address information * `host` — node IP address * `port` — node port number ## Use case The `wallet/listnodes` method is used for: * Discovering available nodes in the TRON network * Building network topology maps and node distribution analysis * Finding nodes for direct P2P connections * Monitoring network health and node availability * Creating decentralized applications that interact with multiple nodes # wallet/listproposals | TRON Source: https://docs.chainstack.com/reference/tron-listproposals openapi/tron_node_api/listproposals.json post /95e61622bf6a8af293978377718e3b77/wallet/listproposals TRON API method that retrieves all governance proposals on the TRON network, providing a comprehensive view of current and past proposals. TRON API method that retrieves all governance proposals on the TRON network, providing a comprehensive view of current and past proposals. ## Parameters This method does not require any parameters. ## Response * `proposals` — array of proposal objects, each containing: * `proposal_id` — the unique identifier of the proposal * `proposer_address` — the address of the Super Representative who created the proposal * `parameters` — object containing the parameter changes proposed * `expiration_time` — timestamp when the proposal expires * `create_time` — timestamp when the proposal was created * `approvals` — array of Super Representatives who have approved the proposal * `state` — current state of the proposal (PENDING, DISAPPROVED, APPROVED, CANCELED) ## Use case The `wallet/listproposals` method is used for: * Displaying all governance proposals in management interfaces * Building comprehensive governance dashboards * Analyzing proposal trends and voting patterns * Creating proposal monitoring and notification systems # wallet/listwitnesses | TRON Source: https://docs.chainstack.com/reference/tron-listwitnesses openapi/tron_node_api/listwitnesses.json post /95e61622bf6a8af293978377718e3b77/wallet/listwitnesses TRON API method that retrieves a complete list of all witnesses (validators) on the TRON network. Available on TRON via Chainstack. TRON API method that retrieves a complete list of all witnesses (validators) on the TRON network. Witnesses are responsible for block production and network governance in TRON's Delegated Proof of Stake (DPoS) consensus mechanism. ## Parameters This method accepts no parameters. ## Response * `witnesses` — array containing information about all witnesses: * `address` — witness address in hexadecimal format * `voteCount` — total number of votes received from TRX holders * `pubKey` — public key used for block signing and validation * `url` — witness website or information URL * `totalProduced` — total number of blocks successfully produced * `totalMissed` — total number of blocks missed during assigned slots * `latestBlockNum` — block number of the most recent block produced * `latestSlotNum` — most recent time slot assigned for block production * `isJobs` — boolean indicating whether the witness is currently active in block production ## Use case The `wallet/listwitnesses` method is used for: * Displaying all available witnesses for voting and governance participation. * Building voting interfaces and governance dashboards. * Analyzing witness performance metrics including blocks produced and missed. * Monitoring network health by tracking active witnesses and their statistics. * Creating witness ranking and comparison tools based on votes and performance. * Implementing governance features that require witness selection and information. # wallet/marketbuyasset | TRON Source: https://docs.chainstack.com/reference/tron-marketbuyasset openapi/tron_node_api/marketsellasset.json post /95e61622bf6a8af293978377718e3b77/wallet/marketsellasset TRON API method that creates a transaction to place a market buy order on the legacy TRON DEX. wallet/marketbuyasset on TRON via Chainstack. TRON API method that creates a transaction to place a market buy order on the legacy TRON DEX. ## Parameters * `owner_address` — address of the account placing the buy order (base58 with `visible: true`, or hex with `visible: false`). * `sell_token_id` — TRC10 token ID of the asset offered, as a numeric string. * `sell_token_quantity` — integer amount to offer (token base units; if TRX, sun). * `buy_token_id` — TRC10 token ID of the asset to purchase, as a numeric string. * `buy_token_quantity` — integer desired amount to purchase (token base units; if TRX, sun). ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the buy order transaction * `raw_data` — raw transaction data containing: * `contract` — array with market buy order contract details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/marketbuyasset` method is used for: * Creating buy orders to acquire tokens at desired prices. * Providing demand liquidity to the TRON DEX marketplace. * Implementing automated token acquisition strategies. * Building custom trading bots and arbitrage systems. * Enabling decentralized token exchange without centralized intermediaries (legacy orderbook). ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/marketbuyasset' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC", "sell_token_id": "1000006", "sell_token_quantity": 100000000, "buy_token_id": "1002000", "buy_token_quantity": 1000000, "visible": true }' ``` > [!NOTE] > > This is a legacy market endpoint and may be disabled on some mainnet nodes. If you receive HTTP 405 or similar, use the AMM endpoints instead: [`exchangecreate`](/reference/tron-exchangecreate), [`exchangeinject`](/reference/tron-exchangeinject), [`exchangetransaction`](/reference/tron-exchangetransaction), and [`exchangewithdraw`](/reference/tron-exchangewithdraw). # wallet/marketcancelorder | TRON Source: https://docs.chainstack.com/reference/tron-marketcancelorder openapi/tron_node_api/marketcancelorder.json post /95e61622bf6a8af293978377718e3b77/wallet/marketcancelorder TRON API method that creates a transaction to cancel an existing market order on the TRON DEX. wallet/marketcancelorder on TRON via Chainstack. TRON API method that creates a transaction to cancel an existing market order on the TRON DEX. This allows users to withdraw their unfilled buy or sell orders from the order book. ## Parameters * `owner_address` — hexadecimal address of the account that created the order * `order_id` — unique identifier of the market order to cancel (as hexadecimal string) ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the order cancellation transaction * `raw_data` — raw transaction data containing: * `contract` — array with market order cancellation contract details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/marketcancelorder` method is used for: * Canceling orders that are no longer needed or desired. * Withdrawing funds locked in unfilled orders back to the account balance. * Adjusting trading strategies by removing stale or mispriced orders. * Managing order book exposure and market position. * Implementing automated order management systems. > [!WARNING] > > You can only cancel orders that you created and that are still active. Once an order is fully filled or already canceled, it cannot be canceled again. The locked tokens will be returned to your account balance upon successful cancellation. # wallet/marketsellasset | TRON Source: https://docs.chainstack.com/reference/tron-marketsellasset openapi/tron_node_api/marketsellasset.json post /95e61622bf6a8af293978377718e3b77/wallet/marketsellasset TRON API method that creates a transaction to place a sell order on the TRON DEX marketplace. wallet/marketsellasset on TRON via Chainstack. TRON API method that creates a transaction to place a sell order on the TRON DEX marketplace. This allows users to offer their tokens for sale at a specified price, creating liquidity in the order book. ## Parameters * `owner_address` — hexadecimal address of the account placing the sell order * `sell_token_id` — token ID being sold (use empty string for TRX) * `sell_token_quantity` — quantity of tokens to sell * `buy_token_id` — token ID desired in exchange (use empty string for TRX) * `buy_token_quantity` — quantity of tokens desired in exchange ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the sell order transaction * `raw_data` — raw transaction data containing: * `contract` — array with market sell order contract details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/marketsellasset` method is used for: * Creating sell orders to exchange tokens at desired prices. * Providing liquidity to the TRON DEX marketplace. * Implementing automated trading strategies and market making. * Converting between TRX and TRC-10 tokens through decentralized trading. * Building custom trading interfaces and portfolio management tools. > [!NOTE] > > The sell order will lock the specified tokens in the order until it's filled or canceled. The price is determined by the ratio between sell\_token\_quantity and buy\_token\_quantity. Use empty strings for token IDs when referring to TRX in the trading pair. # wallet/participateassetissue | TRON Source: https://docs.chainstack.com/reference/tron-participateassetissue openapi/tron_node_api/participateassetissue.json post /95e61622bf6a8af293978377718e3b77/wallet/participateassetissue TRON API method that creates an unsigned transaction for participating in a TRC10 token Initial Coin Offering (ICO). TRON via Chainstack. TRON API method that creates an unsigned transaction for participating in a TRC10 token Initial Coin Offering (ICO). This allows users to purchase newly issued TRC10 tokens during the offering period. ## Parameters * `owner_address` — the address participating in the token offering * `to_address` — the address of the token issuer (who created the asset) * `asset_name` — the name or ID of the TRC10 token to purchase * `amount` — the amount of TRX to spend on purchasing tokens * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `visible` — indicates the address format used in the response * `txID` — the transaction hash * `raw_data` — raw transaction data including: * `contract` — array containing the participate asset issue contract * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — hexadecimal representation of the raw transaction ## Use case The `wallet/participateassetissue` method is used for: * Participating in TRC10 token ICOs and crowdfunding campaigns * Building automated investment tools for token offerings * Creating token purchase functionality in wallet applications * Implementing token sale participation features in DeFi platforms # wallet/proposalapprove | TRON Source: https://docs.chainstack.com/reference/tron-proposalapprove openapi/tron_node_api/proposalapprove.json post /95e61622bf6a8af293978377718e3b77/wallet/proposalapprove TRON API method that allows Super Representatives to approve a governance proposal. wallet/proposalapprove on TRON via Chainstack. TRON API method that allows Super Representatives to approve a governance proposal. Proposals need majority approval from Super Representatives to be executed. ## Parameters * `owner_address` — the Super Representative address approving the proposal. Must be in base58 or hex format. * `proposal_id` — the ID of the proposal to approve. * `is_add_approval` — boolean indicating whether to approve (true) or remove approval (false). * `visible` — optional boolean parameter. When set to `true`, the address should be in base58 format. Default is `false`. ## Response * `txID` — the transaction hash of the approval action * `raw_data` — the raw transaction data * `raw_data_hex` — the raw transaction data in hex format * `signature` — array of transaction signatures ## Use case The `wallet/proposalapprove` method is used for: * Approving governance proposals by Super Representatives * Participating in the proposal voting process * Supporting or rejecting proposed network parameter changes * Managing proposal approval status in governance workflows # wallet/proposalcreate | TRON Source: https://docs.chainstack.com/reference/tron-proposalcreate openapi/tron_node_api/proposalcreate.json post /95e61622bf6a8af293978377718e3b77/wallet/proposalcreate TRON API method that creates a new governance proposal on the TRON network. Reference for wallet/proposalcreate on TRON via Chainstack. TRON API method that creates a new governance proposal on the TRON network. Super Representatives can use this method to propose changes to network parameters. ## Parameters * `owner_address` — the Super Representative address creating the proposal. Must be in base58 or hex format. * `parameters` — object containing the parameter ID and its new value to be proposed. * `visible` — optional boolean parameter. When set to `true`, the address should be in base58 format. Default is `false`. ## Response * `txID` — the transaction hash of the proposal creation * `raw_data` — the raw transaction data * `raw_data_hex` — the raw transaction data in hex format * `signature` — array of transaction signatures ## Use case The `wallet/proposalcreate` method is used for: * Creating governance proposals to modify network parameters * Proposing changes to transaction fees and resource costs * Initiating protocol upgrades and parameter adjustments * Participating in TRON's decentralized governance system # wallet/proposaldelete | TRON Source: https://docs.chainstack.com/reference/tron-proposaldelete openapi/tron_node_api/proposaldelete.json post /95e61622bf6a8af293978377718e3b77/wallet/proposaldelete TRON API method that allows the proposal creator to delete or cancel a governance proposal before it reaches the execution phase. On TRON. TRON API method that allows the proposal creator to delete or cancel a governance proposal before it reaches the execution phase. ## Parameters * `owner_address` — the address of the proposal creator who wants to delete the proposal. Must be in base58 or hex format. * `proposal_id` — the ID of the proposal to delete. * `visible` — optional boolean parameter. When set to `true`, the address should be in base58 format. Default is `false`. ## Response * `txID` — the transaction hash of the proposal deletion * `raw_data` — the raw transaction data * `raw_data_hex` — the raw transaction data in hex format * `signature` — array of transaction signatures ## Use case The `wallet/proposaldelete` method is used for: * Cancelling proposals that are no longer needed * Withdrawing proposals with errors or incorrect parameters * Managing proposal lifecycle in governance systems * Preventing unwanted proposals from reaching execution # wallet/createshieldedcontractparameters | TRON Source: https://docs.chainstack.com/reference/tron-shielded-createshieldedcontractparameters openapi/tron_node_api/createshieldedcontractparameters.json post /95e61622bf6a8af293978377718e3b77/wallet/createshieldedcontractparameters TRON API method that creates parameters for a shielded TRC20 contract transaction. wallet/createshieldedcontractparameters on TRON via Chainstack. TRON API method that creates parameters for a shielded TRC20 contract transaction. This method generates the necessary cryptographic parameters required to execute shielded transfers while maintaining privacy. > [!WARNING] > > This endpoint requires valid cryptographic material generated from your shielded keys and transaction data. All hex string parameters must be properly formatted (0x-prefixed) and derived from legitimate shielded transaction construction. Invalid or placeholder values will result in parsing errors. ## Parameters * `spend_authority_signature` — the spending authority signature for the transaction * `view_signature` — the view signature component * `proof_generation_key` — the proof generation key for creating zero-knowledge proofs * `payment_address` — the recipient's shielded payment address * `rcm` — the randomness commitment value * `ar` — the authentication randomness value * `alpha` — the alpha parameter for proof generation * `rsk` — the randomness for spending key * `ask` — the authentication secret key ## Response * `receive_description` — the receive description for the shielded transaction * `spend_description` — the spend description for the shielded transaction * `binding_signature` — the binding signature that ties the transaction together ## Use case The `wallet/createshieldedcontractparameters` method is used for: * Creating the cryptographic parameters needed for shielded TRC20 transfers * Generating zero-knowledge proofs for privacy-preserving transactions * Preparing transaction data that maintains sender and recipient anonymity * Supporting advanced shielded contract functionality in wallets and applications # wallet/createshieldedcontractparameterswithoutask | TRON Source: https://docs.chainstack.com/reference/tron-shielded-createshieldedcontractparameterswithoutask openapi/tron_node_api/createshieldedcontractparameterswithoutask.json post /95e61622bf6a8af293978377718e3b77/wallet/createshieldedcontractparameterswithoutask TRON API method that creates parameters for a shielded TRC20 contract transaction without requiring the authentication secret key (ask). TRON API method that creates parameters for a shielded TRC20 contract transaction without requiring the authentication secret key (ask). This simplified version is used when the ask is not available or needed for the specific transaction type. > [!WARNING] > > This endpoint requires valid shielded addresses and cryptographic material. All parameters must be properly formatted: shielded addresses must be valid ztron1 addresses, the outgoing viewing key must be a valid 0x-prefixed hex string, and optional transparent addresses must be valid TRON addresses (starting with T). Invalid or placeholder values will result in parsing errors. ## Parameters * `ovk` — the outgoing viewing key for transaction monitoring (0x-prefixed hex string) * `from_address` — the sender's shielded address (ztron1 format) * `to_address` — the recipient's shielded address (ztron1 format) * `amount` — the amount to transfer (in sun, as string) * `transparent_from_address` — optional transparent sender address (T-address format) * `transparent_to_address` — optional transparent recipient address (T-address format) ## Response * `receive_description` — the receive description for the shielded transaction * `spend_description` — the spend description for the shielded transaction * `binding_signature` — the binding signature that ties the transaction together ## Use case The `wallet/createshieldedcontractparameterswithoutask` method is used for: * Creating shielded transaction parameters without access to authentication secret keys * Generating transaction data for view-only or monitoring applications * Supporting simplified wallet implementations that don't store sensitive key material * Enabling third-party services to prepare shielded transactions with limited key access # wallet/createshieldedtransaction | TRON Source: https://docs.chainstack.com/reference/tron-shielded-createshieldedtransaction openapi/tron_node_api/shielded_createshieldedtransaction.json post /95e61622bf6a8af293978377718e3b77/wallet/createshieldedtransaction Create fully signed shielded TRX transactions client-side. Public TRON nodes generally do not expose an RPC for wallet/createshieldedtransaction. Create fully signed shielded TRX transactions client-side. Public TRON nodes generally do not expose an RPC for `wallet/createshieldedtransaction`; attempting to POST to that route returns HTTP 405. Build shielded transactions locally using TRON shielded transaction libraries. ## Parameters * `transparent_from_address` — the transparent TRX address sending funds (optional) * `from_amount` — the amount to send from transparent address (in sun) * `spend_authority_signature` — array of spending authority signatures for shielded inputs * `ask` — authentication secret key for transaction signing * `nsk` — nullifier secret key for creating nullifiers * `ovk` — outgoing viewing key for transaction encryption * `to_address` — recipient shielded address * `to_amount` — amount to send to shielded address (in sun) ## Response * `txID` — the transaction ID hash * `raw_data_hex` — the raw transaction data in hexadecimal format * `raw_data` — the structured raw transaction data ## Use case The `wallet/createshieldedtransaction` method is used for: * Creating fully signed shielded TRX transactions for immediate broadcast * Transferring TRX between transparent and shielded addresses * Supporting complete privacy-preserving TRX transactions * Enabling applications to create ready-to-broadcast shielded transactions ## Client-side implementation > [!NOTE] > > Public nodes typically return HTTP 405 for this route. Build shielded transactions client-side using TRON privacy libraries (e.g., tronweb with shielded support) or Sapling-compatible implementations. Generate and sign the transaction locally, then broadcast via `wallet/broadcasttransaction`. Use TRON's shielded transaction libraries to: 1. Construct the shielded inputs (spends) and outputs (receives) using your key material (ask, nsk, ovk) 2. Generate the zero-knowledge proof parameters offline 3. Sign the transaction with spending authority signatures 4. Build the complete signed raw transaction structure 5. Broadcast the transaction via `wallet/broadcasttransaction` # wallet/createshieldedtransactionwithoutspendauthsig | TRON Source: https://docs.chainstack.com/reference/tron-shielded-createshieldedtransactionwithoutspendauthsig openapi/tron_node_api/shielded_createshieldedtransactionwithoutspendauthsig.json post /95e61622bf6a8af293978377718e3b77/wallet/createshieldedtransactionwithoutspendauthsig Create unsigned shielded TRX transactions. Reference for wallet/createshieldedtransactionwithoutspendauthsig on TRON via Chainstack. Create unsigned shielded TRX transactions. This endpoint is not exposed via RPC for security reasons—shielded transaction construction requires local key material and zero-knowledge proof generation. Build shielded transactions client-side using TRON shielded transaction libraries. ## Parameters * `transparent_from_address` — the transparent TRX address sending funds (optional) * `from_amount` — the amount to send from transparent address (in sun) * `shielded_spends` — array of shielded input specifications * `shielded_receives` — array of shielded output specifications * `to_address` — recipient shielded address * `to_amount` — amount to send to shielded address (in sun) ## Response * `raw_data_hex` — the raw unsigned transaction data in hexadecimal format * `raw_data` — the structured raw unsigned transaction data * `txID` — the transaction ID hash (will change after signing) ## Use case The `wallet/createshieldedtransactionwithoutspendauthsig` method is used for: * Creating unsigned shielded transactions for offline signing workflows * Separating transaction creation from signing for enhanced security * Supporting hardware wallet integration with shielded transactions * Enabling multi-step transaction creation processes ## Client-side implementation > [!NOTE] > > This endpoint is not available via RPC. Build shielded transactions client-side using TRON privacy libraries (e.g., tronweb with shielded support) or Sapling-compatible implementations. Generate the raw transaction locally with your key material, then sign and broadcast separately. Use TRON's shielded transaction libraries to: 1. Construct the shielded inputs (spends) and outputs (receives) using your local key material 2. Generate the zero-knowledge proof parameters offline 3. Build the raw transaction structure without spending authority signatures 4. Sign the transaction in a separate step (e.g., with hardware wallet) 5. Broadcast the signed transaction via `wallet/broadcasttransaction` # wallet/createspendauthsig | TRON Source: https://docs.chainstack.com/reference/tron-shielded-createspendauthsig openapi/tron_node_api/createspendauthsig.json post /95e61622bf6a8af293978377718e3b77/wallet/createspendauthsig TRON API method that creates a spending authorization signature for shielded TRC20 transactions. Available on TRON via Chainstack. TRON API method that creates a spending authorization signature for shielded TRC20 transactions. This signature authorizes the spending of shielded notes and is a critical component of the privacy-preserving transaction protocol. > [!WARNING] > > All cryptographic parameters (`ask`, `alpha`, `tx_hash`) must be exactly 32 bytes (64 hexadecimal characters) provided as raw hex strings **without the 0x prefix**. Values that are shorter, longer, or include the 0x prefix will result in a "param length must be 32" error. ## Parameters * `ask` — the authentication secret key used for signing (32 bytes, hex without 0x prefix) * `alpha` — the alpha parameter for randomization (32 bytes, hex without 0x prefix) * `tx_hash` — the transaction hash being authorized (32 bytes, hex without 0x prefix) ## Response * `value` — the generated spending authorization signature (hex string without 0x prefix) ## Use case The `wallet/createspendauthsig` method is used for: * Authorizing the spending of shielded notes in privacy transactions * Creating cryptographic signatures that prove spending authority without revealing identity * Supporting the zero-knowledge proof system in shielded transactions * Enabling secure authorization of shielded TRC20 transfers # wallet/getakfromask | TRON Source: https://docs.chainstack.com/reference/tron-shielded-getakfromask openapi/tron_node_api/shielded_getakfromask.json post /95e61622bf6a8af293978377718e3b77/wallet/getakfromask TRON API method that derives an authentication key (ak) from an authentication secret key (ask) for shielded TRC20 transactions. On TRON. TRON API method that derives an authentication key (ak) from an authentication secret key (ask) for shielded TRC20 transactions. This is part of the key derivation process in the shielded transaction protocol. ## Parameters * `value` — authentication secret key (ask) as a 32‑byte (64‑hex) string, no `0x` prefix. ## Response * `value` — the derived authentication key (ak) in hexadecimal format ## Use case The `wallet/getakfromask` method is used for: * Deriving authentication keys needed for shielded transaction verification * Converting secret keys to public key components in the shielded protocol * Supporting cryptographic operations in privacy-preserving transactions * Enabling wallet implementations of shielded TRC20 functionality ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getakfromask' \ --header 'Content-Type: application/json' \ --data '{ "value": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" }' ``` > [!NOTE] > > Ensure `value` is exactly 64 hexadecimal characters without a `0x` prefix. Using the wrong length or including `0x` will cause a validation error. # wallet/getdiversifier | TRON Source: https://docs.chainstack.com/reference/tron-shielded-getdiversifier openapi/tron_node_api/shielded_getdiversifier.json post /95e61622bf6a8af293978377718e3b77/wallet/getdiversifier TRON API method that generates a random diversifier for shielded TRC20 transactions. wallet/getdiversifier on TRON via Chainstack. TRON API method that generates a random diversifier for shielded TRC20 transactions. The diversifier is used to create multiple payment addresses from a single incoming viewing key, enhancing privacy. ## Parameters This method takes no parameters. ## Response * `d` — the generated diversifier as a hexadecimal string ## Use case The `wallet/getdiversifier` method is used for: * Generating multiple payment addresses from a single viewing key * Enhancing privacy by using different addresses for different transactions * Creating diversified shielded addresses for improved anonymity * Supporting address diversification in shielded transaction protocols # wallet/getexpandedspendingkey | TRON Source: https://docs.chainstack.com/reference/tron-shielded-getexpandedspendingkey openapi/tron_node_api/shielded_getexpandedspendingkey.json post /95e61622bf6a8af293978377718e3b77/wallet/getexpandedspendingkey TRON API method that derives an expanded spending key from a spending key for shielded TRC20 transactions. Chainstack TRON reference. TRON API method that derives an expanded spending key from a spending key for shielded TRC20 transactions. The expanded spending key contains additional cryptographic material used in the shielded transaction protocol. ## Parameters * `value` — spending key as a 32‑byte (64‑hex) string, no `0x` prefix. ## Response * `ask` — the authentication secret key component * `nsk` — the nullifier secret key component * `ovk` — the outgoing viewing key component ## Use case The `wallet/getexpandedspendingkey` method is used for: * Deriving cryptographic components needed for shielded transactions * Generating keys required for creating shielded TRC20 transfers * Supporting wallet implementations of the shielded transaction protocol * Enabling advanced privacy features in TRON applications ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getexpandedspendingkey' \ --header 'Content-Type: application/json' \ --data '{ "value": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" }' ``` > [!NOTE] > > Ensure the key is exactly 64 hexadecimal characters without a prefix. Including `0x` or using the wrong length (for example, 66 hex chars) produces the error: "the length of spendingKey's hexString should be 64". # wallet/getincomingviewingkey | TRON Source: https://docs.chainstack.com/reference/tron-shielded-getincomingviewingkey openapi/tron_node_api/shielded_getincomingviewingkey.json post /95e61622bf6a8af293978377718e3b77/wallet/getincomingviewingkey TRON API method that derives an incoming viewing key (ivk) from authentication key (ak) and nullifier key (nk) for shielded TRC20 transactions. TRON API method that derives an incoming viewing key (ivk) from authentication key (ak) and nullifier key (nk) for shielded TRC20 transactions. The incoming viewing key is used to scan for incoming shielded payments. ## Parameters * `ak` — authentication key as a 32‑byte (64‑hex) string, no `0x` prefix. * `nk` — nullifier key as a 32‑byte (64‑hex) string, no `0x` prefix. ## Response * `ivk` — the derived incoming viewing key in hexadecimal format ## Use case The `wallet/getincomingviewingkey` method is used for: * Generating keys needed to scan for incoming shielded payments * Creating viewing keys that allow monitoring received transactions without spending authority * Supporting wallet implementations that need to detect incoming shielded transfers * Enabling privacy-preserving transaction monitoring ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getincomingviewingkey' \ --header 'Content-Type: application/json' \ --data '{ "ak": "03b2c3d4e5f67890123456789012345678901234567890123456789012345678", "nk": "04c3d4e5f6789012345678901234567890123456789012345678901234567890" }' ``` > [!NOTE] > > All keys must be exactly 64 hexadecimal characters without a `0x` prefix. Otherwise the node returns an error like: "param length must be 32". # wallet/getnewshieldedaddress | TRON Source: https://docs.chainstack.com/reference/tron-shielded-getnewshieldedaddress openapi/tron_node_api/getnewshieldedaddress.json post /95e61622bf6a8af293978377718e3b77/wallet/getnewshieldedaddress TRON API method that generates a complete new shielded address with all necessary keys for shielded TRC20 transactions. TRON via Chainstack. TRON API method that generates a complete new shielded address with all necessary keys for shielded TRC20 transactions. This is a convenience method that creates a full shielded address setup. ## Parameters This method takes no parameters. ## Response * `sk` — the spending key (private key for the address) * `ask` — the authentication secret key * `nsk` — the nullifier secret key * `ovk` — the outgoing viewing key * `ak` — the authentication key * `nk` — the nullifier key * `ivk` — the incoming viewing key * `d` — the diversifier * `pkD` — the payment address public key * `payment_address` — the complete shielded payment address ## Use case The `wallet/getnewshieldedaddress` method is used for: * Quickly setting up new shielded addresses for privacy-preserving transactions * Generating complete key sets needed for shielded TRC20 operations * Simplifying wallet implementations that need full shielded address functionality * Creating addresses for users who want to participate in private transactions # wallet/getnkfromnsk | TRON Source: https://docs.chainstack.com/reference/tron-shielded-getnkfromnsk openapi/tron_node_api/shielded_getnkfromnsk.json post /95e61622bf6a8af293978377718e3b77/wallet/getnkfromnsk TRON API method that derives a nullifier key (nk) from a nullifier secret key (nsk) for shielded TRC20 transactions. TRON via Chainstack. TRON API method that derives a nullifier key (nk) from a nullifier secret key (nsk) for shielded TRC20 transactions. The nullifier key is used to generate nullifiers that prevent double-spending in the shielded transaction protocol. ## Parameters * `value` — nullifier secret key (nsk) as a 32‑byte (64‑hex) string, no `0x` prefix. ## Response * `value` — the derived nullifier key (nk) in hexadecimal format ## Use case The `wallet/getnkfromnsk` method is used for: * Deriving nullifier keys required for preventing double-spending in shielded transactions * Converting secret nullifier keys to public nullifier key components * Supporting the cryptographic foundation for privacy-preserving transaction verification * Enabling secure nullifier generation in shielded TRC20 implementations ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getnkfromnsk' \ --header 'Content-Type: application/json' \ --data '{ "value": "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd" }' ``` > [!NOTE] > > Ensure `value` is exactly 64 hexadecimal characters without a `0x` prefix; otherwise the node returns a validation error indicating the expected length. # derive shielded payment address (client‑side) | TRON Source: https://docs.chainstack.com/reference/tron-shielded-getshieldedpaymentaddress Generate a shielded payment address locally from an incoming viewing key (ivk) and a diversifier (d). Use it on TRON via Chainstack. Generate a shielded payment address locally from an incoming viewing key (ivk) and a diversifier (d). TRON nodes typically do not expose an RPC for this; derive on the client using a Sapling library and then use the address to receive shielded TRC20 payments. ## Inputs * `ivk` — incoming viewing key as a 32‑byte (64‑hex) string, no `0x` prefix. * `d` — diversifier as 11 bytes (22 hex), no `0x` prefix. ## Outputs * `pkd` — payment address public key in hexadecimal format (no `0x`). * `payment_address` — the complete shielded payment address. ## Use case The `wallet/getshieldedpaymentaddress` method is used for: * Generating shielded addresses to receive private TRC20 payments * Creating diversified payment addresses from viewing keys and diversifiers * Supporting wallet implementations that need to generate receiving addresses * Enabling privacy-preserving transaction receiving capabilities ## How it works (Sapling) 1. Parse `ivk` (32 bytes) and `d` (11 bytes) from hex. 2. Compute `g_d = diversifyHash(d)` (Jubjub point derived from the diversifier). 3. Compute `pkd = ivk · g_d` (scalar multiplication on Jubjub). 4. Encode `(d, pkd)` into a TRON Sapling payment address (Bech32 with HRP `ztron`). Use a Sapling implementation (Rust/WASM or native). Do not implement the primitives yourself. ## Example (JavaScript, WASM Sapling) ```js theme={"system"} // Pseudocode — use a Sapling/Jubjub library that exposes diversifyHash and scalarMul import { diversifyHash, scalarMul, bech32mEncode } from 'sapling-lib'; const ivkHex = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; const dHex = '06a1b2c3d4e5f6789012ab'; const ivk = Buffer.from(ivkHex, 'hex'); // 32 bytes const d = Buffer.from(dHex, 'hex'); // 11 bytes const gd = diversifyHash(d); // Jubjub point const pkd = scalarMul(ivk, gd); // 32‑byte compressed point // payment address = Bech32m("ztron", d || pkd) const payload = Buffer.concat([d, pkd]); const paymentAddress = bech32mEncode('ztron', payload); console.log({ pkd: pkd.toString('hex'), paymentAddress }); ``` ## Example (Python, Rust FFI/WASM) ```python theme={"system"} # Pseudocode — use a Sapling library (e.g., librustzcash bindings) that exposes diversify_hash and scalar multiplication from sapling import diversify_hash, scalar_mul, bech32m_encode ivk_hex = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' d_hex = '06a1b2c3d4e5f6789012ab' ivk = bytes.fromhex(ivk_hex) d = bytes.fromhex(d_hex) g_d = diversify_hash(d) pkd = scalar_mul(ivk, g_d) payload = d + pkd payment_address = bech32m_encode('ztron', payload) print({'pkd': pkd.hex(), 'payment_address': payment_address}) ``` > [!NOTE] > > * Exact lengths: `ivk` = 64 hex (32 bytes), `d` = 22 hex (11 bytes). No `0x` prefix. > * Use a maintained Sapling library (Rust → WASM/FFI). The node does not provide this derivation. # wallet/getspendingkey | TRON Source: https://docs.chainstack.com/reference/tron-shielded-getspendingkey openapi/tron_node_api/shielded_getspendingkey.json post /95e61622bf6a8af293978377718e3b77/wallet/getspendingkey TRON API method that generates a new spending key for shielded TRC20 transactions. Available on TRON via Chainstack JSON-RPC nodes. TRON API method that generates a new spending key for shielded TRC20 transactions. The spending key is the master private key used to derive other keys in the shielded transaction system and must be kept secure. ## Parameters This method takes no parameters. ## Response * `value` — the generated spending key as a hexadecimal string ## Use case The `wallet/getspendingkey` method is essential for: * Creating new shielded addresses for privacy-preserving transactions * Setting up wallets that support shielded TRC20 transfers * Generating the master key from which all other shielded transaction keys are derived * Enabling users to participate in private transactions on the TRON network # wallet/gettriggerinputforshieldedtrc20contract | TRON Source: https://docs.chainstack.com/reference/tron-shielded-gettriggerinputforshieldedtrc20contract openapi/tron_node_api/gettriggerinputforshieldedtrc20contract.json post /95e61622bf6a8af293978377718e3b77/wallet/gettriggerinputforshieldedtrc20contract TRON API method that generates trigger input parameters for shielded TRC20 contract calls. Available on TRON via Chainstack JSON-RPC nodes. TRON API method that generates trigger input parameters for shielded TRC20 contract calls. This method converts shielded TRC20 transaction parameters into formatted input data that can be used to trigger shielded contract operations. > [!WARNING] > > This endpoint requires valid shielded TRC20 parameters generated from prior shielded transaction preparation. All hex values must be provided **without the 0x prefix**. You must specify the `parameter_type` field with one of these values: "mint", "burn", or "transfer". The parameters must include valid zero-knowledge proofs, binding signature, and message hash from the shielded transaction protocol. ## Parameters * `shielded_TRC20_Parameters` — object containing shielded transaction parameters * `spend_description` — array of spend descriptions for transaction inputs * `value_commitment` — value commitment (hex without 0x prefix) * `anchor` — Merkle root of note commitment tree (hex without 0x prefix) * `nullifier` — nullifier to prevent double spending (hex without 0x prefix) * `rk` — public key for spend authorization signature verification (hex without 0x prefix) * `zkproof` — zero-knowledge proof data (hex without 0x prefix) * `receive_description` — array of receive descriptions for transaction outputs (required for transfer operations) * `value_commitment` — value commitment (hex without 0x prefix) * `note_commitment` — note commitment (hex without 0x prefix) * `epk` — ephemeral public key (hex without 0x prefix) * `c_enc` — encrypted note ciphertext (hex without 0x prefix) * `c_out` — output ciphertext for auditing (hex without 0x prefix) * `zkproof` — zero-knowledge proof data (hex without 0x prefix) * `binding_signature` — binding signature for the transaction (hex without 0x prefix) * `message_hash` — hash of the transaction message (hex without 0x prefix) * `parameter_type` — type of shielded operation: "mint", "burn", or "transfer" * `spend_authority_signature` — array of spend authority signatures (one for each spend description) * `value` — spend authorization signature (hex without 0x prefix) ## Response * `trigger_contract_input` — the formatted input data for triggering the shielded TRC20 contract ## Use case The `wallet/gettriggerinputforshieldedtrc20contract` method is used for: * Preparing input data for shielded TRC20 contract interactions * Converting shielded transaction parameters into contract-compatible format * Enabling smart contract integration with shielded TRC20 functionality * Supporting advanced DeFi applications with privacy features # derive zen payment address (client‑side) | TRON Source: https://docs.chainstack.com/reference/tron-shielded-getzenpaymentaddress Generate a legacy zen payment address locally from an incoming viewing key (ivk) and a diversifier (d). Chainstack TRON reference. Generate a legacy zen payment address locally from an incoming viewing key (ivk) and a diversifier (d). Nodes commonly do not expose an RPC for this; derive the address client‑side with a Sapling library. ## Inputs * `ivk` — incoming viewing key as a 32‑byte (64‑hex) string, no `0x` prefix. * `d` — diversifier as 11 bytes (22 hex), no `0x` prefix. ## Outputs * `pkD` — payment address public key component in hex (no `0x`). * `payment_address` — complete shielded payment address. ## Use case The `wallet/getzenpaymentaddress` method is used for: * Creating legacy zen payment addresses for compatibility with older systems * Generating addresses in the legacy zen protocol format * Supporting backwards compatibility in shielded transaction implementations * Maintaining compatibility with legacy shielded TRC20 applications ## How it works (legacy zen) 1. Parse `ivk` (32 bytes) and `d` (11 bytes) from hex. 2. Compute `g_d = diversifyHash(d)` (Jubjub point). If invalid, choose a new `d` (rare) until `diversifyHash` returns a valid point. 3. Compute `pkD = ivk · g_d`. 4. Encode `(d, pkD)` as the legacy zen payment address (Bech32m with HRP `ztron`). ## Example (JavaScript, WASM Sapling) ```js theme={"system"} // Pseudocode — use a Sapling/Jubjub library import { diversifyHash, scalarMul, bech32mEncode, randomDiversifier } from 'sapling-lib'; const ivkHex = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; const ivk = Buffer.from(ivkHex, 'hex'); // 32 bytes let d; while (true) { d = randomDiversifier(); // 11 bytes const gd = diversifyHash(d); // returns null if invalid if (gd) { const pkD = scalarMul(ivk, gd); const addr = bech32mEncode('ztron', Buffer.concat([d, pkD])); console.log({ pkD: pkD.toString('hex'), payment_address: addr }); break; } } ``` ## Example (Python) ```python theme={"system"} # Pseudocode — rely on a Sapling binding exposing diversify_hash, scalar_mul, bech32m_encode, random_diversifier from sapling import diversify_hash, scalar_mul, bech32m_encode, random_diversifier ivk_hex = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' ivk = bytes.fromhex(ivk_hex) while True: d = random_diversifier() # 11 bytes gd = diversify_hash(d) if gd is None: continue pkD = scalar_mul(ivk, gd) addr = bech32m_encode('ztron', d + pkD) print({'pkD': pkD.hex(), 'payment_address': addr}) break ``` > [!NOTE] > > * Exact lengths: `ivk` = 64 hex (32 bytes), `d` = 22 hex (11 bytes), no `0x`. > * If a specific `d` is "not valid", generate a new diversifier until `diversifyHash(d)` returns a valid point. # wallet/isshieldedtrc20contractnotespent | TRON Source: https://docs.chainstack.com/reference/tron-shielded-isshieldedtrc20contractnotespent openapi/tron_node_api/isshieldedtrc20contractnotespent.json post /95e61622bf6a8af293978377718e3b77/wallet/isshieldedtrc20contractnotespent TRON API method that checks whether a specific shielded TRC20 contract note has been spent. wallet/isshieldedtrc20contractnotespent on TRON via Chainstack. TRON API method that checks whether a specific shielded TRC20 contract note has been spent. This method is crucial for preventing double-spending in shielded transactions and maintaining the integrity of the privacy protocol. > [!WARNING] > > All hex values (`ak`, `nk`, `rcm`) must be provided **without the 0x prefix** and must be exactly 64 hexadecimal characters (32 bytes). The `shielded_TRC20_contract_address` must be in **hex format** (starting with 41), not base58 format. The `value` represents the scaled value multiplied by the contract's scalingFactor. ## Parameters * `note` — the note object to check for spending status * `value` — scaled value (multiplied by contract's scalingFactor) * `payment_address` — shielded payment address (ztron1 format) * `rcm` — random commitment trapdoor (64 hex characters, no `0x` prefix) * `ak` — authorization key (64 hex characters, no `0x` prefix) * `nk` — nullifier key (64 hex characters, no `0x` prefix) * `position` — leaf position index in Merkle tree * `shielded_TRC20_contract_address` — the shielded TRC20 contract address (hex format starting with 41, no `0x` prefix) ## Response * `is_spent` — boolean indicating if the note has been spent (true = spent, false = unspent) ## Use case The `wallet/isshieldedtrc20contractnotespent` method is used for: * Preventing double-spending attacks in shielded transactions * Validating that notes are available for spending before creating transactions * Maintaining the security and integrity of the shielded transaction system * Supporting wallet implementations that need to track note spending status > [!NOTE] > > The nullifier key (nk) and authorization key (ak) are used together with the note's position in the Merkle tree to generate a unique nullifier for each note. This nullifier is checked to determine if the note has been spent, preventing double-spending while maintaining privacy. The note's value is scaled by the contract's scalingFactor to maintain precision in token amounts. # wallet/scanshieldedtrc20notesbyivk | TRON Source: https://docs.chainstack.com/reference/tron-shielded-scanshieldedtrc20notesbyivk openapi/tron_node_api/shielded_scanshieldedtrc20notesbyivk.json post /95e61622bf6a8af293978377718e3b77/wallet/scanshieldedtrc20notesbyivk TRON API method that scans for shielded TRC20 notes using an incoming viewing key (ivk). wallet/scanshieldedtrc20notesbyivk on TRON via Chainstack. TRON API method that scans for shielded TRC20 notes using an incoming viewing key (ivk). This method allows users to detect incoming shielded transactions without revealing their spending keys or compromising privacy. > [!WARNING] > > All hex values (`ivk`, `ak`, `nk`) must be provided **without the 0x prefix** and must be exactly 64 hexadecimal characters (32 bytes). The `shielded_TRC20_contract_address` must be in **hex format** (starting with 41), not base58 format. There is a block range limit: `(end_block_index - start_block_index) <= 1000`. ## Parameters * `start_block_index` — the starting block number for scanning (inclusive) * `end_block_index` — the ending block number for scanning (exclusive, max 1000 blocks from start) * `ivk` — the incoming viewing key for decrypting notes (64 hex characters, no `0x` prefix) * `ak` — the authentication key for verification (64 hex characters, no `0x` prefix) * `nk` — the nullifier key for note identification (64 hex characters, no `0x` prefix) * `shielded_TRC20_contract_address` — the shielded TRC20 contract address to scan (hex format starting with 41, no `0x` prefix) ## Response * `noteTxs` — array of found shielded note transactions * `txid` — transaction ID containing the note * `index` — index of the note within the transaction * `note` — the decrypted note details * `value` — note value * `payment_address` — shielded payment address * `rcm` — randomness commitment * `memo` — optional memo field * `position` — position in the merkle tree * `is_spent` — whether the note has been spent ## Use case The `wallet/scanshieldedtrc20notesbyivk` method is used for: * Scanning the blockchain for incoming shielded TRC20 payments * Detecting received transactions without compromising sender privacy * Building wallet interfaces that show shielded transaction history * Monitoring shielded contract activity for specific addresses > [!NOTE] > > The incoming viewing key (ivk) is generated from the authentication key (ak) and nullifier key (nk) using a BLAKE2s hash. This key hierarchy allows recipients to view shielded transactions they receive without exposing their spending keys. ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/scanshieldedtrc20notesbyivk' \ --header 'Content-Type: application/json' \ --data '{ "start_block_index": 10000000, "end_block_index": 10000001, "ivk": "9f8e74bb3d7188a2781dc1db38810c6914eef4570a79e8ec8404480948e4e305", "ak": "8072d9110c9de9d9ade33d5d0f5890a7aa65b0cde42af7816d187297caf2fd64", "nk": "590bf33f93f792be659fd404df91e75c3b08d38d4e08ee226c3f5219cf598f14", "shielded_TRC20_contract_address": "41274fc7464fadac5c00c893c58bce6c39bf59e4c7" }' ``` # wallet/scanshieldedtrc20notesbyovk | TRON Source: https://docs.chainstack.com/reference/tron-shielded-scanshieldedtrc20notesbyovk openapi/tron_node_api/shielded_scanshieldedtrc20notesbyovk.json post /95e61622bf6a8af293978377718e3b77/wallet/scanshieldedtrc20notesbyovk TRON API method that scans for shielded TRC20 notes using an outgoing viewing key (ovk). wallet/scanshieldedtrc20notesbyovk on TRON via Chainstack. TRON API method that scans for shielded TRC20 notes using an outgoing viewing key (ovk). This method allows senders to track their outgoing shielded transactions and monitor sent payments for record-keeping purposes. > [!WARNING] > > The `ovk` must be provided **without the 0x prefix** and must be exactly 64 hexadecimal characters (32 bytes). The `shielded_TRC20_contract_address` must be in **hex format** (starting with 41), not base58 format. There is a block range limit: `(end_block_index - start_block_index) <= 1000`. ## Parameters * `start_block_index` — the starting block number for scanning (inclusive) * `end_block_index` — the ending block number for scanning (exclusive, max 1000 blocks from start) * `ovk` — the outgoing viewing key for decrypting sent notes (64 hex characters, no `0x` prefix) * `shielded_TRC20_contract_address` — the shielded TRC20 contract address to scan (hex format starting with 41, no `0x` prefix) ## Response * `noteTxs` — array of found outgoing shielded note transactions * `txid` — transaction ID containing the note * `index` — index of the note within the transaction * `note` — the decrypted note details * `value` — note value * `payment_address` — recipient shielded payment address * `rcm` — randomness commitment * `memo` — optional memo field * `position` — position in the merkle tree * `is_spent` — whether the note has been spent ## Use case The `wallet/scanshieldedtrc20notesbyovk` method is used for: * Tracking outgoing shielded TRC20 payments for transaction history * Monitoring sent transactions without revealing recipient details * Building wallet interfaces that show outbound shielded transaction records * Providing audit trails for shielded transaction activity > [!NOTE] > > The outgoing viewing key (ovk) is derived from the spending key (sk) and is used by the sender to view shielded transactions they send. This 256-bit key allows transaction creators to track their outgoing payments without compromising the privacy of recipients. # walletsolidity/getaccount | TRON Source: https://docs.chainstack.com/reference/tron-solidity-getaccount openapi/tron_node_api/walletsolidity_getaccount.json post /95e61622bf6a8af293978377718e3b77/walletsolidity/getaccount TRON API method that retrieves account details from solidified blocks. This method queries confirmed and finalized blockchain state. TRON API method that retrieves account details from solidified blocks. This method queries confirmed and finalized blockchain state, providing reliable account information that cannot be rolled back. ## Parameters * `address` — the account address in hex format (41 prefix) or base58check format * `visible` — whether the address is in readable format (base58check) ## Response Returns detailed account information including: * `account_name` — the account name (if set) * `address` — the account address * `balance` — TRX balance in sun (1 TRX = 1,000,000 sun) * `frozen` — frozen balance information for energy and bandwidth * `asset_v2` — TRC-10 token balances * `allowance` — delegated resource allowances * `votes` — voting information for Super Representatives * `create_time` — account creation timestamp ## Use case The `walletsolidity/getaccount` method is used for: * Retrieving confirmed account balances and asset holdings from solidified blocks * Checking account resource delegations and frozen balances in finalized state * Analyzing account voting patterns from confirmed blockchain data * Building applications that require reliable, non-reversible account information # walletsolidity/getblockbyid | TRON Source: https://docs.chainstack.com/reference/tron-solidity-getblockbyid openapi/tron_node_api/walletsolidity_getblockbyid.json post /95e61622bf6a8af293978377718e3b77/walletsolidity/getblockbyid TRON API method that retrieves a solidified block by its block ID (hash). This method queries confirmed and finalized blockchain state. TRON API method that retrieves a solidified block by its block ID (hash). This method queries confirmed and finalized blockchain state, providing reliable block information that cannot be rolled back. ## Parameters * `value` — the block ID (hash) in hexadecimal format ## Response Returns detailed block information including: * `blockID` — the block hash * `block_header` — block header information * `raw_data` — raw block header data * `witness_signature` — witness signature for the block * `transactions` — array of transactions in the block * `blockSize` — size of the block in bytes * `timestamp` — block creation timestamp * `txTrieRoot` — transaction trie root hash * `parentHash` — hash of the parent block * `number` — block number * `witness_address` — address of the block producer ## Use case The `walletsolidity/getblockbyid` method is used for: * Retrieving confirmed block data by hash from solidified blockchain state * Verifying block integrity using finalized block information * Building blockchain explorers with reliable, non-reversible block data * Implementing applications that require confirmed block information by hash lookup # walletsolidity/getblockbylatestnum | TRON Source: https://docs.chainstack.com/reference/tron-solidity-getblockbylatestnum openapi/tron_node_api/walletsolidity_getblockbylatestnum.json post /95e61622bf6a8af293978377718e3b77/walletsolidity/getblockbylatestnum TRON API method that retrieves the latest solidified blocks by specifying a count. walletsolidity/getblockbylatestnum on TRON via Chainstack. TRON API method that retrieves the latest solidified blocks by specifying a count. This method queries confirmed and finalized blockchain state, providing reliable recent block information that cannot be rolled back. ## Parameters * `num` — the number of latest blocks to retrieve (integer) ## Response Returns an array of the most recent block objects, each containing: * `blockID` — the block hash * `block_header` — block header information * `transactions` — array of transactions in each block * `blockSize` — size of each block in bytes * `timestamp` — block creation timestamps * `number` — block numbers ## Use case The `walletsolidity/getblockbylatestnum` method is used for: * Retrieving the most recent confirmed blocks from solidified blockchain state * Monitoring latest finalized blockchain activity * Building applications that display recent reliable block data * Implementing real-time dashboards with confirmed block information # walletsolidity/getblockbylimitnext | TRON Source: https://docs.chainstack.com/reference/tron-solidity-getblockbylimitnext openapi/tron_node_api/walletsolidity_getblockbylimitnext.json post /95e61622bf6a8af293978377718e3b77/walletsolidity/getblockbylimitnext TRON API method that retrieves a range of solidified blocks by specifying start block number and limit. Chainstack TRON reference. TRON API method that retrieves a range of solidified blocks by specifying start block number and limit. This method queries confirmed and finalized blockchain state, providing reliable block information that cannot be rolled back. ## Parameters * `startNum` — the starting block number * `endNum` — the ending block number (range limit) ## Response Returns an array of block objects, each containing: * `blockID` — the block hash * `block_header` — block header information * `transactions` — array of transactions in each block * `blockSize` — size of each block in bytes * `timestamp` — block creation timestamps * `number` — block numbers in the range ## Use case The `walletsolidity/getblockbylimitnext` method is used for: * Retrieving multiple confirmed blocks in sequence from solidified blockchain state * Batch processing finalized block data for analysis * Building blockchain explorers with paginated, reliable block data * Implementing applications that require sequential confirmed block information # walletsolidity/getblockbynum | TRON Source: https://docs.chainstack.com/reference/tron-solidity-getblockbynum openapi/tron_node_api/walletsolidity_getblockbynum.json post /95e61622bf6a8af293978377718e3b77/walletsolidity/getblockbynum TRON API method that retrieves a solidified block by its block number. This method queries confirmed and finalized blockchain state. TRON API method that retrieves a solidified block by its block number. This method queries confirmed and finalized blockchain state, providing reliable block information that cannot be rolled back. ## Parameters * `num` — the block number to retrieve (integer) ## Response Returns detailed block information including: * `blockID` — the block hash * `block_header` — block header information * `raw_data` — raw block header data * `witness_signature` — witness signature for the block * `transactions` — array of transactions in the block * `blockSize` — size of the block in bytes * `timestamp` — block creation timestamp * `txTrieRoot` — transaction trie root hash * `parentHash` — hash of the parent block * `number` — block number * `witness_address` — address of the block producer ## Use case The `walletsolidity/getblockbynum` method is used for: * Retrieving confirmed block data by number from solidified blockchain state * Analyzing transaction patterns within finalized blocks * Building blockchain explorers with reliable, non-reversible block data * Implementing applications that require confirmed block information for auditing # walletsolidity/getnowblock | TRON Source: https://docs.chainstack.com/reference/tron-solidity-getnowblock openapi/tron_node_api/walletsolidity_getnowblock.json post /95e61622bf6a8af293978377718e3b77/walletsolidity/getnowblock TRON API method that retrieves the current solidified block (latest confirmed block). walletsolidity/getnowblock on TRON via Chainstack. TRON API method that retrieves the current solidified block (latest confirmed block). This method queries the most recent confirmed and finalized blockchain state, providing reliable current block information that cannot be rolled back. ## Parameters No parameters required. ## Response Returns the current solidified block information including: * `blockID` — the current block hash * `block_header` — current block header information * `transactions` — array of transactions in the current block * `blockSize` — size of the current block in bytes * `timestamp` — current block creation timestamp * `number` — current block number * `witness_address` — address of the current block producer ## Use case The `walletsolidity/getnowblock` method is used for: * Retrieving the latest confirmed block from solidified blockchain state * Getting current finalized blockchain height and timestamp * Building applications that need reliable current block information * Implementing monitoring systems that track confirmed blockchain progress # walletsolidity/gettransactionbyid | TRON Source: https://docs.chainstack.com/reference/tron-solidity-gettransactionbyid openapi/tron_node_api/walletsolidity_gettransactionbyid.json post /95e61622bf6a8af293978377718e3b77/walletsolidity/gettransactionbyid TRON API method that retrieves transaction details by transaction ID from solidified blocks. walletsolidity/gettransactionbyid on TRON via Chainstack. TRON API method that retrieves transaction details by transaction ID from solidified blocks. This method queries confirmed and finalized blockchain state, providing reliable transaction information that cannot be rolled back. ## Parameters * `value` — the transaction ID (hash) in hexadecimal format ## Response Returns detailed transaction information including: * `txID` — the transaction hash * `raw_data` — raw transaction data * `contract` — contract details * `timestamp` — transaction timestamp * `expiration` — transaction expiration time * `signature` — transaction signatures array * `ret` — transaction execution result * `raw_data_hex` — raw data in hexadecimal format ## Use case The `walletsolidity/gettransactionbyid` method is used for: * Retrieving confirmed transaction details from solidified blockchain state * Verifying transaction execution status using finalized data * Building transaction explorers with reliable, non-reversible information * Implementing applications that require confirmed transaction data for auditing # walletsolidity/gettransactioncountbyblocknum | TRON Source: https://docs.chainstack.com/reference/tron-solidity-gettransactioncountbyblocknum openapi/tron_node_api/walletsolidity_gettransactioncountbyblocknum.json post /95e61622bf6a8af293978377718e3b77/walletsolidity/gettransactioncountbyblocknum TRON API method that retrieves the transaction count within a solidified block by block number. Available on TRON via Chainstack JSON-RPC nodes. TRON API method that retrieves the transaction count within a solidified block by block number. This method queries confirmed and finalized blockchain state, providing reliable transaction count information that cannot be rolled back. ## Parameters * `num` — the block number to get transaction count for (integer) ## Response Returns transaction count information: * `count` — the number of transactions in the specified block * `blockNumber` — the block number queried ## Use case The `walletsolidity/gettransactioncountbyblocknum` method is used for: * Counting confirmed transactions in specific solidified blocks * Analyzing block activity and transaction volume from finalized data * Building blockchain statistics and analytics with reliable information * Implementing applications that require confirmed transaction count data # walletsolidity/gettransactioninfobyid | TRON Source: https://docs.chainstack.com/reference/tron-solidity-gettransactioninfobyid openapi/tron_node_api/walletsolidity_gettransactioninfobyid.json post /95e61622bf6a8af293978377718e3b77/walletsolidity/gettransactioninfobyid TRON API method that retrieves transaction execution information by transaction ID from solidified blocks. Chainstack TRON reference. TRON API method that retrieves transaction execution information by transaction ID from solidified blocks. This method queries confirmed and finalized blockchain state, providing reliable transaction execution details that cannot be rolled back. ## Parameters * `value` — the transaction ID (hash) in hexadecimal format ## Response Returns detailed transaction execution information including: * `id` — the transaction hash * `blockNumber` — block number containing the transaction * `blockTimeStamp` — block timestamp * `contractResult` — contract execution results * `fee` — transaction fee paid * `result` — transaction execution result (SUCCESS/REVERT) * `resMessage` — result message if failed * `receipt` — transaction receipt with resource usage * `log` — event logs generated by the transaction ## Use case The `walletsolidity/gettransactioninfobyid` method is used for: * Retrieving confirmed transaction execution results from solidified blockchain state * Analyzing gas usage and fees for finalized transactions * Monitoring contract execution outcomes using reliable data * Building applications that require confirmed transaction execution information # wallet/transferasset | TRON Source: https://docs.chainstack.com/reference/tron-transferasset openapi/tron_node_api/transferasset.json post /95e61622bf6a8af293978377718e3b77/wallet/transferasset TRON API method that creates an unsigned transaction for transferring TRC10 tokens between accounts. Use it on TRON via Chainstack. TRON API method that creates an unsigned transaction for transferring TRC10 tokens between accounts. ## Parameters * `owner_address` — the address sending the TRC10 tokens * `to_address` — the address receiving the TRC10 tokens * `asset_name` — the name or ID of the TRC10 token to transfer * `amount` — the amount of tokens to transfer * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `visible` — indicates the address format used in the response * `txID` — the transaction hash * `raw_data` — raw transaction data including: * `contract` — array containing the asset transfer contract * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — hexadecimal representation of the raw transaction ## Use case The `wallet/transferasset` method is used for: * Transferring TRC10 tokens between accounts * Building token transfer functionality in wallets and DApps * Creating automated token distribution systems * Implementing payment systems using TRC10 tokens * Facilitating token trading and exchange operations Note: The returned transaction must be signed and broadcast to complete the transfer. # wallet/triggerconstantcontract | TRON Source: https://docs.chainstack.com/reference/tron-triggerconstantcontract openapi/tron_node_api/triggerconstantcontract.json post /95e61622bf6a8af293978377718e3b77/wallet/triggerconstantcontract TRON API method that calls a constant (read-only) smart contract method without consuming energy or bandwidth. TRON via Chainstack. TRON API method that calls a constant (read-only) smart contract method without consuming energy or bandwidth. This method allows you to query contract state and call view functions without broadcasting a transaction to the blockchain. ## Parameters * `owner_address` — (optional) address calling the contract method in hexadecimal format * `contract_address` — smart contract address to call in hexadecimal format * `function_selector` — function signature or selector (e.g., "balanceOf(address)") * `parameter` — (optional) encoded parameters for the function call in hexadecimal format ## Response * `result` — execution result object containing: * `result` — boolean indicating if the call was successful * `energy_used` — estimated energy consumption if this were a real transaction * `constant_result` — array of return values from the contract method in hexadecimal format * `transaction` — transaction object that would be created (for reference, not executed) ## Use case The `wallet/triggerconstantcontract` method is used for: * Calling read-only smart contract methods to query blockchain state. * Reading token balances, contract variables, and other view data without costs. * Testing contract method calls before executing actual transactions. * Building user interfaces that need to display current contract state. * Implementing contract interaction tools that require state queries. * Estimating energy costs for contract method calls before execution. # wallet/triggersmartcontract | TRON Source: https://docs.chainstack.com/reference/tron-triggersmartcontract openapi/tron_node_api/triggersmartcontract.json post /95e61622bf6a8af293978377718e3b77/wallet/triggersmartcontract TRON API method that triggers a smart contract function call. This method creates a transaction to interact with deployed smart contracts on the TRON network. TRON API method that triggers a smart contract function call. This method creates a transaction to interact with deployed smart contracts on the TRON network, allowing you to call contract functions with specified parameters. ## Parameters * `contract_address` — the smart contract address (hex format) * `function_selector` — the function signature to call (e.g., "transfer(address,uint256)") * `parameter` — encoded parameters for the function call (hex format) * `owner_address` — the caller's TRON address (hex format) * `call_value` — optional TRX amount to send with the call (in sun) * `fee_limit` — maximum energy cost for the transaction (in sun) * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `result` — execution result object containing: * `result` — boolean indicating call success * `code` — status code * `message` — error message if call failed * `energy_used` — amount of energy consumed * `constant_result` — array of return values from the contract function * `transaction` — transaction object if creating a transaction * `logs` — array of event logs generated during execution ## Use case The `wallet/triggersmartcontract` method is used for: * Calling smart contract functions to read data or execute state changes. * Interacting with DApps and DeFi protocols on the TRON network. * Token transfers using TRC-20 contract methods. * Querying contract state and executing complex business logic. # wallet/undelegateresource | TRON Source: https://docs.chainstack.com/reference/tron-undelegateresource openapi/tron_node_api/undelegateresource.json post /95e61622bf6a8af293978377718e3b77/wallet/undelegateresource TRON API method that revokes delegated bandwidth or energy resources from another address. wallet/undelegateresource on TRON via Chainstack. TRON API method that revokes delegated bandwidth or energy resources from another address. This method allows you to reclaim resources that were previously delegated to another account using `delegateresource`, making those resources available to your own account again. ## Parameters * `owner_address` — the address that originally delegated the resources (hex format) * `receiver_address` — the address that was receiving the delegated resources (hex format) * `balance` — the amount of TRX worth of resources to undelegate (in sun, where 1 TRX = 1,000,000 sun) * `resource` — the resource type to undelegate ("BANDWIDTH" or "ENERGY") * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `visible` — boolean indicating address format used * `txID` — transaction ID hash * `raw_data` — raw transaction data object * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/undelegateresource` method is used for: * Reclaiming bandwidth or energy resources that were delegated to other accounts. * Managing resource allocation when delegation is no longer needed. * Redistributing resources between different accounts in your control. * Optimizing resource utilization across multiple addresses. # wallet/unfreezeasset | TRON Source: https://docs.chainstack.com/reference/tron-unfreezeasset openapi/tron_node_api/unfreezeasset.json post /95e61622bf6a8af293978377718e3b77/wallet/unfreezeasset TRON API method that creates an unsigned transaction for unfreezing TRC10 tokens that were previously frozen during token creation. TRON API method that creates an unsigned transaction for unfreezing TRC10 tokens that were previously frozen during token creation. This releases frozen tokens back to the token issuer's available balance. ## Parameters * `owner_address` — the address that issued the token and wants to unfreeze tokens * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `visible` — indicates the address format used in the response * `txID` — the transaction hash * `raw_data` — raw transaction data including: * `contract` — array containing the unfreeze asset contract * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — hexadecimal representation of the raw transaction ## Use case The `wallet/unfreezeasset` method is used for: * Releasing frozen TRC10 tokens after the freeze period expires * Managing token supply and liquidity by unfreezing previously locked tokens * Building token management tools that handle frozen supply schedules * Implementing automated token unlock mechanisms in smart contracts # wallet/unfreezebalance | TRON Source: https://docs.chainstack.com/reference/tron-unfreezebalance openapi/tron_node_api/unfreezebalance.json post /95e61622bf6a8af293978377718e3b77/wallet/unfreezebalance TRON API method that unstakes TRX previously frozen for bandwidth or energy resources (deprecated method). Chainstack TRON reference. TRON API method that unstakes TRX previously frozen for bandwidth or energy resources (deprecated method). This method releases TRX tokens that were previously frozen, making them available for transfer after the lock period expires. This is the legacy unstaking mechanism; for the current staking model, use `unfreezebalancev2`. > [!WARNING] > > legacy unstake works only for legacy freezes > > On mainnet, new legacy freezes are disabled. You can only use `wallet/unfreezebalance` if your account still has legacy‑frozen balance. For v2 staking, use [`wallet/unfreezebalancev2`](/reference/tron-unfreezebalancev2). ## Parameters * `owner_address` — the address that owns the frozen TRX to unfreeze (hex format) * `resource` — the resource type to release ("BANDWIDTH" or "ENERGY") * `receiver_address` — optional address that was receiving the resources. Omit to unfreeze to yourself. If provided, it must be different from `owner_address`. * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `visible` — boolean indicating address format used * `txID` — transaction ID hash * `raw_data` — raw transaction data object * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/unfreezebalance` method is used for: * Unstaking TRX to make tokens transferable again (legacy method). * Releasing bandwidth or energy resources that are no longer needed (legacy method). * Supporting older applications that still use the original unstaking mechanism. * Migrating from the deprecated unstaking system to the new `unfreezebalancev2` method. ## curl examples unfreeze to yourself (omit `receiver_address`): ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/unfreezebalance' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "41608f8da72479edc7dd921e4c30bb7e7cddbe722e", "resource": "BANDWIDTH", "visible": false }' ``` unfreeze delegated resources to another account (receiver must differ from owner): ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/unfreezebalance' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "41608f8da72479edc7dd921e4c30bb7e7cddbe722e", "resource": "ENERGY", "receiver_address": "41e9d79cc47518930bc322d9bf7cddd260a0260a8d", "visible": false }' ``` > [!NOTE] > > if you pass `receiver_address` equal to `owner_address`, the node returns: > > ```json theme={"system"} > { > "Error": "class org.tron.core.exception.ContractValidateException : receiverAddress must not be the same as ownerAddress" > } > ``` > > For the current staking model, use [`wallet/unfreezebalancev2`](/reference/tron-unfreezebalancev2). After the v2 waiting period ends, withdraw with [`wallet/withdrawexpireunfreeze`](/reference/tron-withdrawexpireunfreeze). # wallet/unfreezebalancev2 | TRON Source: https://docs.chainstack.com/reference/tron-unfreezebalancev2 openapi/tron_node_api/unfreezebalancev2.json post /95e61622bf6a8af293978377718e3b77/wallet/unfreezebalancev2 TRON API method that unstakes TRX from the new staking mechanism. This method releases TRX tokens that were previously staked using freezebalancev2. TRON API method that unstakes TRX from the new staking mechanism. This method releases TRX tokens that were previously staked using `freezebalancev2`, initiating the unstaking process with a 14-day waiting period before tokens become available for withdrawal. This is the current recommended unstaking method. ## Parameters * `owner_address` — the address that owns the staked TRX to unstake (hex format) * `unfreeze_balance` — the amount of TRX to unstake (in sun, where 1 TRX = 1,000,000 sun) * `resource` — the resource type to release ("BANDWIDTH" or "ENERGY") * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `visible` — boolean indicating address format used * `txID` — transaction ID hash * `raw_data` — raw transaction data object * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/unfreezebalancev2` method is used for: * Unstaking TRX using the current staking mechanism. * Initiating the 14-day unstaking period before tokens become withdrawable. * Managing bandwidth and energy resource allocation by reducing staked amounts. * Flexible unstaking of partial amounts rather than all-or-nothing. # wallet/updateaccount | TRON Source: https://docs.chainstack.com/reference/tron-updateaccount openapi/tron_node_api/updateaccount.json post /95e61622bf6a8af293978377718e3b77/wallet/updateaccount TRON API method that updates an account's name on the TRON blockchain. This method creates an unsigned transaction to set or modify the account name field. TRON API method that updates an account's name on the TRON blockchain. This method creates an unsigned transaction to set or modify the account name field. ## Parameters * `owner_address` — the account address whose name will be updated. * `account_name` — the new name for the account in hexadecimal format. Must be converted from string to hex before sending. * `visible` — optional boolean parameter. When set to `true`, the address is in base58 format. Default is `false`. ## Response * `visible` — indicates the address format used in the response * `txID` — the transaction hash * `raw_data` — raw transaction data including: * `contract` — array containing the account update contract * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — hexadecimal representation of the raw transaction ## Use case The `wallet/updateaccount` method is used for: * Setting a human-readable name for an account. * Updating account metadata for identification purposes. * Labeling accounts in wallet applications and explorers. * Organizing multiple accounts with descriptive names. Note: Account names are stored on-chain and consume bandwidth when set or updated. The returned transaction must be signed and broadcast to complete the update. # wallet/updateasset | TRON Source: https://docs.chainstack.com/reference/tron-updateasset openapi/tron_node_api/updateasset.json post /95e61622bf6a8af293978377718e3b77/wallet/updateasset TRON API method that creates an unsigned transaction for updating the information of an existing TRC10 token. TRON via Chainstack. TRON API method that creates an unsigned transaction for updating the information of an existing TRC10 token. This allows token issuers to modify certain properties of their tokens after creation. ## Parameters * `owner_address` — the address that issued the token and wants to update it * `description` — new description of the token project (string) * `url` — new official website URL for the token project (string) * `new_limit` — new bandwidth limit provided by token issuer for token operations (integer, optional) * `new_public_limit` — new public bandwidth limit available for this token (integer, optional) * `visible` — optional boolean parameter. When set to `true`, addresses are in base58 format. Default is `false`. ## Response * `visible` — indicates the address format used in the response * `txID` — the transaction hash * `raw_data` — raw transaction data including: * `contract` — array containing the update asset contract * `ref_block_bytes` — reference block bytes * `ref_block_hash` — reference block hash * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — hexadecimal representation of the raw transaction ## Use case The `wallet/updateasset` method is used for: * Updating token project information such as description and website URL * Modifying bandwidth allocations for token operations * Building token management interfaces that allow post-issuance updates * Implementing governance features that allow token information changes # wallet/updateBrokerage | TRON Source: https://docs.chainstack.com/reference/tron-updatebrokerage openapi/tron_node_api/updatebrokerage.json post /95e61622bf6a8af293978377718e3b77/wallet/updateBrokerage TRON API method that creates a transaction to update the brokerage rate for witness rewards. wallet/updateBrokerage on TRON via Chainstack. TRON API method that creates a transaction to update the brokerage rate for witness rewards. Only witnesses can call this method to adjust how they share block production rewards with their voters. ## Parameters * `owner_address` — address of the witness account updating the brokerage. Use base58 with `visible: true`, or hex with `visible: false`. * `brokerage` — new brokerage percentage (0–100); lower values share more rewards with voters. * `visible` — optional boolean. When `true`, addresses are base58; when `false`, hex. Default is `true`. ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the brokerage update transaction * `raw_data` — raw transaction data containing: * `contract` — array with brokerage update contract details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/updatebrokerage` method is used for: * Adjusting witness reward sharing policies to attract more voters. * Setting competitive brokerage rates compared to other witnesses. * Implementing dynamic reward sharing strategies based on market conditions. * Managing witness economics to balance profitability and voter incentives. > [!WARNING] > > Only registered witnesses can update their brokerage rate. The brokerage rate determines what percentage of block rewards the witness keeps (0-100%). A rate of 20% means the witness keeps 20% and shares 80% with voters. Changes may take effect after the next maintenance period. ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/updateBrokerage' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC", "brokerage": 15, "visible": true }' ``` > [!NOTE] > > common validation errors: > > * `No permission` — the `owner_address` is not a registered witness. > * `Account […] does not exist` — ensure the address is activated and the format matches `visible`. > * Rate bounds — brokerage must be between 0 and 100. > * `Not existed witness` — register the witness first via [`wallet/createwitness`](/reference/tron-createwitness) and wait until it is recognized on-chain. # wallet/updateenergylimit | TRON Source: https://docs.chainstack.com/reference/tron-updateenergylimit openapi/tron_node_api/updateenergylimit.json post /95e61622bf6a8af293978377718e3b77/wallet/updateenergylimit TRON API method that updates the origin energy limit for a smart contract. Reference for wallet/updateenergylimit on TRON via Chainstack. TRON API method that updates the origin energy limit for a smart contract. This setting determines the maximum amount of energy the contract creator provides for contract execution, affecting how much energy users need to provide when calling the contract. ## Parameters * `owner_address` — address of the contract owner who can update the energy limit * `contract_address` — address of the smart contract to update energy limit for * `origin_energy_limit` — maximum energy amount the contract creator provides (in energy units, not TRX/sun) * `visible` — boolean indicating whether to use visible (Base58) address format instead of hex ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the energy limit update transaction * `raw_data` — raw transaction data containing: * `contract` — array with contract update details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/updateenergylimit` method is used for: * Setting energy subsidies for contract users to improve user experience. * Managing contract execution costs by controlling energy allocation. * Optimizing contract economics by adjusting energy provision strategies. * Implementing freemium models where basic operations are subsidized. * Balancing between contract owner costs and user accessibility. * Adjusting energy limits based on contract usage patterns and feedback. > [!NOTE] > > Only the contract owner can update the origin energy limit. Higher limits mean the contract owner covers more execution costs, making the contract cheaper for users to interact with. The value is specified in energy units (not TRX/sun). ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/updateenergylimit' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC", "contract_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "origin_energy_limit": 100000000, "visible": true }' ``` This builds an unsigned transaction if the `owner_address` exists and is authorized (contract owner). Otherwise the node returns a validation error. **1. verify owner account exists** Use `wallet/getaccount` to make sure the address is activated and format matches `visible`. ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getaccount' \ --header 'Content-Type: application/json' \ --data '{ "address": "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC", "visible": true }' ``` **2. confirm contract and owner** `wallet/getcontract` expects a hex address. It returns `origin_address` (the deployer/owner) and current settings. ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getcontract' \ --header 'Content-Type: application/json' \ --data '{ "value": "41a614f803b6fd780986a42c78ec9c7f77e6ded13c" }' ``` > [!NOTE] > > common validation errors: > > * `Account [41…] does not exist` — the payer address does not exist on-chain or the format does not match `visible`. Query it with `wallet/getaccount` and fund/activate it first. > * `No permission` or similar — the `owner_address` is not the contract owner; only the `origin_address` can update settings. > * `Contract not found` — check the contract address and use hex for `wallet/getcontract`. # wallet/updatesetting | TRON Source: https://docs.chainstack.com/reference/tron-updatesetting openapi/tron_node_api/updatesetting.json post /95e61622bf6a8af293978377718e3b77/wallet/updatesetting TRON API method that updates the consume user resource percentage setting for a smart contract. wallet/updatesetting on TRON via Chainstack. TRON API method that updates the consume user resource percentage setting for a smart contract. This setting determines what percentage of the contract caller's resources (bandwidth and energy) should be consumed when executing the contract. ## Parameters * `owner_address` — address of the contract owner who can update settings * `contract_address` — address of the smart contract to update settings for * `consume_user_resource_percent` — percentage (0–100) of caller's resources to consume * `visible` — boolean indicating whether to use visible (Base58) address format instead of hex ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the setting update transaction * `raw_data` — raw transaction data containing: * `contract` — array with contract update details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/updatesetting` method is used for: * Adjusting resource consumption patterns for smart contract execution. * Optimizing contract costs by controlling user vs contract owner resource usage. * Managing energy and bandwidth allocation between contract and users. * Implementing different pricing models for contract interactions. * Fine-tuning contract economics and user experience trade-offs. * Setting up contracts to be more or less resource-friendly to callers. > [!NOTE] > > Only the contract owner can update the consume user resource percentage. Setting this to 100 means all resources come from the caller, while 0 means all resources come from the contract owner. The default is typically 100. ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/updatesetting' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC", "contract_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "consume_user_resource_percent": 10, "visible": true }' ``` **1. verify owner account exists** ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getaccount' \ --header 'Content-Type: application/json' \ --data '{ "address": "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC", "visible": true }' ``` **2. confirm contract owner** Use `wallet/getcontract` with a hex address to retrieve `origin_address` and ensure it matches `owner_address` (converted to hex if needed). ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/getcontract' \ --header 'Content-Type: application/json' \ --data '{ "value": "41a614f803b6fd780986a42c78ec9c7f77e6ded13c" }' ``` > [!NOTE] > > common validation errors: > > * `Account [41…] does not exist` — the payer address is not activated or `visible` does not match the provided address format. > * `No permission` — the `owner_address` is not the contract owner (`origin_address`). > * `Contract not found` — verify the contract address and format. # wallet/updatewitness | TRON Source: https://docs.chainstack.com/reference/tron-updatewitness openapi/tron_node_api/updatewitness.json post /95e61622bf6a8af293978377718e3b77/wallet/updatewitness TRON API method that creates a transaction to update a witness account's URL. Reference for wallet/updatewitness on TRON via Chainstack. TRON API method that creates a transaction to update a witness account's URL. This allows witnesses to update their public information and promotional content displayed to voters in the TRON network governance system. ## Parameters * `owner_address` — hexadecimal address of the witness account to update * `update_url` — new URL string for the witness (maximum 256 bytes) * `visible` — boolean indicating whether addresses should be in visible format (Base58Check) ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the witness update transaction * `raw_data` — raw transaction data containing: * `contract` — array with witness update contract details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/updatewitness` method is used for: * Updating witness promotional URLs and information for voters. * Maintaining current witness contact information and websites. * Building witness management dashboards and governance tools. * Implementing automated witness information updates in applications. * Creating witness profile management systems for TRON governance participants. > [!NOTE] > > Only the witness account owner can update their witness information. The URL field can contain website links, social media profiles, or other promotional content to help voters make informed decisions. Updates require network confirmation and consume bandwidth. # wallet/validateaddress | TRON Source: https://docs.chainstack.com/reference/tron-validateaddress openapi/tron_node_api/validateaddress.json post /95e61622bf6a8af293978377718e3b77/wallet/validateaddress TRON API method that validates whether a given address is a valid TRON address. Reference for wallet/validateaddress on TRON via Chainstack. TRON API method that validates whether a given address is a valid TRON address. This method checks the format and structure of the address to ensure it conforms to TRON's address specifications. ## Parameters * `address` — the TRON address to validate. Can be in base58 or hex format. * `visible` — optional boolean parameter. When set to `true`, the address is expected in base58 format (T-address). When `false` or omitted, hex format is expected. Default is `false`. ## Response * `result` — boolean indicating whether the address is valid (`true`) or invalid (`false`). * `message` — hexadecimal representation of the address if valid, or error message if invalid. ## Use case The `wallet/validateaddress` method is essential for: * Validating user input before processing transactions to prevent errors. * Verifying addresses received from external sources or user interfaces. * Implementing address validation in wallets and DApps to ensure data integrity. * Checking address format compatibility between base58 and hex representations. # wallet/votewitnessaccount | TRON Source: https://docs.chainstack.com/reference/tron-votewitnessaccount openapi/tron_node_api/votewitnessaccount.json post /95e61622bf6a8af293978377718e3b77/wallet/votewitnessaccount TRON API method that creates a transaction to vote for witnesses (validators) using frozen TRX balance. Chainstack TRON reference. TRON API method that creates a transaction to vote for witnesses (validators) using frozen TRX balance. Voting is essential for TRON's Delegated Proof of Stake (DPoS) governance system and helps determine which witnesses become Super Representatives. ## Parameters * `owner_address` — address of the account casting votes. Use base58 with `visible: true`, or hex with `visible: false`. * `votes` — array of vote allocations, each containing: * `vote_address` — witness address to vote for (base58 with `visible: true`, or a 21‑byte hex address starting with `41` when `visible: false`). * `vote_count` — number of votes to allocate to this witness. * `visible` — optional boolean. When `true`, addresses are base58; when `false`, hex. Default is `true`. ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the voting transaction * `raw_data` — raw transaction data containing: * `contract` — array with witness voting contract details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/votewitnessaccount` method is used for: * Participating in TRON network governance by voting for preferred witnesses. * Supporting witnesses that align with your vision for network development. * Earning voting rewards from witnesses who share profits with their voters. * Influencing which witnesses become the top 27 Super Representatives. * Distributing voting power across multiple witnesses for diversification. * Contributing to network decentralization through democratic participation. > [!NOTE] > > You can only vote using frozen TRX balance. You must have TRX frozen for Energy or Bandwidth to be eligible for voting. The total number of votes cannot exceed your frozen TRX balance, and votes can be distributed among multiple witnesses. ## curl example ```shell Shell theme={"system"} curl --request POST \ --url 'https://tron-mainnet.core.chainstack.com/95e61622bf6a8af293978377718e3b77/wallet/votewitnessaccount' \ --header 'Content-Type: application/json' \ --data '{ "owner_address": "THPvaUhoh2Qn2y9THCZML3H815hhFhn5YC", "votes": [ { "vote_address": "TDdeM7G4HSxhn2MfovsiMWwXZkiFaHhjMB", "vote_count": 1000000 }, { "vote_address": "TAybtvPZCSj5kumiU4myD28xBy6WFtkgCu", "vote_count": 500000 } ], "visible": true }' ``` > [!NOTE] > > avoid `Invalid address` by matching address format with `visible`: > > * base58 addresses → set `visible: true` (recommended for humans) > * hex addresses → set `visible: false` and use 21‑byte hex (42 hex chars) starting with `41` > > to get valid witness addresses, call [`wallet/listwitnesses`](/reference/tron-listwitnesses) and copy candidate addresses from the response. # wallet/withdrawbalance | TRON Source: https://docs.chainstack.com/reference/tron-withdrawbalance openapi/tron_node_api/withdrawbalance.json post /95e61622bf6a8af293978377718e3b77/wallet/withdrawbalance TRON API method that allows Super Representatives (witnesses) to withdraw their accumulated block production rewards. TRON via Chainstack. TRON API method that allows Super Representatives (witnesses) to withdraw their accumulated block production rewards. Witnesses earn TRX rewards for producing blocks and maintaining the network, and this method enables them to claim these earnings. ## Parameters * `owner_address` — address of the Super Representative (witness) withdrawing rewards * `visible` — boolean indicating whether to use visible (Base58) address format instead of hex ## Response * `visible` — boolean indicating whether addresses are in visible format * `txID` — unique transaction ID for the balance withdrawal transaction * `raw_data` — raw transaction data containing: * `contract` — array with withdrawal contract details * `ref_block_bytes` — reference block bytes for transaction validation * `ref_block_hash` — hash of the reference block * `expiration` — transaction expiration timestamp * `timestamp` — transaction creation timestamp * `raw_data_hex` — complete transaction data encoded in hexadecimal format ## Use case The `wallet/withdrawbalance` method is used for: * Super Representatives claiming their block production rewards. * Witnesses accessing earnings from network validation activities. * Managing witness node economics and profitability calculations. * Implementing automated reward collection systems for witness operations. * Building witness management tools and dashboards. * Creating financial reporting systems for TRON network validators. > [!NOTE] > > This method can only be used by addresses that are elected as Super Representatives (witnesses). Regular users cannot withdraw balance using this method. Witnesses must wait at least 24 hours after the last withdrawal before making another withdrawal. # wallet/withdrawexpireunfreeze | TRON Source: https://docs.chainstack.com/reference/tron-withdrawexpireunfreeze openapi/tron_node_api/withdrawexpireunfreeze.json post /95e61622bf6a8af293978377718e3b77/wallet/withdrawexpireunfreeze TRON API method that withdraws TRX tokens that have completed the unstaking waiting period. wallet/withdrawexpireunfreeze on TRON via Chainstack. TRON API method that withdraws TRX tokens that have completed the unstaking waiting period. This method allows you to claim TRX that was previously unstaked using `unfreezebalancev2` and has passed the mandatory 14-day waiting period, making the tokens available for transfer. ## Parameters * `owner_address` — the address that owns the expired unstaked TRX (hex format) * `visible` — optional boolean to specify address format (default: false for hex format) ## Response * `visible` — boolean indicating address format used * `txID` — transaction ID hash * `raw_data` — raw transaction data object * `raw_data_hex` — hexadecimal representation of raw transaction data ## Use case The `wallet/withdrawexpireunfreeze` method is used for: * Claiming TRX tokens that have completed the 14-day unstaking period. * Making previously staked TRX available for transfers and trading. * Completing the unstaking process started with `unfreezebalancev2`. * Managing liquidity by converting staked TRX back to transferable tokens. # eth_uninstallFilter | Polygon Source: https://docs.chainstack.com/reference/uninstallfilter openapi/polygon_node_api/filter_handling/eth_uninstallFilter.json POST /0615fdf3c9eaf0681469e61a4308ea09 Polygon API method used to remove a filter previously created using one of the following methods:. Use it on Polygon via Chainstack. Polygon API method used to remove a filter previously created using one of the following methods: * [eth\_newFilter | Polygon](/reference/newfilter) * [eth\_newPendingTransactionFilter | Polygon](/reference/polygon-newpendingtransactionfilter) * [eth\_newBlockFilter](/reference/polygon-newblockfilter) Upon successful execution, the filter is removed and will no longer emit events. This method can be useful for optimizing resource usage when a developer no longer needs to monitor certain events or blocks. It can also be used to clean up unused filters in order to reduce clutter in the client-side filter list. > [!WARNING] > > **Disclaimer** > > Note that the default interactive example in this page will not work as the filter will be expired. > > To test `eth_uninstallFilter` in this page, first create a new filter using one of the following: > > * [eth\_newFilter | Polygon](/reference/newfilter) > * [eth\_newPendingTransactionFilter | Polygon](/reference/polygon-newpendingtransactionfilter) > * [eth\_newBlockFilter](/reference/polygon-newblockfilter) > > Then use the fresh filter ID as the parameter for `eth_uninstallFilter`. ## Parameters * `string` — the filter ID that you want to uninstall ## Response * `boolean` — a boolean value indicating whether the filter was successfully uninstalled. `true` if successfully removed, and `false` if not. ## `eth_uninstallFilter` code examples > [!NOTE] > > Note that the `web3.eth.filter` methods have been deprecated and replaced with the `web3.eth.subscribe` in web3.js. See [web3.js subscriptions](/reference/polygon-web3js-subscriptions-methods). ```javascript ethers.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.JsonRpcProvider(NODE_URL); async function removeFilter(filter) { try { const removed = await provider.send('eth_uninstallFilter', [filter]); console.log(removed); // Log whether the filter is removed } catch (err) { console.error(err); // Handle errors that may occur } } const filterId = '0x4e7ef166cd43f188b0f8f9e218966a8f' //'YOUR_FILTER_iD' removeFilter(filterId) ``` ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) def remove_filter(filter_id): try: removed = web3.eth.uninstall_filter(filter_id) return removed except Exception as e: print(e) filter_id = '0x0' print(remove_filter(filter_id)) ``` ## Use case One use case for `eth_uninstallFilter` is to optimize resource usage in a DApp. When a DApp needs to monitor events on the blockchain, it can create a filter using [eth\_newFilter | Polygon](/reference/newfilter), [eth\_newPendingTransactionFilter | Polygon](/reference/polygon-newpendingtransactionfilter) , or [eth\_newBlockFilter](/reference/polygon-newblockfilter) to listen for specific events or blocks. However, once the DApp no longer needs to monitor these events or blocks, it can use `eth_uninstallFilter` to stop watching for them. By doing so, the DApp can reduce the number of active filters and free up resources, such as network bandwidth and computational power, that would otherwise be used to maintain the filter. For example, let's say that a DApp is monitoring incoming blocks, logs batches of 50 blocks, and then uses `uninstallFilter` to remove the filter using. Here is an implementation of this concept using ethers.js: ```javascript index.js theme={"system"} const ethers = require('ethers'); const NODE_URL = "CHAINSTACK_NODE_URL"; const provider = new ethers.providers.JsonRpcProvider(NODE_URL); const BLOCKS_TO_FETCH = 50; const BLOCK_FETCH_INTERVAL_MS = 200; async function getNewBlocks() { const filterId = await provider.send("eth_newBlockFilter", []); let blocks = []; while (blocks.length < BLOCKS_TO_FETCH) { const changes = await provider.send("eth_getFilterChanges", [filterId]); //console.log(changes) // show the changes if (changes.length === 0) { await new Promise(resolve => setTimeout(resolve, BLOCK_FETCH_INTERVAL_MS)); // wait for new blocks } else { blocks = blocks.concat(changes); } } const removeFilter = await provider.send("eth_uninstallFilter", [filterId]); // clean up filter if (removeFilter) { console.log(`We got 50 blocks, filter ${filterId} was removed.`) return blocks } } async function main() { const fiftyBlocks = await getNewBlocks(); console.log(fiftyBlocks) } main() ``` This code fetches the latest 50 blocks using a filter and creates an array. The `BLOCKS_TO_FETCH` constant is set to 50, which represents the number of blocks to fetch. The `BLOCK_FETCH_INTERVAL_MS` constant is set to 200, which represents the time interval (in milliseconds) to wait for new blocks to arrive. The `getNewBlocks` function is defined to fetch the blocks. This function uses a loop to retrieve new blocks until it has fetched the desired number of blocks. The loop waits for new blocks to arrive for a certain amount of time before trying again. Inside the `getNewBlocks` function, the [eth\_newBlockFilter](/reference/polygon-newblockfilter) JSON-RPC method is called to create a new filter to watch for new blocks. The filter ID is returned and stored in the `filterId` variable. An array stores the blocks fetched. Inside the loop, the [eth\_getFilterChanges | Polygon](/reference/getfilterchanges) JSON-RPC method is called to retrieve any new blocks that have arrived since the filter was created. If there are no new blocks, the code waits a certain amount of time before trying again. If there are new blocks, they are stored in the `blocks` array. Once the `blocks` array has reached the `BLOCKS_TO_FETCH` limit, the filter is uninstalled using the `eth_uninstallFilter` JSON-RPC method. The `removeFilter` variable is set to true if the filter is successfully removed. If the filter is removed successfully, a message is logged to the console indicating that `BLOCKS_TO_FETCH` blocks have been fetched and the filter was removed. The blocks array is returned as the result of the `getNewBlocks` function. The main function is defined to call the `getNewBlocks` function and store the result in the `fiftyBlocks` variable. The `fiftyBlocks` variable is then logged to the console. # Web3 libraries & tools Source: https://docs.chainstack.com/reference/web3-libraries Overview of Web3 libraries for blockchain development — connect to nodes, build dapps, manage wallets, and interact with smart contracts across EVM chains. The most popular Web3 libraries are [web3.js](https://web3js.readthedocs.io/), [ethers.js](https://docs.ethers.org/), and [web3.py](https://web3py.readthedocs.io/). These libraries are widely used in the Web3 community and provide similar functionality. ## Install a Web3 library Learn how to install Web3 libraries and connect to a blockchain for developing DApps. ### JavaScript libraries To install a Web3 JavaScript library, you need node.js installed on your machine: * Install [node.js](https://nodejs.org/en/download/) Then you can install the library using npm: ```shell ethers.js theme={"system"} npm install ethers ``` ```shell viem theme={"system"} npm install viem ``` ### Python libraries To install and use a Python library, you first need to install Python in your system: > [card] [Install Python](https://realpython.com/installing-python/) Then you can install the library using pip: ```shell web3.py theme={"system"} pip install web3 ``` ## Get started To get started with Web3 libraries, you will need to have a basic understanding of the EVM and smart contracts. It is also recommended to have a local development environment set up. Once you have set up your development environment, you can start interacting with different blockchains using the Web3 library of your choice. ### Create a node instance #### web3.js To get started with web3.js, you will need to create a provider instance. This can be done by instantiating the `Web3` class and passing in the URL of an Ethereum node deployed with Chainstack. For example: > [!NOTE] > > In `web3.js` V4, numeric values are returned as `BigInt`, identifiable by the n suffix (e.g., `247n`). This change ensures high-precision handling of large integers in blockchain applications. Remember to either convert these values to standard integers or adapt other values to BigInt when performing mathematical operations. #### ethers.js Ethers.js supports `ChainstackProvider`. You can create a `ChainstackProvider` instance and connect to Ethereum Mainnet (mainnet), Arbitrum (arbitrum), BNB Smart Chain Mainnet (bnb), Polygon (matic). For example: ```javascript ethers.js theme={"system"} const ethers = require("ethers"); // Create a ChainstackProvider instance for Ethereum mainnet const chainstack = new ethers.ChainstackProvider("mainnet"); // Swap 'mainnet' for 'arbitrum', 'bnb' , and 'matic' ``` #### Viem To get started with Viem, set up `client` instance with a `transport`. This can be done with `createPublicClient` and passing in the URL of an Ethereum node deployed with Chainstack. For example: ```javascript Viem theme={"system"} import { createPublicClient, http } from 'viem'; const publicClient = createPublicClient({ transport: http('CHAINSTACK_NODE_URL') }); ``` > [!NOTE] > > Viem typically adheres to the ES6 standard for module imports. #### web3.py To start with web3.py, you will need to create a provider instance. This can be done by instantiating the `Web3` class and passing in the URL of an Ethereum node deployed with Chainstack. For example: ```python web3.py theme={"system"} from web3 import Web3 node_url = "CHAINSTACK_NODE_URL" web3 = Web3(Web3.HTTPProvider(node_url)) ``` Once you have created a provider instance, you can use the functions provided by the library to interact with a blockchain. # zks_estimateFee | zkSync Era Source: https://docs.chainstack.com/reference/zks_estimatefee openapi/zksync_node_api/zks_estimateFee.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 zkSync Era API method that returns an estimate of the transaction fee for a given transaction in the zkSync network. On zkSync Era. zkSync Era API method that returns an estimate of the transaction fee for a given transaction in the zkSync network. This method estimates the fee required to process a transaction on the zkSync network. It considers the current network load and the transaction's complexity. Developers can use this estimate to inform users of the expected cost to ensure transactions are processed efficiently on the zkSync network. ## Parameters The transaction request object for which the fee is estimated. It includes several fields similar to Ethereum transactions but tailored for zkSync's L2 operations: * `from` — the string of the address initiating the transaction. * `to` — the string of the address to which the transaction is directed. Can be a wallet address or a smart contract on zkSync. * `data` — (optional) additional data sent with the transaction, typically used for smart contract interactions. It's a string of the hash of the method signature and encoded parameters, aligning with the Ethereum Contract ABI. ## Response The response includes detailed fee information, breaking down the costs associated with executing the transaction on zkSync and any associated costs on the L1 network: * `gasLimit` — the maximum gas limit that can be used for the transaction's execution. * `gasPerPubdataLimit` — the gas cost per byte of public data in the transaction, a unique aspect of zkSync's scaling solution. * `maxFeePerGas` — the maximum fee (in Wei) the sender will pay for each gas unit. * `maxPriorityFeePerGas` — the maximum priority fee per gas. ## Use Case Using `zks_estimateFee` applications can provide accurate fee estimates for various operations, ensuring users are fully informed of upfront costs. This facilitates a more efficient allocation of resources and enhances user trust through transparency. # zks_estimateGasL1ToL2 | zkSync Era Source: https://docs.chainstack.com/reference/zks_estimategasl1tol2 openapi/zksync_node_api/zks_estimateGasL1ToL2.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 The zks_estimateGasL1ToL2 API method estimates the gas required to execute a transaction from Layer 1 (Ethereum) to Layer 2 (zkSync). The `zks_estimateGasL1ToL2` API method estimates the gas required to execute a transaction from Layer 1 (Ethereum) to Layer 2 (zkSync). This estimation is crucial for users and applications that optimize their transactions by accurately forecasting the gas needed for bridging assets or executing operations from L1 to L2 in the zkSync network. ## Parameters The transaction request object for which the L1 to L2 gas estimation is made. It encapsulates details about the transaction, including: * `from` — a string representing the Ethereum address from which the transaction is initiated. * `to` — a string indicating the target address on zkSync Layer 2. This could be a wallet address or a smart contract within the zkSync ecosystem. * `data` — (Optional) a string of the hashed method signature and encoded parameters intended for smart contract interactions. This aligns with the Ethereum Contract ABI, facilitating seamless operations across layers. ## Response The response to the `zks_estimateGasL1ToL2` request provides the estimated gas requirement for the transaction, enabling precise budgeting for gas expenses: * `result` — the estimated gas required for the transaction, represented as a hex string. ## Use case A practical application of `zks_estimateGasL1ToL2` is for a DApp that bridges assets from Ethereum to zkSync, offering users lower transaction costs and faster processing. By estimating the required gas before initiating the bridge, the DApp ensures users have sufficient funds for the transaction, enhancing transparency and efficiency in cross-layer operations. # zks_getAllAccountBalances | zkSync Era Source: https://docs.chainstack.com/reference/zks_getallaccountbalances openapi/zksync_node_api/zks_getAllAccountBalances.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 The zks_getAllAccountBalances API method retrieves the balances of all confirmed tokens for a given account address on the zkSync network. The `zks_getAllAccountBalances` API method retrieves the balances of all confirmed tokens for a given account address on the zkSync network. This method is integral for users and applications requiring a comprehensive overview of an account's assets on Layer 2 (zkSync), facilitating asset management, portfolio tracking, and interoperability within decentralized applications (DApps). ## Parameters The method accepts a single parameter within its request: * `address` — a string representing the account address for the requested balance information. This address should be associated with the user's wallet on zkSync Layer 2. ## Response * `result` — an object where each key represents the token address, and the value is the balance in Wei of that token for the specified account, in hexadecimal. > [!NOTE] > > In the `zks_getAllAccountBalances API response`, a balance associated with the address `0x0000000000000000000000000000000000000000` specifically represents Ethereum (ETH). ## Use case A key use case for `zks_getAllAccountBalances` is within a DApp that requires real-time balance information across various tokens for a user's wallet on zkSync. This could be crucial for financial DApps, portfolio trackers, or any application that manages or displays user assets across multiple tokens. > [!NOTE] > > You can use the Chainstack [EVM Swiss Army Knife](https://web3tools.chainstacklabs.com/) to convert values. # zks_getBlockDetails | zkSync Era Source: https://docs.chainstack.com/reference/zks_getblockdetails openapi/zksync_node_api/zks_getBlockDetails.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 The zks_getBlockDetails API method provides comprehensive zkSync-specific information about a Layer 2 block. zkSync Era via Chainstack. The `zks_getBlockDetails` API method provides comprehensive zkSync-specific information about a Layer 2 block. This includes details on the block's commitment, proof, and execution on Layer 1, offering a deep dive into its lifecycle and current state within the zkSync network. ## Parameters This method requires a single input parameter: * `block` — a `uint32` value representing the block number for which details are being requested. This is a specific identifier for the block within the zkSync Layer 2 network. ## Response Upon execution, the `zks_getBlockDetails` method returns an object with information about the block: * `baseSystemContractsHashes` — an object containing hashes of the base system contracts involved in the block's transactions, offering insights into the foundational components of the zkSync network for this block. This includes: * `bootloader` — the hash of the bootloader contract. * `default_aa` — the hash of the default account abstraction contract. * `commitTxHash` — the hash of the transaction that has been committed to the network, marking the initial stage of the transaction's lifecycle on Layer 1. * `committedAt` — the timestamp (ISO 8601 format) indicating when the transaction was committed to the network, providing a precise temporal context. * `executeTxHash` — the transaction hash executed on the network, signifying the application of the transaction's effects on the zkSync state. * `executedAt` — the timestamp (ISO 8601 format) when the transaction was executed on the network, marking the completion of its effects on the state. * `l1BatchNumber` — the batch number associated with this transaction on the Layer 1 (Ethereum) network, linking the zkSync activity back to its Ethereum roots. * `l1GasPrice` — the gas price utilized for the transaction on the Layer 1 network, reflecting the cost of execution on Ethereum. * `l1TxCount` — the number of transactions related to this batch on the Layer 1 network, providing a measure of the batch's complexity and size. * `l2FairGasPrice` — the fair gas price applied to transactions within the zkSync network. * `l2TxCount` — the total number of transactions processed within this block on the Layer 2 network, indicating the block's activity level. * `number` — the specific number of the block within the zkSync blockchain. * `operatorAddress` — the address of the operator who executed the transaction, offering accountability and traceability within the network's operations. * `proveTxHash` — the hash of the proof transaction on the Layer 1 network, crucial for validating the block's integrity and finality. * `provenAt` — the timestamp (ISO 8601 format) when the proof transaction was completed on the Layer 1 network, marking a key verification step. * `rootHash` — the root hash representing the updated state of the network following the transaction, essential for ensuring network consistency. * `status` — the status of the block. * `timestamp` — the UNIX timestamp marking the time of the block's processing, providing a global temporal reference. ## Use case A blockchain analytics platform may use the `zks_getBlockDetails` method to track block statuses, analyze gas prices or monitor operator activity. Similarly, a wallet service could leverage this data to inform users about the confirmation and finality of their transactions, enhancing trust and transparency in the service. # zks_getBridgeContracts | zkSync Era Source: https://docs.chainstack.com/reference/zks_getbridgecontracts openapi/zksync_node_api/zks_getBridgeContracts.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 The zks_getBridgeContracts API method fetches the addresses of the default bridges between Layer 1 (Ethereum) and Layer 2 (zkSync). The `zks_getBridgeContracts` API method fetches the addresses of the default bridges between Layer 1 (Ethereum) and Layer 2 (zkSync). These bridges are pivotal for transferring ERC-20 tokens across layers, playing an essential role in the zkSync ecosystem by enabling efficient, secure, and low-cost asset transfers. This method is especially useful for developers and applications requiring integration with the default bridging mechanisms provided by zkSync. ## Parameters * `none` ## Response The response includes the addresses of the default bridge contracts for ERC-20 tokens between Layer 1 and Layer 2: * `l1Erc20DefaultBridge` — the default ERC-20 bridge contract address on Layer 1 (Ethereum). This contract is used for initiating transfers from Ethereum to zkSync. * `l2Erc20DefaultBridge` — the default ERC-20 bridge contract address on Layer 2 (zkSync). ## Use Case A wallet application integrating cross-layer transfer functionalities can use the `zks_getBridgeContracts` method to dynamically fetch the current default bridge contracts, ensuring that transactions are routed through the appropriate channels. # zks_getBytecodeByHash | zkSync Era Source: https://docs.chainstack.com/reference/zks_getbytecodebyhash openapi/zksync_node_api/zks_getBytecodeByHash.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 The zks_getBytecodeByHash API method provides the functionality to retrieve the bytecode of a transaction given its hash. On zkSync Era. The `zks_getBytecodeByHash` API method provides the functionality to retrieve the bytecode of a transaction given its hash. This is particularly useful for developers and applications that require inspection or verification of the transaction's bytecode for analysis, security audits, or integration purposes within the zkSync network. ## Parameters * `hash` — a `H256` string representing the hash of the transaction whose bytecode is being requested. This hash acts as a unique identifier for transactions within the zkSync network. ## Response The response returns the bytecode of the specified transaction as an array of bytes: * `result` — the bytecode is returned as an array, with each element representing a byte. ## Use case For a blockchain analytics platform, understanding the bytecode of transactions can be crucial for analyzing contract interactions, detecting anomalies, or verifying contract logic on the zkSync network. The `zks_getBytecodeByHash` method enables such platforms to fetch transaction bytecode dynamically, providing a powerful tool for in-depth blockchain analysis. # zks_getL1BatchBlockRange | zkSync Era Source: https://docs.chainstack.com/reference/zks_getl1batchblockrange openapi/zksync_node_api/zks_getL1BatchBlockRange.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 The zks_getL1BatchBlockRange API method is designed to provide the range of blocks within a specific zkSync network batch. On zkSync Era. The `zks_getL1BatchBlockRange` API method is designed to provide the range of blocks within a specific zkSync network batch. Given a batch number, it returns the starting and ending block numbers in hexadecimal format. ## Parameters * `batch` — an `L1BatchNumber` representing the [batch number](https://explorer.zksync.io/batch/443314) for which the block range is being requested. ## Response The response provides the starting and ending block numbers of the specified batch, returned as hexadecimal strings: * `result` — the block range is returned as an array containing two elements: the starting and ending block numbers in hexadecimal format. > [!NOTE] > > You can use the Chainstack [EVM Swiss Army Knife](https://web3tools.chainstacklabs.com/) to convert values. ## Use case A developer creating a security audit tool for DeFi applications on zkSync could use `zks_getL1BatchBlockRange` to monitor and analyze transactions within specific batches. This enables timely detection of suspicious patterns or anomalies, enhancing the application's security by ensuring transactions align with expected behavior and regulatory compliance. # zks_getL1BatchDetails | zkSync Era Source: https://docs.chainstack.com/reference/zks_getl1batchdetails openapi/zksync_node_api/zks_getL1BatchDetails.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 The zks_getL1BatchDetails API method offers a comprehensive look into a specific batch's details on Layer 1. zkSync Era via Chainstack. The `zks_getL1BatchDetails` API method offers a comprehensive look into a specific batch's details on Layer 1. By inputting a batch number, developers can retrieve a wealth of information, including transaction hashes, execution details, gas prices, and the batch's overall status. ## Parameters * `batch` — an `L1BatchNumber` that specifies the batch number on Layer 1 for which details are being requested. ## Response Upon execution, the `zks_getL1BatchDetails` method returns a detailed object encompassing various aspects of the specified batch: * `baseSystemContractsHashes` — contains hashes for key system contracts, offering insight into the infrastructure underpinning the transactions within the batch. * `bootloader` — the hash of the bootloader contract, a fundamental component in initializing contract execution. * `default_aa` — the hash of the default account abstraction contract, facilitating user interactions with contracts. * `commitTxHash` — the transaction hash for the commitment phase on the network, highlighting the first step in the transaction lifecycle. * `committedAt` — the ISO 8601 timestamp marking the commitment of the transaction to the network, providing a precise timeline. * `executeTxHash` — the hash of the executed transaction, indicating the application of the transaction's effects on the zkSync network. * `executedAt` — the ISO 8601 timestamp for when the transaction execution took place, marking the conclusion of its network effects. * `l1GasPrice` — shows the gas price on Layer 1 at the time of the transaction, reflecting the execution cost on Ethereum. * `l1TxCount` — the count of transactions in this batch on Layer 1, offering insight into the batch's complexity. * `l2FairGasPrice` — the fair gas price within the zkSync network, guiding transaction cost efficiency on Layer 2. * `l2TxCount` — the number of transactions processed in the zkSync network for this batch, indicating its activity level. * `number` — the unique number of the batch. * `proveTxHash` — the hash of the transaction providing proof on Layer 1, essential for validating the batch's integrity and finality. * `provenAt` — the ISO 8601 timestamp when the proof transaction was verified on Layer 1, marking a crucial step in batch validation. * `rootHash` — the root hash of the network state post-transaction, key for ensuring the consistency and integrity of the network state. * `status` — the current status of the batch, providing immediate insight into its processing stage and overall health. * `timestamp` — a UNIX timestamp that marks the exact time of batch processing, serving as a universal reference point. ## Use case A smart contract developer optimizing a decentralized finance (DeFi) platform on zkSync could use the `zks_getL1BatchDetails` method to analyze gas usage across different batches. By examining details like gas prices, transaction counts, and execution times, the developer can identify patterns of high gas costs. # zks_getL2ToL1LogProof | zkSync Era Source: https://docs.chainstack.com/reference/zks_getl2tol1logproof openapi/zksync_node_api/zks_getL2ToL1LogProof.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 Reference docs for the zks_getL2ToL1LogProof JSON-RPC method on the zkSync Era blockchain, available via Chainstack JSON-RPC nodes. The `zks_getL2ToL1LogProof` API method facilitates the retrieval of proofs for logs generated from Layer 2 (L2) to Layer 1 (L1) transactions within the zkSync network. It's especially valuable for developers and applications that require verifiable cross-layer communication for operations like withdrawals or contract interactions. ## Parameters * `tx_hash` — a `bytes32` hash of the L2 transaction within which the L2 to L1 log was produced. This hash uniquely identifies the transaction across the network. * `l2_to_l1_log_index` — (optional) a numeric index specifying the position of the L2 to L1 log within the transaction. This optional parameter is used to pinpoint the exact log for which the proof is requested. ## Response The response includes the proof for the specified L2 to L1 log, if available: * `id` — the position of the log's leaf in the Merkle tree of L2 to L1 messages for the associated block, providing a unique identifier for the log within the tree. * `proof` — an array of hexadecimal strings representing the Merkle proof for the log. This proof is essential for verifying the log's presence and integrity within the block's Merkle tree. * `root` — the root hash of the Merkle tree of L2 to L1 messages for the block, serving as the cryptographic summary of all L2 to L1 logs within the block. The Merkle tree uses SHA-256, ensuring robust and secure hashing. ## Use case The `zks_getL2ToL1LogProof` method can be used to verify the legitimacy of withdrawal transactions from Layer 2 (zkSync) to Layer 1 (Ethereum). By obtaining and validating the Merkle proofs for these transactions, they ensure that each withdrawal is accurately recorded and immutable, reinforcing regulatory compliance and enhancing trust among exchange users in the security of cross-layer transactions. # zks_getMainContract | zkSync Era Source: https://docs.chainstack.com/reference/zks_getmaincontract openapi/zksync_node_api/zks_getMainContract.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 The zks_getMainContract API method is designed to return the address of the main zkSync Era smart contract the L1. zkSync Era via Chainstack. The `zks_getMainContract` API method is designed to return the address of the main zkSync Era smart contract the L1. This contract address is fundamental for developers and applications interfacing with the zkSync network. ## Parameters * `none` ## Response * `result` — the address of the zkSync Era main contract on the L1 blockchain. ## Use case This method returns the main contract address where zkSync holds funds on the L1 network (Ethereum). It could be used as interface to make a bridge between L1 and L2. # zks_getRawBlockTransactions | zkSync Era Source: https://docs.chainstack.com/reference/zks_getrawblocktransactions openapi/zksync_node_api/zks_getRawBlockTransactions.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 zkSync Era API method that returns data of transactions in a specified block. zks_getRawBlockTransactions on zkSync Era via Chainstack. zkSync Era API method that returns data of transactions in a specified block. This method allows developers and users to query and retrieve raw transaction data from a specific block on the zkSync network. ## Parameters `block` — the block number for which transactions are to be retrieved. It is a `uint32` value indicating the specific block on the zkSync network. ## Response The response provides a structured JSON object that encapsulates detailed transaction data within the specified block. This data is inclusive of both the execution specifics and relevant L1 network information, described as follows: * `common_data` — a structured collection of data common to the transaction being executed. This includes general transaction information and metadata. * `L1` — details specific to the Layer 1 (Ethereum) network that correlate with the zkSync transaction, comprising: * `canonicalTxHash` — the unique hash identifier of the transaction as recorded on the Ethereum network. * `deadlineBlock` — the last block by which the transaction must be processed on the Ethereum network to remain valid. * `ethBlock` — the specific block number on the Ethereum blockchain in which this transaction was included or referenced. * `ethHash` — the transaction hash on the Ethereum network, serving as a direct link to the transaction's Ethereum footprint. * `fullFee` — the total transaction fee calculated in hexadecimal format, encompassing all associated costs. * `gasLimit` — the upper cap on the gas that can be consumed by the transaction, denoted in hexadecimal format. * `gasPerPubdataLimit` — the cost of gas per byte of public data included in the transaction, also in hexadecimal format. * `layer2TipFee` — an optional tip fee for prioritizing the transaction on the zkSync network, represented in hexadecimal. * `maxFeePerGas` — the maximum amount (in Wei, the smallest ETH unit) that the sender is willing to pay per unit of gas, noted in hexadecimal. * `opProcessingType` — indicates the processing approach used for the transaction, distinguishing between standard and priority operations. * `priorityQueueType` — identifies the queue type for prioritized transactions, if applicable. * `refundRecipient` — the address designated to receive any refunds that may arise from the transaction process. * `sender` — the Ethereum address initiating the transaction. * `serialId` — a sequential identifier assigned to the transaction, facilitating tracking and management. * `toMint` — specifies an address, if any, where new tokens are minted as a result of the transaction. * `execute` — contains execution-specific information about the transaction, outlined as follows: * `calldata` — the encoded data sent with the transaction for interacting with smart contracts, formatted in hexadecimal. * `contractAddress` — the address of the smart contract being called or interacted with in the transaction. * `factoryDeps` — an array of dependencies required for the contract's execution, relevant in complex transactions or deployments. * `value` — the amount in Wei transferred or involved in the transaction, expressed in hexadecimal. * `received_timestamp_ms` — records the precise moment (in milliseconds since the Unix epoch) when the system received the transaction data, providing a timestamp for data entry. ## **Use Case** A developer could integrate `zks_getRawBlockTransactions` into a real-time financial tracking app to alert users of high-value transactions. The app provides insights by analyzing transaction patterns and values, helping users make informed investment decisions and monitor the market's pulse directly from the zkSync network. # zks_getTestnetPaymaster | zkSync Era Source: https://docs.chainstack.com/reference/zks_gettestnetpaymaster openapi/zksync_node_api/zks_getTestnetPaymaster.json POST /da5f4c6274cd225217d352308a952d2f zkSync Era API method that returns the address of the testnet paymaster. Reference for zks_getTestnetPaymaster on zkSync Era via Chainstack. zkSync Era API method that returns the address of the testnet paymaster. This paymaster is available on testnets and facilitates transactions where fees can be paid using ERC-20 compatible tokens, enabling developers to test their applications without needing native network tokens for fee payment. ## Parameters * `none` ## Response * `result` — the address of the testnet paymaster. > [!NOTE] > > The interactive example fetches the paymaster address from the zkSync Sepolia testnet. ## **Use case** Developers working on DApps for the zkSync network could use the `zks_getTestnetPaymaster` method to configure their applications to use the testnet paymaster for transactions. This allows for seamless testing of fee payments in ERC-20 tokens, enabling broader testing scenarios without the need for acquiring native testnet tokens. # zks_getTransactionDetails | zkSync Era Source: https://docs.chainstack.com/reference/zks_gettransactiondetails openapi/zksync_node_api/zks_getTransactionDetails.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 zkSync Era API method that returns detailed data from a specific transaction, identified by the transaction hash. zkSync Era via Chainstack. # zks\_getTransactionDetails | zkSync Era zkSync Era API method that returns detailed data from a specific transaction, identified by the transaction hash. This functionality is crucial for developers and users needing to retrieve comprehensive information about a transaction's lifecycle and status within the zkSync network. ## Parameters * `hash` — a string representing the transaction hash (H256 format) for which details are requested. ## Response The response is a JSON object containing key data points about the specified transaction: * `ethCommitTxHash` — the transaction hash of the commit phase on Ethereum, marking the transaction's initial recording. * `ethExecuteTxHash` — the transaction hash of the execute phase on Ethereum, indicating the transaction's execution or application. * `ethProveTxHash` — the Ethereum transaction hash for the proof phase, providing cryptographic proof of the transaction's correctness. * `fee` — the fee amount for the transaction, presented in hexadecimal format. * `gasPerPubdata` — the gas cost per byte of public data included in the transaction, in hexadecimal format. * `initiatorAddress` — the Ethereum address of the entity that initiated the transaction. * `isL1Originated` — a boolean flag indicating whether the transaction originated from the Layer 1 (Ethereum) network. * `receivedAt` — the timestamp indicating when the transaction was received on the Layer 2 (zkSync) network. * `status` — The current status of the transaction, which can be one of the following: * `Pending`: The transaction is in the mempool but has not yet been included in a block. * `Included`: The transaction has been included in a block, but the batch containing the block has not yet been committed to the Ethereum network. * `Verified`: The transaction has been included in a block and verified. Verification means the transaction has been committed, proven, and executed on the Ethereum L1 network, ensuring its finality and immutability. * `Failed`: The transaction is unverified or has failed due to various reasons such as insufficient balance, invalid parameters, or execution errors. ## **Use case** The `zks_getTransactionDetails` method is particularly useful for applications requiring real-time tracking and verifying transaction statuses. For example, a wallet application can use this method to provide users with up-to-date information on their transactions, including confirmation status, fee details, and the transaction's progression through the zkSync network. # zks_L1BatchNumber | zkSync Era Source: https://docs.chainstack.com/reference/zks_l1batchnumber openapi/zksync_node_api/zks_L1BatchNumber.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 zkSync Era API method that returns the latest Layer 1 (L1) batch number. Reference for zks_L1BatchNumber on zkSync Era via Chainstack. zkSync Era API method that returns the latest Layer 1 (L1) batch number. This method is crucial for developers who need to track the progress of batch processing on the zkSync network. Batches are groups of transactions processed together on L1 for efficiency and cost-effectiveness. ## Parameters * `none` ## Response * `result` — the latest L1 batch number, returned as a hexadecimal string. This number increments with each new batch processed on the L1 network, providing a sequential identifier that can be used to track processing progress and batch-related activities. > [!NOTE] > > You can use the Chainstack [EVM Swiss Army Knife](https://web3tools.chainstacklabs.com/) to convert values. ## **Use case** For developers implementing monitoring tools or analytics for zkSync, `zks_L1BatchNumber` could be used to obtain the most recent batch number. This can be particularly useful for applications that need to display the current processing state of the zkSync network or to analyze transaction throughput over time. # zks_L1ChainId | zkSync Era Source: https://docs.chainstack.com/reference/zks_l1chainid openapi/zksync_node_api/zks_L1ChainId.json POST /e4507ac2a9e34706a69fea9b2f3a9a15 zkSync Era API method that returns the chain ID of the underlying Layer 1 (L1) network. zks_L1ChainId on zkSync Era via Chainstack. zkSync Era API method that returns the chain ID of the underlying Layer 1 (L1) network. This method is essential for developers who must confirm the specific blockchain network their zkSync instance interacts with, ensuring compatibility and correct operation of their applications across different environments. ## Parameters * `none` ## Response * `result` — the chain ID of the underlying L1 network, returned as a hexadecimal string. > [!NOTE] > > You can use the Chainstack [EVM Swiss Army Knife](https://web3tools.chainstacklabs.com/) to convert values. ## **Use case** For developers building on zkSync, `zks_L1ChainId` provides a straightforward way to verify the L1 network their applications are operating on programmatically. This is particularly useful for applications that are deployed across multiple environments to avoid common pitfalls such as contract interaction on an unintended network, which can lead to loss of funds or data integrity issues.