Skip to main content

Fork EthTx transaction decoder by Palmera

Project description

Palmera EthTx - Ethereum transactions decoder by Palmera

Python Black OpenSource Apache EthTxPyPi

Introduction

Source Code: https://github.com/ethtx/ethtx

Installation

pip install palmera-ethtx

Requirements

The package needs a few external resources, defined in EthTxConfig object:

  1. RPC node - required to have access to the raw Ethereum data; it must be a full archive node with the debug option ON
  2. Etherscan API key - required to get the source code and ABI for smart contracts used in transaction
  3. (Optional) MongoDB database - required to store smart contracts' ABI and semantics used in the decoding process. If you don't want to setup permanent database, you can enter mongomock://localhost/ethtx, then in-memory mongo will be set up that discards all data with every run.

Getting started

from ethtx import EthTx, EthTxConfig
from ethtx.models.decoded_model import DecodedTransaction

ethtx_config = EthTxConfig(
    mongo_connection_string="mongomock://localhost/ethtx",  ##MongoDB connection string,
    etherscan_api_key="",  ##Etherscan API key,
    web3nodes={
        "mainnet": {
            "hook": "_Geth_archive_node_URL_",  # multiple nodes supported, separate them with comma
            "poa": _POA_chain_indicator_  # represented by bool value
        }
    },
    default_chain="mainnet",
    etherscan_urls={"mainnet": "https://api.etherscan.io/api", },
)

ethtx = EthTx.initialize(ethtx_config)
decoded_transaction: DecodedTransaction = ethtx.decoders.decode_transaction(
    '0x50051e0a6f216ab9484c2080001c7e12d5138250acee1f4b7c725b8fb6bb922d')

Features

EthTx most important functions:

  1. Raw node data access:
web3provider = ethtx.providers.web3provider

from ethtx.models.w3_model import W3Transaction, W3Block, W3Receipt, W3CallTree

# read raw transaction data directly from the node
w3transaction: W3Transaction = web3provider.get_transaction(
    '0x50051e0a6f216ab9484c2080001c7e12d5138250acee1f4b7c725b8fb6bb922d')
w3block: W3Block = web3provider.get_block(w3transaction.blockNumber)
w3receipt: W3Receipt = web3provider.get_receipt(w3transaction.hash.hex())
w3calls: W3CallTree = web3provider.get_calls(w3transaction.hash.hex())
  1. ABI decoding:
from ethtx.models.decoded_model import (
    DecodedTransfer,
    DecodedBalance,
    DecodedEvent, DecodedCall,
)
from ethtx.models.objects_model import Transaction, Event, Block, Call

# read the raw transaction from the node
transaction = Transaction.from_raw(
    w3transaction=w3transaction, w3receipt=w3receipt, w3calltree=w3calls
)

# get proxies used in the transaction
proxies = ethtx.decoders.get_proxies(transaction.root_call, "mainnet")

block: Block = Block.from_raw(
    w3block=web3provider.get_block(transaction.metadata.block_number),
    chain_id="mainnet",
)

# decode transaction components
abi_decoded_events: List[Event] = ethtx.decoders.abi_decoder.decode_events(
    transaction.events, block.metadata, transaction.metadata
)
abi_decoded_calls: DecodedCall = ethtx.decoders.abi_decoder.decode_calls(
    transaction.root_call, block.metadata, transaction.metadata, proxies
)
abi_decoded_transfers: List[
    DecodedTransfer
] = ethtx.decoders.abi_decoder.decode_transfers(abi_decoded_calls, abi_decoded_events)
abi_decoded_balances: List[DecodedBalance] = ethtx.decoders.abi_decoder.decode_balances(
    abi_decoded_transfers
)

# decode a single event
raw_event: Event = transaction.events[3]
abi_decoded_event: DecodedEvent = ethtx.decoders.abi_decoder.decode_event(
    raw_event, block.metadata, transaction.metadata
)

