Cómo obtener transacciones internas de contrato

Me gustaría obtener las transacciones internas del contrato como: https://etherscan.io/address/0xd654bdd32fc99471455e86c2e7f7d7b6437e9179#internaltx

Estoy usando la API web3. ¿Hay alguna forma de hacerlo? ¿Dónde aparecen en la cadena de bloques?

con suerte, la respuesta explicará qué es una transacción interna
Son las transacciones desencadenadas por los contratos.
seguro, pero todavía no estoy seguro de lo que significa. No recuerdo una palabra clave de solidez para esto. ¿Enlace?
@PaulS Es cualquier transacción enviada a través de las funciones address.send()o address.call()en Solidity
No, hay otras formas de 'enviar' ether como suicide() o cuando creas un nuevo contrato

Respuestas (13)

Actualmente no hay ninguna forma de hacer esto usando la API web3. Las transacciones internas, a pesar del nombre (que no forma parte del papel amarillo; es una convención que la gente ha establecido) no son transacciones reales y no están incluidas directamente en la cadena de bloques; son transferencias de valor que se iniciaron mediante la ejecución de un contrato.

Como tal, no se almacenan explícitamente en ningún lugar: son los efectos de ejecutar la transacción en cuestión en el estado de la cadena de bloques. Los exploradores de blockchain como etherscan los obtienen ejecutando un nodo modificado con un EVM instrumentado, que registra todas las transferencias de valor que tuvieron lugar como parte de la ejecución de transacciones, almacenándolas por separado.

¡Entiendo! ¿Sabe si hay un plan para agregar una especie de mecanismo para obtener estas transacciones o debo crear un EVM instrumentado?
No estoy al tanto del trabajo en curso para agregar una API como esta, aunque podría haber; si su objetivo es obtener transferencias internas para una transacción determinada, eso sería práctico. Sin embargo, si su objetivo es obtener todas las transferencias en una cuenta, esto no es suficiente, y hay un error para eso .
Ya tengo la lista de tx para transacciones sin contrato, pero me gustaría agregar las 'transacciones internas' a la lista de transacciones. ¿Crees que un EVM instrumentado podría funcionar para eso?
@PabloYabo Lo haría, pero si lo que le importa es "todas las transacciones a esta cuenta", debe ejecutar cada transacción a través de ella, en caso de que produzca una transferencia a la cuenta que le interesa. Si le importan "todas las transferencias de valor iniciadas por esta transacción", puede ejecutarlo solo en esa transacción.
La idea es tener una base de datos con todas las transacciones de todas las cuentas, por lo que ejecutar todas las transacciones es posible en este esquema. Ahora estamos recibiendo todos los bloques, transacciones y recibos.
@PabloYabo En ese caso, tal API hipotética probablemente cumpliría sus propósitos, pero mientras tanto, tendrá que instrumentar el EVM usted mismo.
@PabloYabo, explique cómo logró tener la lista de tx para transacciones sin contrato .

En el protocolo Ethereum solo hay transacciones y llamadas de mensajes. Una transacción es un tipo de llamada de mensaje.

Una transacción puede realizar otras llamadas de mensajes, pero estas no son transacciones (aunque los exploradores de cadenas de bloques pueden etiquetarlas incorrectamente como "transacciones internas"). Estas llamadas de mensajes (internos) no se publican en la cadena de bloques. Para encontrar las llamadas internas , la transacción debe procesarse a través de EVM (por ejemplo, https://github.com/ethereumjs/ethereumjs-vm ).

Para intentar ilustrar, una transacción en Javascript se ve así:

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

Esto es lo que verá en la cadena de bloques. Las llamadas internas son los efectos de tomar la dataparte, alimentarla con toel contrato C1 y ejecutar la máquina virtual Ethereum. Esto dataes lo que le dice a C1 que debe llamar a otro contrato C2: no hay ningún {from:C1, to:C2,...}objeto separado en la cadena de bloques que se necesite.

El dataestá codificado de acuerdo con una ABI que dice cosas como qué función debe llamarse y cuáles son los argumentos.

Nota: con la respuesta de @Nick, todas las transferencias de valor son una llamada de mensaje. Pero no todas las llamadas de mensajes son transferencias de valor. Una transferencia de valor es cuando a un contrato simplemente se le paga algo de Ether/wei (los datos son cero), pero los contratos pueden llamarse entre sí sin pagarse entre sí (los datos no son cero, el valor es cero).

¿Es posible obtener todos esos registros?
Sí, la ejecución de EVM incluye todos los códigos de operación de LOG.

Afortunadamente, Geth EVM tiene nuevas herramientas para hacer esto. Es posible usar debug_traceTransaction con la API de RPC.

En NodeJS:

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

Luego, necesitará 'CREATE', 'CALL', 'CALLCODE' y 'DELEGATECALL' códigos de operación y realizar un seguimiento de la pila. Puede leer la explicación detallada de Nick Johnson : Instrumenting EVM

Si finalmente lo implemento, escribiré un artículo completo con el código.

El problema aquí es que cualquier contrato puede invocar el contrato que le interesa en cualquier momento; si desea que todas las transacciones internas se envíen a su contrato, la única forma de hacerlo es buscar los rastros de cada transacción que ocurrió desde su contrato fue implementado. Un semi-atajo es filtrar eventos, pero esto ignora las 'transacciones internas' que no generan eventos. Publiqué un código aquí en C++ hace un par de días: github.com/Great-Hill-Corporation/ethrpc
Sí, lo sé, era sólo un ejemplo. Luego, deberá hacer lo mismo para cada transacción. Mi idea es indexar todo y luego poner la información en una base de datos.
Estoy trabajando en la misma idea exacta. Indexe todo y colóquelo en una base de datos. El problema que veo con esto es que centraliza esos datos. Si creo esos datos, ¿cómo sé que no los falsifiqué? He estado tratando de encontrar una manera de indexarlo y descentralizar el 'cálculo de indexación'. Sé cómo descentralizar el almacenamiento (IPFS), pero no cómo descentralizar el cálculo de indexación. ¿Tiene alguna idea sobre eso? ¿Tienes un github que pueda consultar? Un comienzo muy difícil para mí está en github.com/Great-Hill-Corporation/ethrpc . Tal vez Golem permita descentralizar el cálculo.
¿Por qué hay web3.web3 en lugar de solo usar web3. ?

Con versiones recientes de Parity (probadas en 1.8.3) también es posible. El método RPC es trace_replayTransaction. El código correspondiente es algo así como

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

La documentación se encuentra en el repositorio de parity github.

Puede usar callTracerintroducido en 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"}]}

La API transmitirá una notificación de IPC por bloque no vacío. Una excepción es el último bloque, que se informará incluso si está vacío para que el usuario sepa que la transmisión ha terminado.

{"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"}}}

