Skip to main content

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

Metadata

Release files for palmera-ethtx 1.0.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for palmera-ethtx 1.0.8
File Size Uploaded
palmera-ethtx-1.0.8.tar.gz 71.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for palmera-ethtx 1.0.8
File Interpreter ABI Platform
palmera_ethtx-1.0.8-py3-none-any.whl Python 3 none any Details

Total release size: 193.1 kB

Release files / palmera-ethtx-1.0.8.tar.gz

Download URL palmera-ethtx-1.0.8.tar.gz
Size 71.3 kB
Tags Source
SHA-256 checksum
How to use checksums
0b92cf23f8bef266d02e158b1f7202bd0dba0d76758be0904017fac262b18b81
BLAKE2b-256 checksum
How to use checksums
88f56a49758cb719345e23b853f1565b80b6856086c40030188c8e89f6da5bb4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.10.1

Release files / palmera_ethtx-1.0.8-py3-none-any.whl

Download URL palmera_ethtx-1.0.8-py3-none-any.whl
Size 121.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
756fd6dc7fede4a2d31649e897d7499fb96812e49716315e3d5b437c7463be29
BLAKE2b-256 checksum
How to use checksums
d34cedd8c3d757e2a460bce08d0f287ff3f434bf6f724b3929b85cd93fb2d1b8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.10.1

Release history Release notifications | RSS feed

This release

1.0.8 This release

2 release files

1.0.7

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page