81

I'd like to get the contract internal transactions like: https://etherscan.io/address/0xd654bdd32fc99471455e86c2e7f7d7b6437e9179#internaltx

I'm using web3 API. Is there any way to do it? Where do they appear in the blockchain?

5
  • 1
    hopefully the answer will explain what an internal transaction is
    – Paul S
    Commented Apr 27, 2016 at 18:47
  • They are the transactions triggered by the contracts.
    – Pablo Yabo
    Commented Apr 27, 2016 at 19:20
  • sure, but still not sure what it means. I don't recall a solidity keyword for this. link?
    – Paul S
    Commented Apr 27, 2016 at 19:47
  • 3
    @PaulS It's any transaction sent via the address.send() or address.call() functions in Solidity Commented Apr 27, 2016 at 23:19
  • No, there are other ways to 'send' ether like suicide() or when you create a new contract
    – Pablo Yabo
    Commented Apr 17, 2017 at 22:03

13 Answers 13

86

There's not currently any way to do this using the web3 API. Internal transactions, despite the name (which isn't part of the yellowpaper; it's a convention people have settled on) aren't actual transactions, and aren't included directly in the blockchain; they're value transfers that were initiated by executing a contract.

As such, they're not stored explicitly anywhere: they're the effects of running the transaction in question on the blockchain state. Blockchain explorers like etherscan obtain them by running a modified node with an instrumented EVM, which record all the value transfers that took place as part of transaction execution, storing them separately.

7
  • 4
    Got it! Do you know if there is a plan to add a sort of mechanism to get these transactions or I must create an instrumented EVM.
    – Pablo Yabo
    Commented Apr 28, 2016 at 19:31
  • 3
    I'm not aware of ongoing work to add an API like this, though there could be; if your goal is to get internal transfers for a given transaction, that'd be practical. If your goal is to get all transfers into an account, though, this isn't sufficient - and there's a bug for that. Commented Apr 28, 2016 at 19:59
  • 1
    I already have the tx list for non contract transactions but I'd like to add the 'internal transactions' to the transaction list. Do you think that an instrumented EVM could work for that?
    – Pablo Yabo
    Commented Apr 28, 2016 at 20:37
  • 2
    @PabloYabo It would, but if what you care about is "all transactions to this account", you have to run every single transaction through it, in case it produced a transfer to the account you care about. If you care about "all value transfers initiated by this transaction", then you can run it just on that transaction. Commented Apr 29, 2016 at 8:52
  • 1
    @PabloYabo In that case, such a hypothetical API would probably serve your purposes - but in the meantime, you'll have to instrument the EVM yourself. Commented Apr 29, 2016 at 13:16
36

In the Ethereum protocol there's only transactions and message calls. A transaction is a type of message call.

