0xFast Streams: Parsed Logs

TL;DR: You can now pass log.parse=true as a URL parameter to get human-readable parsed event logs from 0xFast Stream.

Ethereum logs are hard to comprehend. In a previous article, I calculated the most popular smart contracts by the number of logs corresponding to them. Let’s take a look at a log from the most popular contract, wrapped ETH, located at 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.

$ curl "https://eth-uswest.0xfast.com/stream/free?range=15791492&log.addr=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" -s | jq '.[0]'

{
  "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
  "topics": [
    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "0x000000000000000000000000610cdc8fb633142ba067efba6fb09fd88b08b2c9",
    "0x0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d"
  ],
  "data": "0x000000000000000000000000000000000000000000000000050c1008aa062506",
  "blockNumber": "0xf0f584",
  "transactionIndex": "0x2",
  "logIndex": "0x3",
  "@type": "Log"
}

Each log event is a function call, storing a hash of the function signature, and the arguments passed to the function. But, looking at the log, all this data looks cryptic.” It doesn’t tell you a lot about what this function is and doesn’t easily tell you which were the arguments and corresponding values passed to that function.

To parse this log, you need an Application Binary Interface specification, aka ABI. As calculated in my previous post, there are over 6 million smart contracts deployed on the Ethereum mainnet. This means, there are 6 million ABIs that are needed to parse all these log events. There’s no single central repository that has access to all these ABIs (though, Etherscan comes close).

Log parsing is cumbersome and required to build analytics or indexing on top of blockchain smart contract data. For example, Log parsing is required to build NFT APIs. It would be hard to calculate how many engineering hours have been spent collectively by the web3 ecosystem to parse logs and make sense of them. So, I thought it was worth providing parsed logs as a feature of 0xFast Streams.

Parsed Logs - For Humans and their Applications

As per my calculations, to cover 95% of all ETH logs, you only need 42K smart contract ABIs. We downloaded 10x of that number. With 450K smart contract ABIs (and growing), we’re able to cover 97% of all logs and provide human-readable events, signatures, and arguments.

To get all this, all you need to do is to add a log.parse=true argument to the curl command above.

$ curl "https://eth-uswest.0xfast.com/stream/free?range=15791492&log.addr=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2&log.parse=true" -s | jq '.[0]'

{
  "@type": "ParsedLog",
  "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
  "arg:dst": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
  "arg:src": "0x610cdc8fb633142ba067efba6fb09fd88b08b2c9",
  "arg:wad": "0x50c1008aa062506",
  "blockNumber": "0xf0f584",
  "event": "event Transfer(address indexed src, address indexed dst, uint256 wad)",
  "logIndex": "0x3",
  "sig": "Transfer(address,address,uint256)",
  "txnIndex": "0x2"
}

This output is immediately more readable than the one before. The function signature and full argument version are stored in sig and event. The arguments to the function are all prefixed with arg:, followed by their name. So, address indexed src becomes JSON key arg:src. And the value is the string-encoded value corresponding to the argument type.

Now, log parsing isn’t foolproof. We might not have the ABI, or might not have the right ABI, etc. So, not all logs would be correctly parsed. In those cases, the original log is returned. To differentiate between a parsed log, and the original log, you can look at the @type, storing ParsedLog” and Log” as values respectively.

In fact, log.parse=true works with all the arguments that I showcased in my last post about indexed streams. For example:

$ curl "https://eth-uswest.0xfast.com/stream/free/latest?log.parse=true" -s | jq
$ curl "https://eth-uswest.0xfast.com/stream/free?from.addr=0x6722e607bc41da172888890699214a78ca0a295b&range=15791000-latest&log.parse=true" -s | jq

... and so on

To get a list of ENS (Ethereum Name Service) registered names and addresses, you can run this command:

$ curl "https://eth-uswest.0xfast.com/stream/free?log.addr=0x283af0b28c62c092c9727f1ee09c02ca627eb7f5&range=15780000-15789999&log.parse=true" -s