# decode a single call
raw_call: Call = transaction.root_call.subcalls[0]
abi_decoded_call: DecodedCall = ethtx.decoders.abi_decoder.decode_call(
    raw_call, block.metadata, transaction.metadata, proxies
)
  1. Semantic decoding:
from ethtx.models.decoded_model import DecodedTransactionMetadata

# semantically decode transaction components
decoded_metadata: DecodedTransactionMetadata = (
    ethtx.decoders.semantic_decoder.decode_metadata(
        block.metadata, transaction.metadata, "mainnet"
    )
)
decoded_events: List[DecodedEvent] = ethtx.decoders.semantic_decoder.decode_events(
    abi_decoded_events, decoded_metadata, proxies
)

decoded_calls: Call = ethtx.decoders.semantic_decoder.decode_calls(
    abi_decoded_calls, decoded_metadata, proxies
)
decoded_transfers: List[
    DecodedTransfer
] = ethtx.decoders.semantic_decoder.decode_transfers(
    abi_decoded_transfers, decoded_metadata
)
decoded_balances: List[
    DecodedBalance
] = ethtx.decoders.semantic_decoder.decode_balances(
    abi_decoded_balances, decoded_metadata
)

# semantically decode a single event
decoded_event: DecodedEvent = ethtx.decoders.semantic_decoder.decode_event(
    abi_decoded_events[0], decoded_metadata, proxies
)
# semantically decode a single call
decoded_call: Call = ethtx.decoders.semantic_decoder.decode_call(
    abi_decoded_calls.subcalls[0], decoded_metadata, proxies
)
  1. ABI decoding:

Test the Eth.tx:

make script
  1. Example
# Decoding transaction on Goerli
decoded_transaction_goerli: DecodedTransaction = ethtx.decoders.decode_transaction(
    "0xd028241a7bbd2068439351994c286614190b26ecbc1cc27f6f2e76c7d2325f5b", "goerli"
)

Etherscan Link: Tx of Safe Address in Goerli

Ethtx.Info Link: Tx of Safe Address in Goerli

Response

