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.
Get your own node endpoint todayStart for free and get your app to production levels immediately. No credit card required.You can sign up with your GitHub, X, Google, or Microsoft account.
debug_traceTransactionwithcallTracerreturns 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. - Node.js 20 or later.
Get an Elysium node
- Sign up with Chainstack.
- Deploy a node on Elysium Testnet.
- Copy the node’s HTTPS endpoint. See View node access and credentials.
The example transaction
This tutorial traces a token sale on Elysium Testnet, transaction0x6034ffc6c2209586e0787f18b0f11805c18dd432618d42c377d0b56433899508. 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
Calldebug_traceTransaction with callTracer. The onlyTopCall option returns only the top-level call:
cURL
JSON
valueis0x0— the sender attached no HYPE.outputis the return value ofsell, the HYPE the sender received:0x423b244aa1db702wei, or 0.298278008318179074 HYPE.beforeEVMTransfersandafterEVMTransfersare Nitro fields that list the balance changes outside EVM execution. See Where the fee went.
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
Step 3. Write the script
Createtrace-elysium-tx.mjs and replace YOUR_CHAINSTACK_ENDPOINT with your Elysium node’s HTTPS endpoint:
trace-elysium-tx.mjs
- 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 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
DELEGATECALLthat carries the same input. The script folds thatDELEGATECALLinto the call that reached the proxy, so each proxied call takes one line. - The call tree hides a
STATICCALLthat succeeded, unless a call inside it failed. ASTATICCALLreads state and can’t change it or move HYPE. - HYPE moves only in
CALL,CREATE,CREATE2, andSELFDESTRUCTframes. ADELEGATECALLframe repeats its parent’svaluewithout 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
ArbOwnerPublicprecompile at0x000000000000000000000000000000000000006b, and it recognizes the L1 pricer funds pool at0xA4B00000000000000000000000000000000000F6, which collects L1 posting fees.
Step 4. Run the script
Shell
Output
Read the output
Where the HYPE came from
The call tree shows the sale step by step:sellmoves the sender’s ELI tokens to the router withtransferFrom.- The router swaps the ELI for WHYPE, the wrapped HYPE token, through
SignalProjectXV2Routerand a Uniswap V2-style pair. The pair sends the WHYPE to the router. - The router calls
withdrawon WHYPE, and WHYPE sends 0.298278008318179074 HYPE to the router. - The router sends the same 0.298278008318179074 HYPE to the sender.
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 selector0xc45a0155, 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.
Where the fee went
Nitro charges and distributes the transaction fee outside EVM execution and records each balance change inbeforeEVMTransfers 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:
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()andgetNetworkFeeAccount()onArbOwnerPublicboth return this address. The infrastructure fee account receives the execution gas at the minimum base fee. When the base fee exceeds the minimum, a secondfeeCollectionentry 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 asgasUsedForL1.
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:
cURL
JSON
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 hastraceAddress[].subtraces— the number of calls that this call made.action.value— the HYPE moved, in wei.
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 — what each trace field means, and
debug_traceCallfor tracing a call before you send it. - Elysium methods — the tracers and debug methods available on Elysium.
- Elysium tooling — the chain configuration and libraries for Elysium Testnet.