...
  {
    "@type": "ParsedLog",
    "address": "0x283af0b28c62c092c9727f1ee09c02ca627eb7f5",
    "arg:cost": "0x368db9e8388a49",
    "arg:expires": "0x6ad59d87",
    "arg:label": "0xf23441b4320d3dd2540b71ce3aa0ff3255261501ce2b019bae5ccb1ff94652f7",
    "arg:name": "tennisworldusa",
    "arg:owner": "0x015a7994a4f2b8b30da46a8e9d3b43aedb55656e",
    "blockNumber": "0xf0c8a6",
    "event": "event NameRegistered(string name, bytes32 indexed label, address indexed owner, uint256 cost, uint256 expires)",
    "logIndex": "0xae",
    "sig": "NameRegistered(string,bytes32,address,uint256,uint256)",
    "txnIndex": "0x60"
  },
  {
    "@type": "ParsedLog",
    "address": "0x283af0b28c62c092c9727f1ee09c02ca627eb7f5",
    "arg:cost": "0x15d24a45af86d",
    "arg:expires": "0x637faf46",
    "arg:label": "0xd73c76603ae0b29072cb1988e7d8539522367b2886d62bb9b95b2eb73c6c68b2",
    "arg:name": "captaintsubasarivals",
    "arg:owner": "0xf72109af2d258df6478884f5b0395528b2a43b63",
    "blockNumber": "0xf0c8aa",
    "event": "event NameRegistered(string name, bytes32 indexed label, address indexed owner, uint256 cost, uint256 expires)",
    "logIndex": "0x67",
    "sig": "NameRegistered(string,bytes32,address,uint256,uint256)",
    "txnIndex": "0x6a"
  },
...

These parsed logs would form the basis for what’s next for 0xFast: 100x Faster NFT APIs. Try out parsed logs feature, and let me know how we can improve.


Meanwhile, here’re some interesting logs I found while testing my parsing code.

{
 "@type": "ParsedLog",
 "address": "0x59728544b08ab483533076417fbbb2fd0b17ce3a",
 "arg:amount": "0x1",
 "arg:collection": "0x3df5c619a4926156f966a64e08f863385c21da0e",
 "arg:currency": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
 "arg:maker": "0x1c1ce70ca6975cf60658fa55ae9358c8b295d062",
 "arg:orderHash": "0x686a4ad8fb8f8790bd450fef494777018579689798fde7849764de265bbbea52        ",
 "arg:orderNonce": "0x9a",
 "arg:price": "0xe2d18d67e0fc000",
 "arg:strategy": "0x56244bb70cbd3ea9dc8007399f61dfc065190031",
 "arg:taker": "0x6722e607bc41da172888890699214a78ca0a295b",
 "arg:tokenId": "0x43",
 "blockNumber": "0xf0f398",
 "event": "event TakerBid(bytes32 orderHash, uint256 orderNonce, address indexed take        r, address indexed maker, address indexed strategy, address currency, address collection, uint25        6 tokenId, uint256 amount, uint256 price)",
 "logIndex": "0x25",
 "sig": "TakerBid(bytes32,uint256,address,address,address,address,address,uint256,uin        t256,uint256)",
 "txnIndex": "0xc"
}

...

{
  "@type": "ParsedLog",
  "address": "0x00000000006c3852cbef3e08e8df289169ede581",
  "arg:consideration": [
    {
      "amount": "0x7f93f469e26000",
      "identifier": "0x0",
      "itemType": "0x00",
      "recipient": "0x8d921f72db4e3dda7f1b231a42b7e83da7938f58",
      "token": "0x0000000000000000000000000000000000000000"
    },
    {
      "amount": "0x38b389129d800",
      "identifier": "0x0",
      "itemType": "0x00",
      "recipient": "0x0000a26b00c1f0df003000390027140000faa719",
      "token": "0x0000000000000000000000000000000000000000"
    },
    {
      "amount": "0xaa1a9b37d8800",
      "identifier": "0x0",
      "itemType": "0x00",
      "recipient": "0x78dd63e2e0a323b673deecdcf626840c49daf090",
      "token": "0x0000000000000000000000000000000000000000"
    }
  ],
  "arg:offer": [
    {
      "amount": "0x1",
      "identifier": "0xb7d",
      "itemType": "0x02",
      "token": "0xc5c17c3d565169f6a968e33e7914bcd0e4894660"
    }
  ],
  "arg:offerer": "0x8d921f72db4e3dda7f1b231a42b7e83da7938f58",
  "arg:orderHash": "0x603a6481fcb4bc80d9e3d1e63e2e6d3270c946afcfe1d70ced404b1031836499",
  "arg:recipient": "0x891b748e08454d5bfa839f1fbe04bf821131e0d3",
  "arg:zone": "0x004c00500000ad104d7dbd00e3ae0a5c00560c00",
  "blockNumber": "0xf0f398",
  "event": "event OrderFulfilled(bytes32 orderHash, address indexed offerer, address indexed zone, address recipient, (uint8,address,uint256,uint256)[] offer, (uint8,address,uint256,uint256,address)[] consideration)",
  "logIndex": "0x36",
  "sig": "OrderFulfilled(bytes32,address,address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256,address)[])",
  "txnIndex": "0x11"
}


Date
October 20, 2022