<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by banteg on Medium]]></title>
        <description><![CDATA[Stories by banteg on Medium]]></description>
        <link>https://medium.com/@banteg?source=rss-2a64f901e4fe------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*5tHVVw843M1JTUjFwIfotw.png</url>
            <title>Stories by banteg on Medium</title>
            <link>https://medium.com/@banteg?source=rss-2a64f901e4fe------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 10:55:50 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@banteg/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Building during bera]]></title>
            <link>https://medium.com/@banteg/building-during-bera-2209f44746fa?source=rss-2a64f901e4fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/2209f44746fa</guid>
            <dc:creator><![CDATA[banteg]]></dc:creator>
            <pubDate>Wed, 01 Jun 2022 14:31:58 GMT</pubDate>
            <atom:updated>2022-06-01T14:34:14.940Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yesQXNJ_3OFc-aMIxoXBag.png" /></figure><p>Haven’t you heard, anon? DeFi is dead. Long live DeFi. Long live Yearn.</p><p>We’ve been preparing for a bear market, which is why we have a buffer of ~$20 million. But it’s not going to last forever, and that’s why we’re taking steps to make our warchest last as long as possible.</p><p>As contributors, we have identified inefficiencies that accumulated during the bull, and are now rethinking how we work and on what. We can do more with less.</p><p>Below we outline the action plan we’re executing against during the current market downturn in order to become more resilient.</p><p>Specifically, we focus our work to:</p><h4><strong>Overhaul V2</strong></h4><p>Simpler and more automated strategies, reduce maintenance and overhead costs. Focus on Ethereum and its Layer 2s, and simplify the product offering. This will allow us to revise our fee structures and become more competitive.</p><h4><strong>Prepare V3</strong></h4><p>Continue iterating, testing, and experimenting with the new V3 design, taking it to production, and ultimately have it replace V2.</p><h4><strong>Grow B2B</strong></h4><p>Tools, documentation, tracking. Focus on automation and helping integrators onboard themselves. Offering a concierge-like experience for the largest depositors to retain their TVL.</p><h4><strong>Release tokenomics</strong></h4><p>Finalize and launch veYFI and redistribute bought back YFI as added APYs to vaults to reclaim some of our lost TVL. Monitor performance and propose evolutions to YFI’s emission model as required.</p><h4><strong>Stay on top of security.</strong></h4><p>Continue best-in-class security to provide the most attractive risk-adjusted yields in the market. Monitoring, peer reviews, audits, risk framework, testing, and simulations.</p><h4><strong>Research new growth verticals</strong></h4><p>Small, contained research efforts to explore new sources of sustainable revenue and business models for yearn.</p><p>We’re doing this with less resources, taking a back-to-basics approach with a heavy focus on our core product and services, strategies, vault operations, engineering, and security. The plan is being actioned as part of our day to day operations. If you have thoughts, ideas, or questions get in touch with us <a href="https://discord.gg/yearn">on Discord</a>.</p><p>These are tough market times, but we’re seeing a lot of untapped opportunities and are confident we will come out stronger from this.</p><p>As always, we appreciate the support and belief in the team of yearn contributors.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2209f44746fa" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deep dive into MakerDAO Governance]]></title>
            <link>https://medium.com/@banteg/deep-dive-into-makerdao-governance-437c89493203?source=rss-2a64f901e4fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/437c89493203</guid>
            <category><![CDATA[makerdao]]></category>
            <category><![CDATA[governance]]></category>
            <category><![CDATA[ethereum]]></category>
            <category><![CDATA[reverse-engineering]]></category>
            <dc:creator><![CDATA[banteg]]></dc:creator>
            <pubDate>Fri, 21 Dec 2018 09:25:23 GMT</pubDate>
            <atom:updated>2018-12-21T13:24:39.512Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QuArnpzxRKzweUBkib8vKg.png" /></figure><p>Today we’ll look into MakerDAO voting process and recover the complete state of the governance contract. There are two types of voting: governance and executive. Governance voting can be seen a simple yes/no poll. Executive voting is much more interesting and it will be the subject of this article.</p><h3>How voting works</h3><p>Executive voting is handled like <a href="https://en.wikipedia.org/wiki/Approval_voting">approval voting</a>:</p><ul><li>Anyone can create a proposal;</li><li>Users can vote for one or more proposals;</li><li>A proposal with the most votes gets the superpowers and can modify the system parameters.</li></ul><h3>Proposal contract</h3><p>A proposal comes in a form of smart contract. The winning proposal is given the authorization to tap into <a href="https://github.com/makerdao/sai/blob/master/src/mom.sol">mom</a> contract. A common pattern for these contracts is to use <a href="https://github.com/dapphub/ds-spell">ds-spell</a>, which performs a single action once.</p><p><a href="https://etherscan.io/address/0x9D67476aC572570d81027ea8B6efbe8f6fD25a42#readContract">The proposal</a> to reduce the stability fee from 2.5% to 0.5% can be seen below. It shows which contract it calls, the call data and whether is has already been executed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*N6eYPGG9dQwoc6mM9t4eLQ.png" /><figcaption>Reads “Decrease the stability fee to 0.5%”.</figcaption></figure><p>We can verify the spell using a simple script that decodes the data using interface (ABI) of the target contract:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/eccb0e7b0c60713e4ba7ed3decb235cd/href">https://medium.com/media/eccb0e7b0c60713e4ba7ed3decb235cd/href</a></iframe><p>The result would be: setFee(uint256 ray 1000000000158153903837946257)</p><p>How is this 0.5%, you may ask. To figure this out, we need to understand the units which this number is expressed in. Maker uses <a href="https://github.com/makerdao/dss/blob/master/DEVELOPING.md#units">a few different units</a> with various degrees of precision: wad, ray and rad.</p><p>The argument title suggests the value is in ray, a fixed point number with 27 decimals. So it expresses 1.000000000158153903837946257.</p><p>Turns out that the unit is the fee multiplier per second, so if we exponentiate it to the number of seconds in a year, we should get 1.005 or 100.5%.</p><p>1.000000000158153903837946257³¹⁵³⁶⁰⁰⁰ = 1.005</p><p>The proposal checks out! Go <a href="http://vote.makerdao.com/">vote for it</a> and come back for more fun.</p><h3>Voting contract</h3><p>Now let’s take a look at the voting contract, <a href="https://github.com/dapphub/ds-chief">ds-chief</a>. To vote for a proposal, one must lock MKR in this contract in return for IOU tokens. Only the locked tokens count as votes.</p><p>Users vote for sets of candidates, which are stored in a slates mapping. There are two ways to vote:</p><ul><li>vote(bytes32 slate) vote for an existing set of candidates.</li><li>vote(address[] yays) vote by providing a set of candidates, which also saves them into a slate using etch(address[] yays).</li></ul><p>The contract tallies the weights and awards the leading proposal with a hat. Having the hat means having superpowers. Now anyone can cast the spell and modify the system parameters.</p><h3>The voters</h3><p>With that knowledge, we can find all the proposals and voters. The plan:</p><ol><li>Find all the slates by looking at Etch(bytes32 indexed slate) events.</li><li>Look up the proposals in the slates mapping.</li><li>Fetch all votes and remember the most recent ones.</li><li>Combine them with deposits of each voter.</li></ol><p>First, we save unique slates from the Etch events. Then we read the values from each slate array starting with index 0 and continue until we are out of bounds.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a9f2da5e94957e6473077fe54dafc355/href">https://medium.com/media/a9f2da5e94957e6473077fe54dafc355/href</a></iframe><p>The vote calls use non-standard events fired by <a href="https://github.com/dapphub/ds-note">ds-note</a>. It provides a note modifier, which stores the function calls as <a href="https://medium.com/p/a8d2047c7371">indexed logs</a>. There is no need to define events for each function, ds-note handles that on a lower level.</p><p>To isolate the two functions (vote slates and vote yays), we need to construct a correct log filter. If you specify a topic in a filter as a list, it works like logical OR, which is exactly what we need.</p><p>The first topic is a 4 byte function signature encoded as bytes32. The log data will have what we need to recover the votes.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/81b4b8cb5eca1075e982539cd92a7a57/href">https://medium.com/media/81b4b8cb5eca1075e982539cd92a7a57/href</a></iframe><p>The voting system is using a proxy contract, so we can’t just look up the sender in the from field of the logs. Luckily for us, ds-note stores msg.sender as the second topic.</p><p>The “slates to yays” we got earlier comes in handy for looking up candidates each user has voted for. The last step here is providing everyone with a voting weight based on their deposit.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1045d5250215c10b6dd1e95ed74bc495/href">https://medium.com/media/1045d5250215c10b6dd1e95ed74bc495/href</a></iframe><p>Now we can combine everything to see <a href="https://gist.github.com/banteg/144b4ab48a9d6d055b7a77a7ea165b3c">the complete picture</a>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8c65d7864e36cababb7637de8f052e21/href">https://medium.com/media/8c65d7864e36cababb7637de8f052e21/href</a></iframe><p>The full script that also tries to decode the spells can be <a href="https://gist.github.com/banteg/abfdc98cd66aad0d86e00a95b74dc5ce">found here</a>.</p><p>Today was indeed a productive day, hope you learned something useful. Subscribe to <a href="https://t.me/crypto_eli5">crypto eli5</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=437c89493203" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ethereum clients initial sync benchmark]]></title>
            <link>https://medium.com/@banteg/ethereum-clients-initial-sync-benchmark-81095a9f4ca3?source=rss-2a64f901e4fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/81095a9f4ca3</guid>
            <category><![CDATA[parity]]></category>
            <category><![CDATA[geth]]></category>
            <category><![CDATA[ethereum]]></category>
            <dc:creator><![CDATA[banteg]]></dc:creator>
            <pubDate>Wed, 07 Nov 2018 20:56:51 GMT</pubDate>
            <atom:updated>2019-01-08T10:27:40.272Z</atom:updated>
            <content:encoded><![CDATA[<p>Syncing Ethereum blockchain head-on by fetching every block and replaying every transaction is impractical. That’s why different full nodes utilize clever techniques to speed up the initial sync and get up to speed in a reasonable time.</p><p>According to Ethernodes, Etherum mainnet is dominated by Geth (55.7%), Parity Ethereum (31.5%), so we’ll discuss these two. All tests are done in October 2018 on a $30/mo dedicated server with the following config:</p><p>2.4 GHz CPU with 8 cores<br>16 GB of RAM<br>250 GB SSD<br>1 Gbit network</p><h3>Geth</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P9sLW9S0pnQMRTTBV-tOFQ.png" /></figure><p><strong>Geth</strong> (also known as <strong>go-ethereum</strong>) is written in golang and uses Google’s LevelDB as a storage backend. The default sync mode is called <strong>fast</strong>, it downloads all the needed data in parallel:</p><ul><li>Block headers (6,475,108 headers took 13 hours 25 minutes)</li><li>Transaction receipts (same amount, finished roughly at the same time)</li><li>State tries (syncing 227.8 million states took 1 day 10 hours)</li></ul><p>A few hours into the sync, a message saying “Database compacting, degraded performance” starts to appear. The first one is at 4 hours 13 minutes mark and it gets worse after that. Geth ends up spending 4 hours 19 minutes (¹⁄₈ of the total time) completely halted by LevelDB compaction. After headers and receipts have finished downloaded, state sync finished without interruptions.</p><p>A few minutes after the last state entry was downloaded, the node appeared fully synced.</p><p>The final time is <strong>1 day 10 hours</strong>. The database size is <strong>130.9 GB</strong>.</p><p>Unfortunately, Geth’s eth_syncing command doesn’t provide enough meaningful info to calculate the remaining time. It would be great to know how many states are there to sync, but traversing the whole state trie is probably a very expensive operation.</p><h3>Parity Ethereum</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gUF6_A2uh2kgaYYWtP1TOA.png" /></figure><p><strong>Parity Ethereum</strong> (formerly <strong>Parity</strong>) is written in Rust and uses Facebook’s RocksDB as a storage backend. It’s backwards compatible with LevelDB, and has a bunch of <a href="https://github.com/facebook/rocksdb/wiki/Features-Not-in-LevelDB">additional features</a>.</p><p>Parity syncs in <strong>warp</strong> mode by default, which works like this:</p><ul><li>Pull a recent snapshot (2209 chunks took 2 hours 20 minutes)</li><li>Sync the few remaining blocks (4651 blocks took 1 hour 33 minutes)</li><li>Process older blocks in the background (6,500,000 blocks in 1 day 2 hours 39 minuts)</li></ul><p>This makes the node usable after <strong>about 4 hours</strong>. You can interact with the blockchain and query the recent state and blocks that came after the snapshot with the older blocks unavailable until they are processed.</p><p>The final time is <strong>1 day 6 hours 32 minutes</strong>. The database size is <strong>125.6 GB</strong>.</p><p>Parity’s eth_syncing reports the total number of warp chunks as well as the total number of blocks, which allows to estimate the remaining time. It doesn’t report the status of the third step, but it can be monitored with a simple script like this:</p><pre>from web3.auto import w3</pre><pre>for n in range(0, w3.eth.blockNumber, 10000):<br>    while not w3.eth.getBlock(n):<br>        sleep(60)<br>    print(n)</pre><p>If you want to get a deeper understanding of these challenges, I recommend reading the <a href="https://www.ideals.illinois.edu/bitstream/handle/2142/101567/QIAN-THESIS-2018.pdf">TurboSync paper</a>.</p><p>Hope you learned something useful. Subscribe to <a href="https://t.me/crypto_eli5">crypto eli5</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=81095a9f4ca3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Announcing Disperse]]></title>
            <link>https://medium.com/@banteg/announcing-disperse-714a8ca9b450?source=rss-2a64f901e4fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/714a8ca9b450</guid>
            <category><![CDATA[dapps]]></category>
            <category><![CDATA[web3]]></category>
            <category><![CDATA[etheruem]]></category>
            <dc:creator><![CDATA[banteg]]></dc:creator>
            <pubDate>Wed, 24 Oct 2018 04:20:12 GMT</pubDate>
            <atom:updated>2018-12-23T04:25:10.664Z</atom:updated>
            <content:encoded><![CDATA[<p><em>verb</em> distribute ether or tokens to multiple addresses</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*45QGRVChv5UHsOTwL3iZWA.png" /><figcaption><a href="https://disperse.app/">https://disperse.app/</a></figcaption></figure><h3>Open Protocols</h3><p>The real beauty of Web 3.0 ecosystem we are building are open protocols. Anyone can tap into dharma loan protocol or 0x protocol exchange contract and get into shared liquidity pool for free. You can mix and match these too, creating completely unique products and markets.</p><p>A protocol for fungible tokens (ERC-20) is probably the most successful thus far. It secures about $13 billion across a thousand assets. This protocol is <a href="https://github.com/OpenZeppelin/openzeppelin-solidity/blob/v2.0.0/contracts/token/ERC20/IERC20.sol">quite simple</a> actually, it defines a transfer function which updates balances in token contract and emits a Transfer event.</p><p>But what allows to tap into it is the concept of allowance. A user can approve another account to transfer some amount on her behalf. For example, an exchange calls transferFrom user wallet when the order is filled. It’s programmed not to transfer more than the signed amount even though the allowance is set to -1 (meaning infinity).</p><h3>Optimization</h3><p>Like a good Unix tool, Disperse contract does a simple job and does it well, that’s why we shaved off every single bit to optimize for gas costs. A naive implementation of Disperse function might look like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XIjF2R5Oc4CYQa9jMhuLvg.png" /><figcaption>disperseTokenSimple(token, recipients, values)</figcaption></figure><p>However, it’s not an optimal solution because the aforementioned transferFrom updates allowance on each call in addition to balances. That’s an additional 5000 gas on each transfer.</p><p>A better way would be to calculate the total, transferFrom it to the contract address and then distribute the tokens using transfer function. There is no risk of funds being stuck because transactions work in “<a href="https://en.wikipedia.org/wiki/Atomicity_(database_systems)">all or nothing</a>” manner and the state will revert if anything goes wrong.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pDc09YgehfO0eBeghrIcbw.png" /><figcaption>disperseToken(token, recipients, values)</figcaption></figure><p>The cost also depends heavily on whether each recipient has previously had any balance. Zero balance is the worst case because storing a new state entry costs 20,000 gas as opposed to 5000 gas for changing the value.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*L0_VN34t9_C5ltrm2KHGZg.png" /><figcaption>Gas costs as described in Ethereum yellow paper</figcaption></figure><h3>Benchmark</h3><p>Here are the benchmark results for this contract (outlined in red) compared to sending tokens to one recipient at a time (grey). One block can fit up to 218 token transfers, while Disperse transaction can fit 2.9x as much, <a href="https://ropsten.etherscan.io/tx/0x88b1d298d42fe64890d955150012105dec0ff1a493da6b75cf746be59a8dea65">up to 634 transfers</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*km8zHL-ORSD9X3GIvqdP-w.png" /><figcaption>disperseEther(recipients, values)</figcaption></figure><p>Same goes for ether, one block can accommodate 380 regular transfers while Disperse can fit 2.18x more, <a href="https://ropsten.etherscan.io/tx/0xc5676896c7ad910400990e1efb48ac442590a907faf370437d51e4e1efef48af">up to 830 transfers</a>. However, distributing ether to uninitialized accounts costs more because contracts pay 25,000 gas for calls that result in creation of new accounts as opposed to a flat 21,000 gas cost of a regular transfer.</p><p>If you are using Metamask, you are probably connected to Infura node. It limits the maximum transaction size to 32 KB. This anti-spam measure caps the amount of recipients you can specify. You can broadcast a larger transaction by connecting to to a local node like Geth or Parity Ethereum. Infura’s limit is shown with a dotted line on the charts.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qvYgcu1Pi1IYqDPRLuv2SQ.png" /></figure><h3>Disperse</h3><p>We believe this space needs more builders and less fraud. Therefore Disperse doesn’t incorporate any useless token, has no sale and takes no fees.</p><p>It’s a useful dapp that’s <strong>available day one on nine networks</strong>:</p><ul><li>Ethereum and its testnets Ropsten, Kovan, Rinkeby;</li><li>POA Network and its testnet Sokol;</li><li>xDAI Chain;</li><li>Ethereum Classic;</li><li>Callisto.</li></ul><p>Go try it out on <a href="https://disperse.app/">disperse.app</a>.</p><p>Read the <a href="https://disperse.app/disperse.pdf">research paper</a>.</p><p>Let us know what you think <a href="https://t.me/disperse">on Telegram</a>.</p><h4>Correction:</h4><ul><li>Benchmark results have been revised accounting for a transaction size limit of 32 KB on Infura which is the default RPC provider on Metamask. Example transactions have been added.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=714a8ca9b450" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deep dive into Ethereum logs]]></title>
            <link>https://codeburst.io/deep-dive-into-ethereum-logs-a8d2047c7371?source=rss-2a64f901e4fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/a8d2047c7371</guid>
            <category><![CDATA[ethereum]]></category>
            <category><![CDATA[technical]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[logs]]></category>
            <category><![CDATA[blockchain]]></category>
            <dc:creator><![CDATA[banteg]]></dc:creator>
            <pubDate>Thu, 04 Jan 2018 13:28:31 GMT</pubDate>
            <atom:updated>2018-11-10T12:29:14.183Z</atom:updated>
            <content:encoded><![CDATA[<p>Hey kids, today we are going low level trying to understand how Ethereum events and logs work. Put <em>web3</em> away for a while as it abstracts things and we’d like to get as bare as it gets.</p><p>Smart contracts generate <em>logs</em> by firing <em>events</em>. Here’s a transaction receipt that contains one log entry.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FGhc_lzuLZ29dsX9VMmRKg.png" /></figure><p>The log entry consists of one <em>topic</em> and a <em>data</em> field. The first topic refers to the specific event, but we don’t know which one yet. To decode the data we need to <a href="https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code">obtain</a> ABI of the contract found in the <em>address</em> field.</p><p>We are interested in items with the <em>type event</em>. Here’s a sample event.</p><pre>{<br>  &quot;anonymous&quot;: false,<br>  &quot;inputs&quot;: [<br>    {<br>      &quot;indexed&quot;: false,<br>      &quot;name&quot;: &quot;from&quot;,<br>      &quot;type&quot;: &quot;address&quot;<br>    },<br>    {<br>      &quot;indexed&quot;: false,<br>      &quot;name&quot;: &quot;to&quot;,<br>      &quot;type&quot;: &quot;address&quot;<br>    },<br>    {<br>      &quot;indexed&quot;: false,<br>      &quot;name&quot;: &quot;tokenId&quot;,<br>      &quot;type&quot;: &quot;uint256&quot;<br>    }<br>  ],<br>  &quot;name&quot;: &quot;Transfer&quot;,<br>  &quot;type&quot;: &quot;event&quot;<br>}</pre><p>To find out which event the <em>topic</em> refers to we need to compute a signature of each event and find the matching one. Signature is a keccak hash of event <em>name</em> and input argument <em>types, </em>argument names are ignored. For event Hello(uint256 worldId) the signature will be keccak(&#39;Hello(uint256)&#39;).</p><pre>&gt; keccak(&#39;Pregnant(address,uint256,uint256,uint256)&#39;)<br>241ea03ca20251805084d27d4440371c34a0b85ff108f6bb5611248f73818b80<br><br><strong>&gt; </strong><strong>keccak(&#39;Transfer(address,address,uint256)&#39;)</strong><br><strong>ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef</strong><br><br>&gt; keccak(&#39;Approval(address,address,uint256)&#39;)<br>8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925<br><br>&gt; keccak(&#39;Birth(address,uint256,uint256,uint256,uint256)&#39;)<br>0a5311bd2a6608f08a180df2ee7c5946819a649b204b554bb8e39825b2c50ad5<br><br>&gt; keccak(&#39;ContractUpgrade(address)&#39;)<br>450db8da6efbe9c22f2347f7c2021231df1fc58d3ae9a2fa75d39fa446199305</pre><p>As we can see, the signature of Transfer event matches the first topic. Now we can decode the <em>data</em>.</p><pre>&gt; types = [i[&#39;type&#39;] for i in e[&#39;inputs&#39;]]<br>[&#39;address&#39;, &#39;address&#39;, &#39;uint256&#39;]</pre><pre>&gt; names = [i[&#39;name&#39;] for i in e[&#39;inputs&#39;]]<br>[&#39;from&#39;, &#39;to&#39;, &#39;tokenId&#39;]</pre><pre>&gt; values = eth_abi.decode_abi(types, log[&#39;data&#39;])<br>(&#39;0x0035fc5208ef989c28d47e552e92b0c507d2b318&#39;,<br> &#39;0x646985c36ad7bf4f3a91283f3ea6eda2af79fac6&#39;,<br> 107696)</pre><pre>&gt; dict(zip(names, values))<br>{&#39;from&#39;: &#39;0x0035fc5208ef989c28d47e552e92b0c507d2b318&#39;,<br> &#39;to&#39;: &#39;0x646985c36ad7bf4f3a91283f3ea6eda2af79fac6&#39;,<br> &#39;tokenId&#39;: 107696}</pre><p>We can finally read the log. The message says:</p><blockquote>“Transfer from 0x0035… to 0x6469… of token 107696”.</blockquote><h4>Indexed fields</h4><p>Each <em>indexed</em> field generates a new topic which is excluded from the data. This allows for efficient search but makes parsing a bit more complicated.</p><p>Let’s look at another receipt:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rr1YAWbvUOM5HyiPwmnTjQ.png" /></figure><p>This one has three <em>topic</em> fields and a small <em>data</em> field. We can already tell that the first topic is Transfer(address,address,uint256) despite the different argument names in this contract.</p><pre>{<br>  &quot;anonymous&quot;: false,<br>  &quot;inputs&quot;: [<br>    {<br><strong>      &quot;indexed&quot;: true,</strong><br>      &quot;name&quot;: &quot;from&quot;,<br>      &quot;type&quot;: &quot;address&quot;<br>    },<br>    {<br><strong>      &quot;indexed&quot;: true,</strong><br>      &quot;name&quot;: &quot;to&quot;,<br>      &quot;type&quot;: &quot;address&quot;<br>    },<br>    {<br>      &quot;indexed&quot;: false,<br>      &quot;name&quot;: &quot;value&quot;,<br>      &quot;type&quot;: &quot;uint256&quot;<br>    }<br>  ],<br>  &quot;name&quot;: &quot;Transfer&quot;,<br>  &quot;type&quot;: &quot;event&quot;<br>}</pre><p>The remaining two topics are simply indexed inputs. The only value in data is the remaining uint256.</p><pre>&gt; types = [i[&#39;type&#39;] for i in e[&#39;inputs&#39;] if not i[&#39;indexed&#39;]]<br>[&#39;uint256&#39;]</pre><pre>&gt; names = [i[&#39;name&#39;] for i in e[&#39;inputs&#39;] if not i[&#39;indexed&#39;]]<br>[&#39;value&#39;]</pre><pre>&gt; values = eth_abi.decode_abi(types, log[&#39;data&#39;])<br>(5000000000,)</pre><pre>&gt; indexed_types = [i[&#39;type&#39;] for i in e[&#39;inputs&#39;] if i[&#39;indexed&#39;]]<br>[&#39;address&#39;, &#39;address&#39;]</pre><pre>&gt; indexed_names = [i[&#39;name&#39;] for i in e[&#39;inputs&#39;] if i[&#39;indexed&#39;]]<br>[&#39;from&#39;, &#39;to&#39;]</pre><pre>&gt; indexed_values = [eth_abi.decode_single(t, v) for t, v in zip(indexed_types, log[&#39;topics&#39;][1:])]<br>[&#39;0x00b46c2526e227482e2ebb8f4c69e4674d262e75&#39;,<br> &#39;0x54a2d42a40f51259dedd1978f6c118a0f0eff078&#39;]</pre><pre>&gt; dict(chain(zip(names, values), zip(indexed_names, indexed_values)))<br>{&#39;from&#39;: &#39;0x00b46c2526e227482e2ebb8f4c69e4674d262e75&#39;,<br> &#39;to&#39;: &#39;0x54a2d42a40f51259dedd1978f6c118a0f0eff078&#39;,<br> &#39;value&#39;: 5000000000}</pre><p>Note that Ethereum always uses integers to represent numeric values, so we got the value in the minimal denomination. Move the decimal point left by <em>decimals</em> found in the contract, 3 in this case. The message reads:</p><blockquote>“Transfer from 0x00b4… to 0x54a2… of 5,000,000 tokens”.</blockquote><h4>Querying</h4><p>Now you are ready to do some search. You can query the blockchain using <a href="https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt">json-rpc api</a> which is provided by full nodes like <em>geth</em> or <em>Parity</em> or a service like <em>Infura</em>. Bloom filters allow you to scan the entire blockchain in seconds and find logs matching the specific topic.</p><p>You can also specify search range with <em>fromBlock</em> and <em>toBlock</em>, limit the search to specific contract <em>address</em> and omit some of the topics with <em>null </em>which works like a wildcard. The full specification <a href="https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter">can be found here</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9x6kZAuo5_oS9oiYcc-s7w.png" /></figure><p>Of course, everything described here is already implemented in <em>web3</em> which allows you to conveniently query the events by name and decodes logs data automatically, but if you care about performance like I do, this approach could save you a lot of time.</p><p>If you learned something new, clap and subscribe to <a href="https://t.me/crypto_eli5">crypto eli5</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a8d2047c7371" width="1" height="1" alt=""><hr><p><a href="https://codeburst.io/deep-dive-into-ethereum-logs-a8d2047c7371">Deep dive into Ethereum logs</a> was originally published in <a href="https://codeburst.io/">codeburst</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>