{
  "balances": [
    {
      "holder": {
        "address": "0x7161be2924b4710d3754be067e01b0cd4544c695",
        "badge": "receiver",
        "name": "GnosisSafeProxy"
      },
      "tokens": [
        {
          "balance": "-100,000.0000",
          "token_address": "0xaaaa826d36d58a9b0e1055a75dce79ae99751c18",
          "token_standard": "ERC20",
          "token_symbol": "OmniseaBaseToken"
        }
      ]
    },
    {
      "holder": {
        "address": "0x00b97496700288c1bceb90ecc04a40c4aeac8bb4",
        "badge": null,
        "name": "0x00b97496700288c1bceb90ecc04a40c4aeac8bb4"
      },
      "tokens": [
        {
          "balance": "100,000.0000",
          "token_address": "0xaaaa826d36d58a9b0e1055a75dce79ae99751c18",
          "token_standard": "ERC20",
          "token_symbol": "OmniseaBaseToken"
        }
      ]
    }
  ],
  "block_metadata": {
    "block_hash": "0xc52f44b85b399d3341181864f85ebbcbf92f6081f74ded078dbbfd8d8b388b03",
    "block_number": "9647726",
    "canonical": true,
    "gas_limit": "30000000",
    "gas_used": "16010508",
    "miner": "0x8dc847af872947ac18d5d63fa646eb65d4d99560",
    "parent_hash": "0x9103881de8ca4f9e7ac0b76114a66fe68be4d5d1991c1bde64ae5a996d8f6883",
    "timestamp": "2023-09-06T16:04:36",
    "tx_count": "90"
  },
  "calls": {
    "arguments": [
      {
        "name": "to",
        "type": "address",
        "value": {
          "address": "0xaaaa826d36d58a9b0e1055a75dce79ae99751c18",
          "badge": null,
          "name": "OmniseaBaseToken"
        }
      },
      {
        "name": "value",
        "type": "uint256",
        "value": "0"
      },
      {
        "name": "data",
        "type": "bytes",
        "value": "0xa9059cbb00000000000000000000000000b97496700288c1bceb90ecc0...800000"
      },
      {
        "name": "operation",
        "type": "uint8",
        "value": "0"
      },
      {
        "name": "safeTxGas",
        "type": "uint256",
        "value": "0"
      },
      {
        "name": "baseGas",
        "type": "uint256",
        "value": "0"
      },
      {
        "name": "gasPrice",
        "type": "uint256",
        "value": "0"
      },
      {
        "name": "gasToken",
        "type": "address",
        "value": {
          "address": "0x0000000000000000000000000000000000000000",
          "badge": null,
          "name": "0x0000000000000000000000000000000000000000"
        }
      },
      {
        "name": "refundReceiver",
        "type": "address",
        "value": {
          "address": "0x0000000000000000000000000000000000000000",
          "badge": null,
          "name": "0x0000000000000000000000000000000000000000"
        }
      },
      {
        "name": "signatures",
        "type": "bytes",
        "value": "0x3fe2737edc8e26ab9aa25ae3f03b783032f140d87d382ca4b1400d8994...000001"
      }
    ],
    "call_id": "",
    "call_type": "call",
    "chain_id": "goerli",
    "error": null,
    "from_address": {
      "address": "0xf285e70ca2002b796a575e473285282bbf39d790",
      "badge": "sender",
      "name": "0xf285e70ca2002b796a575e473285282bbf39d790"
    },
    "function_guessed": false,
    "function_name": "execTransaction",
    "function_signature": "0x6a761202",
    "gas_used": "90538",
    "indent": "0",
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "value": "True"
      }
    ],
    "status": true,
    "subcalls": [
      {
        "arguments": [
          {
            "name": "to",
            "type": "address",
            "value": {
              "address": "0xaaaa826d36d58a9b0e1055a75dce79ae99751c18",
              "badge": null,
              "name": "OmniseaBaseToken"
            }
          },
          {
            "name": "value",
            "type": "uint256",
            "value": "0"
          },
          {
            "name": "data",
            "type": "bytes",
            "value": "0xa9059cbb00000000000000000000000000b97496700288c1bceb90ecc0...800000"
          },
          {
            "name": "operation",
            "type": "uint8",
            "value": "0"
          },
          {
            "name": "safeTxGas",
            "type": "uint256",
            "value": "0"
          },
          {
            "name": "baseGas",
            "type": "uint256",
            "value": "0"
          },
          {
            "name": "gasPrice",
            "type": "uint256",
            "value": "0"
          },
          {
            "name": "gasToken",
            "type": "address",
            "value": {
              "address": "0x0000000000000000000000000000000000000000",
              "badge": null,
              "name": "0x0000000000000000000000000000000000000000"
            }
          },
          {
            "name": "refundReceiver",
            "type": "address",
            "value": {
              "address": "0x0000000000000000000000000000000000000000",
              "badge": null,
              "name": "0x0000000000000000000000000000000000000000"
            }
          },
          {
            "name": "signatures",
            "type": "bytes",
            "value": "0x3fe2737edc8e26ab9aa25ae3f03b783032f140d87d382ca4b1400d8994...000001"
          }
        ],
        "call_id": "0",
        "call_type": "delegatecall",
        "chain_id": "goerli",
        "error": null,
        "from_address": {
          "address": "0x7161be2924b4710d3754be067e01b0cd4544c695",
          "badge": "receiver",
          "name": "GnosisSafeProxy"
        },
        "function_guessed": false,
        "function_name": "execTransaction",
        "function_signature": "0x6a761202",
        "gas_used": "65065",
        "indent": "1",
        "outputs": [
          {
            "name": "",
            "type": "bool",
            "value": "True"
          }
        ],
        "status": true,
        "subcalls": [
          {
            "arguments": [
              {
                "name": "hash",
                "type": "bytes32",
                "value": "0x2f1aa87e0b2fc0d0f8b6b33ec3718da2b110d59197ceb66e3c74c338b169a6e2"
              },
              {
                "name": "v",
                "type": "bytes8",
                "value": "0x000000000000000000000000000000000000000000000000000000000000001b"
              },
              {
                "name": "r",
                "type": "bytes32",
                "value": "0x3fe2737edc8e26ab9aa25ae3f03b783032f140d87d382ca4b1400d899483f17a"
              },
              {
                "name": "s",
                "type": "bytes32",
                "value": "0x71292f1b4be1e6b8caf9927caade2fdc5043aed142a34d2ef4af23ed809ef558"
              }
            ],
            "call_id": "0_0000",
            "call_type": "staticcall",
            "chain_id": "goerli",
            "error": null,
            "from_address": {
              "address": "0x7161be2924b4710d3754be067e01b0cd4544c695",
              "badge": "receiver",
              "name": "GnosisSafeProxy"
            },
            "function_guessed": false,
            "function_name": "ecrecover",
            "function_signature": "0x2f1aa87e",
            "gas_used": "3000",
            "indent": "2",
            "outputs": [
              {
                "name": "",
                "type": "address",
                "value": {
                  "address": "0x00b97496700288c1bceb90ecc04a40c4aeac8bb4",
                  "badge": null,
                  "name": "0x00b97496700288c1bceb90ecc04a40c4aeac8bb4"
                }
              }
            ],
            "status": true,
            "subcalls": [],
            "timestamp": "2023-09-06T16:04:36",
            "to_address": {
              "address": "0x0000000000000000000000000000000000000001",
              "badge": null,
              "name": "Precompiled"
            },
            "tx_hash": "0xd028241a7bbd2068439351994c286614190b26ecbc1cc27f6f2e76c7d2325f5b",
            "value": "0.0"
          },
          {
            "arguments": [
              {
                "name": "recipient",
                "type": "address",
                "value": {
                  "address": "0x00b97496700288c1bceb90ecc04a40c4aeac8bb4",
                  "badge": null,
                  "name": "0x00b97496700288c1bceb90ecc04a40c4aeac8bb4"
                }
              },
              {
                "name": "amount",
                "type": "uint256",
                "value": "100000"
              }
            ],
            "call_id": "0_0001",
            "call_type": "call",
            "chain_id": "goerli",
            "error": null,
            "from_address": {
              "address": "0x7161be2924b4710d3754be067e01b0cd4544c695",
              "badge": "receiver",
              "name": "GnosisSafeProxy"
            },
            "function_guessed": false,
            "function_name": "transfer",
            "function_signature": "0xa9059cbb",
            "gas_used": "13260",
            "indent": "2",
            "outputs": [
              {
                "name": "",
                "type": "bool",
                "value": "True"
              }
            ],
            "status": true,
            "subcalls": [],
            "timestamp": "2023-09-06T16:04:36",
            "to_address": {
              "address": "0xaaaa826d36d58a9b0e1055a75dce79ae99751c18",
              "badge": null,
              "name": "OmniseaBaseToken"
            },
            "tx_hash": "0xd028241a7bbd2068439351994c286614190b26ecbc1cc27f6f2e76c7d2325f5b",
            "value": "0.0"
          }
        ],
        "timestamp": "2023-09-06T16:04:36",
        "to_address": {
          "address": "0x3e5c63644e683549055b9be8653de26e0b4cd36e",
          "badge": null,
          "name": "GnosisSafeL2"
        },
        "tx_hash": "0xd028241a7bbd2068439351994c286614190b26ecbc1cc27f6f2e76c7d2325f5b",
        "value": "0.0"
      }
    ],
    "timestamp": "2023-09-06T16:04:36",
    "to_address": {
      "address": "0x7161be2924b4710d3754be067e01b0cd4544c695",
      "badge": "receiver",
      "name": "GnosisSafeProxy"
    },
    "tx_hash": "0xd028241a7bbd2068439351994c286614190b26ecbc1cc27f6f2e76c7d2325f5b",
    "value": "0.0"
  },
  "events": [
    {
      "call_id": null,
      "chain_id": "goerli",
      "contract": {
        "address": "0x7161be2924b4710d3754be067e01b0cd4544c695",
        "badge": "receiver",
        "name": "GnosisSafeProxy"
      },
      "event_guessed": false,
      "event_name": "SafeMultiSigTransaction",
      "event_signature": "0x66753cd2356569ee081232e3be8909b950e0a76c1f8460c3a5e3c2be32b11bed",
      "index": "81",
      "parameters": [
        {
          "name": "to",
          "type": "address",
          "value": {
            "address": "0xaaaa826d36d58a9b0e1055a75dce79ae99751c18",
            "badge": null,
            "name": "OmniseaBaseToken"
          }
        },
        {
          "name": "value",
          "type": "uint256",
          "value": "0"
        },
        {
          "name": "data",
          "type": "bytes",
          "value": "0xa9059cbb00000000000000000000000000b97496700288c1bceb90ecc0...800000"
        },
        {
          "name": "operation",
          "type": "uint8",
          "value": "0"
        },
        {
          "name": "safeTxGas",
          "type": "uint256",
          "value": "0"
        },
        {
          "name": "baseGas",
          "type": "uint256",
          "value": "0"
        },
        {
          "name": "gasPrice",
          "type": "uint256",
          "value": "0"
        },
        {
          "name": "gasToken",
          "type": "address",
          "value": {
            "address": "0x0000000000000000000000000000000000000000",
            "badge": null,
            "name": "0x0000000000000000000000000000000000000000"
          }
        },
        {
          "name": "refundReceiver",
          "type": "address",
          "value": {
            "address": "0x0000000000000000000000000000000000000000",
            "badge": null,
            "name": "0x0000000000000000000000000000000000000000"
          }
        },
        {
          "name": "signatures",
          "type": "bytes",
          "value": "0x3fe2737edc8e26ab9aa25ae3f03b783032f140d87d382ca4b1400d8994...000001"
        },
        {
          "name": "additionalInfo",
          "type": "bytes",
          "value": "0x0000000000000000000000000000000000000000000000000000000000...000002"
        }
      ],
      "timestamp": "2023-09-06T16:04:36",
      "tx_hash": "0xd028241a7bbd2068439351994c286614190b26ecbc1cc27f6f2e76c7d2325f5b"
    },
    {
      "call_id": null,
      "chain_id": "goerli",
      "contract": {
        "address": "0xaaaa826d36d58a9b0e1055a75dce79ae99751c18",
        "badge": null,
        "name": "OmniseaBaseToken"
      },
      "event_guessed": false,
      "event_name": "Transfer",
      "event_signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
      "index": "82",
      "parameters": [
        {
          "name": "from",
          "type": "address",
          "value": {
            "address": "0x7161be2924b4710d3754be067e01b0cd4544c695",
            "badge": "receiver",
            "name": "GnosisSafeProxy"
          }
        },
        {
          "name": "to",
          "type": "address",
          "value": {
            "address": "0x00b97496700288c1bceb90ecc04a40c4aeac8bb4",
            "badge": null,
            "name": "0x00b97496700288c1bceb90ecc04a40c4aeac8bb4"
          }
        },
        {
          "name": "value",
          "type": "uint256",
          "value": "100000"
        }
      ],
      "timestamp": "2023-09-06T16:04:36",
      "tx_hash": "0xd028241a7bbd2068439351994c286614190b26ecbc1cc27f6f2e76c7d2325f5b"
    },
    {
      "call_id": null,
      "chain_id": "goerli",
      "contract": {
        "address": "0x7161be2924b4710d3754be067e01b0cd4544c695",
        "badge": "receiver",
        "name": "GnosisSafeProxy"
      },
      "event_guessed": false,
      "event_name": "ExecutionSuccess",
      "event_signature": "0x442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e",
      "index": "83",
      "parameters": [
        {
          "name": "txHash",
          "type": "bytes32",
          "value": "0x2f1aa87e0b2fc0d0f8b6b33ec3718da2b110d59197ceb66e3c74c338b169a6e2"
        },
        {
          "name": "payment",
          "type": "uint256",
          "value": "0"
        }
      ],
      "timestamp": "2023-09-06T16:04:36",
      "tx_hash": "0xd028241a7bbd2068439351994c286614190b26ecbc1cc27f6f2e76c7d2325f5b"
    }
  ],
  "metadata": {
    "block_hash": "0xc52f44b85b399d3341181864f85ebbcbf92f6081f74ded078dbbfd8d8b388b03",
    "block_number": "9647726",
    "chain_id": "goerli",
    "from_address": null,
    "gas_limit": "96325",
    "gas_price": "1",
    "gas_used": "90538",
    "receiver": {
      "address": "0x7161be2924b4710d3754be067e01b0cd4544c695",
      "badge": "receiver",
      "name": "GnosisSafeProxy"
    },
    "sender": {
      "address": "0xf285e70ca2002b796a575e473285282bbf39d790",
      "badge": "sender",
      "name": "0xf285e70ca2002b796a575e473285282bbf39d790"
    },
    "success": true,
    "timestamp": "2023-09-06 16:04:36",
    "to_address": null,
    "tx_hash": "0xd028241a7bbd2068439351994c286614190b26ecbc1cc27f6f2e76c7d2325f5b",
    "tx_index": "21",
    "tx_value": "0"
  },
  "status": true,
  "transfers": [
    {
      "from_address": {
        "address": "0x7161be2924b4710d3754be067e01b0cd4544c695",
        "badge": "receiver",
        "name": "GnosisSafeProxy"
      },
      "to_address": {
        "address": "0x00b97496700288c1bceb90ecc04a40c4aeac8bb4",
        "badge": null,
        "name": "0x00b97496700288c1bceb90ecc04a40c4aeac8bb4"
      },
      "token_address": "0xaaaa826d36d58a9b0e1055a75dce79ae99751c18",
      "token_standard": "ERC20",
      "token_symbol": "OmniseaBaseToken",
      "value": "100000"
    }
  ]
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

