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?
Ethereum Stack Exchange is a question and answer site for users of Ethereum, the decentralized application platform and smart contract enabled blockchain. It only takes a minute to sign up.
Sign up to join this communityI'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?
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.
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).
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.
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).
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.
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.
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)
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
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.
address.send()
oraddress.call()
functions in Solidity – Tjaden Hess♦ Apr 27 '16 at 23:19