El seguimiento de bloques individuales es concurrente en las transacciones (limitado a un número de núcleos) y también hace que el seguimiento de la cadena sea concurrente en los bloques (limitado a un número de núcleos).

Entonces, ¿cómo se identifica la transferencia de valor para un ERC-20 en ese flujo?

Para obtener información sobre transacciones internas, puede utilizar debug_traceTransaction. El método devolverá un seguimiento completo de la transacción. Mediante los códigos de operación y los parámetros de cada paso, puede obtener la información que necesita.

Hay 2 problemas principales: 1. Identificar el principio de funcionamiento de los códigos de operación porque, por ejemplo, no siempre CALL da como resultado una transacción interna 2. Con una gran cantidad de pasos en el seguimiento, es posible que la respuesta no quepa en el búfer

El segundo problema se puede resolver pasando el segundo parámetro a los métodos para procesar los pasos en el lado geth. Puede encontrar más detalles aquí: https://github.com/ethereum/go-ethereum/wiki/Management-APIs#debug_tracetransaction

La lógica de cómo manejar los códigos de operación se puede encontrar aquí https://github.com/Arachnid/etherquery/blob/master/etherquery/trace.go#L102 (para la implementación de go) o aquí https://github.com/tet32 /etherscanner/blob/master/traceStepFunction.js (para la implementación de nodejs)

De acuerdo con el anuncio de la versión 1.1 de Parity , podría hacerlo si cambia a este cliente. Citando:

Nuevas API JSONRPC para rastrear, rastrear e inspeccionar todas las llamadas de mensajes y transferencias de saldo, incluidas las que ocurren como "transacciones internas";

Aunque todavía no lo he probado.

Esto parece interesante, pero no pude encontrar un ejemplo bien documentado a pesar de que las preguntas frecuentes sobre paridad mencionan una página wiki para ello. Publicado en github como problema de paridad #1969
@Paul: eso da un 404 ahora, ¿se migró a otro lugar?

Actualmente no hay forma de hacer esto usando la API de Web3. Sus dos opciones son (a) ejecutar un nodo Parity (puede ser costoso y demorar hasta semanas en sincronizarse) o (b) usar el seguimiento de la actividad de la dirección (como transacciones internas) de Alchemy Notify.

Recomiendo encarecidamente usar Alchemy Notify, era gratis y estaba listo y funcionando en menos de 5 minutos. Aquí hay un enlace a sus documentos: https://docs.alchemyapi.io/alchemy/guides/using-webhooks#address-activity

¡La mejor de las suertes!

los webhooks de notificación son geniales 💙
+1 a estos webhooks de notificación

Si bien las transacciones internas tienen consecuencias reales en los saldos de las cuentas, sorprendentemente, las transacciones internas en sí mismas no se almacenan en la cadena. Para ver las transacciones internas, debe ejecutar la transacción y rastrear las llamadas que realiza. Si bien algunos contratos registran eventos en la cadena que registran la actividad interna, muchos no lo hacen porque hacerlo requiere gas adicional.

^ del blog Blocknative.com: https://blog.blocknative.com/blog/eth-internal-transactions

Su sistema Notify ahora admite transacciones internas. Puede obtener una actualización a través de su API cuando su billetera o contrato sea parte de una transacción interna

A partir del 20-5-2021 no está disponible en BSC, solo Ethereum Mainnet.

Una manera fácil de hacer esto es averiguar el bloque en el que ocurrió la transacción. Sabiendo que realiza una llamada web3 a un nodo archivado completo. web3.eth.getBalance(address, block)que restar 1 bloque y hacerlo de nuevo. Resta la diferencia y ahí está tu valor.

Creo que solo miras los registros de transacciones. Si hay una "transacción interna", esa transacción debe ser uno de los registros en el objeto de recibo de la transacción. Pero podría estar equivocado. @vitalikbuterin

Eso no es cierto, solo se crea un registro si un contrato emite un evento. Si un contrato no emite un evento, no habrá entrada de registro.

Si conoce el bloque de la transacción, puede conocer el valor transferido:

                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;
                        }
                    }
                }

Para obtener el valor, llame a:tx.value