IMPORTANT ASSUMPTION: this procedure does not verify the signatures associated to the tx, nor that the downloaded record file and corresponding txs hashes! If you want to query a verified data source, use your own mirror node to download and verify the signatures associated to the record file.
To get all the information you need to configure AWS CLI with your credentials for this tool to work correctly; it downloads files from AWS S3.
Alternatively, you can download the (alpha and deprecated) state proofs from the mirror node, and get the record stream elements from there. Note that if a transaction generates child transactions, this method will only allow you to decode information for the parent transaction. Also keep in mind that state proofs will be removed soon, so don't rely on this script method too much ;)
You have two options:
-
Set
HPE_STORAGE_PROVIDER="aws"in theconfigfile and initialize your AWS CLI with your credentials. Those credentials will be billed for the requests. -
Set
HPE_STORAGE_PROVIDER="gcp"and a billing project ID (HPE_GCP_BILLING_PROJECT_ID) in theconfigfile. Initialize the GCP CLI with your credentials.
The script support both human-friendly (i.e., 0.0.513587@1714079813.090631706) and mirror node (i.e., 0.0.513587-1714079813-090631706) transaction ID format.
Run the following script:
./examine.sh 0.0.513587-1714079813-090631706The output will be:
❯ ./examine.sh 0.0.513587-1714079813-090631706
2025-03-05T13:08:08.3NZ-3870 ⚑ Started ./examine.sh (PID 3870) with the following configuration
2025-03-05T13:08:08.3NZ-3870 ⛶ Network ..............................: mainnet
2025-03-05T13:08:08.3NZ-3870 ⛶ Transaction ID .......................: 0.0.513587-1714079813-090631706
2025-03-05T13:08:08.3NZ-3870 ⛶ Records folder .......................: ./records
2025-03-05T13:08:09.3NZ-3870 ⚙ Transactions with the same ID: 1
2025-03-05T13:08:09.3NZ-3870 ☕ Downloading recordstreams/record0.0.3/2024-04-25T21_17_08.000248496Z.rcd.gz
bytes 800018 binary/octet-stream "a88a4de02cfc283c9bb8f53473d5e875" 2024-04-25T21:17:15+00:00 COMPLETED requester AES256 z3SIH.ypHpLh1BLugG6JA3W7DdFcxbMH
=== Transaction #0 ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+ 01319c315d17ad89a22356691a6679979216e300b95c30758cee66f6a9409a65f25b7ba52d6ef0e593d9b6c6395f017e ===
{
transaction: {
signedTransactionBytes: 'ClgKGQoLCMWQq7EGEJrcmysSCAgAEAAYs6wfGAASBggAEAAYAxiAhK9fIgIIeDIAggMnEiUKCQgAEAAY0ciIAhIICAAQABizrB8aCQgAEAAYxaHpASID9vUBEswBCmQKIHcoN6wujR2qXjZWVOxzYBEP6a9i8khL0gIgT2JuXIKyGkDu761b22vDr/chkr5qaR/QqXPIdV2Y08/9S11nlChfAlMXZj0jIWgvFyN25CXzGB4rw+KhwOU8jPpnF3d+pBYACmQKIJUSLeJ3nu+RKNA7rsFMFMNOlg2U59HkwXbTZQS+/kh7GkDgkkcbbok6JZVz6dGgmaXQPTEz66X6EcvKx3BQID+b/BbwSaA6G9114w+olR3jqrIv2gMIAFkH6a+eexzVbCMK'
},
record: {
receipt: {
status: 'SUCCESS',
exchangeRate: {
currentRate: {
hbarEquiv: 30000,
centEquiv: 353419,
expirationTime: { seconds: '1714082400' }
},
nextRate: {
hbarEquiv: 30000,
centEquiv: 357345,
expirationTime: { seconds: '1714086000' }
}
}
},
transactionHash: 'ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+',
consensusTimestamp: { seconds: '1714079829', nanos: 44599703 },
transactionID: {
transactionValidStart: { seconds: '1714079813', nanos: 90631706 },
accountID: { accountNum: '513587' }
},
transactionFee: '68984744',
transferList: {
accountAmounts: [
{ accountID: { accountNum: '3' }, amount: '1765339' },
{ accountID: { accountNum: '98' }, amount: '60497465' },
{ accountID: { accountNum: '800' }, amount: '6721940' },
{ accountID: { accountNum: '513587' }, amount: '-68984744' }
]
}
}
}
=== Signatures #0 ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+ 01319c315d17ad89a22356691a6679979216e300b95c30758cee66f6a9409a65f25b7ba52d6ef0e593d9b6c6395f017e ===
{
pubKeyPrefix: 'dyg3rC6NHapeNlZU7HNgEQ/pr2LySEvSAiBPYm5cgrI=',
ed25519: '7u+tW9trw6/3IZK+amkf0KlzyHVdmNPP/UtdZ5QoXwJTF2Y9IyFoLxcjduQl8xgeK8PiocDlPIz6Zxd3fqQWAA==',
pubKeyPrefixHex: '772837ac2e8d1daa5e365654ec7360110fe9af62f2484bd202204f626e5c82b2'
}
{
pubKeyPrefix: 'lRIt4nee75Eo0DuuwUwUw06WDZTn0eTBdtNlBL7+SHs=',
ed25519: '4JJHG26JOiWVc+nRoJml0D0xM+ul+hHLysdwUCA/m/wW8EmgOhvddeMPqJUd46qyL9oDCABZB+mvnnsc1WwjCg==',
pubKeyPrefixHex: '95122de2779eef9128d03baec14c14c34e960d94e7d1e4c176d36504befe487b'
}
=== Body #0 ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+ 01319c315d17ad89a22356691a6679979216e300b95c30758cee66f6a9409a65f25b7ba52d6ef0e593d9b6c6395f017e ===
{
transactionID: {
transactionValidStart: { seconds: '1714079813', nanos: 90631706 },
accountID: { shardNum: '0', realmNum: '0', accountNum: '513587' },
scheduled: false
},
nodeAccountID: { shardNum: '0', realmNum: '0', accountNum: '3' },
transactionFee: '200000000',
transactionValidDuration: { seconds: '120' },
memo: '',
cryptoApproveAllowance: {
nftAllowances: [
{
tokenId: { shardNum: '0', realmNum: '0', tokenNum: '4334673' },
owner: { shardNum: '0', realmNum: '0', accountNum: '513587' },
spender: { shardNum: '0', realmNum: '0', accountNum: '3821765' },
serialNumbers: [ '31478' ]
}
]
}
}
2025-03-05T13:08:16.3NZ-3870 ✔ Transactions details in ./logs/0.0.513587-1714079813-090631706.txt
2025-03-05T13:08:16.3NZ-3870 🏁 Script ./examine.sh (PID 3870) endedThe output will be also saved in the default logs folder.
By default the script does not download the record file if it is already present. Override this behaviour with the overwrite-if-present parameter.
./examine.sh 0.0.513587-1714079813-090631706 overwrite-if-presentYou can also use a testnet parameter to switch network, like this:
./examine.sh 0.0.513587-1714079813-090631706 testnetIf you want to change the default values, edit the config file.
The script support both human-friendly (i.e., 0.0.513587@1714079813.090631706) and mirror node (i.e., 0.0.513587-1714079813-090631706) transaction ID format.
Run the following script:
node proto-decode-transaction-via-mirror-node 0.0.513587@1714079813.090631706The output will be:
Processing transaction: 0.0.513587-1714079813-090631706
Using mainnet mirror node
=== Record information ===
{
receipt: {
status: 'SUCCESS',
exchangeRate: {
currentRate: {
hbarEquiv: 30000,
centEquiv: 353419,
expirationTime: { seconds: '1714082400' }
},
nextRate: {
hbarEquiv: 30000,
centEquiv: 357345,
expirationTime: { seconds: '1714086000' }
}
}
},
transactionHash: 'ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+',
consensusTimestamp: { seconds: '1714079829', nanos: 44599703 },
transactionID: {
transactionValidStart: { seconds: '1714079813', nanos: 90631706 },
accountID: { accountNum: '513587' }
},
transactionFee: '68984744',
transferList: {
accountAmounts: [
{ accountID: { accountNum: '3' }, amount: '1765339' },
{ accountID: { accountNum: '98' }, amount: '60497465' },
{ accountID: { accountNum: '800' }, amount: '6721940' },
{ accountID: { accountNum: '513587' }, amount: '-68984744' }
]
}
}
=== Transaction raw information ===
{
signedTransactionBytes: 'ClgKGQoLCMWQq7EGEJrcmysSCAgAEAAYs6wfGAASBggAEAAYAxiAhK9fIgIIeDIAggMnEiUKCQgAEAAY0ciIAhIICAAQABizrB8aCQgAEAAYxaHpASID9vUBEswBCmQKIHcoN6wujR2qXjZWVOxzYBEP6a9i8khL0gIgT2JuXIKyGkDu761b22vDr/chkr5qaR/QqXPIdV2Y08/9S11nlChfAlMXZj0jIWgvFyN25CXzGB4rw+KhwOU8jPpnF3d+pBYACmQKIJUSLeJ3nu+RKNA7rsFMFMNOlg2U59HkwXbTZQS+/kh7GkDgkkcbbok6JZVz6dGgmaXQPTEz66X6EcvKx3BQID+b/BbwSaA6G9114w+olR3jqrIv2gMIAFkH6a+eexzVbCMK'
}
=== Signatures ===
{
pubKeyPrefix: 'dyg3rC6NHapeNlZU7HNgEQ/pr2LySEvSAiBPYm5cgrI=',
ed25519: '7u+tW9trw6/3IZK+amkf0KlzyHVdmNPP/UtdZ5QoXwJTF2Y9IyFoLxcjduQl8xgeK8PiocDlPIz6Zxd3fqQWAA==',
pubKeyPrefixHex: '772837ac2e8d1daa5e365654ec7360110fe9af62f2484bd202204f626e5c82b2'
}
{
pubKeyPrefix: 'lRIt4nee75Eo0DuuwUwUw06WDZTn0eTBdtNlBL7+SHs=',
ed25519: '4JJHG26JOiWVc+nRoJml0D0xM+ul+hHLysdwUCA/m/wW8EmgOhvddeMPqJUd46qyL9oDCABZB+mvnnsc1WwjCg==',
pubKeyPrefixHex: '95122de2779eef9128d03baec14c14c34e960d94e7d1e4c176d36504befe487b'
}
=== Body ===
{
transactionID: {
transactionValidStart: { seconds: '1714079813', nanos: 90631706 },
accountID: { shardNum: '0', realmNum: '0', accountNum: '513587' },
scheduled: false
},
nodeAccountID: { shardNum: '0', realmNum: '0', accountNum: '3' },
transactionFee: '200000000',
transactionValidDuration: { seconds: '120' },
memo: '',
cryptoApproveAllowance: {
nftAllowances: [
{
tokenId: { shardNum: '0', realmNum: '0', tokenNum: '4334673' },
owner: { shardNum: '0', realmNum: '0', accountNum: '513587' },
spender: { shardNum: '0', realmNum: '0', accountNum: '3821765' },
serialNumbers: [ '31478' ]
}
]
}
}You can add the testnet parameter to execute the analysis on the testnet.
Run the following script:
node proto-decode-schedule-via-mirror-node.js 0.0.226412The output will be:
Processing scheduleID: 0.0.226412
Using mainnet mirror node
=== Transaction details ===
{
transactionFee: '100000000',
memo: 'Shocked, I tell you!',
cryptoTransfer: {
transfers: {
accountAmounts: [
{ accountID: { accountNum: '98' }, amount: '1' },
{ accountID: { accountNum: '950' }, amount: '-1' }
]
}
}
}You can add the testnet parameter to execute the analysis on the testnet.
Please consider scheduled transactions expire, so don't be surprised if the example above does not work with the mentioned scheduleID. You can retrieve a list of current active schedules via Mirror Node's APIs, like this:
❯ curl --silent https://mainnet.mirrornode.hedera.com/api/v1/schedules | jq
{
"schedules": [
{
"admin_key": null,
"deleted": false,
"consensus_timestamp": "1620327749.254652999",
"creator_account_id": "0.0.950",
"executed_timestamp": "1620327749.254653000",
"expiration_time": null,
"memo": "",
"payer_account_id": "0.0.950",
"schedule_id": "0.0.226412",
"signatures": [
{
"consensus_timestamp": "1620327749.254652999",
"public_key_prefix": "Qg==",
"signature": "SUZHg7uixmb5MjubWNSs4H1FRQdgHwgj9KEe0XWQD4yvD9W+AAQkZBy+FdOnYbz/bfTT3op++Opg\nfGKKRqYKAg==",
"type": "ED25519"
}
],
"transaction_body": "CIDC1y8SFFNob2NrZWQsIEkgdGVsbCB5b3UhShMKEQoGCgIYYhACCgcKAxi2BxAB",
"wait_for_expiry": false
},
...
}-
First you want the tx hash
curl -s https://mainnet-public.mirrornode.hedera.com/api/v1/transactions/0.0.513587-1714079813-090631706 | jq '.transactions[] | { consensus_timestamp, transaction_hash}'
The output will be:
{ "consensus_timestamp": "1714079829.044599703", "transaction_hash": "ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+" } -
Then, you want the record file containing that tx
curl -s "https://mainnet-public.mirrornode.hedera.com/api/v1/blocks?limit=1&order=asc×tamp=gte:1714079829.044599703" | jq -r ".blocks[].name"
The output will be:
2024-04-25T21_17_08.000248496Z.rcd.gz -
Then you download the record file. Note: if 0.0.3 does not have that file, try 0.0.4, 0.0.5, and so on.
mkdir records aws s3 cp --request-payer requester s3://hedera-mainnet-streams/recordstreams/record0.0.3/2024-04-25T21_17_08.000248496Z.rcd.gz ./records
-
Ungzip the record file
gzip -d ./records/2024-04-25T21_17_08.000248496Z.rcd.gz
-
Decode the record file and show tx's detail (--record parameter accepts also a path)
node proto-decode-transaction.js --record=./records/2024-04-25T21_17_08.000248496Z.rcd --txhash=ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+
The output will be:
=== Transaction #0 ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+ === { transaction: { signedTransactionBytes: 'ClgKGQoLCMWQq7EGEJrcmysSCAgAEAAYs6wfGAASBggAEAAYAxiAhK9fIgIIeDIAggMnEiUKCQgAEAAY0ciIAhIICAAQABizrB8aCQgAEAAYxaHpASID9vUBEswBCmQKIHcoN6wujR2qXjZWVOxzYBEP6a9i8khL0gIgT2JuXIKyGkDu761b22vDr/chkr5qaR/QqXPIdV2Y08/9S11nlChfAlMXZj0jIWgvFyN25CXzGB4rw+KhwOU8jPpnF3d+pBYACmQKIJUSLeJ3nu+RKNA7rsFMFMNOlg2U59HkwXbTZQS+/kh7GkDgkkcbbok6JZVz6dGgmaXQPTEz66X6EcvKx3BQID+b/BbwSaA6G9114w+olR3jqrIv2gMIAFkH6a+eexzVbCMK' }, record: { receipt: { status: 'SUCCESS', exchangeRate: { currentRate: { hbarEquiv: 30000, centEquiv: 353419, expirationTime: { seconds: '1714082400' } }, nextRate: { hbarEquiv: 30000, centEquiv: 357345, expirationTime: { seconds: '1714086000' } } } }, transactionHash: 'ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+', consensusTimestamp: { seconds: '1714079829', nanos: 44599703 }, transactionID: { transactionValidStart: { seconds: '1714079813', nanos: 90631706 }, accountID: { accountNum: '513587' } }, transactionFee: '68984744', transferList: { accountAmounts: [ { accountID: { accountNum: '3' }, amount: '1765339' }, { accountID: { accountNum: '98' }, amount: '60497465' }, { accountID: { accountNum: '800' }, amount: '6721940' }, { accountID: { accountNum: '513587' }, amount: '-68984744' } ] } } } === Signatures #0 ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+ === { pubKeyPrefix: 'dyg3rC6NHapeNlZU7HNgEQ/pr2LySEvSAiBPYm5cgrI=', ed25519: '7u+tW9trw6/3IZK+amkf0KlzyHVdmNPP/UtdZ5QoXwJTF2Y9IyFoLxcjduQl8xgeK8PiocDlPIz6Zxd3fqQWAA==', pubKeyPrefixHex: '772837ac2e8d1daa5e365654ec7360110fe9af62f2484bd202204f626e5c82b2' } { pubKeyPrefix: 'lRIt4nee75Eo0DuuwUwUw06WDZTn0eTBdtNlBL7+SHs=', ed25519: '4JJHG26JOiWVc+nRoJml0D0xM+ul+hHLysdwUCA/m/wW8EmgOhvddeMPqJUd46qyL9oDCABZB+mvnnsc1WwjCg==', pubKeyPrefixHex: '95122de2779eef9128d03baec14c14c34e960d94e7d1e4c176d36504befe487b' } === Body #0 ATGcMV0XrYmiI1ZpGmZ5l5IW4wC5XDB1jO5m9qlAmmXyW3ulLW7w5ZPZtsY5XwF+ === { transactionID: { transactionValidStart: { seconds: '1714079813', nanos: 90631706 }, accountID: { shardNum: '0', realmNum: '0', accountNum: '513587' }, scheduled: false }, nodeAccountID: { shardNum: '0', realmNum: '0', accountNum: '3' }, transactionFee: '200000000', transactionValidDuration: { seconds: '120' }, memo: '', cryptoApproveAllowance: { nftAllowances: [ { tokenId: { shardNum: '0', realmNum: '0', tokenNum: '4334673' }, owner: { shardNum: '0', realmNum: '0', accountNum: '513587' }, spender: { shardNum: '0', realmNum: '0', accountNum: '3821765' }, serialNumbers: [ '31478' ] } ] } }
Convert tx hash from base64 (MN API output) to hex (HashScan and MN API input)
❯ echo "pPcK5zG1wfQqqd29LJOtp8HKof/OLSzdMA+rhBVLKmu4Tl0CfwDFLreHkXnAequi" | base64 -d | perl -pe 'BEGIN{$/=\1e6} $_=unpack "H*"'
a4f70ae731b5c1f42aa9ddbd2c93ada7c1caa1ffce2d2cdd300fab84154b2a6bb84e5d027f00c52eb7879179c07aaba2Query a consensus node about account's balance
❯ grpcurl -import-path ./hedera-protobufs-static/services -proto=crypto_service.proto --plaintext -d '{"cryptogetAccountBalance":{"header":{"responseType":0},"accountID":{"accountNum":2}}}' 34.125.200.96:50211 proto.CryptoService/cryptoGetBalance
{
"cryptogetAccountBalance": {
"header": {},
"accountID": {
"accountNum": "2"
},
"balance": "1662808162851446"
}
}