palmera-ethtx-1.0.8.tar.gz (71.3 kB view details)

Uploaded Source

Built Distribution

palmera_ethtx-1.0.8-py3-none-any.whl (121.9 kB view details)

Uploaded Python 3

File details

Details for the file palmera-ethtx-1.0.8.tar.gz.

File metadata

  • Download URL: palmera-ethtx-1.0.8.tar.gz
  • Upload date:
  • Size: 71.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.1

File hashes

Hashes for palmera-ethtx-1.0.8.tar.gz
Algorithm Hash digest
SHA256 0b92cf23f8bef266d02e158b1f7202bd0dba0d76758be0904017fac262b18b81
MD5 e07f187732c5159fa6e36ebc1d184819
BLAKE2b-256 88f56a49758cb719345e23b853f1565b80b6856086c40030188c8e89f6da5bb4

See more details on using hashes here.

File details

Details for the file palmera_ethtx-1.0.8-py3-none-any.whl.

File metadata

File hashes

Hashes for palmera_ethtx-1.0.8-py3-none-any.whl
Algorithm Hash digest
SHA256 756fd6dc7fede4a2d31649e897d7499fb96812e49716315e3d5b437c7463be29
MD5 e5adae4d0226746fc4375407b371e630
BLAKE2b-256 d34cedd8c3d757e2a460bce08d0f287ff3f434bf6f724b3929b85cd93fb2d1b8

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page