A transaction may perform other message calls, but these are not transactions (even though blockchain explorers may label them inaccurately as "internal transactions"). These (internal) message calls are not published on the blockchain. To find the internal calls, the transaction needs to be processed through the EVM (for example, https://github.com/ethereumjs/ethereumjs-vm).

To try illustrating, a transaction in Javascript looks like:

{
  from: ...,
  to: "C1",
  value: ...,
  gas: ...,
  data: ...,
  gasPrice: ...,
  nonce: ...
}

This is what you will see on the blockchain. Internal calls are the effects of taking the data part, feeding it to the contract C1, and executing the Ethereum Virtual Machine. The data is what tells C1 that it should call another contract C2: there is no separate {from:C1, to:C2,...} object on the blockchain that's needed.

The data is encoded according to an ABI that says things like which function should be called and what the arguments are.

Note: With @Nick's answer, all value transfers are a message call. But not all message calls are value transfers. A value transfer is when a contract is simply paid some Ether/wei (data is zero), but contracts can call each other without paying each other (data is non-zero, value is zero).

2
  • Is it possible to get all those logs?
    – Pablo Yabo
    Commented Jun 23, 2016 at 20:16
  • 3
    Yes, executing the EVM includes all LOG opcodes.
    – eth
    Commented Jun 23, 2016 at 20:31
32

Fortunately, Geth EVM has new tools to get this done. It's possible to use debug_traceTransaction with RPC API.

In NodeJS:

var web3 = require('web3').web3;
web3.currentProvider.sendAsync({
    method: "debug_traceTransaction",
    params: ['0x3fac854179691e377fc1aa180b71a4033b6bb3bde2a7ef00bc8e78f849ad356e', {}],
    jsonrpc: "2.0",
    id: "2"
}, function (err, result) {
    ...
});

Then, you'll need to'CREATE', 'CALL', 'CALLCODE' and 'DELEGATECALL' opcodes and keep track of the stack. You can read Nick Johnson detailed explanation: Instrumenting EVM

If I finally implement it I'll write a full article with the code.

6
  • 1
    The problem here is that any contract can call into the contract you're interested in at any time--if you want every internal transaction sent to your contract, the only way to do that is to search the traces of every transaction that happened since your contract was deployed. One semi-shortcut is to filter events, but this ignores 'internal transactions' that do not generate events. I posted some code here in C++ a couple of days ago: github.com/Great-Hill-Corporation/ethrpc Commented Oct 21, 2016 at 20:58
  • Yes, I know, it was just an example. Then, you'll need to do the same for each transaction. My idea is to index everything and then put the information in a database.
    – Pablo Yabo
    Commented Oct 22, 2016 at 0:05
  • I'm working on the same exact idea. Index everything and put it in a database. The trouble I see with this is that this centralizes that data. If I create that data, how I you know I didn't fake it? I've been trying to figure out a way to both index it and decentralize the 'indexing calculation.' I know how to decentralize the storage (IPFS), but not how to decentralize the indexing calculation. Do you have any thoughts on that? Do you have a github I can check out? A very rough start of mine is at github.com/Great-Hill-Corporation/ethrpc. Maybe Golem allows to decentralize the calc. Commented Oct 26, 2016 at 15:47
  • Why there is web3.web3 instead of only using web3. ?
    – alper
    Commented Dec 19, 2016 at 10:00
  • did you finish the implementation?
    – Artem
    Commented Mar 14, 2023 at 13:12
7

With recent versions of Parity (tested on 1.8.3) it is also possible. The RPC method is trace_replayTransaction. The corresponding code is something like

web3.currentProvider.sendAsync({
    method: "trace_replayTransaction",
    params: [desiredTransactionHash, ['trace']],
    jsonrpc: "2.0",
    id: "1"
}, function (err, out) {
    console.log(out);
}

Documentation is at the parity github repository.

6

You can use callTracer introduced in geth 1.8 https://github.com/ethereum/go-ethereum/pull/15516

$ nc -U /work/temp/rinkeby/geth.ipc
{"id": 1, "method": "debug_subscribe", "params": ["traceChain", "0x0", "0xffff", {"tracer": "callTracer"}]}

The API will stream back one IPC notification per non-empty block. An exception is the very last block, which will be reported even if empty so the user knows the stream is done.

{"jsonrpc":"2.0","id":1,"result":"0xe1deecc4b399e5fd2b2a8abbbc4624e2"}
{"jsonrpc":"2.0","method":"debug_subscription","params":{"subscription":"0xe1deecc4b399e5fd2b2a8abbbc4624e2","result":{"block":"0x37","hash":"0xdb16f0d4465f2fd79f10ba539b169404a3e026db1be082e7fd6071b4c5f37db7","traces":[{"from":"0x31b98d14007bdee637298086988a0bbd31184523","gas":"0x0","gasUsed":"0x0","input":"0x","output":"0x","time":"1.077µs","to":"0x2ed530faddb7349c1efdbf4410db2de835a004e4","type":"CALL","value":"0xde0b6b3a7640000"}]}}}
{"jsonrpc":"2.0","method":"debug_subscription","params":{"subscription":"0xe1deecc4b399e5fd2b2a8abbbc4624e2","result":{"block":"0xf43","hash":"0xacb74aa08838896ad60319bce6e07c92edb2f5253080eb3883549ed8f57ea679","traces":[{"from":"0x31b98d14007bdee637298086988a0bbd31184523","gas":"0x0","gasUsed":"0x0","input":"0x","output":"0x","time":"1.568µs","to":"0xbedcf417ff2752d996d2ade98b97a6f0bef4beb9","type":"CALL","value":"0xde0b6b3a7640000"}]}}}
{"jsonrpc":"2.0","method":"debug_subscription","params":{"subscription":"0xe1deecc4b399e5fd2b2a8abbbc4624e2","result":{"block":"0xf47","hash":"0xea841221179e37ca9cc23424b64201d8805df327c3296a513e9f1fe6faa5ffb3","traces":[{"from":"0xbedcf417ff2752d996d2ade98b97a6f0bef4beb9","gas":"0x4687a0","gasUsed":"0x12e0d","input":"0x6060604052341561000c57fe5b5b6101828061001c6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063230925601461003b575bfe5b341561004357fe5b61008360048080356000191690602001909190803560ff1690602001909190803560001916906020019091908035600019169060200190919050506100c5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000600185858585604051806000526020016040526000604051602001526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000866161da5a03f1151561014257fe5b50506020604051035190505b9493505050505600a165627a7a7230582054abc8e7b2d8ea0972823aa9f0df23ecb80ca0b58be9f31b7348d411aaf585be0029","output":"0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063230925601461003b575bfe5b341561004357fe5b61008360048080356000191690602001909190803560ff1690602001909190803560001916906020019091908035600019169060200190919050506100c5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000600185858585604051806000526020016040526000604051602001526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000866161da5a03f1151561014257fe5b50506020604051035190505b9493505050505600a165627a7a7230582054abc8e7b2d8ea0972823aa9f0df23ecb80ca0b58be9f31b7348d411aaf585be0029","time":"658.529µs","to":"0x5481c0fe170641bd2e0ff7f04161871829c1902d","type":"CREATE","value":"0x0"}]}}}
{"jsonrpc":"2.0","method":"debug_subscription","params":{"subscription":"0xe1deecc4b399e5fd2b2a8abbbc4624e2","result":{"block":"0xfff","hash":"0x254ccbc40eeeb183d8da11cf4908529f45d813ef8eefd0fbf8a024317561ac6b"}}}

Individual block tracing is concurrent in the transactions (limited to num cores) and also makes chain tracing concurrent in the blocks (limited to num cores).

1
  • 1
    So how does one identify value transfer for an ERC-20 in that stream?
    – sat
    Commented Jan 6, 2020 at 19:37
4

For get information about internal transactions, you can use the debug_traceTransaction The method will return a full trace of the transaction. By the opcodes and parameters of each step, you can get the information you need.

There are 2 main problems: 1. Identify the working principle of the opcodes, because for example, not always CALL results in an internal transaction 2. With a large number of steps in the trace, the response may not fit in the buffer

The second problem can be solved by passing the second parameter to the methods for processing the steps on the geth side. More details can be found here - https://github.com/ethereum/go-ethereum/wiki/Management-APIs#debug_tracetransaction

The logic of how to handle opcodes can be found here https://github.com/Arachnid/etherquery/blob/master/etherquery/trace.go#L102 (for go implementation) or here https://github.com/tet32/etherscanner/blob/master/traceStepFunction.js (for nodejs implementation)

3

According to Parity's 1.1 version announcement, you could do it with it if you switch to this client. Quoting:

New JSONRPC APIs for tracking, tracing and inspecting all message-calls and balance transfers, including those that happen as "internal transactions";

Haven't tested it yet though.

3
  • 1
    This looks interesting, but couldn't find a well documented example even though the parity FAQ mentions a wiki page for it. Posted to github as Parity issue #1969
    – Paul
    Commented Aug 20, 2016 at 5:49
  • @Paul: that gives a 404 now, was it migrated to somewhere else?
    – knocte
    Commented Jul 4, 2018 at 8:51
  • github.com/paritytech/parity/issues/1969
    – Paul
    Commented Jul 4, 2018 at 8:53
3

There's no way to currently do this using the Web3 API. Your two options are (a) running a Parity node (can be expensive and take up to weeks to sync) or (b) using address activity tracking (such as internal transactions) from Alchemy Notify.

I'd highly recommend using Alchemy Notify, it was free, and I was up and running in less than 5 minutes. Here's a link to their docs: https://docs.alchemyapi.io/alchemy/guides/using-webhooks#address-activity

Best of luck!

1
  • the notify webhooks are awesome 💙 Commented Aug 4, 2022 at 3:05
2

While internal transactions have real consequences to account balances, surprisingly the internal transactions themselves are not stored in on-chain. To see internal transactions, you have to run the transaction and trace the calls that it makes. While some contracts do log events to the chain that record internal activity, many do not because doing so requires additional gas.

^ from the Blocknative.com blog — https://blog.blocknative.com/blog/eth-internal-transactions

Their Notify system now has support for Internal Transactions. You can get an update via their API when your wallet or contract is party to an internal transaction

1
  • 2
    As of 5-20-2021 it's not available on BSC, only Ethereum Mainnet. Commented May 20, 2021 at 17:54
1

Using Etherscan's API, you can do the job.

For more info on the concept, see this detailed answer.

0

Easy way to do this is to figure out the block the transaction happened. Knowing that you make a web3 call to a full archived node.web3.eth.getBalance(address, block) than subtract 1 block and do it again. Subtract the difference and there is your value.

-1

I think you just look at the transaction logs. If there is an "internal transaction", that transaction should be one of the logs in the transaction receipt object. But I could be wrong though. @vitalikbuterin

1
  • 2
    That is not true, a log is only created if a contract emits an event. If a contract doesn't emit an event there will be no log entry.
    – Ismael
    Commented Oct 22, 2021 at 6:10
-1

If you know the block of the transaction, you can know the value transferred:

                let tx;
                const txs = await web3.eth.getPastLogs({
                    fromBlock: block,
                    toBlock: block,
                    address: contractAddress
                });

                if(txs){
                    for(let i=0;i<txs.length;i++){
                        const tx_ = await web3.eth.getTransaction(txs[i].transactionHash);
                        if(tx_ && tx_.from === from){
                            tx = tx_;
                            break;
                        }
                    }
                }

To get the value, you call: tx.value

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.